sequel 3.34.1 → 3.35.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 (101) hide show
  1. data/CHANGELOG +52 -0
  2. data/README.rdoc +3 -1
  3. data/Rakefile +2 -10
  4. data/doc/active_record.rdoc +1 -0
  5. data/doc/migration.rdoc +18 -7
  6. data/doc/model_hooks.rdoc +6 -0
  7. data/doc/opening_databases.rdoc +3 -0
  8. data/doc/prepared_statements.rdoc +0 -1
  9. data/doc/release_notes/3.35.0.txt +144 -0
  10. data/doc/schema_modification.rdoc +16 -1
  11. data/doc/thread_safety.rdoc +17 -0
  12. data/lib/sequel/adapters/do.rb +2 -2
  13. data/lib/sequel/adapters/do/postgres.rb +1 -52
  14. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  15. data/lib/sequel/adapters/firebird.rb +1 -1
  16. data/lib/sequel/adapters/ibmdb.rb +2 -2
  17. data/lib/sequel/adapters/jdbc.rb +23 -19
  18. data/lib/sequel/adapters/jdbc/db2.rb +0 -5
  19. data/lib/sequel/adapters/jdbc/derby.rb +29 -2
  20. data/lib/sequel/adapters/jdbc/firebird.rb +0 -5
  21. data/lib/sequel/adapters/jdbc/h2.rb +1 -1
  22. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  23. data/lib/sequel/adapters/jdbc/informix.rb +0 -5
  24. data/lib/sequel/adapters/jdbc/jtds.rb +0 -5
  25. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  26. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -35
  27. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  28. data/lib/sequel/adapters/jdbc/sqlserver.rb +0 -5
  29. data/lib/sequel/adapters/jdbc/transactions.rb +4 -4
  30. data/lib/sequel/adapters/mysql2.rb +1 -1
  31. data/lib/sequel/adapters/odbc.rb +3 -3
  32. data/lib/sequel/adapters/odbc/mssql.rb +14 -1
  33. data/lib/sequel/adapters/oracle.rb +6 -18
  34. data/lib/sequel/adapters/postgres.rb +36 -53
  35. data/lib/sequel/adapters/shared/db2.rb +16 -2
  36. data/lib/sequel/adapters/shared/mssql.rb +40 -9
  37. data/lib/sequel/adapters/shared/mysql.rb +16 -4
  38. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +2 -2
  39. data/lib/sequel/adapters/shared/oracle.rb +2 -0
  40. data/lib/sequel/adapters/shared/postgres.rb +135 -211
  41. data/lib/sequel/adapters/sqlite.rb +2 -2
  42. data/lib/sequel/adapters/swift.rb +1 -1
  43. data/lib/sequel/adapters/swift/postgres.rb +1 -71
  44. data/lib/sequel/adapters/tinytds.rb +3 -3
  45. data/lib/sequel/core.rb +27 -4
  46. data/lib/sequel/database/connecting.rb +7 -8
  47. data/lib/sequel/database/logging.rb +6 -1
  48. data/lib/sequel/database/misc.rb +20 -4
  49. data/lib/sequel/database/query.rb +38 -18
  50. data/lib/sequel/database/schema_generator.rb +5 -2
  51. data/lib/sequel/database/schema_methods.rb +34 -8
  52. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  53. data/lib/sequel/dataset/sql.rb +18 -24
  54. data/lib/sequel/extensions/core_extensions.rb +0 -23
  55. data/lib/sequel/extensions/migration.rb +22 -8
  56. data/lib/sequel/extensions/pg_auto_parameterize.rb +4 -0
  57. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  58. data/lib/sequel/model.rb +2 -2
  59. data/lib/sequel/model/associations.rb +95 -70
  60. data/lib/sequel/model/base.rb +16 -18
  61. data/lib/sequel/plugins/dirty.rb +214 -0
  62. data/lib/sequel/plugins/identity_map.rb +1 -1
  63. data/lib/sequel/plugins/json_serializer.rb +16 -1
  64. data/lib/sequel/plugins/many_through_many.rb +22 -32
  65. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +2 -2
  66. data/lib/sequel/plugins/prepared_statements.rb +22 -8
  67. data/lib/sequel/plugins/prepared_statements_associations.rb +2 -3
  68. data/lib/sequel/plugins/prepared_statements_with_pk.rb +1 -1
  69. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  70. data/lib/sequel/plugins/subclasses.rb +10 -2
  71. data/lib/sequel/plugins/timestamps.rb +1 -1
  72. data/lib/sequel/plugins/xml_serializer.rb +12 -1
  73. data/lib/sequel/sql.rb +1 -1
  74. data/lib/sequel/version.rb +2 -2
  75. data/spec/adapters/postgres_spec.rb +30 -79
  76. data/spec/core/database_spec.rb +46 -2
  77. data/spec/core/dataset_spec.rb +28 -22
  78. data/spec/core/schema_generator_spec.rb +1 -1
  79. data/spec/core/schema_spec.rb +51 -0
  80. data/spec/extensions/arbitrary_servers_spec.rb +0 -4
  81. data/spec/extensions/association_autoreloading_spec.rb +17 -0
  82. data/spec/extensions/association_proxies_spec.rb +4 -4
  83. data/spec/extensions/core_extensions_spec.rb +1 -24
  84. data/spec/extensions/dirty_spec.rb +155 -0
  85. data/spec/extensions/json_serializer_spec.rb +13 -0
  86. data/spec/extensions/migration_spec.rb +28 -15
  87. data/spec/extensions/named_timezones_spec.rb +6 -8
  88. data/spec/extensions/pg_auto_parameterize_spec.rb +6 -5
  89. data/spec/extensions/schema_dumper_spec.rb +3 -1
  90. data/spec/extensions/xml_serializer_spec.rb +13 -0
  91. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/001_create_alt_basic.rb +1 -1
  92. data/spec/files/{transactionless_migrations → transaction_specified_migrations}/002_create_basic.rb +0 -0
  93. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/001_create_alt_basic.rb +0 -0
  94. data/spec/files/{transaction_migrations → transaction_unspecified_migrations}/002_create_basic.rb +0 -0
  95. data/spec/integration/associations_test.rb +5 -7
  96. data/spec/integration/dataset_test.rb +25 -7
  97. data/spec/integration/plugin_test.rb +1 -1
  98. data/spec/integration/schema_test.rb +16 -1
  99. data/spec/model/associations_spec.rb +2 -2
  100. metadata +14 -9
  101. data/lib/sequel/adapters/odbc/db2.rb +0 -17
