sequel 5.61.0 → 5.63.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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +46 -0
  3. data/README.rdoc +20 -19
  4. data/doc/advanced_associations.rdoc +13 -13
  5. data/doc/association_basics.rdoc +21 -15
  6. data/doc/cheat_sheet.rdoc +3 -3
  7. data/doc/model_hooks.rdoc +1 -1
  8. data/doc/object_model.rdoc +8 -8
  9. data/doc/opening_databases.rdoc +4 -4
  10. data/doc/postgresql.rdoc +8 -8
  11. data/doc/querying.rdoc +1 -1
  12. data/doc/release_notes/5.62.0.txt +132 -0
  13. data/doc/release_notes/5.63.0.txt +33 -0
  14. data/doc/schema_modification.rdoc +1 -1
  15. data/doc/security.rdoc +9 -9
  16. data/doc/sql.rdoc +13 -13
  17. data/doc/testing.rdoc +13 -11
  18. data/doc/transactions.rdoc +6 -6
  19. data/doc/virtual_rows.rdoc +1 -1
  20. data/lib/sequel/adapters/postgres.rb +4 -0
  21. data/lib/sequel/adapters/shared/access.rb +9 -1
  22. data/lib/sequel/adapters/shared/mssql.rb +9 -5
  23. data/lib/sequel/adapters/shared/mysql.rb +7 -0
  24. data/lib/sequel/adapters/shared/oracle.rb +7 -0
  25. data/lib/sequel/adapters/shared/postgres.rb +275 -152
  26. data/lib/sequel/adapters/shared/sqlanywhere.rb +7 -0
  27. data/lib/sequel/adapters/shared/sqlite.rb +5 -0
  28. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  29. data/lib/sequel/connection_pool/threaded.rb +8 -8
  30. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  31. data/lib/sequel/connection_pool.rb +47 -30
  32. data/lib/sequel/database/connecting.rb +24 -0
  33. data/lib/sequel/database/misc.rb +8 -2
  34. data/lib/sequel/database/query.rb +37 -0
  35. data/lib/sequel/dataset/actions.rb +56 -11
  36. data/lib/sequel/dataset/features.rb +5 -0
  37. data/lib/sequel/dataset/misc.rb +1 -1
  38. data/lib/sequel/dataset/query.rb +9 -9
  39. data/lib/sequel/dataset/sql.rb +5 -1
  40. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  41. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  42. data/lib/sequel/extensions/async_thread_pool.rb +11 -11
  43. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  44. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  45. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  46. data/lib/sequel/extensions/migration.rb +1 -1
  47. data/lib/sequel/extensions/named_timezones.rb +21 -5
  48. data/lib/sequel/extensions/pg_array.rb +22 -3
  49. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  50. data/lib/sequel/extensions/pg_extended_date_support.rb +12 -0
  51. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  52. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  53. data/lib/sequel/extensions/pg_inet.rb +9 -10
  54. data/lib/sequel/extensions/pg_interval.rb +9 -10
  55. data/lib/sequel/extensions/pg_json.rb +10 -10
  56. data/lib/sequel/extensions/pg_multirange.rb +5 -10
  57. data/lib/sequel/extensions/pg_range.rb +5 -10
  58. data/lib/sequel/extensions/pg_row.rb +18 -13
  59. data/lib/sequel/model/associations.rb +9 -4
  60. data/lib/sequel/model/base.rb +6 -5
  61. data/lib/sequel/plugins/auto_validations.rb +53 -15
  62. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  63. data/lib/sequel/plugins/composition.rb +2 -2
  64. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  65. data/lib/sequel/plugins/dirty.rb +1 -1
  66. data/lib/sequel/plugins/finder.rb +3 -1
  67. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  68. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
  69. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  70. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  71. data/lib/sequel/plugins/sql_comments.rb +1 -1
  72. data/lib/sequel/plugins/tactical_eager_loading.rb +14 -14
  73. data/lib/sequel/plugins/validate_associated.rb +22 -12
  74. data/lib/sequel/plugins/validation_helpers.rb +20 -0
  75. data/lib/sequel/version.rb +1 -1
  76. metadata +14 -6
