sequel 5.93.0 → 5.95.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/adapters/ado/access.rb +2 -2
  3. data/lib/sequel/adapters/ado.rb +1 -1
  4. data/lib/sequel/adapters/ibmdb.rb +1 -1
  5. data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
  6. data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
  7. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
  8. data/lib/sequel/adapters/jdbc/sqlserver.rb +1 -1
  9. data/lib/sequel/adapters/jdbc.rb +21 -7
  10. data/lib/sequel/adapters/mysql.rb +1 -1
  11. data/lib/sequel/adapters/mysql2.rb +2 -2
  12. data/lib/sequel/adapters/odbc.rb +1 -1
  13. data/lib/sequel/adapters/shared/db2.rb +8 -3
  14. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  15. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  16. data/lib/sequel/adapters/shared/postgres.rb +191 -22
  17. data/lib/sequel/adapters/shared/sqlite.rb +3 -3
  18. data/lib/sequel/adapters/tinytds.rb +1 -1
  19. data/lib/sequel/adapters/trilogy.rb +1 -1
  20. data/lib/sequel/connection_pool/sharded_timed_queue.rb +6 -0
  21. data/lib/sequel/connection_pool/timed_queue.rb +6 -1
  22. data/lib/sequel/core.rb +1 -1
  23. data/lib/sequel/database/misc.rb +3 -3
  24. data/lib/sequel/database/query.rb +1 -1
  25. data/lib/sequel/database/schema_generator.rb +66 -10
  26. data/lib/sequel/database/schema_methods.rb +79 -28
  27. data/lib/sequel/dataset/query.rb +10 -2
  28. data/lib/sequel/dataset/sql.rb +1 -1
  29. data/lib/sequel/extensions/async_thread_pool.rb +1 -1
  30. data/lib/sequel/extensions/caller_logging.rb +1 -3
  31. data/lib/sequel/extensions/eval_inspect.rb +1 -1
  32. data/lib/sequel/extensions/inflector.rb +2 -2
  33. data/lib/sequel/extensions/migration.rb +1 -1
  34. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  35. data/lib/sequel/extensions/provenance.rb +1 -3
  36. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  37. data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
  38. data/lib/sequel/plugins/constraint_validations.rb +15 -10
  39. data/lib/sequel/plugins/json_serializer.rb +3 -3
  40. data/lib/sequel/plugins/unused_associations.rb +4 -2
  41. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  42. data/lib/sequel/version.rb +1 -1
  43. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd35e8223b7f6d450cf4c1efeaad152236b4a1e360299aa46b03841bb45996c6
4
- data.tar.gz: '096a62cc241cf6f48d0c2b3e0799402f21aa24a1196a332e022ef53ebaaeea85'
3
+ metadata.gz: 7a008ffa4ce90081d7259c785ca68f6f3e3209d789ce6016da8040ea89acca49
4
+ data.tar.gz: a9f7af4d4214ca4dd82d4909e8aa2c79c96821245f39cc34278df788ae319cb6
5
5
  SHA512:
6
- metadata.gz: 0b66c7e30dfb69468ad41bfe64de4e0442ad62590ea735b2e4bb591c46b949794a7a453c21532999c89296694f0136d9353041ec56fc470ab2ba0483f0534d61
7
- data.tar.gz: 978dcd457829ff39f1ae9769dc574049cf29d07a2dbc8e307aa806a7b1954cd72432257de37d9e5236269b5746f615f8d0f7feb4979e9b86b619f582344afc29
6
+ metadata.gz: 9aa9d1fb1cb8a0a323331b2cdccc48b51b5b9f38289be280f693e0b998747d6c7a5e204c29c13cb89bd50caeb5bde604527eb8966249edfe3ab0866bb039bdf2
7
+ data.tar.gz: 894997717d624f602d9dbfaa554ac03270a50a69f5da6982fd687101af95e94c02a3c82b0fbe9e5ec8a5c4d39265fb4e18d9b9f706be216fccc7c45cd72a00ce
@@ -281,7 +281,7 @@ module Sequel
281
281
  fetch_ado_schema(:columns, [nil,nil,table_name.to_s,nil]) do |row|
