sequel 5.101.0 → 5.104.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/lib/sequel/adapters/jdbc/derby.rb +2 -0
  4. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  5. data/lib/sequel/adapters/postgres.rb +1 -1
  6. data/lib/sequel/adapters/shared/mssql.rb +3 -3
  7. data/lib/sequel/adapters/shared/mysql.rb +5 -4
  8. data/lib/sequel/adapters/shared/postgres.rb +16 -16
  9. data/lib/sequel/adapters/shared/sqlite.rb +3 -3
  10. data/lib/sequel/adapters/sqlite.rb +1 -1
  11. data/lib/sequel/adapters/tinytds.rb +1 -1
  12. data/lib/sequel/connection_pool/sharded_timed_queue.rb +36 -11
  13. data/lib/sequel/connection_pool/timed_queue.rb +27 -9
  14. data/lib/sequel/connection_pool.rb +6 -0
  15. data/lib/sequel/database/schema_generator.rb +36 -5
  16. data/lib/sequel/database/schema_methods.rb +1 -1
  17. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
  18. data/lib/sequel/dataset/prepared_statements.rb +7 -4
  19. data/lib/sequel/dataset/query.rb +6 -3
  20. data/lib/sequel/dataset/sql.rb +6 -1
  21. data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
  22. data/lib/sequel/extensions/connection_expiration.rb +1 -1
  23. data/lib/sequel/extensions/connection_validator.rb +1 -1
  24. data/lib/sequel/extensions/dataset_run.rb +2 -2
  25. data/lib/sequel/extensions/date_arithmetic.rb +6 -6
  26. data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
  27. data/lib/sequel/extensions/migration.rb +14 -17
  28. data/lib/sequel/extensions/pg_enum.rb +1 -1
  29. data/lib/sequel/model/associations.rb +180 -6
  30. data/lib/sequel/plugins/dataset_associations.rb +20 -1
  31. data/lib/sequel/plugins/dirty.rb +5 -2
  32. data/lib/sequel/plugins/many_through_many.rb +21 -0
  33. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  34. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
  35. data/lib/sequel/plugins/serialization.rb +10 -1
  36. data/lib/sequel/version.rb +1 -1
  37. metadata +3 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9de2d20e1483de5f15a285b695d5eab98601c26f1e711e378de1a03331f197a1
4
- data.tar.gz: 4d7df1811d7242dad22f6c9968fb43feecb5a86fd814aa2ba7434bdb44369cb8
3
+ metadata.gz: a28329284b305f2ea739dc8ca05aa690db11c9c68c0dcd5fb47035ee621edbf9
4
+ data.tar.gz: 8cdf6a6239baddff4fc926718409413713801d4d49481b7f151a439fa8a8bbe1
5
5
  SHA512:
6
- metadata.gz: a9aa7a3bd15f4f35457aace92b68b4734d367273996dc605d80adbe87bca1c945fc484571cad90149e69807980eea2f8b2a6a6fea85a4a0670c10a082a056a03
7
- data.tar.gz: ca5c1eb34d71d16660a0b5eb2b9e5b4c784f06d6e4f89b036f00a62f31b57d564f071b8346bdcb9ebd9cced7bcfbe429e2c50bcda1b8b150d723b1e59105c474
6
+ metadata.gz: 9f1f61fc96b631a8c081b831fa8a9dfa962908bcb11bde6975cc75c234e416941492ac140b6b9d995aa8fcd84ffec0c519eb11e4aba43b16fe779cf66e23973e
7
+ data.tar.gz: ae638f7d62552732ebe9fd7af00fb3bdb931c078cabb3cc134e3d370a3104b5ce711a454d57f14e427fb7e522b95755366d7128cb3d1de3f325da19dd3e8ce5f
data/MIT-LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2007-2008 Sharon Rosner
2
- Copyright (c) 2008-2023 Jeremy Evans
2
+ Copyright (c) 2008-2026 Jeremy Evans and contributors
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to
@@ -1,5 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ # SEQUEL6: Remove
4
+
3
5
  Sequel::JDBC.load_driver('Java::OrgApacheDerbyJdbc::EmbeddedDriver', :Derby)
4
6
  require_relative 'transactions'
5
7
  require_relative '../utils/columns_limit_1'
@@ -18,7 +18,7 @@ module Sequel
18
18
  include AutoCastDateAndTime
19
19
 