@@ -0,0 +1,257 @@
1
+ # frozen-string-literal: true
2
+
3
+ # :nocov:
4
+ raise LoadError, "Sequel::TimedQueueConnectionPool is only available on Ruby 3.2+" unless RUBY_VERSION >= '3.2'
5
+ # :nocov:
6
+
7
+ # A connection pool allowing multi-threaded access to a pool of connections,
8
+ # using a timed queue (only available in Ruby 3.2+).
9
+ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
10
+ # The maximum number of connections this pool will create.
11
+ attr_reader :max_size
12
+
13
+ # The following additional options are respected:
14
+ # :max_connections :: The maximum number of connections the connection pool
15
+ # will open (default 4)
16
+ # :pool_timeout :: The amount of seconds to wait to acquire a connection
17
+ # before raising a PoolTimeout (default 5)
18
+ def initialize(db, opts = OPTS)
19
+ super
20
+ @max_size = Integer(opts[:max_connections] || 4)
21
+ raise(Sequel::Error, ':max_connections must be positive') if @max_size < 1
22
+ @mutex = Mutex.new
23
+ # Size inside array so this still works while the pool is frozen.
24
+ @size = [0]
25
+ @allocated = {}
26
+ @allocated.compare_by_identity
27
+ @timeout = Float(opts[:pool_timeout] || 5)
28
+ @queue = Queue.new
29
+ end
30
+
31
+ # Yield all of the available connections, and the one currently allocated to
32
+ # this thread. This will not yield connections currently allocated to other
33
+ # threads, as it is not safe to operate on them.
34
+ def all_connections
35
+ hold do |conn|
36
+ yield conn
37
+
38
+ # Use a hash to record all connections already seen. As soon as we
39
+ # come across a connection we've already seen, we stop the loop.
40
+ conns = {}
41
+ conns.compare_by_identity
42
+ while true
43
+ conn = nil
44
+ begin
45
+ break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
46
+ conns[conn] = true
47
+ yield conn
48
+ ensure
49
+ @queue.push(conn) if conn
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # Removes all connections currently in the pool's queue. This method has the effect of
56
+ # disconnecting from the database, assuming that no connections are currently
57
+ # being used.
58
+ #
59
+ # Once a connection is requested using #hold, the connection pool
60
+ # creates new connections to the database.
61
+ def disconnect(opts=OPTS)
62
+ while conn = @queue.pop(timeout: 0)
63
+ disconnect_connection(conn)
64
+ end
65
+ fill_queue
66
+ nil
67
+ end
68
+
69
+ # Chooses the first available connection, or if none are
70
+ # available, creates a new connection. Passes the connection to the supplied
71
+ # block:
72
+ #
73
+ # pool.hold {|conn| conn.execute('DROP TABLE posts')}
74
+ #
75
+ # Pool#hold is re-entrant, meaning it can be called recursively in
76
+ # the same thread without blocking.
77
+ #
78
+ # If no connection is immediately available and the pool is already using the maximum
79
+ # number of connections, Pool#hold will block until a connection
80
+ # is available or the timeout expires. If the timeout expires before a
81
+ # connection can be acquired, a Sequel::PoolTimeout is raised.
82
+ def hold(server=nil)
83
+ t = Sequel.current
84
+ if conn = sync{@allocated[t]}
85
+ return yield(conn)
86
+ end
87
+
88
+ begin
89
+ conn = acquire(t)
90
+ yield conn
91
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
92
+ if disconnect_error?(e)
93
+ oconn = conn
94
+ conn = nil
95
+ disconnect_connection(oconn) if oconn
96
+ sync{@allocated.delete(t)}
97
+ fill_queue
98
+ end
99
+ raise
100
+ ensure
101
+ release(t) if conn
102
+ end
103
+ end
104
+
105
+ def pool_type
106
+ :timed_queue
107
+ end
108
+
109
+ # The total number of connections in the pool.
110
+ def size
111
+ sync{@size[0]}
112
+ end
113
+
114
+ private
115
+
116
+ # Create a new connection, after the pool's current size has already
117
+ # been updated to account for the new connection. If there is an exception
118
+ # when creating the connection, decrement the current size.
119
+ #
120
+ # This should only be called after can_make_new?. If there is an exception
121
+ # between when can_make_new? is called and when preallocated_make_new
122
+ # is called, it has the effect of reducing the maximum size of the
123
+ # connection pool by 1, since the current size of the pool will show a
124
+ # higher number than the number of connections allocated or
125
+ # in the queue.
126
+ #
127
+ # Calling code should not have the mutex when calling this.
128
+ def preallocated_make_new
129
+ make_new(:default)
130
+ rescue Exception
131
+ sync{@size[0] -= 1}
132
+ raise
133
+ end
134
+
135
+ # Decrement the current size of the pool when disconnecting connections.
136
+ #
137
+ # Calling code should not have the mutex when calling this.
138
+ def disconnect_connection(conn)
139
+ sync{@size[0] -= 1}
140
+ super
141
+ end
142
+
143
+ # If there are any threads waiting on the queue, try to create
144
+ # new connections in a separate thread if the pool is not yet at the
145
+ # maximum size.
146
+ #
147
+ # The reason for this method is to handle cases where acquire
148
+ # could not retrieve a connection immediately, and the pool
149
+ # was already at the maximum size. In that case, the acquire will
150
+ # wait on the queue until the timeout. This method is called
151
+ # after disconnecting to potentially add new connections to the
152
+ # pool, so the threads that are currently waiting for connections
153
+ # do not timeout after the pool is no longer full.
154
+ def fill_queue
155
+ if @queue.num_waiting > 0
156
+ Thread.new do
157
+ while @queue.num_waiting > 0 && (conn = try_make_new)
158
+ @queue.push(conn)
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ # Whether the given size is less than the maximum size of the pool.
165
+ # In that case, the pool's current size is incremented. If this
166
+ # method returns true, space in the pool for the connection is
167
+ # preallocated, and preallocated_make_new should be called to
168
+ # create the connection.
169
+ #
170
+ # Calling code should have the mutex when calling this.
171
+ def can_make_new?(current_size)
172
+ if @max_size > current_size
173
+ @size[0] += 1
174
+ end
175
+ end
176
+
177
+ # Try to make a new connection if there is space in the pool.
178
+ # If the pool is already full, look for dead threads/fibers and
179
+ # disconnect the related connections.
180
+ #
181
+ # Calling code should not have the mutex when calling this.
182
+ def try_make_new
183
+ return preallocated_make_new if sync{can_make_new?(@size[0])}
184
+
185
+ to_disconnect = nil
186
+ do_make_new = false
187
+
188
+ sync do
189
+ current_size = @size[0]
190
+ @allocated.keys.each do |t|
191
+ unless t.alive?
192
+ (to_disconnect ||= []) << @allocated.delete(t)
193
+ current_size -= 1
194
+ end
195
+ end
196
+
197
+ do_make_new = true if can_make_new?(current_size)
198
+ end
199
+
200
+ begin
201
+ preallocated_make_new if do_make_new
202
+ ensure
203
+ if to_disconnect
204
+ to_disconnect.each{|conn| disconnect_connection(conn)}
205
+ fill_queue
206
+ end
207
+ end
208
+ end
209
+
210
+ # Assigns a connection to the supplied thread, if one
211
+ # is available.
212
+ #
213
+ # This should return a connection is one is available within the timeout,
214
+ # or raise PoolTimeout if a connection could not be acquired within the timeout.
215
+ #
216
+ # Calling code should not have the mutex when calling this.
217
+ def acquire(thread)
218
+ if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
219
+ sync{@allocated[thread] = conn}
220
+ else
221
+ name = db.opts[:name]
222
+ raise ::Sequel::PoolTimeout, "timeout: #{@timeout}#{", database name: #{name}" if name}"
223
+ end
224
+ end
225
+
226
+ # Create the maximum number of connections immediately. This should not be called
227
+ # with a true argument unles no code is currently operating on the database.
228
+ #
229
+ # Calling code should not have the mutex when calling this.
230
+ def preconnect(concurrent = false)
231
+ if concurrent
232
+ if times = sync{@max_size > (size = @size[0]) ? @max_size - size : false}
233
+ times.times.map{Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
234
+ end
235
+ else
236
+ while conn = try_make_new
237
+ @queue.push(conn)
238
+ end
239
+ end
240
+
241
+ nil
242
+ end
243
+
244
+ # Releases the connection assigned to the supplied thread back to the pool.
245
+ #
246
+ # Calling code should not have the mutex when calling this.
247
+ def release(thread)
248
+ @queue.push(sync{@allocated.delete(thread)})
249
+ end
250
+
251
+ # Yield to the block while inside the mutex.
252
+ #
253
+ # Calling code should not have the mutex when calling this.
254
+ def sync
255
+ @mutex.synchronize{yield}
256
+ end
257
+ end
@@ -30,8 +30,11 @@ class Sequel::ConnectionPool
30
30
  :threaded => :ThreadedConnectionPool,
31
31
  :single => :SingleConnectionPool,
32
32
  :sharded_threaded => :ShardedThreadedConnectionPool,
33
- :sharded_single => :ShardedSingleConnectionPool
34
- }.freeze
33
+ :sharded_single => :ShardedSingleConnectionPool,
34
+ :timed_queue => :TimedQueueConnectionPool,
35
+ }
36
+ POOL_CLASS_MAP.to_a.each{|k, v| POOL_CLASS_MAP[k.to_s] = v}
37
+ POOL_CLASS_MAP.freeze
35
38
 