282
282
  rows << AdoSchema::Column.new(row)
283
283
  end
284
- rows.sort!{|a,b| a["ORDINAL_POSITION"] <=> b["ORDINAL_POSITION"]}
284
+ rows.sort_by! { |a| a["ORDINAL_POSITION"] }
285
285
  end
286
286
 
287
287
  def ado_schema_foreign_keys(table_name)
@@ -289,7 +289,7 @@ module Sequel
289
289
  fetch_ado_schema(:foreign_keys, [nil,nil,nil,nil,nil,table_name.to_s]) do |row|
290
290
  rows << row
291
291
  end
292
- rows.sort!{|a,b| a["ORDINAL"] <=> b["ORDINAL"]}
292
+ rows.sort_by! { |a| a["ORDINAL"] }
293
293
  end
294
294
 
295
295
  def fetch_ado_schema(type, criteria=[])
@@ -234,7 +234,7 @@ module Sequel
234
234
  end
235
235
 
236
236
  def disconnect_error?(e, opts)
237
- super || (e.is_a?(::WIN32OLERuntimeError) && e.message =~ /Communication link failure/)
237
+ super || (e.is_a?(::WIN32OLERuntimeError) && e.message.include?('Communication link failure'))
238
238
  end
239
239
 
240
240
  def rollback_transaction(conn, opts=OPTS)
@@ -324,7 +324,7 @@ module Sequel
324
324
  when Numeric
325
325
  v.to_s
326
326
  when Date, Time
327
- literal(v).gsub("'", '')
327
+ literal(v).delete("'")
328
328
  else
329
329
  v
330
330
  end
@@ -26,7 +26,7 @@ module Sequel
26
26
 
27
27
  # Raise a disconnect error if the SQL state of the cause of the exception indicates so.
28
28
  def disconnect_error?(exception, opts)
29
- exception.message =~ /\ACommunications link failure/ || super
29
+ exception.message.start_with?("Communications link failure") || super
30
30
  end
31
31
 
32
32
  # Get the last inserted id using LAST_INSERT_ID().
@@ -64,7 +64,7 @@ module Sequel
64
64
 
65
65
  # Convert tinyint(1) type to boolean
66
66
  def schema_column_type(db_type)
67
- db_type =~ /\Atinyint\(1\)/ ? :boolean : super
67
+ db_type.start_with?("tinyint(1)") ? :boolean : super
68
68
  end
69
69
 
70
70
  # Run the default connection setting SQL statements.
@@ -55,7 +55,7 @@ module Sequel
55
55
  end
56
56
 
57
57
  def disconnect_error?(exception, opts)
58
- super || exception.message =~ /\AClosed Connection/
58
+ super || exception.message.start_with?("Closed Connection")
59
59
  end
60
60
 
61
61
  # Default the fetch size for statements to 100, similar to the oci8-based oracle adapter.
@@ -98,7 +98,7 @@ module Sequel
98
98
 
99
99
  # Whether the given exception is due to a foreign key error.
100
100
  def foreign_key_error?(exception)
101
- exception.message =~ /query does not return ResultSet/
101
+ exception.message.include?('query does not return ResultSet')
102
102
  end
103
103
 
104
104
  # Use getLong instead of getInt for converting integers on SQLite, since SQLite does not enforce a limit of 2**32.
@@ -84,7 +84,7 @@ module Sequel
84
84
  end
85
85
 
86
86
  def disconnect_error?(exception, opts)
87
- super || (exception.message =~ /connection is closed/)
87
+ super || (exception.message.include?('connection is closed'))
88
88
  end
89
89
  end
90
90
  end
@@ -305,17 +305,31 @@ module Sequel
305
305
  m = output_identifier_meth
306
306
  schema, table = metadata_schema_and_table(table, opts)
307
307
  foreign_keys = {}
