sequel 3.12.1 → 3.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/CHANGELOG +42 -0
  2. data/README.rdoc +137 -118
  3. data/Rakefile +21 -66
  4. data/doc/active_record.rdoc +9 -9
  5. data/doc/advanced_associations.rdoc +59 -188
  6. data/doc/association_basics.rdoc +15 -2
  7. data/doc/cheat_sheet.rdoc +38 -33
  8. data/doc/dataset_filtering.rdoc +16 -7
  9. data/doc/prepared_statements.rdoc +7 -7
  10. data/doc/querying.rdoc +5 -4
  11. data/doc/release_notes/3.13.0.txt +210 -0
  12. data/doc/sharding.rdoc +1 -1
  13. data/doc/sql.rdoc +5 -5
  14. data/doc/validations.rdoc +11 -11
  15. data/lib/sequel/adapters/ado.rb +1 -1
  16. data/lib/sequel/adapters/do.rb +3 -3
  17. data/lib/sequel/adapters/firebird.rb +3 -3
  18. data/lib/sequel/adapters/jdbc/h2.rb +39 -0
  19. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  20. data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
  21. data/lib/sequel/adapters/mysql.rb +7 -4
  22. data/lib/sequel/adapters/oracle.rb +3 -3
  23. data/lib/sequel/adapters/shared/mssql.rb +10 -1
  24. data/lib/sequel/adapters/shared/mysql.rb +63 -0
  25. data/lib/sequel/adapters/shared/postgres.rb +61 -3
  26. data/lib/sequel/adapters/sqlite.rb +105 -18
  27. data/lib/sequel/connection_pool.rb +31 -30
  28. data/lib/sequel/core.rb +58 -58
  29. data/lib/sequel/core_sql.rb +52 -43
  30. data/lib/sequel/database/misc.rb +11 -0
  31. data/lib/sequel/database/query.rb +55 -17
  32. data/lib/sequel/dataset/actions.rb +2 -1
  33. data/lib/sequel/dataset/query.rb +2 -3
  34. data/lib/sequel/dataset/sql.rb +24 -11
  35. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  36. data/lib/sequel/metaprogramming.rb +4 -0
  37. data/lib/sequel/model.rb +37 -19
  38. data/lib/sequel/model/associations.rb +33 -25
  39. data/lib/sequel/model/base.rb +2 -2
  40. data/lib/sequel/model/plugins.rb +7 -2
  41. data/lib/sequel/plugins/active_model.rb +1 -1
  42. data/lib/sequel/plugins/association_pks.rb +2 -2
  43. data/lib/sequel/plugins/association_proxies.rb +1 -1
  44. data/lib/sequel/plugins/boolean_readers.rb +2 -2
  45. data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
  46. data/lib/sequel/plugins/identity_map.rb +3 -3
  47. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  48. data/lib/sequel/plugins/json_serializer.rb +212 -0
  49. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  50. data/lib/sequel/plugins/list.rb +174 -0
  51. data/lib/sequel/plugins/many_through_many.rb +2 -2
  52. data/lib/sequel/plugins/rcte_tree.rb +6 -7
  53. data/lib/sequel/plugins/tree.rb +118 -0
  54. data/lib/sequel/plugins/xml_serializer.rb +321 -0
  55. data/lib/sequel/sql.rb +315 -206
  56. data/lib/sequel/timezones.rb +40 -17
  57. data/lib/sequel/version.rb +8 -2
  58. data/spec/adapters/firebird_spec.rb +2 -2
  59. data/spec/adapters/informix_spec.rb +1 -1
  60. data/spec/adapters/mssql_spec.rb +2 -2
  61. data/spec/adapters/mysql_spec.rb +2 -2
  62. data/spec/adapters/oracle_spec.rb +1 -1
  63. data/spec/adapters/postgres_spec.rb +36 -6
  64. data/spec/adapters/spec_helper.rb +2 -2
  65. data/spec/adapters/sqlite_spec.rb +1 -1
  66. data/spec/core/connection_pool_spec.rb +3 -3
  67. data/spec/core/core_sql_spec.rb +31 -13
  68. data/spec/core/database_spec.rb +39 -2
  69. data/spec/core/dataset_spec.rb +24 -12
  70. data/spec/core/expression_filters_spec.rb +5 -1
  71. data/spec/core/object_graph_spec.rb +1 -1
  72. data/spec/core/schema_generator_spec.rb +1 -1
  73. data/spec/core/schema_spec.rb +1 -1
  74. data/spec/core/spec_helper.rb +1 -1
  75. data/spec/core/version_spec.rb +1 -1
  76. data/spec/extensions/active_model_spec.rb +82 -67
  77. data/spec/extensions/association_dependencies_spec.rb +1 -1
  78. data/spec/extensions/association_pks_spec.rb +1 -1
  79. data/spec/extensions/association_proxies_spec.rb +1 -1
  80. data/spec/extensions/blank_spec.rb +1 -1
  81. data/spec/extensions/boolean_readers_spec.rb +1 -1
  82. data/spec/extensions/caching_spec.rb +1 -1
  83. data/spec/extensions/class_table_inheritance_spec.rb +3 -2
  84. data/spec/extensions/composition_spec.rb +2 -5
  85. data/spec/extensions/force_encoding_spec.rb +3 -1
  86. data/spec/extensions/hook_class_methods_spec.rb +1 -1
  87. data/spec/extensions/identity_map_spec.rb +1 -1
  88. data/spec/extensions/inflector_spec.rb +1 -1
  89. data/spec/extensions/instance_filters_spec.rb +1 -1
  90. data/spec/extensions/instance_hooks_spec.rb +1 -1
  91. data/spec/extensions/json_serializer_spec.rb +154 -0
  92. data/spec/extensions/lazy_attributes_spec.rb +1 -2
  93. data/spec/extensions/list_spec.rb +251 -0
  94. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  95. data/spec/extensions/many_through_many_spec.rb +3 -3
  96. data/spec/extensions/migration_spec.rb +1 -1
  97. data/spec/extensions/named_timezones_spec.rb +5 -6
  98. data/spec/extensions/nested_attributes_spec.rb +1 -1
  99. data/spec/extensions/optimistic_locking_spec.rb +1 -1
  100. data/spec/extensions/pagination_spec.rb +1 -1
  101. data/spec/extensions/pretty_table_spec.rb +1 -1
  102. data/spec/extensions/query_spec.rb +1 -1
  103. data/spec/extensions/rcte_tree_spec.rb +1 -1
  104. data/spec/extensions/schema_dumper_spec.rb +3 -2
  105. data/spec/extensions/schema_spec.rb +1 -1
  106. data/spec/extensions/serialization_spec.rb +6 -2
  107. data/spec/extensions/sharding_spec.rb +1 -1
  108. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  109. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  110. data/spec/extensions/spec_helper.rb +7 -3
  111. data/spec/extensions/sql_expr_spec.rb +1 -1
  112. data/spec/extensions/string_date_time_spec.rb +1 -1
  113. data/spec/extensions/string_stripper_spec.rb +1 -1
  114. data/spec/extensions/subclasses_spec.rb +1 -1
  115. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  116. data/spec/extensions/thread_local_timezones_spec.rb +1 -1
  117. data/spec/extensions/timestamps_spec.rb +1 -1
  118. data/spec/extensions/touch_spec.rb +1 -1
  119. data/spec/extensions/tree_spec.rb +119 -0
  120. data/spec/extensions/typecast_on_load_spec.rb +1 -1
  121. data/spec/extensions/update_primary_key_spec.rb +1 -1
  122. data/spec/extensions/validation_class_methods_spec.rb +1 -1
  123. data/spec/extensions/validation_helpers_spec.rb +1 -1
  124. data/spec/extensions/xml_serializer_spec.rb +142 -0
  125. data/spec/integration/associations_test.rb +1 -1
  126. data/spec/integration/database_test.rb +1 -1
  127. data/spec/integration/dataset_test.rb +29 -14
  128. data/spec/integration/eager_loader_test.rb +1 -1
  129. data/spec/integration/migrator_test.rb +1 -1
  130. data/spec/integration/model_test.rb +1 -1
  131. data/spec/integration/plugin_test.rb +316 -1
  132. data/spec/integration/prepared_statement_test.rb +1 -1
  133. data/spec/integration/schema_test.rb +8 -8
  134. data/spec/integration/spec_helper.rb +1 -1
  135. data/spec/integration/timezone_test.rb +1 -1
  136. data/spec/integration/transaction_test.rb +35 -20
  137. data/spec/integration/type_test.rb +1 -1
  138. data/spec/model/association_reflection_spec.rb +1 -1
  139. data/spec/model/associations_spec.rb +49 -34
  140. data/spec/model/base_spec.rb +1 -1
  141. data/spec/model/dataset_methods_spec.rb +4 -4
  142. data/spec/model/eager_loading_spec.rb +1 -1
  143. data/spec/model/hooks_spec.rb +1 -1
  144. data/spec/model/inflector_spec.rb +1 -1
  145. data/spec/model/model_spec.rb +7 -1
  146. data/spec/model/plugins_spec.rb +1 -1
  147. data/spec/model/record_spec.rb +1 -3
  148. data/spec/model/spec_helper.rb +2 -2
  149. data/spec/model/validations_spec.rb +1 -1
  150. metadata +29 -5