36
39
  # Class methods used to return an appropriate pool subclass, separated
37
40
  # into a module for easier overridding by extensions.
@@ -74,26 +77,35 @@ class Sequel::ConnectionPool
74
77
 
75
78
  # The after_connect proc used for this pool. This is called with each new
76
79
  # connection made, and is usually used to set custom per-connection settings.
77
- attr_accessor :after_connect
80
+ # Deprecated.
81
+ attr_reader :after_connect # SEQUEL6: Remove
78
82
 
79
- # An array of sql strings to execute on each new connection.
80
- attr_accessor :connect_sqls
83
+ # Override the after_connect proc for the connection pool. Deprecated.
84
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
85
+ def after_connect=(v) # SEQUEL6: Remove
86
+ @use_old_connect_api = true
87
+ @after_connect = v
88
+ end
89
+
90
+ # An array of sql strings to execute on each new connection. Deprecated.
91
+ attr_reader :connect_sqls # SEQUEL6: Remove
92
+
93
+ # Override the connect_sqls for the connection pool. Deprecated.
94
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
95
+ def connect_sqls=(v) # SEQUEL6: Remove
96
+ @use_old_connect_api = true
97
+ @connect_sqls = v
98
+ end
81
99
 
82
100
  # The Sequel::Database object tied to this connection pool.