308
+
308
309
  metadata(:getImportedKeys, nil, schema, table) do |r|
309
- if fk = foreign_keys[r[:fk_name]]
310
- fk[:columns] << [r[:key_seq], m.call(r[:fkcolumn_name])]
311
- fk[:key] << [r[:key_seq], m.call(r[:pkcolumn_name])]
312
- elsif r[:fk_name]
313
- foreign_keys[r[:fk_name]] = {:name=>m.call(r[:fk_name]), :columns=>[[r[:key_seq], m.call(r[:fkcolumn_name])]], :table=>m.call(r[:pktable_name]), :key=>[[r[:key_seq], m.call(r[:pkcolumn_name])]]}
310
+ next unless fk_name = r[:fk_name]
311
+
312
+ key_seq = r[:key_seq]
313
+ columns = [key_seq, m.call(r[:fkcolumn_name])]
314
+ key = [key_seq, m.call(r[:pkcolumn_name])]
315
+
316
+ if fk = foreign_keys[fk_name]
317
+ fk[:columns] << columns
318
+ fk[:key] << key
319
+ else
320
+ foreign_keys[fk_name] = {
321
+ :name=>m.call(fk_name),
322
+ :columns=>[columns],
323
+ :table=>m.call(r[:pktable_name]),
324
+ :key=>[key]
325
+ }
314
326
  end
315
327
  end
328
+
329
+ fk_keys = [:columns, :key]
316
330
  foreign_keys.values.each do |fk|
317
- [:columns, :key].each do |k|
318
- fk[k] = fk[k].sort.map{|_, v| v}
331
+ fk_keys.each do |k|
332
+ fk[k].sort!.map!{|_, v| v}
319
333
  end
320
334
  end
321
335
  end
@@ -290,7 +290,7 @@ module Sequel
290
290
 
291
291
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
292
292
  def schema_column_type(db_type)
293
- convert_tinyint_to_bool && db_type =~ /\Atinyint\(1\)/ ? :boolean : super
293
+ convert_tinyint_to_bool && db_type.start_with?("tinyint(1)") ? :boolean : super
294
294
  end
295
295
  end
296
296
 
@@ -221,13 +221,13 @@ module Sequel
221
221
  super ||
222
222
  ((conn = opts[:conn]) && !conn.ping) ||
223
223
  (e.is_a?(::Mysql2::Error) &&
224
- (e.sql_state =~ /\A08/ ||
224
+ ((e.sql_state && e.sql_state.start_with?("08")) ||
225
225
  MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)))
226
226
  end
227
227
 
228
228
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
229
229
  def schema_column_type(db_type)
230
- convert_tinyint_to_bool && db_type =~ /\Atinyint\(1\)/ ? :boolean : super
230
+ convert_tinyint_to_bool && db_type.start_with?("tinyint(1)") ? :boolean : super
231
231
  end
232
232
  end
233
233
 
@@ -81,7 +81,7 @@ module Sequel
81
81
  end
82
82
 
83
83
  def disconnect_error?(e, opts)
84
- super || (e.is_a?(::ODBC::Error) && /\A08S01/.match(e.message))
84
+ super || (e.is_a?(::ODBC::Error) && e.message.start_with?("08S01"))
85
85
  end
86
86
  end
87
87
 
@@ -108,7 +108,7 @@ module Sequel
108
108
  from(name).first
109
109
  true
110
110
  rescue DatabaseError => e
111
- if e.to_s =~ /Operation not allowed for reason code "7" on table/ && v == false
111
+ if e.to_s.include?('Operation not allowed for reason code "7" on table') && v == false
112
112
  # table probably needs reorg
113
113
  reorg(name)
114
114
  v = true
@@ -175,8 +175,13 @@ module Sequel
175
175
  # Supply columns with NOT NULL if they are part of a composite
176
176
  # primary key or unique constraint
177
177
  def column_list_sql(g)
