sequel 3.21.0 → 3.32.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.
Files changed (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  class Database
3
3
  # ---------------------
4
- # :section: Methods that execute queries and/or return results
4
+ # :section: 1 - Methods that execute queries and/or return results
5
5
  # This methods generally execute SQL code on the database server.
6
6
  # ---------------------
7
7
 
@@ -37,9 +37,12 @@ module Sequel
37
37
  attr_accessor :transaction_isolation_level
38
38
 
39
39
  # Runs the supplied SQL statement string on the database server.
40
- # Alias for run.
40
+ # Returns self so it can be safely chained:
41
+ #
42
+ # DB << "UPADTE albums SET artist_id = NULL" << "DROP TABLE artists"
41
43
  def <<(sql)
42
44
  run(sql)
45
+ self
43
46
  end
44
47
 
45
48
  # Call the prepared statement with the given name with the given hash
@@ -82,12 +85,12 @@ module Sequel
82
85
  #
83
86
  # DB.get(1) # SELECT 1
84
87
  # # => 1
85
- # DB.get{version{}} # SELECT server_version()
88
+ # DB.get{server_version{}} # SELECT server_version()
86
89
  def get(*args, &block)
87
90
  dataset.get(*args, &block)
88
91
  end
89
92
 
90
- # Return a hash containing index information. Hash keys are index name symbols.
93
+ # Return a hash containing index information for the table. Hash keys are index name symbols.
91
94
  # Values are subhashes with two keys, :columns and :unique. The value of :columns
92
95
  # is an array of symbols of column names. The value of :unique is true or false
93
96
  # depending on if the index is unique.
@@ -110,17 +113,17 @@ module Sequel
110
113
  nil
111
114
  end
112
115
 
113
- # Parse the schema from the database.
114
116
  # Returns the schema for the given table as an array with all members being arrays of length 2,
115
117
  # the first member being the column name, and the second member being a hash of column information.
118
+ # The table argument can also be a dataset, as long as it only has one table.
116
119
  # Available options are:
117
120
  #
118
121
  # :reload :: Ignore any cached results, and get fresh information from the database.
119
122
  # :schema :: An explicit schema to use. It may also be implicitly provided
120
123
  # via the table name.
121
124
  #
122
- # If schema parsing is supported by the database, the column information should at least contain the
123
- # following columns:
125
+ # If schema parsing is supported by the database, the column information should hash at least contain the
126
+ # following entries:
124
127
  #
125
128
  # :allow_null :: Whether NULL is an allowed value for the column.
126
129
  # :db_type :: The database type for the column, as a database specific string.
@@ -129,7 +132,8 @@ module Sequel
129
132
  # it means that primary key information is unavailable, not that the column
130
133
  # is not a primary key.
131
134
  # :ruby_default :: The database default for the column, as a ruby object. In many cases, complex
132
- # database defaults cannot be parsed into ruby objects.
135
+ # database defaults cannot be parsed into ruby objects, in which case nil will be
136
+ # used as the value.
133
137
  # :type :: A symbol specifying the type, such as :integer or :string.
134
138
  #
135
139
  # Example:
@@ -152,9 +156,20 @@ module Sequel
152
156
  def schema(table, opts={})
153
157
  raise(Error, 'schema parsing is not implemented on this database') unless respond_to?(:schema_parse_table, true)
154
158
 
155
- sch, table_name = schema_and_table(table)
156
- quoted_name = quote_schema_table(table)
157
- opts = opts.merge(:schema=>sch) if sch && !opts.include?(:schema)
159
+ opts = opts.dup
160
+ if table.is_a?(Dataset)
161
+ o = table.opts
162
+ from = o[:from]
163
+ raise(Error, "can only parse the schema for a dataset with a single from table") unless from && from.length == 1 && !o.include?(:join) && !o.include?(:sql)
164
+ tab = table.first_source_table
165
+ sch, table_name = schema_and_table(tab)
166
+ quoted_name = table.literal(tab)
167
+ opts[:dataset] = table
168
+ else
169
+ sch, table_name = schema_and_table(table)
170
+ quoted_name = quote_schema_table(table)
171
+ end
172
+ opts[:schema] = sch if sch && !opts.include?(:schema)
158
173
 
159
174
  @schemas.delete(quoted_name) if opts[:reload]
160
175
  return @schemas[quoted_name] if @schemas[quoted_name]
@@ -169,15 +184,19 @@ module Sequel
169
184
  # to the database.
170
185
  #
171
186
  # DB.table_exists?(:foo) # => false
187
+ # # SELECT NULL FROM foo LIMIT 1
188
+ #
189
+ # Note that since this does a SELECT from the table, it can give false negatives
190
+ # if you don't have permission to SELECT from the table.
172
191
  def table_exists?(name)
173
- begin
174
- from(name).first
175
- true
176
- rescue
177
- false
178
- end
192
+ sch, table_name = schema_and_table(name)
193
+ name = SQL::QualifiedIdentifier.new(sch, table_name) if sch
194
+ _table_exists?(from(name))
195
+ true
196
+ rescue DatabaseError
197
+ false
179
198
  end
180
-
199
+
181
200
  # Return all tables in the database as an array of symbols.
182
201
  #
183
202
  # DB.tables # => [:albums, :artists]
@@ -198,6 +217,9 @@ module Sequel
198
217
  # :prepare :: A string to use as the transaction identifier for a
199
218
  # prepared transaction (two-phase commit), if the database/adapter
200
219
  # supports prepared transactions.
220
+ # :rollback :: Can the set to :reraise to reraise any Sequel::Rollback exceptions
221
+ # raised, or :always to always rollback even if no exceptions occur
222
+ # (useful for testing).
201
223
  # :server :: The server to use for the transaction.
202
224
  # :savepoint :: Whether to create a new savepoint for this transaction,
203
225
  # only respected if the database/adapter supports savepoints. By
@@ -210,47 +232,85 @@ module Sequel
210
232
  end
211
233
  end
212
234
 
235
+ # Return all views in the database as an array of symbols.
236
+ #
237
+ # DB.views # => [:gold_albums, :artists_with_many_albums]
238
+ def views(opts={})
239
+ raise NotImplemented, "#views should be overridden by adapters"
240
+ end
241
+
213
242
  private
214
243
 
244
+ # Should raise an error if the table doesn't not exist,
245
+ # and not raise an error if the table does exist.
246
+ def _table_exists?(ds)
247
+ ds.get(Sequel::NULL)
248
+ end
249
+
215
250
  # Internal generic transaction method. Any exception raised by the given
216
251
  # block will cause the transaction to be rolled back. If the exception is
217
252
  # not a Sequel::Rollback, the error will be reraised. If no exception occurs
218
253
  # inside the block, the transaction is commited.
219
254
  def _transaction(conn, opts={})
255
+ rollback = opts[:rollback]
220
256
  begin
221
- add_transaction
222
- t = begin_transaction(conn, opts)
223
- yield(conn)
257
+ add_transaction(conn, opts)
258
+ begin_transaction(conn, opts)
259
+ if rollback == :always
260
+ begin
261
+ yield(conn)
262
+ rescue Exception => e1
263
+ raise e1
264
+ ensure
265
+ raise ::Sequel::Rollback unless e1
266
+ end
267
+ else
268
+ yield(conn)
269
+ end
224
270
  rescue Exception => e
225
- rollback_transaction(t, opts) if t
226
- transaction_error(e)
271
+ rollback_transaction(conn, opts)
272
+ transaction_error(e, :conn=>conn, :rollback=>rollback)
227
273
  ensure
228
274
  begin
229
- commit_transaction(t, opts) unless e
230
- rescue Exception => e
231
- raise_error(e, :classes=>database_error_classes)
275
+ committed = commit_or_rollback_transaction(e, conn, opts)
276
+ rescue Exception => e2
277
+ raise_error(e2, :classes=>database_error_classes, :conn=>conn)
232
278
  ensure
233
- remove_transaction(t)
279
+ remove_transaction(conn, committed)
234
280
  end
235
281
  end
236
282
  end
237
-
283
+
238
284
  # Add the current thread to the list of active transactions
239
- def add_transaction
240
- th = Thread.current
285
+ def add_transaction(conn, opts)
241
286
  if supports_savepoints?
242
- unless @transactions.include?(th)
243
- th[:sequel_transaction_depth] = 0
244
- @transactions << th
287
+ unless @transactions[conn]
288
+ @transactions[conn] = {:savepoint_level=>0}
289
+ @transactions[conn][:prepare] = opts[:prepare] if supports_prepared_transactions?
245
290
  end
246
291
  else
247
- @transactions << th
292
+ @transactions[conn] = {}
293
+ @transactions[conn][:prepare] = opts[:prepare] if supports_prepared_transactions?
248
294
  end
249
295
  end
250
296
 
297
+ # Call all stored after_commit blocks for the given transaction
298
+ def after_transaction_commit(conn)
299
+ if ary = @transactions[conn][:after_commit]
300
+ ary.each{|b| b.call}
301
+ end
302
+ end
303
+
304
+ # Call all stored after_rollback blocks for the given transaction
305
+ def after_transaction_rollback(conn)
306
+ if ary = @transactions[conn][:after_rollback]
307
+ ary.each{|b| b.call}
308
+ end
309
+ end
310
+
251
311
  # Whether the current thread/connection is already inside a transaction
252
312
  def already_in_transaction?(conn, opts)
253
- @transactions.include?(Thread.current) && (!supports_savepoints? || !opts[:savepoint])
313
+ @transactions.has_key?(conn) && (!supports_savepoints? || !opts[:savepoint])
254
314
  end
255
315
 
256
316
  # SQL to start a new savepoint
@@ -267,17 +327,16 @@ module Sequel
267
327
  # Start a new database transaction or a new savepoint on the given connection.
268
328
  def begin_transaction(conn, opts={})
269
329
  if supports_savepoints?
270
- th = Thread.current
271
- if (depth = th[:sequel_transaction_depth]) > 0
330
+ th = @transactions[conn]
331
+ if (depth = th[:savepoint_level]) > 0
272
332
  log_connection_execute(conn, begin_savepoint_sql(depth))
273
333
  else
274
334
  begin_new_transaction(conn, opts)
275
335
  end
276
- th[:sequel_transaction_depth] += 1
336
+ th[:savepoint_level] += 1
277
337
  else
278
338
  begin_new_transaction(conn, opts)
279
339
  end
280
- conn
281
340
  end
282
341
 
283
342
  # SQL to BEGIN a transaction.
@@ -335,6 +394,38 @@ module Sequel
335
394
  end
336
395
  end
337
396
 
397
+ if (! defined?(RUBY_ENGINE) or RUBY_ENGINE == 'ruby' or RUBY_ENGINE == 'rbx') and RUBY_VERSION < '1.9'
398
+ # Whether to commit the current transaction. On ruby 1.8 and rubinius,
399
+ # Thread.current.status is checked because Thread#kill skips rescue
400
+ # blocks (so exception would be nil), but the transaction should
401
+ # still be rolled back.
402
+ def commit_or_rollback_transaction(exception, conn, opts)
403
+ if exception
404
+ false
405
+ else
406
+ if Thread.current.status == 'aborting'
407
+ rollback_transaction(conn, opts)
408
+ false
409
+ else
410
+ commit_transaction(conn, opts)
411
+ true
412
+ end
413
+ end
414
+ end
415
+ else
416
+ # Whether to commit the current transaction. On ruby 1.9 and JRuby,
417
+ # transactions will be committed if Thread#kill is used on an thread
418
+ # that has a transaction open, and there isn't a work around.
419
+ def commit_or_rollback_transaction(exception, conn, opts)
420
+ if exception
421
+ false
422
+ else
423
+ commit_transaction(conn, opts)
424
+ true
425
+ end
426
+ end
427
+ end
428
+
338
429
  # SQL to commit a savepoint
339
430
  def commit_savepoint_sql(depth)
340
431
  SQL_RELEASE_SAVEPOINT % depth
@@ -343,7 +434,7 @@ module Sequel
343
434
  # Commit the active transaction on the connection
344
435
  def commit_transaction(conn, opts={})
345
436
  if supports_savepoints?
346
- depth = Thread.current[:sequel_transaction_depth]
437
+ depth = @transactions[conn][:savepoint_level]
347
438
  log_connection_execute(conn, depth > 1 ? commit_savepoint_sql(depth-1) : commit_transaction_sql)
348
439
  else
349
440
  log_connection_execute(conn, commit_transaction_sql)
@@ -364,8 +455,8 @@ module Sequel
364
455
  # Return a Method object for the dataset's output_identifier_method.
365
456
  # Used in metadata parsing to make sure the returned information is in the
366
457
  # correct format.
367
- def input_identifier_meth
368
- dataset.method(:input_identifier)
458
+ def input_identifier_meth(ds=nil)
459
+ (ds || dataset).method(:input_identifier)
369
460
  end
370
461
 
371
462
  # Return a dataset that uses the default identifier input and output methods
@@ -382,24 +473,28 @@ module Sequel
382
473
  # Return a Method object for the dataset's output_identifier_method.
383
474
  # Used in metadata parsing to make sure the returned information is in the
384
475
  # correct format.
385
- def output_identifier_meth
386
- dataset.method(:output_identifier)
476
+ def output_identifier_meth(ds=nil)
477
+ (ds || dataset).method(:output_identifier)
387
478
  end
388
479
 
389
- # SQL to ROLLBACK a transaction.
390
- def rollback_transaction_sql
391
- SQL_ROLLBACK
392
- end
393
-
394
480
  # Remove the cached schema for the given schema name
395
481
  def remove_cached_schema(table)
396
482
  @schemas.delete(quote_schema_table(table)) if @schemas
397
483
  end
398
484
 
399
485
  # Remove the current thread from the list of active transactions
400
- def remove_transaction(conn)
401
- th = Thread.current
402
- @transactions.delete(th) if !supports_savepoints? || ((th[:sequel_transaction_depth] -= 1) <= 0)
486
+ def remove_transaction(conn, committed)
487
+ if !supports_savepoints? || ((@transactions[conn][:savepoint_level] -= 1) <= 0)
488
+ begin
489
+ if committed
490
+ after_transaction_commit(conn)
491
+ else
492
+ after_transaction_rollback(conn)
493
+ end
494
+ ensure
495
+ @transactions.delete(conn)
496
+ end
497
+ end
403
498
  end
404
499
 
405
500
  # SQL to rollback to a savepoint
@@ -410,13 +505,18 @@ module Sequel
410
505
  # Rollback the active transaction on the connection
411
506
  def rollback_transaction(conn, opts={})
412
507
  if supports_savepoints?
413
- depth = Thread.current[:sequel_transaction_depth]
508
+ depth = @transactions[conn][:savepoint_level]
414
509
  log_connection_execute(conn, depth > 1 ? rollback_savepoint_sql(depth-1) : rollback_transaction_sql)
415
510
  else
416
511
  log_connection_execute(conn, rollback_transaction_sql)
417
512
  end
418
513
  end
419
514
 
515
+ # SQL to ROLLBACK a transaction.
516
+ def rollback_transaction_sql
517
+ SQL_ROLLBACK
518
+ end
519
+
420
520
  # Match the database's column type to a ruby type via a
421
521
  # regular expression, and return the ruby type as a symbol
422
522
  # such as :integer or :string.
@@ -434,13 +534,13 @@ module Sequel
434
534
  :datetime
435
535
  when /\Atime( with(out)? time zone)?\z/io
436
536
  :time
437
- when /\A(boolean|bit)\z/io
537
+ when /\A(bool(ean)?)\z/io
438
538
  :boolean
439
539
  when /\A(real|float|double( precision)?)\z/io
440
540
  :float
441
- when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+)\))?)|(?:small)?money)\z/io
442
- $1 && $1 == '0' ? :integer : :decimal
443
- when /bytea|blob|image|(var)?binary/io
541
+ when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?)|(?:small)?money)\z/io
542
+ $1 && ['0', 'false'].include?($1) ? :integer : :decimal
543
+ when /bytea|[bc]lob|image|(var)?binary/io
444
544
  :blob