20
20
  def commit_prepared_transaction(transaction_id, opts=OPTS)
21
- run("COMMIT TRANSACTION #{transaction_id}", opts)
21
+ run("COMMIT TRANSACTION #{transaction_id}".freeze, opts)
22
22
  end
23
23
 
24
24
  def database_type
@@ -36,7 +36,7 @@ module Sequel
36
36
  end
37
37
 
38
38
  def rollback_prepared_transaction(transaction_id, opts=OPTS)
39
- run("ROLLBACK TRANSACTION #{transaction_id}", opts)
39
+ run("ROLLBACK TRANSACTION #{transaction_id}".freeze, opts)
40
40
  end
41
41
 
42
42
  # H2 uses an IDENTITY type for primary keys
@@ -727,7 +727,7 @@ module Sequel
727
727
  prepared_args << y
728
728
  i = prepared_args.length
729
729
  end
730
- LiteralString.new("#{prepared_arg_placeholder}#{i}")
730
+ LiteralString.new("#{prepared_arg_placeholder}#{i}".freeze)
731
731
  end
732
732
  end
733
733
 
@@ -106,7 +106,7 @@ module Sequel
106
106
  values << value
107
107
  end
108
108
 
109
- sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
109
+ sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}".freeze
110
110
 
111
111
  ds = dataset.with_sql(sql)
112
112
  ds = ds.server(opts[:server]) if opts[:server]
@@ -400,7 +400,7 @@ module Sequel
400
400
  # Error if a string is given.
401
401
  def create_table_as(name, ds, options)
402
402
  raise(Error, "must provide dataset instance as value of create_table :as option on MSSQL") unless ds.is_a?(Sequel::Dataset)
403
- run(ds.into(name).sql)
403
+ run(ds.into(name).sql.freeze)
404
404
  end
405
405
 