178
- ks = []
179
- g.constraints.each{|c| ks = c[:columns] if [:primary_key, :unique].include?(c[:type])}
178
+ ks = {}
179
+ g.constraints.each do |c|
180
+ case c[:type]
181
+ when :primary_key, :unique
182
+ c[:columns].each{|c| ks[c] = true}
183
+ end
184
+ end
180
185
  g.columns.each{|c| c[:null] = false if ks.include?(c[:name]) }
181
186
  super
182
187
  end
@@ -705,7 +705,7 @@ module Sequel
705
705
 
706
706
  # Allows you to do a dirty read of uncommitted data using WITH (NOLOCK).
707
707
  def nolock
708
- lock_style(:dirty)
708
+ cached_lock_style_dataset(:_nolock_ds, :dirty)
709
709
  end
710
710
 
711
711
  # Uses OUTER APPLY to join the given table into the current dataset.
@@ -456,7 +456,7 @@ module Sequel
456
456
  [pk]
457
457
  elsif !(pkc = generator.constraints.select{|con| con[:type] == :primary_key}).empty?
458
458
  pkc.first[:columns]
459
- elsif !(pkc = generator.columns.select{|con| con[:primary_key] == true}).empty?
459
+ elsif !(pkc = generator.columns.select{|con| con[:primary_key]}).empty?
460
460
  pkc.map{|c| c[:name]}
461
461
  end
462
462
  else
@@ -787,7 +787,7 @@ module Sequel
787
787
 
788
788
  # Return a cloned dataset which will use LOCK IN SHARE MODE to lock returned rows.
789
789
  def for_share
790
- lock_style(:share)
790
+ cached_lock_style_dataset(:_for_share_ds, :share)
791
791
  end
792
792
 
793
793
  # Adds full text filter
@@ -48,7 +48,7 @@ module Sequel
48
48
  TYPE_TRANSLATOR_DATE = date.freeze
49
49
  bytea = Object.new
50
50
  def bytea.call(str)
51
- str = if str =~ /\A\\x/
51
+ str = if str.start_with?('\\x')
52
52
  # PostgreSQL 9.0+ bytea hex format
53
53
  str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
54
54
  else
@@ -124,6 +124,8 @@ module Sequel
124
124
  #
125
125
  # Options supported:
126
126
  #
127
+ # :include :: Include additional columns in the underlying index, to
128
+ # allow for index-only scans in more cases (PostgreSQL 11+).
127
129
  # :name :: Name the constraint with the given name (useful if you may
128
130
  # need to drop the constraint later)
129
131
  # :using :: Override the index_method for the exclusion constraint (defaults to gist).
@@ -141,6 +143,17 @@ module Sequel
141
143
  @operations << {:op => :add_constraint, :type => :exclude, :elements => elements}.merge!(opts)
142
144
  end
143
145
 
146
+ # Alter an existing constraint. Options:
147
+ # :deferrable :: Modify deferrable setting for constraint (PostgreSQL 9.4+):
148
+ # true :: DEFERRABLE INITIALLY DEFERRED
149
+ # false :: NOT DEFERRABLE
150
+ # :immediate :: DEFERRABLE INITIALLY IMMEDIATE
151
+ # :enforced :: Set true to use ENFORCED, or false to use NOT ENFORCED (PostgreSQL 18+)
152
+ # :inherit :: Set true to use INHERIT, or false to use NO INHERIT (PostgreSQL 18+)
153
+ def alter_constraint(name, opts=OPTS)
154
+ @operations << {:op => :alter_constraint, :name => name}.merge!(opts)
155
+ end
156
+
144
157
  # Validate the constraint with the given name, which should have
145
158
  # been added previously with NOT VALID.
146
159
  def validate_constraint(name)
@@ -335,7 +348,7 @@ module Sequel
335
348
  hash = {}
336
349
  _check_constraints_ds.where_each(:conrelid=>regclass_oid(table)) do |row|
337
350
  constraint = m.call(row[:constraint])
338
- entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
351
+ entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[], :validated=>row[:validated], :enforced=>row[:enforced]}
339
352
  entry[:columns] << m.call(row[:column]) if row[:column]