445
545
  when /\Aenum/io
446
546
  :enum
@@ -460,8 +560,12 @@ module Sequel
460
560
  end
461
561
 
462
562
  # Raise a database error unless the exception is an Rollback.
463
- def transaction_error(e)
464
- raise_error(e, :classes=>database_error_classes) unless e.is_a?(Rollback)
563
+ def transaction_error(e, opts={})
564
+ if e.is_a?(Rollback)
565
+ raise e if opts[:rollback] == :reraise
566
+ else
567
+ raise_error(e, opts.merge(:classes=>database_error_classes))
568
+ end
465
569
  end
466
570
  end
467
571
  end
@@ -86,7 +86,8 @@ module Sequel
86
86
  # :index :: Create an index on this column.
87
87
  # :key :: For foreign key columns, the column in the associated table
88
88
  # that this column references. Unnecessary if this column
89
- # references the primary key of the associated table.
89
+ # references the primary key of the associated table, except if you are
90
+ # using MySQL.
90
91
  # :null :: Mark the column as allowing NULL values (if true),
91
92
  # or not allowing NULL values (if false). If unspecified, will default
92
93
  # to whatever the database default is.
@@ -124,6 +125,13 @@ module Sequel
124
125
  # foreign_key(:artist_id) # artist_id INTEGER
