1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
= Performance Enhancements
* Dataset::PlaceholderLiteralizer has been added as an optimization
framework. This allows you to record changes to a given dataset
using placeholder arguments, and later quickly execute the query
providing values for the placeholders. This is similar in idea
to prepared statements, except that the SQL for each query can
change depending on the values for the placeholders.
Using this optimization framework, generating the SQL for query
is about 3x faster, and since SQL generation time is a significant
portion of total time for simple queries, simple queries can
execute up to 50% faster.
There are two APIs for this optimization framework. There is a
lower level dataset API:
loader = Sequel::Dataset::PlaceholderLiteralizer.
loader(DB[:items]) do |pl, ds|
ds.where(:id=>pl.arg).exclude(:name=>pl.arg).limit(1)
end
loader.first(1, "foo")
# SELECT * FROM items WHERE ((id = 1) AND (name != 'foo')) LIMIT 1
loader.first([1, 2], %w"foo bar")
# SELECT * FROM items WHERE ((id IN (1, 2)) AND
# (name NOT IN ('foo', 'bar'))) LIMIT 1
There is also a higher level model API (Model.finder):
class Item < Sequel::Model
# Given class method that returns a dataset
def self.by_id_and_not_name(id, not_name)
where(:id=>id).exclude(:name=>not_name)
end
# Create optimized method that returns first value
finder :by_id_and_not_name
end
# Call optimized method
Album.first_by_id_and_not_name(1, 'foo')
# SELECT * FROM items WHERE ((id = 1) AND (name != 'foo')) LIMIT 1
Model.finder defaults to creating a method that returns the first
matching row, but using the :type option you can create methods
that call each, all, or get. There is also an option to choose the
method name (:name), as well as one to specify the number of
arguments to use if the method doesn't take a fixed number
(:arity).
Finally, Model.find, .first, and .first! now automatically use an
optimized finder if given a single argument. Model.[] uses an
optimized finder if given a single hash, and Model.[], .with_pk,
and .with_pk! use an optimized finder if the model has a composite
primary key. In all of these cases, these methods are about 50%
faster than before.
* The pure-ruby PostgreSQL array parser that ships with Sequel has
been replaced with a strscan-based parser. This parser avoids
O(n^2) performance for arrays with multibyte strings, and in general
is much faster. Parsing an array with a single string with 100,000
multibyte characters is about 1000x faster, and now about half the
speed of the C implementation in sequel_pg.
* Dataset#paged_each now has a :strategy=>:filter option that
dramatically improves performance, especially if the columns
being ordered by are indexed.
Unfortunately, there are enough corner cases to this approach
that it cannot be used by default. At the least, the dataset
needs to be selecting the columns it is ordering by, not aliasing
the columns it is ordering by in the SELECT clause, not have
NULLs in any of the columns being ordered by, and not itself use
a limit or offset.
If you are ordering by expressions that are not simple column
values, you can provide a :filter_value option proc that takes the
last retrieved row and array of order by expressions, and returns
an array of values in the last retrieved row for those order by
expressions.
* In the postgres adapter, Dataset#paged_each now automatically uses
a cursor for improved performance.
* In the mysql2 adapter, Dataset#paged_each now automatically uses
streaming for improved performance, if streaming is supported.
* Dataset#with_sql_{each,all,first,single_value,insert,update}
have been added. These methods take specific SQL and execute
it on the database, returning the appropriate value. They
are significantly faster than the previous approach of
with_sql(SQL).{each,all,first,single_value,insert,update},
as they don't require cloning the dataset.
= New Features
* Database#create_join_table! and #create_join_table? have been added,
for consistency with #create_table! and #create_table?.
* A :hold option has been added to Dataset#use_cursor in the postgres
adapter, which uses WITH HOLD in the query, allowing for usage of
the cursor outside the enclosing transaction. When :hold is used,
Sequel does not automatically use a transaction around the cursor
call.
* Dataset#where_current_of has been added to the postgres adapter,
for updating rows based on a cursor's current position. This can
be used to update a large dataset where new values depend on
some ruby method, without keeping all rows in memory.
ds = DB[:huge_table]
ds.use_cursor(:rows_per_fetch=>1).each do |row|
ds.where_current_of.update(:column=>ruby_method(row))
end
* A current_datetime_timestamp extension has been added, for
creating Time/DateTime instances that are literalized as
CURRENT_TIMESTAMP. When the dataset uses this extension, models
that use the touch and timestamps plugins will use
CURRENT_TIMESTAMP for the timestamps.
* The jdbc adapter now supports a :driver option, useful when
Sequel doesn't have direct support for the underlying driver, and
where java.sql.DriverManager.getConnection does not work
correctly due to Java class loading issues.
= Other Improvements
* Multiple corner cases in Dataset#eager_graph have been fixed.
* Calling Dataset#columns when using the eager_each plugin no
longer triggers eager loading.
* Database#column_schema_to_ruby_default is now a public method
in the schema_dumper extension.
* When validating associated objects for one_to_many and one_to_one
associations in the nested_attributes plugin, don't remove column
values if the association's foreign key is the associated model's
primary key.
* On PostgreSQL, Dataset#disable_insert_returning has been added
back. This disables the automatic use of RETURNING for INSERTs
for the dataset. This is necessary in cases where INSERT
RETURNING doesn't work, such as PostgreSQL <8.2 (or PostgreSQL
variants that forked before 8.2), or when using partitioning
with trigger functions, or conditional rules.
Note that if you use disable_insert_returning, insert will not
return the autoincremented primary key. You need to call
currval or lastval manually using the same connection to get
the value, or use nextval to get the value to use before
inserting.
* The pg_array extension now uses the correct database type when
typecasting values for smallint, oid, real, character, and varchar
arrays. Previously, Sequel did not use the correct database type
in some cases (e.g. text[] for a varchar[]), which resulted in
errors if the value was used in a filter expression.
* Additional unique constraint violations are now recognized on
SQLite.
* Check constraint violations are now recognized on SQLite >=3.8.2.
* Adapters that emulate bitwise operators now do so using an append
only design, similar to how all other queries are built in Sequel.
= Backwards Compatibility
* In some cases Sequel no longer adds superfluous parentheses when
constructing SQL strings. If you are testing for specific SQL,
this can cause test failures.
* The pg_array extension no longer recognizes the :typecast_method
option when registering an array type. The option allowed reuse
of an existing typecast method, but as that results in an incorrect
type at the database level, the option was fundementally broken.
* The internals of the PostgreSQL array parser have changed. If you
were relying on them, you'll need to update your code.
* Dataset#complex_expression_arg_pairs private method now returns
nested expression objects instead of an already literalized string
in some cases. Custom adapters that call this method will probably
need to be changed. It's recommended that such adapters switch to
using the new Dataset#complex_expression_emulate_append method if
possible.
|