340
353
  end
341
354
 
@@ -601,6 +614,8 @@ module Sequel
601
614
  :on_update=>fklod_map[row[:on_update]],
602
615
  :on_delete=>fklod_map[row[:on_delete]],
603
616
  :deferrable=>row[:deferrable],
617
+ :validated=>row[:validated],
618
+ :enforced=>row[:enforced],
604
619
  :table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
605
620
  }
606
621
 
@@ -898,11 +913,15 @@ module Sequel
898
913
 
899
914
  # Dataset used to retrieve CHECK constraint information
900
915
  def _check_constraints_ds
901
- @_check_constraints_ds ||= metadata_dataset.
902
- from{pg_constraint.as(:co)}.
903
- left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
904
- where(:contype=>'c').
905
- select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
916
+ @_check_constraints_ds ||= begin
917
+ ds = metadata_dataset.
918
+ from{pg_constraint.as(:co)}.
919
+ left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
920
+ where(:contype=>'c').
921
+ select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
922
+
923
+ _add_validated_enforced_constraint_columns(ds)
924
+ end
906
925
  end
907
926
 
908
927
  # Dataset used to retrieve foreign keys referenced by a table
@@ -968,6 +987,28 @@ module Sequel
968
987
  ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
969
988
  end
970
989
 
990
+ _add_validated_enforced_constraint_columns(ds)
991
+ end
992
+
993
+ def _add_validated_enforced_constraint_columns(ds)
994
+ validated_cond = if server_version >= 90100
995
+ Sequel[:convalidated]
996
+ # :nocov:
997
+ else
998
+ Sequel.cast(true, TrueClass)
999
+ # :nocov:
1000
+ end
1001
+ ds = ds.select_append(validated_cond.as(:validated))
1002
+
1003
+ enforced_cond = if server_version >= 180000
1004
+ Sequel[:conenforced]
1005
+ # :nocov:
1006
+ else
1007
+ Sequel.cast(true, TrueClass)
1008
+ # :nocov:
1009
+ end
1010
+ ds = ds.select_append(enforced_cond.as(:enforced))
1011
+
971
1012
  ds
972
1013
  end
973
1014
 
@@ -1140,6 +1181,31 @@ module Sequel
1140
1181
  "ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
1141
1182
  end
1142
1183
 
1184
+ def alter_table_alter_constraint_sql(table, op)
1185
+ sql = String.new
1186
+ sql << "ALTER CONSTRAINT #{quote_identifier(op[:name])}"
1187
+
1188
+ constraint_deferrable_sql_append(sql, op[:deferrable])
1189
+
1190
+ case op[:enforced]
1191
+ when nil
1192
+ when false
1193
+ sql << " NOT ENFORCED"
1194
+ else
1195
+ sql << " ENFORCED"
1196
+ end
1197
+
1198
+ case op[:inherit]
1199
+ when nil
1200
+ when false
1201
+ sql << " NO INHERIT"
1202
+ else
1203
+ sql << " INHERIT"
1204
+ end
1205
+
1206
+ sql
1207
+ end
1208
+
1143
1209
  def alter_table_generator_class
1144
1210
  Postgres::AlterTableGenerator
1145
1211
  end
@@ -1190,6 +1256,22 @@ module Sequel
1190
1256
  end
1191
1257
  end
1192
1258
 
1259
+ def column_definition_append_include_sql(sql, constraint)
1260
+ if include_cols = constraint[:include]
1261
+ sql << " INCLUDE " << literal(Array(include_cols))
1262
+ end
1263
+ end
1264
+
1265
+ def column_definition_append_primary_key_sql(sql, constraint)
1266
+ super
1267
+ column_definition_append_include_sql(sql, constraint)
1268
+ end
1269
+
1270
+ def column_definition_append_unique_sql(sql, constraint)
1271
+ super
1272
+ column_definition_append_include_sql(sql, constraint)
1273
+ end
1274
+
1193
1275
  # Literalize non-String collate options. This is because unquoted collatations