125
126
  # foreign_key(:artist_id, :artists) # artist_id INTEGER REFERENCES artists
126
127
  # foreign_key(:artist_id, :artists, :key=>:id) # artist_id INTEGER REFERENCES artists(id)
128
+ #
129
+ # If you want a foreign key constraint without adding a column (usually because it is a
130
+ # composite foreign key), you can provide an array of columns as the first argument, and
131
+ # you can provide the :name option to name the constraint:
132
+ #
133
+ # foreign_key([:artist_name, :artist_location], :artists, :name=>:artist_fk)
134
+ # # ADD CONSTRAINT artist_fk FOREIGN KEY (artist_name, artist_location) REFERENCES artists
127
135
  def foreign_key(name, table=nil, opts = {})
128
136
  opts = case table
129
137
  when Hash
@@ -281,8 +289,8 @@ module Sequel
281
289
  # to the DDL for the table. See Generator#column for the available options.
282
290
  #
283
291
  # You can also pass an array of column names for creating composite foreign
284
- # keys. In this case, it will assume the columns exists and will only add
285
- # the constraint.
292
+ # keys. In this case, it will assume the columns exist and will only add
293
+ # the constraint. You can provide a :name option to name the constraint.
286
294
  #
287
295
  # NOTE: If you need to add a foreign key constraint to a single existing column
288
296
  # use the composite key syntax even if it is only one column.
@@ -329,15 +337,17 @@ module Sequel
329
337
  # Remove a column from the DDL for the table.
330
338
  #
331
339
  # drop_column(:artist_id) # DROP COLUMN artist_id
332
- def drop_column(name)
333
- @operations << {:op => :drop_column, :name => name}
340
+ # drop_column(:artist_id, :cascade=>true) # DROP COLUMN artist_id CASCADE
341
+ def drop_column(name, opts={})
342
+ @operations << {:op => :drop_column, :name => name}.merge(opts)
334
343
  end
335
344
 
336
345
  # Remove a constraint from the DDL for the table.
337
346
  #
338
347
  # drop_constraint(:unique_name) # DROP CONSTRAINT unique_name
339
- def drop_constraint(name)
340
- @operations << {:op => :drop_constraint, :name => name}
348
+ # drop_constraint(:unique_name, :cascade=>true) # DROP CONSTRAINT unique_name CASCADE
349
+ def drop_constraint(name, opts={})
350
+ @operations << {:op => :drop_constraint, :name => name}.merge(opts)
341
351
  end
342
352
 
343
353
  # Remove an index from the DDL for the table.