sequel 3.12.1 → 3.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +42 -0
- data/README.rdoc +137 -118
- data/Rakefile +21 -66
- data/doc/active_record.rdoc +9 -9
- data/doc/advanced_associations.rdoc +59 -188
- data/doc/association_basics.rdoc +15 -2
- data/doc/cheat_sheet.rdoc +38 -33
- data/doc/dataset_filtering.rdoc +16 -7
- data/doc/prepared_statements.rdoc +7 -7
- data/doc/querying.rdoc +5 -4
- data/doc/release_notes/3.13.0.txt +210 -0
- data/doc/sharding.rdoc +1 -1
- data/doc/sql.rdoc +5 -5
- data/doc/validations.rdoc +11 -11
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/do.rb +3 -3
- data/lib/sequel/adapters/firebird.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +39 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
- data/lib/sequel/adapters/mysql.rb +7 -4
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +10 -1
- data/lib/sequel/adapters/shared/mysql.rb +63 -0
- data/lib/sequel/adapters/shared/postgres.rb +61 -3
- data/lib/sequel/adapters/sqlite.rb +105 -18
- data/lib/sequel/connection_pool.rb +31 -30
- data/lib/sequel/core.rb +58 -58
- data/lib/sequel/core_sql.rb +52 -43
- data/lib/sequel/database/misc.rb +11 -0
- data/lib/sequel/database/query.rb +55 -17
- data/lib/sequel/dataset/actions.rb +2 -1
- data/lib/sequel/dataset/query.rb +2 -3
- data/lib/sequel/dataset/sql.rb +24 -11
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/metaprogramming.rb +4 -0
- data/lib/sequel/model.rb +37 -19
- data/lib/sequel/model/associations.rb +33 -25
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/model/plugins.rb +7 -2
- data/lib/sequel/plugins/active_model.rb +1 -1
- data/lib/sequel/plugins/association_pks.rb +2 -2
- data/lib/sequel/plugins/association_proxies.rb +1 -1
- data/lib/sequel/plugins/boolean_readers.rb +2 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
- data/lib/sequel/plugins/identity_map.rb +3 -3
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +212 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/list.rb +174 -0
- data/lib/sequel/plugins/many_through_many.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +6 -7
- data/lib/sequel/plugins/tree.rb +118 -0
- data/lib/sequel/plugins/xml_serializer.rb +321 -0
- data/lib/sequel/sql.rb +315 -206
- data/lib/sequel/timezones.rb +40 -17
- data/lib/sequel/version.rb +8 -2
- data/spec/adapters/firebird_spec.rb +2 -2
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +2 -2
- data/spec/adapters/mysql_spec.rb +2 -2
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/adapters/postgres_spec.rb +36 -6
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +3 -3
- data/spec/core/core_sql_spec.rb +31 -13
- data/spec/core/database_spec.rb +39 -2
- data/spec/core/dataset_spec.rb +24 -12
- data/spec/core/expression_filters_spec.rb +5 -1
- data/spec/core/object_graph_spec.rb +1 -1
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -1
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core/version_spec.rb +1 -1
- data/spec/extensions/active_model_spec.rb +82 -67
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_pks_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/blank_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/caching_spec.rb +1 -1
- data/spec/extensions/class_table_inheritance_spec.rb +3 -2
- data/spec/extensions/composition_spec.rb +2 -5
- data/spec/extensions/force_encoding_spec.rb +3 -1
- data/spec/extensions/hook_class_methods_spec.rb +1 -1
- data/spec/extensions/identity_map_spec.rb +1 -1
- data/spec/extensions/inflector_spec.rb +1 -1
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/instance_hooks_spec.rb +1 -1
- data/spec/extensions/json_serializer_spec.rb +154 -0
- data/spec/extensions/lazy_attributes_spec.rb +1 -2
- data/spec/extensions/list_spec.rb +251 -0
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -3
- data/spec/extensions/migration_spec.rb +1 -1
- data/spec/extensions/named_timezones_spec.rb +5 -6
- data/spec/extensions/nested_attributes_spec.rb +1 -1
- data/spec/extensions/optimistic_locking_spec.rb +1 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +3 -2
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +6 -2
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +7 -3
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_date_time_spec.rb +1 -1
- data/spec/extensions/string_stripper_spec.rb +1 -1
- data/spec/extensions/subclasses_spec.rb +1 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
- data/spec/extensions/thread_local_timezones_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +119 -0
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/update_primary_key_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +1 -1
- data/spec/extensions/validation_helpers_spec.rb +1 -1
- data/spec/extensions/xml_serializer_spec.rb +142 -0
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +29 -14
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +1 -1
- data/spec/integration/plugin_test.rb +316 -1
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +8 -8
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +35 -20
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +49 -34
- data/spec/model/base_spec.rb +1 -1
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +1 -1
- data/spec/model/hooks_spec.rb +1 -1
- data/spec/model/inflector_spec.rb +1 -1
- data/spec/model/model_spec.rb +7 -1
- data/spec/model/plugins_spec.rb +1 -1
- data/spec/model/record_spec.rb +1 -3
- data/spec/model/spec_helper.rb +2 -2
- data/spec/model/validations_spec.rb +1 -1
- metadata +29 -5
data/lib/sequel/core_sql.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# Sequel extends
|
1
|
+
# Sequel extends +Array+ to add methods to implement the SQL DSL.
|
2
2
|
# Most of these methods require that the array not be empty and that it
|
3
3
|
# must consist solely of other arrays that have exactly two elements.
|
4
4
|
class Array
|
5
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, not matching all of the
|
5
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, not matching all of the
|
6
6
|
# conditions.
|
7
7
|
#
|
8
8
|
# ~[[:a, true]] # SQL: a IS NOT TRUE
|
@@ -11,8 +11,8 @@ class Array
|
|
11
11
|
sql_expr_if_all_two_pairs(:OR, true)
|
12
12
|
end
|
13
13
|
|
14
|
-
#
|
15
|
-
# arrays of size 2, false otherwise. This is used to determine if the array
|
14
|
+
# +true+ if the array is not empty and all of its elements are
|
15
|
+
# arrays of size 2, +false+ otherwise. This is used to determine if the array
|
16
16
|
# could be a specifier of conditions, used similarly to a hash
|
17
17
|
# but allowing for duplicate keys and a specific order.
|
18
18
|
#
|
@@ -24,28 +24,37 @@ class Array
|
|
24
24
|
!empty? && all?{|i| (Array === i) && (i.length == 2)}
|
25
25
|
end
|
26
26
|
|
27
|
-
# Return a Sequel::SQL::CaseExpression with this array as the conditions and the given
|
27
|
+
# Return a <tt>Sequel::SQL::CaseExpression</tt> with this array as the conditions and the given
|
28
28
|
# default value and expression.
|
29
29
|
#
|
30
30
|
# [[{:a=>[2,3]}, 1]].case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
|
31
31
|
# [[:a, 1], [:b, 2]].case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
|
32
|
-
def case(
|
33
|
-
::Sequel::SQL::CaseExpression.new(self,
|
32
|
+
def case(*args)
|
33
|
+
::Sequel::SQL::CaseExpression.new(self, *args)
|
34
34
|
end
|
35
35
|
|
36
|
-
# Return a Sequel::SQL::
|
37
|
-
# all two
|
38
|
-
# conditions.
|
36
|
+
# Return a <tt>Sequel::SQL::ValueList</tt> created from this array. Used if this array contains
|
37
|
+
# all two element arrays and you want it treated as an SQL value list (IN predicate)
|
38
|
+
# instead of as a conditions specifier (similar to a hash). This is not necessary if you are using
|
39
|
+
# this array as a value in a filter, but may be necessary if you are using it as a
|
40
|
+
# value with placeholder SQL:
|
39
41
|
#
|
40
|
-
# [[1, 2], [3, 4]] # SQL: 1
|
41
|
-
# [[1, 2], [3, 4]]
|
42
|
-
|
43
|
-
|
42
|
+
# DB[:a].filter([:a, :b]=>[[1, 2], [3, 4]]) # SQL: (a, b) IN ((1, 2), (3, 4))
|
43
|
+
# DB[:a].filter('(a, b) IN ?', [[1, 2], [3, 4]]) # SQL: (a, b) IN ((1 = 2) AND (3 = 4))
|
44
|
+
# DB[:a].filter('(a, b) IN ?', [[1, 2], [3, 4]].sql_value_list) # SQL: (a, b) IN ((1, 2), (3, 4))
|
45
|
+
def sql_value_list
|
46
|
+
::Sequel::SQL::ValueList.new(self)
|
44
47
|
end
|
48
|
+
|
49
|
+
# Deprecated alias for sql_value_list
|
50
|
+
alias sql_array sql_value_list
|
45
51
|
|
46
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, matching all of the
|
52
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching all of the
|
47
53
|
# conditions. Rarely do you need to call this explicitly, as Sequel generally
|
48
|
-
# assumes that arrays of
|
54
|
+
# assumes that arrays of two element arrays specify this type of condition. One case where
|
55
|
+
# it can be necessary to use this is if you are using the object as a value in a filter hash
|
56
|
+
# and want to use the = operator instead of the IN operator (which is used by default for
|
57
|
+
# arrays of two element arrays).
|
49
58
|
#
|
50
59
|
# [[:a, true]].sql_expr # SQL: a IS TRUE
|
51
60
|
# [[:a, 1], [:b, [2, 3]]].sql_expr # SQL: a = 1 AND b IN (2, 3)
|
@@ -53,7 +62,7 @@ class Array
|
|
53
62
|
sql_expr_if_all_two_pairs
|
54
63
|
end
|
55
64
|
|
56
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, matching none
|
65
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching none
|
57
66
|
# of the conditions.
|
58
67
|
#
|
59
68
|
# [[:a, true]].sql_negate # SQL: a IS NOT TRUE
|
@@ -62,7 +71,7 @@ class Array
|
|
62
71
|
sql_expr_if_all_two_pairs(:AND, true)
|
63
72
|
end
|
64
73
|
|
65
|
-
# Return a Sequel::SQL::BooleanExpression created from this array, matching any of the
|
74
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this array, matching any of the
|
66
75
|
# conditions.
|
67
76
|
#
|
68
77
|
# [[:a, true]].sql_or # SQL: a IS TRUE
|
@@ -71,10 +80,10 @@ class Array
|
|
71
80
|
sql_expr_if_all_two_pairs(:OR)
|
72
81
|
end
|
73
82
|
|
74
|
-
# Return a Sequel::SQL::BooleanExpression representing an SQL string made up of the
|
83
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> representing an SQL string made up of the
|
75
84
|
# concatenation of this array's elements. If an argument is passed
|
76
85
|
# it is used in between each element of the array in the SQL
|
77
|
-
# concatenation.
|
86
|
+
# concatenation.
|
78
87
|
#
|
79
88
|
# [:a].sql_string_join # SQL: a
|
80
89
|
# [:a, :b].sql_string_join # SQL: a || b
|
@@ -93,16 +102,16 @@ class Array
|
|
93
102
|
|
94
103
|
private
|
95
104
|
|
96
|
-
# Raise an error if this array is not made up
|
105
|
+
# Raise an error if this array is not made up all two element arrays, otherwise create a <tt>Sequel::SQL::BooleanExpression</tt> from this array.
|
97
106
|
def sql_expr_if_all_two_pairs(*args)
|
98
107
|
raise(Sequel::Error, 'Not all elements of the array are arrays of size 2, so it cannot be converted to an SQL expression') unless all_two_pairs?
|
99
108
|
::Sequel::SQL::BooleanExpression.from_value_pairs(self, *args)
|
100
109
|
end
|
101
110
|
end
|
102
111
|
|
103
|
-
# Sequel extends
|
112
|
+
# Sequel extends +Hash+ to add methods to implement the SQL DSL.
|
104
113
|
class Hash
|
105
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching
|
114
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching
|
106
115
|
# all of the conditions in this hash and the condition specified by
|
107
116
|
# the given argument.
|
108
117
|
#
|
@@ -112,7 +121,7 @@ class Hash
|
|
112
121
|
::Sequel::SQL::BooleanExpression.new(:AND, self, ce)
|
113
122
|
end
|
114
123
|
|
115
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching
|
124
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching
|
116
125
|
# all of the conditions in this hash or the condition specified by
|
117
126
|
# the given argument.
|
118
127
|
#
|
@@ -122,7 +131,7 @@ class Hash
|
|
122
131
|
::Sequel::SQL::BooleanExpression.new(:OR, self, ce)
|
123
132
|
end
|
124
133
|
|
125
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, not matching all of the
|
134
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, not matching all of the
|
126
135
|
# conditions.
|
127
136
|
#
|
128
137
|
# ~{:a=>true} # SQL: a IS NOT TRUE
|
@@ -131,18 +140,18 @@ class Hash
|
|
131
140
|
::Sequel::SQL::BooleanExpression.from_value_pairs(self, :OR, true)
|
132
141
|
end
|
133
142
|
|
134
|
-
# Return a Sequel::SQL::CaseExpression with this hash as the conditions and the given
|
135
|
-
# default value. Note that the order of the conditions will be arbitrary, so all
|
143
|
+
# Return a <tt>Sequel::SQL::CaseExpression</tt> with this hash as the conditions and the given
|
144
|
+
# default value. Note that the order of the conditions will be arbitrary on ruby 1.8, so all
|
136
145
|
# conditions should be orthogonal.
|
137
146
|
#
|
138
147
|
# {{:a=>[2,3]}=>1}.case(0) # SQL: CASE WHEN a IN (2, 3) THEN 1 ELSE 0 END
|
139
|
-
# {:a=>1,
|
148
|
+
# {:a=>1, :b=>2}.case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
|
140
149
|
# # or: CASE c WHEN b THEN 2 WHEN a THEN 1 ELSE d END
|
141
|
-
def case(
|
142
|
-
::Sequel::SQL::CaseExpression.new(to_a,
|
150
|
+
def case(*args)
|
151
|
+
::Sequel::SQL::CaseExpression.new(to_a, *args)
|
143
152
|
end
|
144
153
|
|
145
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching all of the
|
154
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching all of the
|
146
155
|
# conditions. Rarely do you need to call this explicitly, as Sequel generally
|
147
156
|
# assumes that hashes specify this type of condition.
|
148
157
|
#
|
@@ -152,7 +161,7 @@ class Hash
|
|
152
161
|
::Sequel::SQL::BooleanExpression.from_value_pairs(self)
|
153
162
|
end
|
154
163
|
|
155
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching none
|
164
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching none
|
156
165
|
# of the conditions.
|
157
166
|
#
|
158
167
|
# {:a=>true}.sql_negate # SQL: a IS NOT TRUE
|
@@ -161,7 +170,7 @@ class Hash
|
|
161
170
|
::Sequel::SQL::BooleanExpression.from_value_pairs(self, :AND, true)
|
162
171
|
end
|
163
172
|
|
164
|
-
# Return a Sequel::SQL::BooleanExpression created from this hash, matching any of the
|
173
|
+
# Return a <tt>Sequel::SQL::BooleanExpression</tt> created from this hash, matching any of the
|
165
174
|
# conditions.
|
166
175
|
#
|
167
176
|
# {:a=>true}.sql_or # SQL: a IS TRUE
|
@@ -171,12 +180,12 @@ class Hash
|
|
171
180
|
end
|
172
181
|
end
|
173
182
|
|
174
|
-
# Sequel extends
|
183
|
+
# Sequel extends +String+ to add methods to implement the SQL DSL.
|
175
184
|
class String
|
176
185
|
include Sequel::SQL::AliasMethods
|
177
186
|
include Sequel::SQL::CastMethods
|
178
187
|
|
179
|
-
# Converts a string into a Sequel::LiteralString
|
188
|
+
# Converts a string into a <tt>Sequel::LiteralString</tt>, in order to override string
|
180
189
|
# literalization, e.g.:
|
181
190
|
#
|
182
191
|
# DB[:items].filter(:abc => 'def').sql #=>
|
@@ -185,7 +194,7 @@ class String
|
|
185
194
|
# DB[:items].filter(:abc => 'def'.lit).sql #=>
|
186
195
|
# "SELECT * FROM items WHERE (abc = def)"
|
187
196
|
#
|
188
|
-
# You can also provide arguments, to create a Sequel::SQL::PlaceholderLiteralString
|
197
|
+
# You can also provide arguments, to create a <tt>Sequel::SQL::PlaceholderLiteralString</tt>:
|
189
198
|
#
|
190
199
|
# DB[:items].select{|o| o.count('DISTINCT ?'.lit(:a))}.sql #=>
|
191
200
|
# "SELECT count(DISTINCT a) FROM items"
|
@@ -193,14 +202,14 @@ class String
|
|
193
202
|
args.empty? ? Sequel::LiteralString.new(self) : Sequel::SQL::PlaceholderLiteralString.new(self, args)
|
194
203
|
end
|
195
204
|
|
196
|
-
# Returns a Sequel::SQL::Blob that holds the same data as this string. Blobs provide proper
|
205
|
+
# Returns a <tt>Sequel::SQL::Blob</tt> that holds the same data as this string. Blobs provide proper
|
197
206
|
# escaping of binary data.
|
198
207
|
def to_sequel_blob
|
199
208
|
::Sequel::SQL::Blob.new(self)
|
200
209
|
end
|
201
210
|
end
|
202
211
|
|
203
|
-
# Sequel extends
|
212
|
+
# Sequel extends +Symbol+ to add methods to implement the SQL DSL.
|
204
213
|
class Symbol
|
205
214
|
include Sequel::SQL::QualifyingMethods
|
206
215
|
include Sequel::SQL::IdentifierMethods
|
@@ -214,9 +223,9 @@ class Symbol
|
|
214
223
|
include Sequel::SQL::ComplexExpressionMethods
|
215
224
|
include Sequel::SQL::InequalityMethods if RUBY_VERSION < '1.9.0'
|
216
225
|
|
217
|
-
# If no argument is given, returns a Sequel::SQL::ColumnAll object specifying all
|
226
|
+
# If no argument is given, returns a <tt>Sequel::SQL::ColumnAll</tt> object specifying all
|
218
227
|
# columns for this table.
|
219
|
-
# If an argument is given, returns a Sequel::SQL::NumericExpression using the *
|
228
|
+
# If an argument is given, returns a <tt>Sequel::SQL::NumericExpression</tt> using the *
|
220
229
|
# (multiplication) operator with this and the given argument.
|
221
230
|
#
|
222
231
|
# :table.* # SQL: table.*
|
@@ -226,9 +235,9 @@ class Symbol
|
|
226
235
|
Sequel::SQL::ColumnAll.new(self);
|
227
236
|
end
|
228
237
|
|
229
|
-
# Returns a Sequel::SQL::Function
|
230
|
-
# and the given arguments. This is aliased as Symbol#[] if the RUBY_VERSION
|
231
|
-
# is less than 1.9.0. Ruby 1.9 defines Symbol#[]
|
238
|
+
# Returns a <tt>Sequel::SQL::Function</tt> with this as the function name,
|
239
|
+
# and the given arguments. This is aliased as <tt>Symbol#[]</tt> if the RUBY_VERSION
|
240
|
+
# is less than 1.9.0. Ruby 1.9 defines <tt>Symbol#[]</tt>, and Sequel
|
232
241
|
# doesn't override methods defined by ruby itself.
|
233
242
|
#
|
234
243
|
# :now.sql_function # SQL: now()
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -79,11 +79,22 @@ module Sequel
|
|
79
79
|
{:primary_key => true, :type => Integer, :auto_increment => true}
|
80
80
|
end
|
81
81
|
|
82
|
+
# Whether the database and adapter support prepared transactions
|
83
|
+
# (two-phase commit), false by default
|
84
|
+
def supports_prepared_transactions?
|
85
|
+
false
|
86
|
+
end
|
87
|
+
|
82
88
|
# Whether the database and adapter support savepoints, false by default
|
83
89
|
def supports_savepoints?
|
84
90
|
false
|
85
91
|
end
|
86
92
|
|
93
|
+
# Whether the database and adapter support transaction isolation levels, false by default
|
94
|
+
def supports_transaction_isolation_levels?
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
87
98
|
# Typecast the value to the given column_type. Calls
|
88
99
|
# typecast_value_#{column_type} if the method exists,
|
89
100
|
# otherwise returns the value.
|
@@ -15,6 +15,11 @@ module Sequel
|
|
15
15
|
TRANSACTION_BEGIN = 'Transaction.begin'.freeze
|
16
16
|
TRANSACTION_COMMIT = 'Transaction.commit'.freeze
|
17
17
|
TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
|
18
|
+
|
19
|
+
TRANSACTION_ISOLATION_LEVELS = {:uncommitted=>'READ UNCOMMITTED'.freeze,
|
20
|
+
:committed=>'READ COMMITTED'.freeze,
|
21
|
+
:repeatable=>'REPEATABLE READ'.freeze,
|
22
|
+
:serializable=>'SERIALIZABLE'.freeze}
|
18
23
|
|
19
24
|
POSTGRES_DEFAULT_RE = /\A(?:B?('.*')::[^']+|\((-?\d+(?:\.\d+)?)\))\z/
|
20
25
|
MSSQL_DEFAULT_RE = /\A(?:\(N?('.*')\)|\(\((-?\d+(?:\.\d+)?)\)\))\z/
|
@@ -24,6 +29,13 @@ module Sequel
|
|
24
29
|
# The prepared statement objects for this database, keyed by name
|
25
30
|
attr_reader :prepared_statements
|
26
31
|
|
32
|
+
# The default transaction isolation level for this database,
|
33
|
+
# used for all future transactions. For MSSQL, this should be set
|
34
|
+
# to something if you ever plan to use the :isolation option to
|
35
|
+
# Database#transaction, as on MSSQL if affects all future transactions
|
36
|
+
# on the same connection.
|
37
|
+
attr_accessor :transaction_isolation_level
|
38
|
+
|
27
39
|
# Runs the supplied SQL statement string on the database server.
|
28
40
|
# Alias for run.
|
29
41
|
def <<(sql)
|
@@ -142,15 +154,20 @@ module Sequel
|
|
142
154
|
#
|
143
155
|
# The following options are respected:
|
144
156
|
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
157
|
+
# :isolation :: The transaction isolation level to use for this transaction,
|
158
|
+
# should be :uncommitted, :committed, :repeatable, or :serializable.
|
159
|
+
# :prepare :: A string to use as the transaction identifier for a
|
160
|
+
# prepared transaction (two-phase commit), if the database/adapter
|
161
|
+
# supports prepared transactions
|
162
|
+
# :server :: The server to use for the transaction
|
163
|
+
# :savepoint :: Whether to create a new savepoint for this transaction,
|
164
|
+
# only respected if the database adapter supports savepoints. By
|
165
|
+
# default Sequel will reuse an existing transaction, so if you want to
|
166
|
+
# use a savepoint you must use this option.
|
150
167
|
def transaction(opts={}, &block)
|
151
168
|
synchronize(opts[:server]) do |conn|
|
152
169
|
return yield(conn) if already_in_transaction?(conn, opts)
|
153
|
-
_transaction(conn, &block)
|
170
|
+
_transaction(conn, opts, &block)
|
154
171
|
end
|
155
172
|
end
|
156
173
|
|
@@ -160,17 +177,17 @@ module Sequel
|
|
160
177
|
# block will cause the transaction to be rolled back. If the exception is
|
161
178
|
# not Sequel::Rollback, the error will be reraised. If no exception occurs
|
162
179
|
# inside the block, the transaction is commited.
|
163
|
-
def _transaction(conn)
|
180
|
+
def _transaction(conn, opts={})
|
164
181
|
begin
|
165
182
|
add_transaction
|
166
|
-
t = begin_transaction(conn)
|
183
|
+
t = begin_transaction(conn, opts)
|
167
184
|
yield(conn)
|
168
185
|
rescue Exception => e
|
169
|
-
rollback_transaction(t) if t
|
186
|
+
rollback_transaction(t, opts) if t
|
170
187
|
transaction_error(e)
|
171
188
|
ensure
|
172
189
|
begin
|
173
|
-
commit_transaction(t) unless e
|
190
|
+
commit_transaction(t, opts) unless e
|
174
191
|
rescue Exception => e
|
175
192
|
raise_error(e, :classes=>database_error_classes)
|
176
193
|
ensure
|
@@ -202,15 +219,24 @@ module Sequel
|
|
202
219
|
SQL_SAVEPOINT % depth
|
203
220
|
end
|
204
221
|
|
205
|
-
# Start a new database
|
206
|
-
def
|
222
|
+
# Start a new database connection on the given connection
|
223
|
+
def begin_new_transaction(conn, opts)
|
224
|
+
log_connection_execute(conn, begin_transaction_sql)
|
225
|
+
set_transaction_isolation(conn, opts)
|
226
|
+
end
|
227
|
+
|
228
|
+
# Start a new database transaction or a new savepoint on the given connection.
|
229
|
+
def begin_transaction(conn, opts={})
|
207
230
|
if supports_savepoints?
|
208
231
|
th = Thread.current
|
209
|
-
depth = th[:sequel_transaction_depth]
|
210
|
-
|
232
|
+
if (depth = th[:sequel_transaction_depth]) > 0
|
233
|
+
log_connection_execute(conn, begin_savepoint_sql(depth))
|
234
|
+
else
|
235
|
+
begin_new_transaction(conn, opts)
|
236
|
+
end
|
211
237
|
th[:sequel_transaction_depth] += 1
|
212
238
|
else
|
213
|
-
|
239
|
+
begin_new_transaction(conn, opts)
|
214
240
|
end
|
215
241
|
conn
|
216
242
|
end
|
@@ -276,7 +302,7 @@ module Sequel
|
|
276
302
|
end
|
277
303
|
|
278
304
|
# Commit the active transaction on the connection
|
279
|
-
def commit_transaction(conn)
|
305
|
+
def commit_transaction(conn, opts={})
|
280
306
|
if supports_savepoints?
|
281
307
|
depth = Thread.current[:sequel_transaction_depth]
|
282
308
|
log_connection_execute(conn, depth > 1 ? commit_savepoint_sql(depth-1) : commit_transaction_sql)
|
@@ -343,7 +369,7 @@ module Sequel
|
|
343
369
|
end
|
344
370
|
|
345
371
|
# Rollback the active transaction on the connection
|
346
|
-
def rollback_transaction(conn)
|
372
|
+
def rollback_transaction(conn, opts={})
|
347
373
|
if supports_savepoints?
|
348
374
|
depth = Thread.current[:sequel_transaction_depth]
|
349
375
|
log_connection_execute(conn, depth > 1 ? rollback_savepoint_sql(depth-1) : rollback_transaction_sql)
|
@@ -382,6 +408,18 @@ module Sequel
|
|
382
408
|
end
|
383
409
|
end
|
384
410
|
|
411
|
+
# Set the transaction isolation level on the given connection
|
412
|
+
def set_transaction_isolation(conn, opts)
|
413
|
+
if supports_transaction_isolation_levels? and level = opts.fetch(:isolation, transaction_isolation_level)
|
414
|
+
log_connection_execute(conn, set_transaction_isolation_sql(level))
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
# SQL to set the transaction isolation level
|
419
|
+
def set_transaction_isolation_sql(level)
|
420
|
+
"SET TRANSACTION ISOLATION LEVEL #{TRANSACTION_ISOLATION_LEVELS[level]}"
|
421
|
+
end
|
422
|
+
|
385
423
|
# Raise a database error unless the exception is an Rollback.
|
386
424
|
def transaction_error(e)
|
387
425
|
raise_error(e, :classes=>database_error_classes) unless e.is_a?(Rollback)
|
@@ -90,7 +90,8 @@ module Sequel
|
|
90
90
|
#
|
91
91
|
# Note that this method is not safe to use on many adapters if you are
|
92
92
|
# running additional queries inside the provided block. If you are
|
93
|
-
# running queries inside the block, you use
|
93
|
+
# running queries inside the block, you should use +all+ instead of +each+
|
94
|
+
# for the outer queries, or use a separate thread or shard inside +each+.
|
94
95
|
def each(&block)
|
95
96
|
if @opts[:graph]
|
96
97
|
graph_each(&block)
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -226,9 +226,8 @@ module Sequel
|
|
226
226
|
group(*columns)
|
227
227
|
end
|
228
228
|
|
229
|
-
# Returns a dataset grouped by the given column with count by group
|
230
|
-
#
|
231
|
-
# be included in the select clause.
|
229
|
+
# Returns a dataset grouped by the given column with count by group.
|
230
|
+
# Column aliases may be supplied, and will be included in the select clause.
|
232
231
|
#
|
233
232
|
# Examples:
|
234
233
|
#
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -228,7 +228,7 @@ module Sequel
|
|
228
228
|
# SQL fragment for specifying given CaseExpression.
|
229
229
|
def case_expression_sql(ce)
|
230
230
|
sql = '(CASE '
|
231
|
-
sql << "#{literal(ce.expression)} " if ce.expression
|
231
|
+
sql << "#{literal(ce.expression)} " if ce.expression?
|
232
232
|
ce.conditions.collect{ |c,r|
|
233
233
|
sql << "WHEN #{literal(c)} THEN #{literal(r)} "
|
234
234
|
}
|
@@ -261,10 +261,10 @@ module Sequel
|
|
261
261
|
when :IN, :"NOT IN"
|
262
262
|
cols = args.at(0)
|
263
263
|
vals = args.at(1)
|
264
|
-
col_array = true if cols.is_a?(Array)
|
265
|
-
if vals.is_a?(Array)
|
264
|
+
col_array = true if cols.is_a?(Array)
|
265
|
+
if vals.is_a?(Array)
|
266
266
|
val_array = true
|
267
|
-
empty_val_array = vals
|
267
|
+
empty_val_array = vals == []
|
268
268
|
end
|
269
269
|
if col_array
|
270
270
|
if empty_val_array
|
@@ -284,7 +284,10 @@ module Sequel
|
|
284
284
|
complex_expression_sql(op, [cols, vals.map!{|x| x.values_at(*val_cols)}])
|
285
285
|
end
|
286
286
|
else
|
287
|
-
|
287
|
+
# If the columns and values are both arrays, use array_sql instead of
|
288
|
+
# literal so that if values is an array of two element arrays, it
|
289
|
+
# will be treated as a value list instead of a condition specifier.
|
290
|
+
"(#{literal(cols)} #{op} #{val_array ? array_sql(vals) : literal(vals)})"
|
288
291
|
end
|
289
292
|
else
|
290
293
|
if empty_val_array
|
@@ -350,7 +353,15 @@ module Sequel
|
|
350
353
|
# SQL fragment for the ordered expression, used in the ORDER BY
|
351
354
|
# clause.
|
352
355
|
def ordered_expression_sql(oe)
|
353
|
-
"#{literal(oe.expression)} #{oe.descending ? 'DESC' : 'ASC'}"
|
356
|
+
s = "#{literal(oe.expression)} #{oe.descending ? 'DESC' : 'ASC'}"
|
357
|
+
case oe.nulls
|
358
|
+
when :first
|
359
|
+
"#{s} NULLS FIRST"
|
360
|
+
when :last
|
361
|
+
"#{s} NULLS LAST"
|
362
|
+
else
|
363
|
+
s
|
364
|
+
end
|
354
365
|
end
|
355
366
|
|
356
367
|
# SQL fragment for a literal string with placeholders
|
@@ -434,8 +445,10 @@ module Sequel
|
|
434
445
|
"ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"
|
435
446
|
when :rows
|
436
447
|
"ROWS UNBOUNDED PRECEDING"
|
448
|
+
when String
|
449
|
+
opts[:frame]
|
437
450
|
else
|
438
|
-
raise Error, "invalid window frame clause, should be :all, :rows, or nil"
|
451
|
+
raise Error, "invalid window frame clause, should be :all, :rows, a string, or nil"
|
439
452
|
end
|
440
453
|
"(#{[window, partition, order, frame].compact.join(' ')})"
|
441
454
|
end
|
@@ -796,19 +809,19 @@ module Sequel
|
|
796
809
|
when SQL::Identifier
|
797
810
|
SQL::QualifiedIdentifier.new(table, e)
|
798
811
|
when SQL::OrderedExpression
|
799
|
-
SQL::OrderedExpression.new(qualified_expression(e.expression, table), e.descending)
|
812
|
+
SQL::OrderedExpression.new(qualified_expression(e.expression, table), e.descending, :nulls=>e.nulls)
|
800
813
|
when SQL::AliasedExpression
|
801
814
|
SQL::AliasedExpression.new(qualified_expression(e.expression, table), e.aliaz)
|
802
815
|
when SQL::CaseExpression
|
803
|
-
|
816
|
+
args = [qualified_expression(e.conditions, table), qualified_expression(e.default, table)]
|
817
|
+
args << qualified_expression(e.expression, table) if e.expression?
|
818
|
+
SQL::CaseExpression.new(*args)
|
804
819
|
when SQL::Cast
|
805
820
|
SQL::Cast.new(qualified_expression(e.expr, table), e.type)
|
806
821
|
when SQL::Function
|
807
822
|
SQL::Function.new(e.f, *qualified_expression(e.args, table))
|
808
823
|
when SQL::ComplexExpression
|
809
824
|
SQL::ComplexExpression.new(e.op, *qualified_expression(e.args, table))
|
810
|
-
when SQL::SQLArray
|
811
|
-
SQL::SQLArray.new(qualified_expression(e.array, table))
|
812
825
|
when SQL::Subscript
|
813
826
|
SQL::Subscript.new(qualified_expression(e.f, table), qualified_expression(e.sub, table))
|
814
827
|
when SQL::WindowFunction
|