83
101
  attr_accessor :db
84
102
 
85
- # Instantiates a connection pool with the given options. The block is called
86
- # with a single symbol (specifying the server/shard to use) every time a new
87
- # connection is needed. The following options are respected for all connection
88
- # pools:
89
- # :after_connect :: A callable object called after each new connection is made, with the
90
- # connection object (and server argument if the callable accepts 2 arguments),
91
- # useful for customizations that you want to apply to all connections.
92
- # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
93
- def initialize(db, opts=OPTS)
103
+ # Instantiates a connection pool with the given Database and options.
104
+ def initialize(db, opts=OPTS) # SEQUEL6: Remove second argument, always use db.opts
94
105
  @db = db
95
- @after_connect = opts[:after_connect]
96
- @connect_sqls = opts[:connect_sqls]
106
+ @use_old_connect_api = false # SEQUEL6: Remove
107
+ @after_connect = opts[:after_connect] # SEQUEL6: Remove
108
+ @connect_sqls = opts[:connect_sqls] # SEQUEL6: Remove
97
109
  @error_classes = db.send(:database_error_classes).dup.freeze
98
110
  end
99
111
 
@@ -119,25 +131,30 @@ class Sequel::ConnectionPool
119
131
  # and checking for connection errors.
120
132
  def make_new(server)
121
133
  begin
122
- conn = @db.connect(server)
134
+ if @use_old_connect_api
135
+ # SEQUEL6: Remove block
136
+ conn = @db.connect(server)
123
137
 
124
- if ac = @after_connect
125
- if ac.arity == 2
126
- ac.call(conn, server)
127
- else
128
- ac.call(conn)
138
+ if ac = @after_connect
139
+ if ac.arity == 2
140
+ ac.call(conn, server)
141
+ else
142
+ ac.call(conn)
143
+ end
129
144
  end
130
- end
131
-
132
- if cs = @connect_sqls
133
- cs.each do |sql|
134
- db.send(:log_connection_execute, conn, sql)
145
+
146
+ if cs = @connect_sqls
147
+ cs.each do |sql|
148
+ db.send(:log_connection_execute, conn, sql)
149
+ end
135
150
  end
151
+
152
+ conn
153
+ else
154
+ @db.new_connection(server)
136
155
  end
137
156
  rescue Exception=>exception
138
157
  raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