1194
1276
  # are folded to lowercase, and PostgreSQL used mixed case or capitalized collations.
1195
1277
  def column_definition_collate_sql(sql, column)
@@ -1233,9 +1315,9 @@ module Sequel
1233
1315
  end
1234
1316
 
1235
1317
  # PostgreSQL can't combine rename_column operations, and it can combine
1236
- # the custom validate_constraint operation.
1318
+ # validate_constraint and alter_constraint operations.
1237
1319
  def combinable_alter_table_op?(op)
1238
- (super || op[:op] == :validate_constraint) && op[:op] != :rename_column
1320
+ (super || op[:op] == :validate_constraint || op[:op] == :alter_constraint) && op[:op] != :rename_column
1239
1321
  end
1240
1322
 
1241
1323
  VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
@@ -1269,30 +1351,49 @@ module Sequel
1269
1351
  sqls
1270
1352
  end
1271
1353
 
1272
- # Handle exclusion constraints.
1354
+ # Handle PostgreSQL-specific constraint features.
1273
1355
  def constraint_definition_sql(constraint)
1274
1356
  case type = constraint[:type]
1275
1357
  when :exclude
1276
1358
  elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
1277
1359
  sql = String.new
1278
- sql << "#{"CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]}EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})#{" WHERE #{filter_expr(constraint[:where])}" if constraint[:where]}"
1360
+ sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
1361
+ sql << "EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})"
1362
+ column_definition_append_include_sql(sql, constraint)
1363
+ sql << " WHERE #{filter_expr(constraint[:where])}" if constraint[:where]
1279
1364
  constraint_deferrable_sql_append(sql, constraint[:deferrable])
1280
1365
  sql
1281
1366
  when :primary_key, :unique
1367
+ sql = String.new
1368
+ sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
1369
+
1370
+ if type == :primary_key
1371
+ sql << primary_key_constraint_sql_fragment(constraint)
1372
+ else
1373
+ sql << unique_constraint_sql_fragment(constraint)
1374
+ end
1375
+
1282
1376
  if using_index = constraint[:using_index]
1283
- sql = String.new
1284
- sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
1285
- if type == :primary_key
1286
- sql << primary_key_constraint_sql_fragment(constraint)
1287
- else
1288
- sql << unique_constraint_sql_fragment(constraint)
1289
- end
1290
1377
  sql << " USING INDEX " << quote_identifier(using_index)
1291
1378
  else
1292
- super
1379
+ cols = literal(constraint[:columns])
1380
+ cols.insert(-2, " WITHOUT OVERLAPS") if constraint[:without_overlaps]
1381
+ sql << " " << cols
1382
+
1383
+ if include_cols = constraint[:include]
1384
+ sql << " INCLUDE " << literal(Array(include_cols))
1385
+ end
1293
1386
  end
1387
+
1388
+ sql
1294
1389
  else # when :foreign_key, :check
1295
1390
  sql = super
1391
+ if constraint[:no_inherit]
1392
+ sql << " NO INHERIT"
1393
+ end
1394
+ if constraint[:not_enforced]
1395
+ sql << " NOT ENFORCED"
1396
+ end
1296
1397
  if constraint[:not_valid]
1297
1398
  sql << " NOT VALID"
1298
1399
  end
@@ -1300,6 +1401,46 @@ module Sequel
1300
1401
  end
1301
1402
  end
1302
1403
 