406
406
  DATABASE_ERROR_REGEXPS = {
@@ -878,7 +878,7 @@ module Sequel
878
878
  elsif @opts[:output]
879
879
  # no transaction: our multi_insert_sql_strategy should guarantee
880
880
  # that there's only ever a single statement.
881
- sql = multi_insert_sql(columns, values)[0]
881
+ sql = multi_insert_sql(columns, values)[0].freeze
882
882
  naked.with_sql(sql).map{|v| v.length == 1 ? v.values.first : v}
883
883
  else
884
884
  super
@@ -40,7 +40,7 @@ module Sequel
40
40
  end
41
41
 
42
42
  def commit_prepared_transaction(transaction_id, opts=OPTS)
43
- run("XA COMMIT #{literal(transaction_id)}", opts)
43
+ run("XA COMMIT #{literal(transaction_id)}".freeze, opts)
44
44
  end
45
45
 
46
46
  def database_type
@@ -103,7 +103,7 @@ module Sequel
103
103
  sql += " FROM #{literal(schema)}"
104
104
  end
105
105
 
106
- metadata_dataset.with_sql(sql).each do |r|
106
+ metadata_dataset.with_sql(sql.freeze).each do |r|
107
107
  name = r[:Key_name]
108
108
  next if name == 'PRIMARY'
109
109
  name = m.call(name)
@@ -115,7 +115,7 @@ module Sequel
115
115
  end
116
116
 
117
117
  def rollback_prepared_transaction(transaction_id, opts=OPTS)
118
- run("XA ROLLBACK #{literal(transaction_id)}", opts)
118
+ run("XA ROLLBACK #{literal(transaction_id)}".freeze, opts)
119
119
  end
120
120
 
121
121
  # Whether the database is MariaDB and not MySQL
@@ -780,7 +780,8 @@ module Sequel
780
780
  # Load the PrettyTable class, needed for explain output
781
781
  Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
782
782
 
783
- ds = db.send(:metadata_dataset).with_sql(((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
783
+ sql = ((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql
784
+ ds = db.send(:metadata_dataset).with_sql(sql.freeze).naked
784
785
  rows = ds.all
785
786
  Sequel::PrettyTable.string(rows, ds.columns)
786
787
  end
@@ -339,7 +339,7 @@ module Sequel
339
339
  end
340
340
 
341
341
  def commit_prepared_transaction(transaction_id, opts=OPTS)
342
- run("COMMIT PREPARED #{literal(transaction_id)}", opts)
342
+ run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
343
343
  end
344
344
 
345
345
  # A hash of metadata for CHECK constraints on the table.
@@ -416,7 +416,7 @@ module Sequel
416
416
  return if already_identity
417
417
 
418
418
  transaction(server_hash) do
419
- run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
419
+ run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
420
420
 
421
421
  ds.from(:pg_depend).
422
422
  where(:classid=>pg_class, :objid=>seq_oid, :objsubid=>0, :deptype=>'a').
@@ -453,7 +453,7 @@ module Sequel
453
453
  # often used here if :security_definer is used.
454
454
  # :strict :: Makes the function return NULL when any argument is NULL.
455
455
  def create_function(name, definition, opts=OPTS)
456
- self << create_function_sql(name, definition, opts)
456
+ self << create_function_sql(name, definition, opts).freeze
457
457
  end
458
458
 
459
459
  # Create the procedural language in the database. Arguments:
@@ -464,7 +464,7 @@ module Sequel
464
464
  # :trusted :: Marks the language being created as trusted, allowing unprivileged users to create functions using this language.
465
465
  # :validator :: The name of previously registered function used as a validator of functions defined in this language.
466
466
  def create_language(name, opts=OPTS)
467
- self << create_language_sql(name, opts)
467
+ self << create_language_sql(name, opts).freeze
468
468
  end
469
469
 
470
470
  # Create a schema in the database. Arguments:
@@ -473,7 +473,7 @@ module Sequel
473
473
  # :if_not_exists :: Don't raise an error if the schema already exists (PostgreSQL 9.3+)
474
474
  # :owner :: The owner to set for the schema (defaults to current user if not specified)
475
475
  def create_schema(name, opts=OPTS)
476
- self << create_schema_sql(name, opts)
476
+ self << create_schema_sql(name, opts).freeze
477
477
  end
478
478
 
479
479
  # Support partitions of tables using the :partition_of option.
@@ -509,7 +509,7 @@ module Sequel
509
509
  # :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
510
510
  # :when :: A filter to use for the trigger
511
511
  def create_trigger(table, name, function, opts=OPTS)
512
- self << create_trigger_sql(table, name, function, opts)
512
+ self << create_trigger_sql(table, name, function, opts).freeze
513
513
  end
514
514
 
515
515
  def database_type
@@ -542,7 +542,7 @@ module Sequel
542
542
  # default is plpgsql. Can be specified as a string or a symbol.
543
543
  def do(code, opts=OPTS)
544
544
  language = opts[:language]
545
- run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
545
+ run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
546
546
  end
547
547
 
548
548
  # Drops the function from the database. Arguments:
@@ -552,7 +552,7 @@ module Sequel
552
552
  # :cascade :: Drop other objects depending on this function.
553
553
  # :if_exists :: Don't raise an error if the function doesn't exist.
554
554
  def drop_function(name, opts=OPTS)
555
- self << drop_function_sql(name, opts)
555
+ self << drop_function_sql(name, opts).freeze
556
556
  end
557
557
 
558
558
  # Drops a procedural language from the database. Arguments:
@@ -561,7 +561,7 @@ module Sequel
561
561
  # :cascade :: Drop other objects depending on this function.
562
562
  # :if_exists :: Don't raise an error if the function doesn't exist.
563
563
  def drop_language(name, opts=OPTS)
564
- self << drop_language_sql(name, opts)
564
+ self << drop_language_sql(name, opts).freeze
565
565
  end
566
566
 
567
567
  # Drops a schema from the database. Arguments:
@@ -570,7 +570,7 @@ module Sequel
570
570
  # :cascade :: Drop all objects in this schema.
571
571
  # :if_exists :: Don't raise an error if the schema doesn't exist.
572
572
  def drop_schema(name, opts=OPTS)
573
- self << drop_schema_sql(name, opts)
573
+ self << drop_schema_sql(name, opts).freeze
574
574
  remove_all_cached_schemas
575
575
  end
576
576
 
@@ -581,7 +581,7 @@ module Sequel
581
581
  # :cascade :: Drop other objects depending on this function.
582
582
  # :if_exists :: Don't raise an error if the function doesn't exist.
583
583
  def drop_trigger(table, name, opts=OPTS)
584
- self << drop_trigger_sql(table, name, opts)
584
+ self << drop_trigger_sql(table, name, opts).freeze
585
585
  end
586
586
 
587
587
  # Return full foreign key information using the pg system tables, including
@@ -745,7 +745,7 @@ module Sequel
745
745
  # name :: Current name of the schema
746
746
  # opts :: New name for the schema
747
747
  def rename_schema(name, new_name)
748
- self << rename_schema_sql(name, new_name)
748
+ self << rename_schema_sql(name, new_name).freeze
749
749
  remove_all_cached_schemas
750
750
  end
751
751
 
@@ -756,7 +756,7 @@ module Sequel
756
756
  # DB.refresh_view(:items_view, concurrently: true)
757
757
  # # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
758
758
  def refresh_view(name, opts=OPTS)
759
- run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
759
+ run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
760
760
  end
761
761
 
762
762
  # Reset the primary key sequence for the given table, basing it on the
@@ -769,7 +769,7 @@ module Sequel
769
769
  table = Sequel.qualify(s, t) if s
770
770
 
771
771
  if server_version >= 100000
772
- seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
772
+ seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
773
773
  increment_by = :seqincrement
774
774
  min_value = :seqmin
775
775
  # :nocov:
@@ -784,7 +784,7 @@ module Sequel
784
784
  end
785
785
 
786
786
  def rollback_prepared_transaction(transaction_id, opts=OPTS)
787
- run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
787
+ run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
788
788
  end
789
789
 
790
790
  # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
@@ -1469,7 +1469,7 @@ module Sequel
1469
1469
 
1470
1470
  def column_references_add_period(cols)
1471
1471
  cols= cols.dup
1472
- cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}")
1472
+ cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
1473
1473
  cols
1474
1474
  end
1475
1475
 
@@ -195,7 +195,7 @@ module Sequel
195
195
 
196
196
  # Dataset used for parsing schema
197
197
  def _parse_pragma_ds(table_name, opts)
198
- metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
198
+ metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)".freeze, input_identifier_meth(opts[:dataset]).call(table_name))
199
199
  end
200
200
 
201
201
  # Run all alter_table commands in a transaction. This is technically only
@@ -407,7 +407,7 @@ module Sequel
407
407
  def defined_columns_for(table)
408
408
  cols = parse_pragma(table, OPTS)
409
409
  cols.each do |c|
410
- c[:default] = LiteralString.new(c[:default]) if c[:default]
410
+ c[:default] = LiteralString.new(c[:default]).freeze if c[:default]
411
411
  c[:type] = c[:db_type]
412
412
  end
413
413
  cols
@@ -684,7 +684,7 @@ module Sequel
684
684
  # Load the PrettyTable class, needed for explain output
685
685
  Sequel.extension(:_pretty_table) unless defined?(Sequel::PrettyTable)
686
686
 
687
- ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
687
+ ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
688
688
  rows = ds.all
689
689
  Sequel::PrettyTable.string(rows, ds.columns)
690
690
  end
@@ -388,7 +388,7 @@ module Sequel
388
388
  # SQLite uses a : before the name of the argument for named
389
389
  # arguments.
390
390
  def prepared_arg(k)
391
- LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
391
+ LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}".freeze)
392
392
  end
393
393
  end
394
394
 
@@ -205,7 +205,7 @@ module Sequel
205
205
  private
206
206
 
207
207
  def prepared_arg(k)
208
- LiteralString.new("@#{k.to_s.gsub('.', '__')}")
208
+ LiteralString.new("@#{k.to_s.gsub('.', '__')}".freeze)
209
209
  end
210
210
  end
211
211
 
@@ -71,7 +71,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
71
71
  while true
72
72
  conn = nil
73
73
  begin
74
- break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
74
+ break unless (conn = available(queue, server)) && !conns[conn]
75
75
  conns[conn] = true
76
76
  yield conn
77
77
  ensure
@@ -91,14 +91,16 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
91
91
  # creates new connections to the database.
92
92
  #
93
93
  # If the :server option is provided, it should be a symbol or array of symbols,
94
- # and then the method will only disconnect connectsion from those specified shards.
94
+ # and then the method will only disconnect connections from those specified shards.
95
95
  def disconnect(opts=OPTS)
96
96
  (opts[:server] ? Array(opts[:server]) : sync{@servers.keys}).each do |server|
97
97
  raise Sequel::Error, "invalid server" unless queue = sync{@queues[server]}
98
- while conn = queue.pop(timeout: 0)
98
+ nconns = 0
99
+ while conn = available(queue, server)
100
+ nconns += 1
99
101
  disconnect_pool_connection(conn, server)
100
102
  end
101
- fill_queue(server)
103
+ fill_queue(server, nconns)
102
104
  end
103
105
  nil
104
106
  end
@@ -132,7 +134,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
132
134
  conn = nil
133
135
  disconnect_pool_connection(oconn, server) if oconn
134
136
  sync{@allocated[server].delete(t)}
135
- fill_queue(server)
137
+ fill_queue(server, 1)
136
138
  end
137
139
  raise
138
140
  ensure
@@ -167,7 +169,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
167
169
 
168
170
  queue = @queues[server]
169
171
 
170
- while conn = queue.pop(timeout: 0)
172
+ while conn = available(queue, server)
171
173
  @sizes[server] -= 1
172
174
  conns << conn
173
175
  end
@@ -225,6 +227,12 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
225
227
  conns.each{|conn| disconnect_connection(conn)}
226
228
  end
227
229
 
230
+ # Only for use by extension that need to disconnect a connection inside acquire.
231
+ # Takes the connection and any arguments accepted by acquire.
232
+ def disconnect_acquired_connection(conn, _, server)
233
+ disconnect_pool_connection(conn, server)
234
+ end
235
+
228
236
  # Decrement the current size of the pool for the server when disconnecting connections.
229
237
  #
230
238
  # Calling code should not have the mutex when calling this.
@@ -244,11 +252,15 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
244
252
  # after disconnecting to potentially add new connections to the
245
253
  # pool, so the threads that are currently waiting for connections
246
254
  # do not timeout after the pool is no longer full.
247
- def fill_queue(server)
255
+ #
256
+ # nconns specifies the maximum number of connections to add, which should
257
+ # be the number of connections that were disconnected.
258
+ def fill_queue(server, nconns)
248
259
  queue = sync{@queues[server]}
249
- if queue.num_waiting > 0
260
+ if nconns > 0 && queue.num_waiting > 0
250
261
  Thread.new do
251
- while queue.num_waiting > 0 && (conn = try_make_new(server))
262
+ while nconns > 0 && queue.num_waiting > 0 && (conn = try_make_new(server))
263
+ nconns -= 1
252
264
  queue.push(conn)
253
265
  end
254
266
  end
@@ -297,7 +309,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
297
309
  ensure
298
310
  if to_disconnect
299
311
  to_disconnect.each{|conn| disconnect_pool_connection(conn, server)}
300
- fill_queue(server)
312
+ fill_queue(server, to_disconnect.size)
301
313
  end
302
314
  end
303
315
  end
@@ -311,7 +323,7 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
311
323
  # Calling code should not have the mutex when calling this.
312
324
  def acquire(thread, server)
313
325
  queue = sync{@queues[server]}
314
- if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
326
+ if conn = available(queue, server) || try_make_new(server) || wait_until_available(queue, server)
315
327
  sync{@allocated[server][thread] = conn}
316
328
  else
317
329
  name = db.opts[:name]
@@ -319,6 +331,19 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
319
331
  end
320
332
  end
321
333
 
334
+ # Return the next connection in the pool if there is one available. Returns nil
335
+ # if no connection is currently available.
336
+ def available(queue, _server)
337
+ queue.pop(timeout: 0)
338
+ end
339
+
340
+ # Return the next connection in the pool if there is one available. If not, wait
341
+ # until the timeout for a connection to become available. If there is still no
342
+ # available connection, return nil.
343
+ def wait_until_available(queue, _server)
344
+ queue.pop(timeout: @timeout)
345
+ end
346
+
322
347
  # Returns the connection owned by the supplied thread for the given server,
323
348
  # if any. The calling code should NOT already have the mutex before calling this.
324
349
  def owned_connection(thread, server)
@@ -42,7 +42,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
42
42
  while true
43
43
  conn = nil
44
44
  begin
45
- break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
45
+ break unless (conn = available) && !conns[conn]
46
46
  conns[conn] = true
47
47
  yield conn
48
48
  ensure
@@ -59,10 +59,12 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
59
59
  # Once a connection is requested using #hold, the connection pool
60
60
  # creates new connections to the database.
61
61
  def disconnect(opts=OPTS)
62
- while conn = @queue.pop(timeout: 0)
62
+ nconns = 0
63
+ while conn = available
64
+ nconns += 1
63
65
  disconnect_connection(conn)
64
66
  end
65
- fill_queue
67
+ fill_queue(nconns)
66
68
  nil
67
69
  end
68
70
 
@@ -94,7 +96,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
94
96
  conn = nil
95
97
  disconnect_connection(oconn) if oconn
96
98
  sync{@allocated.delete(t)}
97
- fill_queue
99
+ fill_queue(1)
98
100
  end
99
101
  raise
100
102
  ensure
@@ -156,10 +158,13 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
156
158
  # after disconnecting to potentially add new connections to the
157
159
  # pool, so the threads that are currently waiting for connections
158
160
  # do not timeout after the pool is no longer full.
159
- def fill_queue
160
- if @queue.num_waiting > 0
161
+ #
162
+ # nconns specifies the maximum number of connections to add, which should
163
+ # be the number of connections that were disconnected.
164
+ def fill_queue(nconns)
165
+ if nconns > 0 && @queue.num_waiting > 0
161
166
  Thread.new do
162
- while @queue.num_waiting > 0 && (conn = try_make_new)
167
+ while nconns > 0 && @queue.num_waiting > 0 && (conn = try_make_new)
163
168
  @queue.push(conn)
164
169
  end
165
170
  end
@@ -207,7 +212,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
207
212
  ensure
208
213
  if to_disconnect
209
214
  to_disconnect.each{|conn| disconnect_connection(conn)}
210
- fill_queue
215
+ fill_queue(to_disconnect.size)
211
216
  end
212
217
  end
213
218
  end
@@ -220,7 +225,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
220
225
  #
221
226
  # Calling code should not have the mutex when calling this.
222
227
  def acquire(thread)
223
- if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
228
+ if conn = available || try_make_new || wait_until_available
224
229
  sync{@allocated[thread] = conn}
225
230
  else
226
231
  name = db.opts[:name]
@@ -228,6 +233,19 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
228
233
  end
229
234
  end
230
235
 
236
+ # Return the next connection in the pool if there is one available. Returns nil
237
+ # if no connection is currently available.
238
+ def available
239
+ @queue.pop(timeout: 0)
240
+ end
241
+
242
+ # Return the next connection in the pool if there is one available. If not, wait
243
+ # until the timeout for a connection to become available. If there is still no
244
+ # available connection, return nil.
245
+ def wait_until_available
246
+ @queue.pop(timeout: @timeout)
247
+ end
248
+
231
249
  # Returns the connection owned by the supplied thread,
232
250
  # if any. The calling code should NOT already have the mutex before calling this.
233
251
  def owned_connection(thread)
@@ -131,6 +131,12 @@ class Sequel::ConnectionPool
131
131
  db.disconnect_connection(conn)
132
132
  end
133
133
 
134
+ # Only for use by extension that need to disconnect a connection inside acquire.
135
+ # Takes the connection and any arguments accepted by acquire.
136
+ def disconnect_acquired_connection(conn, *)
137
+ disconnect_connection(conn)
138
+ end
139
+
134
140
  # Whether the given exception is a disconnect exception.
135
141
  def disconnect_error?(exception)
136
142
  exception.is_a?(Sequel::DatabaseDisconnectError) || db.send(:disconnect_error?, exception, OPTS)
@@ -3,6 +3,33 @@
3
3
  module Sequel
4
4
  # The Schema module holds the schema generators.
5
5
  module Schema
6
+ module ColumnOptionMerger
7
+ private
8
+
9
+ # Merge given options into the column's default options. For backwards compatibility,
10
+ # the options take priority, but in cases where the option value overrides the argument
11
+ # value, and the values are different, we warn as this is likely to be an error in the
12
+ # code.
13
+ def _merge_column_options(defaults, opts)
14
+ defaults.merge!(opts) do |k, defv, v|
15
+ unless defv == v
16
+ # :nocov:
17
+ if RUBY_VERSION >= "3.2"
18
+ # :nocov:
19
+ caller_loc = Thread.each_caller_location do |loc|
20
+ break loc unless loc.path == __FILE__
21
+ end
22
+ caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
23
+ end
24
+ warn("#{caller_loc}#{k.inspect} option value (#{v.inspect}) overrides argument value (#{defv.inspect})")
25
+ end
26
+
27
+ v
28
+ end
29
+ end
30
+ end
31
+ private_constant :ColumnOptionMerger
32
+
6
33
  # Schema::CreateTableGenerator is an internal class that the user is not expected
7
34
  # to instantiate directly. Instances are created by Database#create_table.
8
35
  # It is used to specify table creation parameters. It takes a Database
@@ -17,6 +44,8 @@ module Sequel
17
44
  # For more information on Sequel's support for schema modification, see
18
45
  # the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
19
46
  class CreateTableGenerator
47
+ include ColumnOptionMerger
48
+
20
49
  # Classes specifying generic types that Sequel will convert to database-specific types.
21
50
  GENERIC_TYPES=%w'String Integer Float Numeric BigDecimal Date DateTime Time File TrueClass FalseClass'.freeze
22
51
 
@@ -173,13 +202,13 @@ module Sequel
173
202
  # :clustered :: When using :primary_key or :unique, marks the primary key or unique
174
203
  # constraint as CLUSTERED (if true), or NONCLUSTERED (if false).
175
204
  def column(name, type, opts = OPTS)
176
- columns << {:name => name, :type => type}.merge!(opts)
205
+ columns << _merge_column_options({:name => name, :type => type}, opts)
177
206
  if index_opts = opts[:index]
178
207
  index(name, index_opts.is_a?(Hash) ? index_opts : OPTS)
179
208
  end
180
209
  nil
181
210
  end
182
-
211
+
183
212
  # Adds a named CHECK constraint (or unnamed if name is nil),
184
213
  # with the given block or args. To provide options for the constraint, pass
185
214
  # a hash as the first argument.
@@ -246,7 +275,7 @@ module Sequel
246
275
  opts.merge(:table=>table)
247
276
  end
248
277
  return composite_foreign_key(name, opts) if name.is_a?(Array)
249
- column(name, Integer, opts)
278
+ column(name, opts.fetch(:type, Integer), opts)
250
279
  end
251
280
 
252
281
  # Add a full text index on the given columns.
@@ -429,6 +458,8 @@ module Sequel
429
458
  # For more information on Sequel's support for schema modification, see
430
459
  # the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
431
460
  class AlterTableGenerator
461
+ include ColumnOptionMerger
462
+
432
463
  # An array of operations to perform
433
464
  attr_reader :operations
434
465
 
@@ -454,7 +485,7 @@ module Sequel
454
485
  # :after :: The name of an existing column that the new column should be positioned after
455
486
  # :first :: Create this new column before all other existing columns
456
487
  def add_column(name, type, opts = OPTS)
457
- op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
488
+ op = _merge_column_options({:op => :add_column, :name => name, :type => type}, opts)
458
489
  index_opts = op.delete(:index)
459
490
  @operations << op
460
491
  add_index(name, index_opts.is_a?(Hash) ? index_opts : OPTS) if index_opts
@@ -519,7 +550,7 @@ module Sequel
519
550
  # sense when using an array of columns.
520
551
  def add_foreign_key(name, table, opts = OPTS)
521
552
  return add_composite_foreign_key(name, table, opts) if name.is_a?(Array)
522
- add_column(name, Integer, {:table=>table}.merge!(opts))
553
+ add_column(name, opts.fetch(:type, Integer), {:table=>table}.merge!(opts))
523
554
  end
524
555
 
525
556
  # Add a full text index on the given columns.
@@ -819,7 +819,7 @@ module Sequel
819
819
  # SELECT sql statement.
820
820
  def create_table_as(name, sql, options)
821
821
  sql = sql.sql if sql.is_a?(Sequel::Dataset)
822
- run(create_table_as_sql(name, sql, options))
822
+ run(create_table_as_sql(name, sql, options).freeze)
823
823
  end
824
824
 
825
825
  # SQL statement for creating a table from the result of a SELECT statement.
@@ -117,6 +117,8 @@ module Sequel
117
117
  frags << final_sql
118
118
  prepared_sql << final_sql
119
119
 
120
+ frags.each(&:freeze)
121
+ frags.freeze
120
122
  [prepared_sql, frags]
121
123
  end
122
124
 
@@ -125,6 +127,7 @@ module Sequel
125
127
  @argn = -1
126
128
  @args = []
127
129
  ds = yield self, dataset
130
+ ds.opts[:sql].freeze
128
131
  sql = ds.clone(:placeholder_literalizer=>self).sql
129
132
 
130
133
  last_offset = 0