139
- end
140
- raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
141
- conn
158
+ end || raise(Sequel::DatabaseConnectionError, "Connection parameters not valid")
142
159
  end
143
160
  end
@@ -241,6 +241,30 @@ module Sequel
241
241
  pool.servers
242
242
  end
243
243
 
244
+ # Connect to the given server/shard. Handles database-generic post-connection
245
+ # setup not handled by #connect, using the :after_connect and :connect_sqls
246
+ # options.
247
+ def new_connection(server)
248
+ conn = connect(server)
249
+ opts = server_opts(server)
250
+
251
+ if ac = opts[:after_connect]
252
+ if ac.arity == 2
253
+ ac.call(conn, server)
254
+ else
255
+ ac.call(conn)
256
+ end
257
+ end
258
+
259
+ if cs = opts[:connect_sqls]
260
+ cs.each do |sql|
261
+ log_connection_execute(conn, sql)
262
+ end
263
+ end
264
+
265
+ conn
266
+ end
267
+
244
268
  # Returns true if the database is using a single-threaded connection pool.
245
269
  def single_threaded?
246
270
  @single_threaded
@@ -100,10 +100,14 @@ module Sequel
100
100
  # options hash.
101
101
  #
102
102
  # Accepts the following options:
103
+ # :after_connect :: A callable object called after each new connection is made, with the
104
+ # connection object (and server argument if the callable accepts 2 arguments),
105
+ # useful for customizations that you want to apply to all connections.
103
106
  # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
104
107
  # but before any connections are created.
105
108
  # :cache_schema :: Whether schema should be cached for this Database instance
106
109
  # :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting.
110
+ # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
107
111
  # :default_string_column_size :: The default size of string columns, 255 by default.
108
112
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
109
113
  # or string with extensions separated by columns. These extensions are loaded after
@@ -126,9 +130,11 @@ module Sequel
126
130
  # :single_threaded :: Whether to use a single-threaded connection pool.
127
131
  # :sql_log_level :: Method to use to log SQL to a logger, :info by default.
128
132
  #
133
+ # For sharded connection pools, :after_connect and :connect_sqls can be specified per-shard.
134
+ #
129
135
  # All options given are also passed to the connection pool. Additional options respected by
130
- # the connection pool are :after_connect, :connect_sqls, :max_connections, :pool_timeout,
131
- # :servers, and :servers_hash. See the connection pool documentation for details.
136
+ # the connection pool are :max_connections, :pool_timeout, :servers, and :servers_hash. See the
137
+ # connection pool documentation for details.
132
138
  def initialize(opts = OPTS)
133
139
  @opts ||= opts
134
140
  @opts = connection_pool_default_options.merge(@opts)
@@ -175,6 +175,9 @@ module Sequel
175
175
  if !c[:max_length] && c[:type] == :string && (max_length = column_schema_max_length(c[:db_type]))
176
176
  c[:max_length] = max_length
177
177
  end
178
+ if !c[:max_value] && !c[:min_value] && c[:type] == :integer && (min_max = column_schema_integer_min_max_values(c[:db_type]))
179
+ c[:min_value], c[:max_value] = min_max
180
+ end
178
181
  end
179
182
  schema_post_process(cols)
180
183
 
@@ -272,6 +275,40 @@ module Sequel
272
275
  column_schema_default_to_ruby_value(default, type) rescue nil
273
276
  end
274
277
 
