sequel 3.34.1 → 3.35.0

Sign up to get free protection for your applications and to get access to all the features.
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)