1404
+ def column_definition_add_references_sql(sql, column)
1405
+ super
1406
+ if column[:not_enforced]
1407
+ sql << " NOT ENFORCED"
1408
+ end
1409
+ end
1410
+
1411
+ def column_definition_null_sql(sql, column)
1412
+ constraint = column[:not_null]
1413
+ constraint = nil unless constraint.is_a?(Hash)
1414
+ if constraint && (name = constraint[:name])
1415
+ sql << " CONSTRAINT #{quote_identifier(name)}"
1416
+ end
1417
+ super
1418
+ if constraint && constraint[:no_inherit]
1419
+ sql << " NO INHERIT"
1420
+ end
1421
+ end
1422
+
1423
+ # Handle :period option
1424
+ def column_references_table_constraint_sql(constraint)
1425
+ sql = String.new
1426
+ sql << "FOREIGN KEY "
1427
+ cols = constraint[:columns]
1428
+ cols = column_references_add_period(cols) if constraint[:period]
1429
+ sql << literal(cols) << column_references_sql(constraint)
1430
+ end
1431
+
1432
+ def column_references_append_key_sql(sql, column)
1433
+ cols = Array(column[:key])
1434
+ cols = column_references_add_period(cols) if column[:period]
1435
+ sql << "(#{cols.map{|x| quote_identifier(x)}.join(', ')})"
1436
+ end
1437
+
1438
+ def column_references_add_period(cols)
1439
+ cols= cols.dup
1440
+ cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}")
1441
+ cols
1442
+ end
1443
+
1303
1444
  def database_specific_error_class_from_sqlstate(sqlstate)
1304
1445
  if sqlstate == '23P01'
1305
1446
  ExclusionConstraintViolation
@@ -1369,7 +1510,8 @@ module Sequel
1369
1510
  # SQL statement to create database function.
1370
1511
  def create_function_sql(name, definition, opts=OPTS)
1371
1512
  args = opts[:args]
1372
- if !opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 and %w'OUT INOUT'.include?(a[2].to_s)}
1513
+ in_out = %w'OUT INOUT'
1514
+ if (!opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 && in_out.include?(a[2].to_s)})
1373
1515
  returns = opts[:returns] || 'void'
1374
1516
  end
1375
1517
  language = opts[:language] || 'SQL'
@@ -1816,6 +1958,15 @@ module Sequel
1816
1958
  end
1817
1959
  end
1818
1960
 
1961
+ # Support :nulls_not_distinct option.
1962
+ def unique_constraint_sql_fragment(constraint)
1963
+ if constraint[:nulls_not_distinct]
1964
+ 'UNIQUE NULLS NOT DISTINCT'
1965
+ else
1966
+ 'UNIQUE'
1967
+ end
1968
+ end
1969
+
1819
1970
  # PostgreSQL 9.4+ supports views with check option.
1820
1971
  def view_with_check_option_support
1821
1972
  # :nocov:
@@ -1932,9 +2083,22 @@ module Sequel
1932
2083
  end
1933
2084
  end
1934
2085
 
2086
+ # Return a cloned dataset which will use FOR KEY SHARE to lock returned rows.
2087
+ # Supported on PostgreSQL 9.3+.
2088
+ def for_key_share
2089
+ cached_lock_style_dataset(:_for_key_share_ds, :key_share)
2090
+ end
2091
+
2092
+ # Return a cloned dataset which will use FOR NO KEY UPDATE to lock returned rows.
2093
+ # This is generally a better choice than using for_update on PostgreSQL, unless
2094
+ # you will be deleting the row or modifying a key column. Supported on PostgreSQL 9.3+.
2095
+ def for_no_key_update
2096
+ cached_lock_style_dataset(:_for_no_key_update_ds, :no_key_update)
2097
+ end
2098
+
1935
2099
  # Return a cloned dataset which will use FOR SHARE to lock returned rows.
1936
2100
  def for_share
1937
- lock_style(:share)
2101
+ cached_lock_style_dataset(:_for_share_ds, :share)
1938
2102
  end
1939
2103
 
1940
2104
  # Run a full text search on PostgreSQL. By default, searching for the inclusion
@@ -2710,8 +2874,13 @@ module Sequel
2710
2874
  # Use SKIP LOCKED if skipping locked rows.
2711
2875
  def select_lock_sql(sql)
2712
2876
  lock = @opts[:lock]
2713
- if lock == :share
2877
+ case lock
2878
+ when :share
2714
2879
  sql << ' FOR SHARE'
2880
+ when :no_key_update
2881
+ sql << ' FOR NO KEY UPDATE'
2882
+ when :key_share
2883
+ sql << ' FOR KEY SHARE'
2715
2884
  else