278
+ INTEGER1_MIN_MAX = [-128, 127].freeze
279
+ INTEGER2_MIN_MAX = [-32768, 32767].freeze
280
+ INTEGER3_MIN_MAX = [-8388608, 8388607].freeze
281
+ INTEGER4_MIN_MAX = [-2147483648, 2147483647].freeze
282
+ INTEGER8_MIN_MAX = [-9223372036854775808, 9223372036854775807].freeze
283
+ UNSIGNED_INTEGER1_MIN_MAX = [0, 255].freeze
284
+ UNSIGNED_INTEGER2_MIN_MAX = [0, 65535].freeze
285
+ UNSIGNED_INTEGER3_MIN_MAX = [0, 16777215].freeze
286
+ UNSIGNED_INTEGER4_MIN_MAX = [0, 4294967295].freeze
287
+ UNSIGNED_INTEGER8_MIN_MAX = [0, 18446744073709551615].freeze
288
+
289
+ # Look at the db_type and guess the minimum and maximum integer values for
290
+ # the column.
291
+ def column_schema_integer_min_max_values(db_type)
292
+ unsigned = /unsigned/i =~ db_type
293
+ case db_type
294
+ when /big|int8/i
295
+ unsigned ? UNSIGNED_INTEGER8_MIN_MAX : INTEGER8_MIN_MAX
296
+ when /medium/i
297
+ unsigned ? UNSIGNED_INTEGER3_MIN_MAX : INTEGER3_MIN_MAX
298
+ when /small|int2/i
299
+ unsigned ? UNSIGNED_INTEGER2_MIN_MAX : INTEGER2_MIN_MAX
300
+ when /tiny/i
301
+ (unsigned || column_schema_tinyint_type_is_unsigned?) ? UNSIGNED_INTEGER1_MIN_MAX : INTEGER1_MIN_MAX
302
+ else
303
+ unsigned ? UNSIGNED_INTEGER4_MIN_MAX : INTEGER4_MIN_MAX
304
+ end
305
+ end
306
+
307
+ # Whether the tinyint type (if supported by the database) is unsigned by default.
308
+ def column_schema_tinyint_type_is_unsigned?
309
+ false
310
+ end
311
+
275
312
  # Look at the db_type and guess the maximum length of the column.
276
313
  # This assumes types such as varchar(255).
277
314
  def column_schema_max_length(db_type)
@@ -127,6 +127,18 @@ module Sequel
127
127
  #
128
128
  # DB[:table].delete # DELETE * FROM table
129
129
  # # => 3
130
+ #
131
+ # Some databases support using multiple tables in a DELETE query. This requires
132
+ # multiple FROM tables (JOINs can also be used). As multiple FROM tables use
133
+ # an implicit CROSS JOIN, you should make sure your WHERE condition uses the
134
+ # appropriate filters for the FROM tables:
135
+ #
136
+ # DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>c[:h]}}.
137
+ # delete
138
+ # # DELETE FROM a
139
+ # # USING b
140
+ # # INNER JOIN c ON (c.d = b.e)
141
+ # # WHERE ((a.f = b.g) AND (a.id = c.h))
130
142
  def delete(&block)
131
143
  sql = delete_sql
132
144
  if uses_returning?(:delete)
@@ -313,14 +325,18 @@ module Sequel
313
325
 
314
326
  # Inserts multiple records into the associated table. This method can be
315
327
  # used to efficiently insert a large number of records into a table in a
316
- # single query if the database supports it. Inserts
317
- # are automatically wrapped in a transaction.
328
+ # single query if the database supports it. Inserts are automatically
329
+ # wrapped in a transaction if necessary.
318
330
  #
319
331
  # This method is called with a columns array and an array of value arrays:
320
332
  #
321
333
  # DB[:table].import([:x, :y], [[1, 2], [3, 4]])
322
334
  # # INSERT INTO table (x, y) VALUES (1, 2)
323
- # # INSERT INTO table (x, y) VALUES (3, 4)
335
+ # # INSERT INTO table (x, y) VALUES (3, 4)
336
+ #
337
+ # or, if the database supports it:
338
+ #
339
+ # # INSERT INTO table (x, y) VALUES (1, 2), (3, 4)
324
340
  #
325
341
  # This method also accepts a dataset instead of an array of value arrays:
326
342
  #
@@ -328,9 +344,13 @@ module Sequel
328
344
  # # INSERT INTO table (x, y) SELECT a, b FROM table2
329
345
  #
330
346
  # Options:
331
- # :commit_every :: Open a new transaction for every given number of records.
332
- # For example, if you provide a value of 50, will commit
333
- # after every 50 records.
347
+ # :commit_every :: Open a new transaction for every given number of
348
+ # records. For example, if you provide a value of 50,
349
+ # will commit after every 50 records. When a
350
+ # transaction is not required, this option controls
351
+ # the maximum number of values to insert with a single
352
+ # statement; it does not force the use of a
353
+ # transaction.
334
354
  # :return :: When this is set to :primary_key, returns an array of
335
355
  # autoincremented primary key values for the rows inserted.
336
356
  # This does not have an effect if +values+ is a Dataset.
@@ -576,7 +596,7 @@ module Sequel
576
596
  # # SELECT * FROM table ORDER BY id LIMIT 1000 OFFSET 1000
577
597
  # # ...
578
598
  #