@@ -216,7 +216,7 @@ module Sequel
216
216
 
217
217
  # Execute a prepared statement on the database using the given name.
218
218
  def execute_prepared_statement(conn, type, name, opts, &block)
219
- ps = prepared_statements[name]
219
+ ps = prepared_statement(name)
220
220
  sql = ps.prepared_sql
221
221
  args = opts[:arguments]
222
222
  ps_args = {}
@@ -382,7 +382,7 @@ module Sequel
382
382
  ps.extend(PreparedStatementMethods)
383
383
  if name
384
384
  ps.prepared_statement_name = name
385
- db.prepared_statements[name] = ps
385
+ db.set_prepared_statement(name, ps)
386
386
  end
387
387
  ps
388
388
  end
@@ -15,7 +15,7 @@ module Sequel
15
15
  DATABASE_SETUP = {:postgres=>proc do |db|
16
16
  Sequel.ts_require 'adapters/swift/postgres'
17
17
  db.extend(Sequel::Swift::Postgres::DatabaseMethods)
18
- db.dataset_class = Sequel::Swift::Postgres::Dataset
18
+ db.extend_datasets Sequel::Postgres::DatasetMethods
19
19
  db.swift_class = ::Swift::DB::Postgres
20
20
  end,
21
21
  :mysql=>proc do |db|
@@ -7,27 +7,6 @@ module Sequel
7
7
  # Adapter, Database, and Dataset support for accessing a PostgreSQL
8
8
  # database via Swift.
9
9
  module Postgres
10
- # Methods to add to the Swift adapter/connection to allow it to work
11
- # with the shared PostgreSQL code.
12
- module AdapterMethods
13
- include Sequel::Postgres::AdapterMethods
14
-
15
- # Log all SQL that goes through the execute method to the related
16
- # database object.
17
- def execute(sql, *args)
18
- @db.log_yield(sql){super}
19
- rescue SwiftError => e
20
- @db.send(:raise_error, e)
21
- end
22
-
23
- private
24
-
25
- # Swift specific method of getting specific values from a result set.
26
- def single_value(row)
27
- row.values.at(0)
28
- end
29
- end
30
-
31
10
  # Methods to add to Database instances that access PostgreSQL via Swift.
32
11
  module DatabaseMethods
33
12
  include Sequel::Postgres::DatabaseMethods
@@ -41,51 +20,8 @@ module Sequel
41
20
  end
42
21
  end
43
22
 
44
- # Run the SELECT SQL on the database and yield the rows
45
- def execute(sql, opts={})
46
- synchronize(opts[:server]) do |conn|
47
- begin
48
- res = conn.execute(sql)
49
- yield res if block_given?
50
- nil
51
- rescue SwiftError => e
52
- raise_error(e)
53
- end
54
- end
55
- end
56
-
57
- # Run the DELETE/UPDATE SQL on the database and return the number
58
- # of matched rows.
59
- def execute_dui(sql, opts={})
60
- synchronize(opts[:server]) do |conn|
61
- begin
62
- conn.execute(sql).rows
63
- rescue SwiftError => e
64
- raise_error(e)
65
- end
66
- end
67
- end
68
-
69
- # Run the INSERT SQL on the database and return the primary key
70
- # for the record.
71
- def execute_insert(sql, opts={})
72
- synchronize(opts[:server]) do |conn|
73
- begin
74
- conn.execute(sql)
75
- insert_result(conn, opts[:table], opts[:values])
76
- rescue SwiftError => e
77
- raise_error(e)
78
- end
79
- end
80
- end
81
-
82
23
  private
83
24
 
84
- # Execute SQL on the connection.
85
- def log_connection_execute(conn, sql)
86
- conn.execute(sql)
87
- end
88
-
89
25
  # Remove all other options except for ones specifically handled, as
90
26
  # otherwise swift passes them to dbic++ which passes them to PostgreSQL
91
27
  # which can raise an error.
@@ -99,16 +35,10 @@ module Sequel
99
35
  # Extend the adapter with the Swift PostgreSQL AdapterMethods.
100
36
  def setup_connection(conn)
101
37
  conn = super(conn)
102
- conn.extend(Sequel::Swift::Postgres::AdapterMethods)
103
- conn.db = self
104
- conn.apply_connection_settings
38
+ connection_configuration_sqls.each{|sql| log_yield(sql){conn.execute(sql)}}
105
39
  conn
106
40
  end
107
41
  end
108
-
109
- class Dataset < Swift::Dataset
110
- include Sequel::Postgres::DatasetMethods
111
- end
112
42
  end
113
43
  end
114
44
  end
@@ -249,7 +249,7 @@ module Sequel
249
249
  ps.extend(PreparedStatementMethods)
250
250
  if name
251
251
  ps.prepared_statement_name = name
252
- db.prepared_statements[name] = ps
252
+ db.set_prepared_statement(name, ps)
253
253
  end
254
254
  ps
255
255
  end
@@ -258,8 +258,8 @@ module Sequel
258
258
 
259
259
  # Properly escape the given string +v+.
260
260
  def literal_string_append(sql, v)
261
- sql << 'N' if mssql_unicode_strings
262
- sql << "'" << db.synchronize{|c| c.escape(v)} << "'"
261
+ sql << (mssql_unicode_strings ? UNICODE_STRING_START : APOS)
262
+ sql << db.synchronize{|c| c.escape(v)}.gsub(BACKSLASH_CRLF_RE, BACKSLASH_CRLF_REPLACE) << APOS
263
263
  end
264
264
  end
265
265
  end
data/lib/sequel/core.rb CHANGED
@@ -1,4 +1,3 @@
1
- warn 'Sequel support for ruby <1.8.7 is deprecated and will be removed in 3.35.0' if RUBY_VERSION < '1.8.7'
2
1
  %w'bigdecimal date thread time uri'.each{|f| require f}
3
2
 
4
3
  # Top level module for Sequel
@@ -35,6 +34,9 @@ module Sequel
35
34
  # Mutex used to protect file loading/requireing
36
35
  @require_mutex = Mutex.new
37
36
 
37
+ # Whether Sequel is being run in single threaded mode
38
+ @single_threaded = false
39
+
38
40
  class << self
39
41
  # Sequel converts two digit years in <tt>Date</tt>s and <tt>DateTime</tt>s by default,
40
42
  # so 01/02/03 is interpreted at January 2nd, 2003, and 12/13/99 is interpreted
@@ -89,6 +91,7 @@ module Sequel
89
91
 
90
92
  # Make thread safe requiring reentrant to prevent deadlocks.
91
93
  def check_requiring_thread
94
+ return yield if @single_threaded
92
95
  t = Thread.current
93
96
  return(yield) if @require_thread == t
94
97
  @require_mutex.synchronize do
@@ -234,13 +237,15 @@ module Sequel
234
237
  Array(files).each{|f| super("#{File.dirname(__FILE__).untaint}/#{"#{subdir}/" if subdir}#{f}")}
235
238
  end
236
239
 
237
- # Set whether to set the single threaded mode for all databases by default. By default,
240
+ # Set whether Sequel is being used in single threaded mode. By default,
238
241
  # Sequel uses a thread-safe connection pool, which isn't as fast as the
239
- # single threaded connection pool. If your program will only have one thread,
240
- # and speed is a priority, you may want to set this to true:
242
+ # single threaded connection pool, and also has some additional thread
243
+ # safety checks. If your program will only have one thread,
244
+ # and speed is a priority, you should set this to true:
241
245
  #
242
246
  # Sequel.single_threaded = true
243
247
  def self.single_threaded=(value)
248
+ @single_threaded = value
244
249
  Database.single_threaded = value
245
250
  end
246
251
 
@@ -283,6 +288,24 @@ module Sequel
283
288
  end
284
289
  end
285
290
 
291
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE != 'ruby'
292
+ # Mutex used to protect mutable data structures
293
+ @data_mutex = Mutex.new
294
+
295
+ # Unless in single threaded mode, protects access to any mutable
296
+ # global data structure in Sequel.
297
+ # Uses a non-reentrant mutex, so calling code should be careful.
298
+ def self.synchronize(&block)
299
+ @single_threaded ? yield : @data_mutex.synchronize(&block)
300
+ end
301
+ else
302
+ # Yield directly to the block. You don't need to synchronize
303
+ # access on MRI because the GVL makes certain methods atomic.
304
+ def self.synchronize(&block)
305
+ yield
306
+ end
307
+ end
308
+
286
309
  # Uses a transaction on all given databases with the given options. This:
287
310
  #
288
311
  # Sequel.transaction([DB1, DB2, DB3]){...}
@@ -81,7 +81,7 @@ module Sequel
81
81
  ensure
82
82
  if block_given?
83
83
  db.disconnect if db
84
- ::Sequel::DATABASES.delete(db)
84
+ Sequel.synchronize{::Sequel::DATABASES.delete(db)}
85
85
  end
86
86
  end
87
87
  block_given? ? result : db
@@ -135,8 +135,10 @@ module Sequel
135
135
  #
136
136
  # DB.add_servers(:f=>{:host=>"hash_host_f"})
137
137
  def add_servers(servers)
138
- @opts[:servers] = @opts[:servers] ? @opts[:servers].merge(servers) : servers
139
- @pool.add_servers(servers.keys)
138
+ if h = @opts[:servers]
139
+ Sequel.synchronize{h.merge!(servers)}
140
+ @pool.add_servers(servers.keys)
141
+ end
140
142
  end
141
143
 
142
144
  # Connects to the database. This method should be overridden by descendants.
@@ -191,11 +193,8 @@ module Sequel
191
193
  #
192
194
  # DB.remove_servers(:f1, :f2)
193
195
  def remove_servers(*servers)
194
- if @opts[:servers] && !@opts[:servers].empty?
195
- servs = @opts[:servers].dup
196
- servers.flatten!
197
- servers.each{|s| servs.delete(s)}
198
- @opts[:servers] = servs
196
+ if h = @opts[:servers]
197
+ servers.flatten.each{|s| Sequel.synchronize{h.delete(s)}}
199
198
  @pool.remove_servers(servers)
200
199
  end
201
200
  end
@@ -17,6 +17,11 @@ module Sequel
17
17
  # is :info, it can be set to :debug to log at DEBUG level.
18
18
  attr_accessor :sql_log_level
19
19
 
20
+ # Log a message at error level, with information about the exception.
21
+ def log_exception(exception, message)
22
+ log_each(:error, "#{exception.class}: #{exception.message.strip}: #{message}")
23
+ end
24
+
20
25
  # Log a message at level info to all loggers.
21
26
  def log_info(message, args=nil)
22
27
  log_each(:info, args ? "#{message}; #{args.inspect}" : message)
@@ -31,7 +36,7 @@ module Sequel
31
36
  begin
32
37
  yield
33
38
  rescue => e
34
- log_each(:error, "#{e.class}: #{e.message.strip}: #{sql}")
39
+ log_exception(e, sql)
35
40
  raise
36
41
  ensure
37
42
  log_duration(Time.now - start, sql) unless e
@@ -59,11 +59,12 @@ module Sequel
59
59
  @quote_identifiers = nil
60
60
  @timezone = nil
61
61
  @dataset_class = dataset_class_default
62
+ @cache_schema = typecast_value_boolean(@opts.fetch(:cache_schema, true))
62
63
  @dataset_modules = []
63
64
  self.sql_log_level = @opts[:sql_log_level] ? @opts[:sql_log_level].to_sym : :info
64
65
  @pool = ConnectionPool.get_pool(@opts, &block)
65
66
 
66
- ::Sequel::DATABASES.push(self)
67
+ Sequel.synchronize{::Sequel::DATABASES.push(self)}
67
68
  end
68
69
 
69
70
  # If a transaction is not currently in process, yield to the block immediately.
@@ -74,7 +75,7 @@ module Sequel
74
75
  def after_commit(opts={}, &block)
75
76
  raise Error, "must provide block to after_commit" unless block
76
77
  synchronize(opts[:server]) do |conn|
77
- if h = @transactions[conn]
78
+ if h = _trans(conn)
78
79
  raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
79
80
  (h[:after_commit] ||= []) << block
80
81
  else
@@ -91,7 +92,7 @@ module Sequel
91
92
  def after_rollback(opts={}, &block)
92
93
  raise Error, "must provide block to after_rollback" unless block
93
94
  synchronize(opts[:server]) do |conn|
94
- if h = @transactions[conn]
95
+ if h = _trans(conn)
95
96
  raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
96
97
  (h[:after_rollback] ||= []) << block
97
98
  end
@@ -117,7 +118,7 @@ module Sequel
117
118
  # false otherwise. Respects the :server option for selecting
118
119
  # a shard.
119
120
  def in_transaction?(opts={})
120
- synchronize(opts[:server]){|conn| !!@transactions[conn]}
121
+ synchronize(opts[:server]){|conn| !!_trans(conn)}
121
122
  end
122
123
 
123
124
  # Returns a string representation of the database object including the
@@ -140,12 +141,22 @@ module Sequel
140
141
  schema_utility_dataset.literal(v)
141
142
  end
142
143
 
144
+ # Synchronize access to the prepared statements cache.
145
+ def prepared_statement(name)
146
+ Sequel.synchronize{prepared_statements[name]}
147
+ end
148
+
143
149
  # Default serial primary key options, used by the table creation
144
150
  # code.
145
151
  def serial_primary_key_options
146
152
  {:primary_key => true, :type => Integer, :auto_increment => true}
147
153
  end
148
154
 
155
+ # Cache the prepared statement object at the given name.
156
+ def set_prepared_statement(name, ps)
157
+ Sequel.synchronize{prepared_statements[name] = ps}
158
+ end
159
+
149
160
  # Whether the database supports CREATE TABLE IF NOT EXISTS syntax,
150
161
  # false by default.
151
162
  def supports_create_table_if_not_exists?
@@ -180,6 +191,11 @@ module Sequel
180
191
  false
181
192
  end
182
193
 
194
+ # Whether DDL statements work correctly in transactions, false by default.
195
+ def supports_transactional_ddl?
196
+ false
197
+ end
198
+
183
199
  # The timezone to use for this database, defaulting to <tt>Sequel.database_timezone</tt>.
184
200
  def timezone
185
201
  @timezone || Sequel.database_timezone
@@ -35,6 +35,11 @@ module Sequel
35
35
  # Database#transaction, as on MSSQL if affects all future transactions
36
36
  # on the same connection.
37
37
  attr_accessor :transaction_isolation_level
38
+
39
+ # Whether the schema should be cached for this database. True by default
40
+ # for performance, can be set to false to always issue a database query to
41
+ # get the schema.
42
+ attr_accessor :cache_schema
38
43
 
39
44
  # Runs the supplied SQL statement string on the database server.
40
45
  # Returns self so it can be safely chained:
@@ -51,7 +56,7 @@ module Sequel
51
56
  # DB[:items].filter(:id=>1).prepare(:first, :sa)
52
57
  # DB.call(:sa) # SELECT * FROM items WHERE id = 1
53
58
  def call(ps_name, hash={})
54
- prepared_statements[ps_name].call(hash)
59
+ prepared_statement(ps_name).call(hash)
55
60
  end
56
61
 
57
62
  # Executes the given SQL on the database. This method should be overridden in descendants.
@@ -189,13 +194,14 @@ module Sequel
189
194
  end
190
195
  opts[:schema] = sch if sch && !opts.include?(:schema)
191
196
 
192
- @schemas.delete(quoted_name) if opts[:reload]
193
- return @schemas[quoted_name] if @schemas[quoted_name]
197
+ Sequel.synchronize{@schemas.delete(quoted_name)} if opts[:reload]
198
+ return Sequel.synchronize{@schemas[quoted_name]} if @schemas[quoted_name]
194
199
 
195
200
  cols = schema_parse_table(table_name, opts)
196
201
  raise(Error, 'schema parsing returned no columns, table probably doesn\'t exist') if cols.nil? || cols.empty?
197
202
  cols.each{|_,c| c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type])}
198
- @schemas[quoted_name] = cols
203
+ Sequel.synchronize{@schemas[quoted_name] = cols} if cache_schema
204
+ cols
199
205
  end
200
206
 
201
207
  # Returns true if a table with the given name exists. This requires a query
@@ -286,7 +292,11 @@ module Sequel
286
292
  yield(conn)
287
293
  end
288
294
  rescue Exception => e
289
- rollback_transaction(conn, opts)
295
+ begin
296
+ rollback_transaction(conn, opts)
297
+ rescue Exception => e3
298
+ raise_error(e3, :classes=>database_error_classes, :conn=>conn)
299
+ end
290
300
  transaction_error(e, :conn=>conn, :rollback=>rollback)
291
301
  ensure
292
302
  begin
@@ -299,36 +309,46 @@ module Sequel
299
309
  end
300
310
  end
301
311
 
312
+ # Synchronize access to the current transactions, returning the hash
313
+ # of options for the current transaction (if any)
314
+ def _trans(conn)
315
+ Sequel.synchronize{@transactions[conn]}
316
+ end
317
+
302
318
  # Add the current thread to the list of active transactions
303
319
  def add_transaction(conn, opts)
304
320
  if supports_savepoints?
305
- unless @transactions[conn]
306
- @transactions[conn] = {:savepoint_level=>0}
307
- @transactions[conn][:prepare] = opts[:prepare] if opts[:prepare] && supports_prepared_transactions?
321
+ unless _trans(conn)
322
+ if (prep = opts[:prepare]) && supports_prepared_transactions?
323
+ Sequel.synchronize{@transactions[conn] = {:savepoint_level=>0, :prepare=>prep}}
324
+ else
325
+ Sequel.synchronize{@transactions[conn] = {:savepoint_level=>0}}
326
+ end
308
327
  end
328
+ elsif (prep = opts[:prepare]) && supports_prepared_transactions?
329
+ Sequel.synchronize{@transactions[conn] = {:prepare => prep}}
309
330
  else
310
- @transactions[conn] = {}
311
- @transactions[conn][:prepare] = opts[:prepare] if opts[:prepare] && supports_prepared_transactions?
331
+ Sequel.synchronize{@transactions[conn] = {}}
312
332
  end
313
333
  end
314
334
 
315
335
  # Call all stored after_commit blocks for the given transaction
316
336
  def after_transaction_commit(conn)
317
- if ary = @transactions[conn][:after_commit]
337
+ if ary = _trans(conn)[:after_commit]
318
338
  ary.each{|b| b.call}
319
339
  end
320
340
  end
321
341
 
322
342
  # Call all stored after_rollback blocks for the given transaction
323
343
  def after_transaction_rollback(conn)
324
- if ary = @transactions[conn][:after_rollback]
344
+ if ary = _trans(conn)[:after_rollback]
325
345
  ary.each{|b| b.call}
326
346
  end
327
347
  end
328
348
 
329
349
  # Whether the current thread/connection is already inside a transaction
330
350
  def already_in_transaction?(conn, opts)
331
- @transactions.has_key?(conn) && (!supports_savepoints? || !opts[:savepoint])
351
+ _trans(conn) && (!supports_savepoints? || !opts[:savepoint])
332
352
  end
333
353
 
334
354
  # SQL to start a new savepoint
@@ -345,7 +365,7 @@ module Sequel
345
365
  # Start a new database transaction or a new savepoint on the given connection.
346
366
  def begin_transaction(conn, opts={})
347
367
  if supports_savepoints?
348
- th = @transactions[conn]
368
+ th = _trans(conn)
349
369
  if (depth = th[:savepoint_level]) > 0
350
370
  log_connection_execute(conn, begin_savepoint_sql(depth))
351
371
  else
@@ -452,7 +472,7 @@ module Sequel
452
472
  # Commit the active transaction on the connection
453
473
  def commit_transaction(conn, opts={})
454
474
  if supports_savepoints?
455
- depth = @transactions[conn][:savepoint_level]
475
+ depth = _trans(conn)[:savepoint_level]
456
476
  log_connection_execute(conn, depth > 1 ? commit_savepoint_sql(depth-1) : commit_transaction_sql)
457
477
  else
458
478
  log_connection_execute(conn, commit_transaction_sql)
@@ -502,7 +522,7 @@ module Sequel
502
522
 
503
523
  # Remove the current thread from the list of active transactions
504
524
  def remove_transaction(conn, committed)
505
- if !supports_savepoints? || ((@transactions[conn][:savepoint_level] -= 1) <= 0)
525
+ if !supports_savepoints? || ((_trans(conn)[:savepoint_level] -= 1) <= 0)
506
526
  begin
507
527
  if committed
508
528
  after_transaction_commit(conn)
@@ -510,7 +530,7 @@ module Sequel
510
530
  after_transaction_rollback(conn)
511
531
  end
512
532
  ensure
513
- @transactions.delete(conn)
533
+ Sequel.synchronize{@transactions.delete(conn)}
514
534
  end
515
535
  end
516
536
  end
@@ -523,7 +543,7 @@ module Sequel
523
543
  # Rollback the active transaction on the connection
524
544
  def rollback_transaction(conn, opts={})
525
545
  if supports_savepoints?
526
- depth = @transactions[conn][:savepoint_level]
546
+ depth = _trans(conn)[:savepoint_level]
527
547
  log_connection_execute(conn, depth > 1 ? rollback_savepoint_sql(depth-1) : rollback_transaction_sql)
528
548
  else
529
549
  log_connection_execute(conn, rollback_transaction_sql)