sequel 3.12.1 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|