@@ -1,8 +1,8 @@
1
- # Sequel extends the Array class to add methods to implement the SQL DSL.
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
- # 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
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(default, expression = nil)
33
- ::Sequel::SQL::CaseExpression.new(self, default, expression)
32
+ def case(*args)
33
+ ::Sequel::SQL::CaseExpression.new(self, *args)
34
34
  end
35
35
 
36
- # Return a Sequel::SQL::Array created from this array. Used if this array contains
37
- # all two pairs and you want it treated as an SQL array instead of a ordered hash-like
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 = 2 AND 3 = 4
41
- # [[1, 2], [3, 4]].sql_array # SQL: ((1, 2), (3, 4))
42
- def sql_array
43
- ::Sequel::SQL::SQLArray.new(self)
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 all two pairs specify this type of condition.
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. This does not require that the array be made up of all two pairs.
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 of all two pairs, otherwise create a Sequel::SQL::BooleanExpression from this array.
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 the Hash class to add methods to implement the SQL DSL.
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, {:b=>2}].case(:d, :c) # SQL: CASE c WHEN a THEN 1 WHEN b THEN 2 ELSE d END
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(default, expression = nil)
142
- ::Sequel::SQL::CaseExpression.new(to_a, default, expression)
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 the String class to add methods to implement the SQL DSL.
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, in order to override string
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 the Symbol class to add methods to implement the SQL DSL.
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 with this as the function name,
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#[], and Sequel
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()
@@ -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
- # * :server - The server to use for the transaction
146
- # * :savepoint - Whether to create a new savepoint for this transaction,
147
- # only respected if the database adapter supports savepoints. By
148
- # default Sequel will reuse an existing transaction, so if you want to
149
- # use a savepoint you must use this option.
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 transaction on the given connection.
206
- def begin_transaction(conn)
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
- log_connection_execute(conn, depth > 0 ? begin_savepoint_sql(depth) : begin_transaction_sql)
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
- log_connection_execute(conn, begin_transaction_sql)
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 should all instead of each.
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)
@@ -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
- # order by the count of records. Column aliases may be supplied, and will
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
  #
@@ -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) || cols.is_a?(SQL::SQLArray)
265
- if vals.is_a?(Array) || vals.is_a?(SQL::SQLArray)
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.to_a == []
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
- "(#{literal(cols)} #{op} #{literal(vals)})"
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
- SQL::CaseExpression.new(qualified_expression(e.conditions, table), qualified_expression(e.default, table), qualified_expression(e.expression, table))
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