2716
2885
  super
2717
2886
  end
@@ -95,14 +95,14 @@ module Sequel
95
95
  metadata_dataset.with_sql("PRAGMA index_list(?)", im.call(table)).each do |r|
96
96
  if opts[:only_autocreated]
97
97
  # If specifically asked for only autocreated indexes, then return those an only those
98
- next unless r[:name] =~ /\Asqlite_autoindex_/
98
+ next unless r[:name].start_with?('sqlite_autoindex_')
99
99
  elsif r.has_key?(:origin)
100
100
  # If origin is set, then only exclude primary key indexes and partial indexes
101
101
  next if r[:origin] == 'pk'
102
102
  next if r[:partial].to_i == 1
103
103
  else
104
104
  # When :origin key not present, assume any autoindex could be a primary key one and exclude it
105
- next if r[:name] =~ /\Asqlite_autoindex_/
105
+ next if r[:name].start_with?('sqlite_autoindex_')
106
106
  end
107
107
 
108
108
  indexes[m.call(r[:name])] = {:unique=>r[:unique].to_i==1}
@@ -302,7 +302,7 @@ module Sequel
302
302
 
303
303
  # A name to use for the backup table
304
304
  def backup_table_name(table, opts=OPTS)
305
- table = table.gsub('`', '')
305
+ table = table.delete('`')
306
306
  (opts[:times]||1000).times do |i|
307
307
  table_name = "#{table}_backup#{i}"
308
308
  return table_name unless table_exists?(table_name)
@@ -14,7 +14,7 @@ module Sequel
14
14
  opts = server_opts(server)
15
15
  opts[:username] = opts[:user]
16
16
  c = TinyTds::Client.new(opts)
17
- c.query_options.merge!(:cache_rows=>false)
17
+ c.query_options[:cache_rows] = false
18
18
 
19
19
  # SEQUEL6: Default to ansi: true
20
20
  if opts[:ansi]
@@ -83,7 +83,7 @@ module Sequel
83
83
 
84
84
  # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
85
85
  def schema_column_type(db_type)
86
- db_type =~ /\Atinyint\(1\)/ ? :boolean : super
86
+ db_type.start_with?("tinyint(1)") ? :boolean : super
87
87
  end
88
88
  end
89
89
 
@@ -140,6 +140,12 @@ class Sequel::ShardedTimedQueueConnectionPool < Sequel::ConnectionPool
140
140
  end
141
141
  end
142
142
 
143
+ # The number of threads waiting to check out a connection for the given
144
+ # server.
145
+ def num_waiting(server=:default)
146
+ @queues[pick_server(server)].num_waiting
147
+ end
148
+
143
149
  # The total number of connections in the pool. Using a non-existant server will return nil.
144
150
  def size(server=:default)
145
151
  sync{@sizes[server]}
@@ -102,6 +102,11 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
102
102
  end
103
103
  end
104
104
 
105
+ # The number of threads waiting to check out a connection.
106
+ def num_waiting(_server=:default)
107
+ @queue.num_waiting
108
+ end
109
+
105
110
  def pool_type
106
111
  :timed_queue
107
112
  end
@@ -236,7 +241,7 @@ class Sequel::TimedQueueConnectionPool < Sequel::ConnectionPool
236
241
  def preconnect(concurrent = false)
237
242
  if concurrent
238
243
  if times = sync{@max_size > (size = @size[0]) ? @max_size - size : false}
239
- times.times.map{Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
244
+ Array.new(times){Thread.new{if conn = try_make_new; @queue.push(conn) end}}.map(&:value)
240
245
  end
241
246
  else
242
247
  while conn = try_make_new
data/lib/sequel/core.rb CHANGED
@@ -403,7 +403,7 @@ module Sequel
403
403
  when -1, 0
404
404
  vr.instance_exec(&block)
405
405
  else
406
- block.call(vr)
406
+ yield(vr)
407
407
  end
408
408
  end
409
409