579
- # DB[:table].order(:id).paged_each(:rows_per_fetch=>100){|row| }
599
+ # DB[:table].order(:id).paged_each(rows_per_fetch: 100){|row| }
580
600
  # # SELECT * FROM table ORDER BY id LIMIT 100
581
601
  # # SELECT * FROM table ORDER BY id LIMIT 100 OFFSET 100
582
602
  # # ...
@@ -918,6 +938,19 @@ module Sequel
918
938
  #
919
939
  # DB[:table].update(x: Sequel[:x]+1, y: 0) # UPDATE table SET x = (x + 1), y = 0
920
940
  # # => 10
941
+ #
942
+ # Some databases support using multiple tables in an UPDATE query. This requires
943
+ # multiple FROM tables (JOINs can also be used). As multiple FROM tables use
944
+ # an implicit CROSS JOIN, you should make sure your WHERE condition uses the
945
+ # appropriate filters for the FROM tables:
946
+ #
947
+ # DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>10}}.
948
+ # update(:f=>Sequel[:c][:h])
949
+ # # UPDATE a
950
+ # # SET f = c.h
951
+ # # FROM b
952
+ # # INNER JOIN c ON (c.d = b.e)
953
+ # # WHERE ((a.f = b.g) AND (a.id = 10))
921
954
  def update(values=OPTS, &block)
922
955
  sql = update_sql(values)
923
956
  if uses_returning?(:update)
@@ -1023,18 +1056,19 @@ module Sequel
1023
1056
 
1024
1057
  # Internals of #import. If primary key values are requested, use
1025
1058
  # separate insert commands for each row. Otherwise, call #multi_insert_sql
1026
- # and execute each statement it gives separately.
1059
+ # and execute each statement it gives separately. A transaction is only used
1060
+ # if there are multiple statements to execute.
1027
1061
  def _import(columns, values, opts)
1028
1062
  trans_opts = Hash[opts]
1029
1063
  trans_opts[:server] = @opts[:server]
1030
1064
  if opts[:return] == :primary_key
1031
- @db.transaction(trans_opts){values.map{|v| insert(columns, v)}}
1065
+ _import_transaction(values, trans_opts){values.map{|v| insert(columns, v)}}
1032
1066
  else
1033
1067
  stmts = multi_insert_sql(columns, values)
1034
- @db.transaction(trans_opts){stmts.each{|st| execute_dui(st)}}
1068
+ _import_transaction(stmts, trans_opts){stmts.each{|st| execute_dui(st)}}
1035
1069
  end
1036
1070
  end
1037
-
1071
+
1038
1072
  # Return an array of arrays of values given by the symbols in ret_cols.
1039
1073
  def _select_map_multiple(ret_cols)
1040
1074
  map{|r| r.values_at(*ret_cols)}
@@ -1073,6 +1107,17 @@ module Sequel
1073
1107
  end
1074
1108
  end
1075
1109
 
1110
+ # Use a transaction when yielding to the block if multiple values/statements
1111
+ # are provided. When only a single value or statement is provided, then yield
1112
+ # without using a transaction.
1113
+ def _import_transaction(values, trans_opts, &block)
1114
+ if values.length > 1
1115
+ @db.transaction(trans_opts, &block)
1116
+ else
1117
+ yield
1118
+ end
1119
+ end
1120
+
1076
1121
  # Internals of +select_hash+ and +select_hash_groups+
1077
1122
  def _select_hash(meth, key_column, value_column, opts=OPTS)
1078
1123
  select(*(key_column.is_a?(Array) ? key_column : [key_column]) + (value_column.is_a?(Array) ? value_column : [value_column])).
@@ -152,6 +152,11 @@ module Sequel
152
152
  supports_distinct_on?
153
153
  end
154
154
 
155
+ # Whether placeholder literalizers are supported, true by default.
156
+ def supports_placeholder_literalizer?
157
+ true
158
+ end
159
+
155
160
  # Whether the dataset supports pattern matching by regular expressions, false by default.
156
161
  def supports_regexp?
157
162
  false
@@ -302,7 +302,7 @@ module Sequel
302
302
  cache_set(key, loader + 1)
303
303
  loader = nil
304
304
  end
305
- elsif cache_sql?
305
+ elsif cache_sql? && supports_placeholder_literalizer?
306
306
  cache_set(key, 1)
307
307
  end
308
308