sequel 4.39.0 → 4.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +34 -0
  3. data/README.rdoc +8 -4
  4. data/doc/active_record.rdoc +1 -1
  5. data/doc/advanced_associations.rdoc +7 -7
  6. data/doc/association_basics.rdoc +7 -7
  7. data/doc/cheat_sheet.rdoc +5 -3
  8. data/doc/core_extensions.rdoc +3 -3
  9. data/doc/dataset_filtering.rdoc +1 -1
  10. data/doc/object_model.rdoc +16 -7
  11. data/doc/postgresql.rdoc +3 -3
  12. data/doc/querying.rdoc +3 -3
  13. data/doc/release_notes/4.40.0.txt +179 -0
  14. data/doc/security.rdoc +2 -1
  15. data/doc/sql.rdoc +34 -18
  16. data/doc/testing.rdoc +1 -0
  17. data/doc/virtual_rows.rdoc +11 -2
  18. data/lib/sequel/adapters/jdbc/derby.rb +7 -1
  19. data/lib/sequel/adapters/jdbc/h2.rb +15 -1
  20. data/lib/sequel/adapters/oracle.rb +9 -5
  21. data/lib/sequel/adapters/postgres.rb +0 -1
  22. data/lib/sequel/adapters/shared/cubrid.rb +11 -11
  23. data/lib/sequel/adapters/shared/db2.rb +4 -8
  24. data/lib/sequel/adapters/shared/mssql.rb +41 -28
  25. data/lib/sequel/adapters/shared/mysql.rb +9 -6
  26. data/lib/sequel/adapters/shared/oracle.rb +16 -5
  27. data/lib/sequel/adapters/shared/postgres.rb +84 -45
  28. data/lib/sequel/adapters/shared/sqlanywhere.rb +29 -15
  29. data/lib/sequel/adapters/shared/sqlite.rb +6 -6
  30. data/lib/sequel/core.rb +61 -10
  31. data/lib/sequel/database/connecting.rb +2 -1
  32. data/lib/sequel/database/features.rb +7 -0
  33. data/lib/sequel/database/query.rb +1 -1
  34. data/lib/sequel/database/schema_methods.rb +30 -3
  35. data/lib/sequel/database/transactions.rb +4 -2
  36. data/lib/sequel/dataset/actions.rb +1 -1
  37. data/lib/sequel/dataset/graph.rb +6 -1
  38. data/lib/sequel/dataset/query.rb +14 -7
  39. data/lib/sequel/dataset/sql.rb +2 -2
  40. data/lib/sequel/extensions/core_extensions.rb +2 -1
  41. data/lib/sequel/extensions/pg_row.rb +2 -2
  42. data/lib/sequel/extensions/s.rb +57 -0
  43. data/lib/sequel/extensions/set_overrides.rb +5 -1
  44. data/lib/sequel/extensions/sql_expr.rb +1 -0
  45. data/lib/sequel/extensions/symbol_aref.rb +71 -0
  46. data/lib/sequel/extensions/symbol_aref_refinement.rb +41 -0
  47. data/lib/sequel/extensions/symbol_as.rb +23 -0
  48. data/lib/sequel/extensions/symbol_as_refinement.rb +35 -0
  49. data/lib/sequel/model/base.rb +3 -3
  50. data/lib/sequel/plugins/class_table_inheritance.rb +14 -3
  51. data/lib/sequel/plugins/column_select.rb +4 -2
  52. data/lib/sequel/plugins/dataset_associations.rb +12 -4
  53. data/lib/sequel/plugins/insert_returning_select.rb +1 -1
  54. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  55. data/lib/sequel/plugins/prepared_statements.rb +1 -0
  56. data/lib/sequel/sql.rb +40 -8
  57. data/lib/sequel/version.rb +1 -1
  58. data/spec/adapters/firebird_spec.rb +3 -3
  59. data/spec/adapters/mssql_spec.rb +40 -40
  60. data/spec/adapters/mysql_spec.rb +5 -5
  61. data/spec/adapters/oracle_spec.rb +4 -4
  62. data/spec/adapters/postgres_spec.rb +135 -124
  63. data/spec/adapters/spec_helper.rb +1 -0
  64. data/spec/adapters/sqlite_spec.rb +6 -6
  65. data/spec/core/dataset_spec.rb +2 -2
  66. data/spec/core/expression_filters_spec.rb +43 -2
  67. data/spec/core/schema_spec.rb +35 -1
  68. data/spec/core_extensions_spec.rb +27 -0
  69. data/spec/extensions/class_table_inheritance_spec.rb +8 -0
  70. data/spec/extensions/column_select_spec.rb +8 -0
  71. data/spec/extensions/core_refinements_spec.rb +1 -1
  72. data/spec/extensions/dataset_associations_spec.rb +9 -0
  73. data/spec/extensions/insert_returning_select_spec.rb +20 -0
  74. data/spec/extensions/prepared_statements_spec.rb +7 -0
  75. data/spec/extensions/s_spec.rb +60 -0
  76. data/spec/extensions/symbol_aref_refinement_spec.rb +28 -0
  77. data/spec/extensions/symbol_as_refinement_spec.rb +21 -0
  78. data/spec/integration/associations_test.rb +62 -57
  79. data/spec/integration/dataset_test.rb +54 -54
  80. data/spec/integration/eager_loader_test.rb +7 -7
  81. data/spec/integration/plugin_test.rb +20 -20
  82. data/spec/integration/prepared_statement_test.rb +1 -1
  83. data/spec/integration/schema_test.rb +21 -0
  84. data/spec/integration/spec_helper.rb +1 -0
  85. metadata +12 -2
@@ -4,10 +4,6 @@ Sequel.require 'adapters/utils/split_alter_table'
4
4
  Sequel.require 'adapters/utils/replace'
5
5
 
6
6
  module Sequel
7
- Dataset::NON_SQL_OPTIONS << :insert_ignore
8
- Dataset::NON_SQL_OPTIONS << :update_ignore
9
- Dataset::NON_SQL_OPTIONS << :on_duplicate_key_update
10
-
11
7
  module MySQL
12
8
  Sequel::Database.set_shared_adapter_scheme(:mysql, self)
13
9
 
@@ -76,11 +72,11 @@ module Sequel
76
72
  m = output_identifier_meth
77
73
  im = input_identifier_meth
78
74
  ds = metadata_dataset.
79
- from(:INFORMATION_SCHEMA__KEY_COLUMN_USAGE).
75
+ from(Sequel[:INFORMATION_SCHEMA][:KEY_COLUMN_USAGE]).
80
76
  where(:TABLE_NAME=>im.call(table), :TABLE_SCHEMA=>Sequel.function(:DATABASE)).
81
77
  exclude(:CONSTRAINT_NAME=>'PRIMARY').
82
78
  exclude(:REFERENCED_TABLE_NAME=>nil).
83
- select(:CONSTRAINT_NAME___name, :COLUMN_NAME___column, :REFERENCED_TABLE_NAME___table, :REFERENCED_COLUMN_NAME___key)
79
+ select(Sequel[:CONSTRAINT_NAME].as(:name), Sequel[:COLUMN_NAME].as(:column), Sequel[:REFERENCED_TABLE_NAME].as(:table), Sequel[:REFERENCED_COLUMN_NAME].as(:key))
84
80
 
85
81
  h = {}
86
82
  ds.each do |row|
@@ -623,6 +619,8 @@ module Sequel
623
619
  # Comes directly from MySQL's documentation, used for queries with limits without offsets
624
620
  ONLY_OFFSET = ",18446744073709551615".freeze
625
621
 
622
+ NON_SQL_OPTIONS = (Dataset::NON_SQL_OPTIONS + [:insert_ignore, :update_ignore, :on_duplicate_key_update]).freeze
623
+
626
624
  Dataset.def_sql_method(self, :delete, %w'delete from where order limit')
627
625
  Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
628
626
  Dataset.def_sql_method(self, :select, %w'select distinct calc_found_rows columns from join where group having compounds order limit lock')
@@ -998,6 +996,11 @@ module Sequel
998
996
  :values
999
997
  end
1000
998
 
999
+ # Dataset options that do not affect the generated SQL.
1000
+ def non_sql_options
1001
+ NON_SQL_OPTIONS
1002
+ end
1003
+
1001
1004
  def select_only_offset_sql(sql)
1002
1005
  sql << LIMIT
1003
1006
  literal_append(sql, @opts[:offset])
@@ -47,11 +47,22 @@ module Sequel
47
47
  im = input_identifier_meth
48
48
  schema, table = schema_and_table(table)
49
49
  ds = metadata_dataset.
50
- from(:all_cons_columns___pc, :all_constraints___p, :all_cons_columns___fc, :all_constraints___f).
51
- where(:f__table_name=>im.call(table), :f__constraint_type=>'R', :p__owner=>:f__r_owner, :p__constraint_name=>:f__r_constraint_name, :pc__owner=>:p__owner, :pc__constraint_name=>:p__constraint_name, :pc__table_name=>:p__table_name, :fc__owner=>:f__owner, :fc__constraint_name=>:f__constraint_name, :fc__table_name=>:f__table_name, :fc__position=>:pc__position).
52
- select(:p__table_name___table, :pc__column_name___key, :fc__column_name___column, :f__constraint_name___name).
53
- order(:table, :fc__position)
54
- ds = ds.where(:f__schema_name=>im.call(schema)) if schema
50
+ from{[all_cons_columns.as(:pc), all_constraints.as(:p), all_cons_columns.as(:fc), all_constraints.as(:f)]}.
51
+ where{{
52
+ f[:table_name]=>im.call(table),
53
+ f[:constraint_type]=>'R',
54
+ p[:owner]=>f[:r_owner],
55
+ p[:constraint_name]=>f[:r_constraint_name],
56
+ pc[:owner]=>p[:owner],
57
+ pc[:constraint_name]=>p[:constraint_name],
58
+ pc[:table_name]=>p[:table_name],
59
+ fc[:owner]=>f[:owner],
60
+ fc[:constraint_name]=>f[:constraint_name],
61
+ fc[:table_name]=>f[:table_name],
62
+ fc[:position]=>pc[:position]}}.
63
+ select{[p[:table_name].as(:table), pc[:column_name].as(:key), fc[:column_name].as(:column), f[:constraint_name].as(:name)]}.
64
+ order{[:table, fc[:position]]}
65
+ ds = ds.where{{f[:schema_name]=>im.call(schema)}} if schema
55
66
 
56
67
  fks = {}
57
68
  ds.each do |r|
@@ -309,11 +309,15 @@ module Sequel
309
309
  m = output_identifier_meth
310
310
  schema, _ = opts.fetch(:schema, schema_and_table(table))
311
311
  range = 0...32
312
+ oid = regclass_oid(table)
312
313
 
313
314
  base_ds = metadata_dataset.
314
- from(:pg_constraint___co).
315
- join(:pg_class___cl, :oid=>:conrelid).
316
- where(:cl__relkind=>'r', :co__contype=>'f', :cl__oid=>regclass_oid(table))
315
+ from{pg_constraint.as(:co)}.
316
+ join(Sequel[:pg_class].as(:cl), :oid=>:conrelid).
317
+ where{{
318
+ cl[:relkind]=>'r',
319
+ co[:contype]=>'f',
320
+ cl[:oid]=>oid}}
317
321
 
318
322
  # We split the parsing into two separate queries, which are merged manually later.
319
323
  # This is because PostgreSQL stores both the referencing and referenced columns in
@@ -322,22 +326,33 @@ module Sequel
322
326
  # the index of that element in the array.
323
327
 
324
328
  ds = base_ds.
325
- join(:pg_attribute___att, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__conkey)).
326
- order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__conkey, [x]), x]}, 32, :att__attnum)).
327
- select(:co__conname___name, :att__attname___column, :co__confupdtype___on_update, :co__confdeltype___on_delete,
328
- SQL::BooleanExpression.new(:AND, :co__condeferrable, :co__condeferred).as(:deferrable))
329
+ join(Sequel[:pg_attribute].as(:att), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
330
+ order{[
331
+ co[:conname],
332
+ SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, att[:attnum])]}.
333
+ select{[
334
+ co[:conname].as(:name),
335
+ att[:attname].as(:column),
336
+ co[:confupdtype].as(:on_update),
337
+ co[:confdeltype].as(:on_delete),
338
+ SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable)]}
329
339
 
330
340
  ref_ds = base_ds.
331
- join(:pg_class___cl2, :oid=>:co__confrelid).
332
- join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
333
- order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__confkey, [x]), x]}, 32, :att2__attnum)).
334
- select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
341
+ join(Sequel[:pg_class].as(:cl2), :oid=>Sequel[:co][:confrelid]).
342
+ join(Sequel[:pg_attribute].as(:att2), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
343
+ order{[
344
+ co[:conname],
345
+ SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, att2[:attnum])]}.
346
+ select{[
347
+ co[:conname].as(:name),
348
+ cl2[:relname].as(:table),
349
+ att2[:attname].as(:refcolumn)]}
335
350
 
336
351
  # If a schema is given, we only search in that schema, and the returned :table
337
352
  # entry is schema qualified as well.
338
353
  if schema
339
- ref_ds = ref_ds.join(:pg_namespace___nsp2, :oid=>:cl2__relnamespace).
340
- select_more(:nsp2__nspname___schema)
354
+ ref_ds = ref_ds.join(Sequel[:pg_namespace].as(:nsp2), :oid=>Sequel[:cl2][:relnamespace]).
355
+ select_more{nsp2[:nspname].as(:schema)}
341
356
  end
342
357
 
343
358
  h = {}
@@ -362,18 +377,25 @@ module Sequel
362
377
  def indexes(table, opts=OPTS)
363
378
  m = output_identifier_meth
364
379
  range = 0...32
365
- attnums = server_version >= 80100 ? SQL::Function.new(:ANY, :ind__indkey) : range.map{|x| SQL::Subscript.new(:ind__indkey, [x])}
380
+ attnums = server_version >= 80100 ? SQL::Function.new(:ANY, Sequel[:ind][:indkey]) : range.map{|x| SQL::Subscript.new(Sequel[:ind][:indkey], [x])}
381
+ oid = regclass_oid(table, opts)
366
382
  ds = metadata_dataset.
367
- from(:pg_class___tab).
368
- join(:pg_index___ind, :indrelid=>:oid).
369
- join(:pg_class___indc, :oid=>:indexrelid).
370
- join(:pg_attribute___att, :attrelid=>:tab__oid, :attnum=>attnums).
371
- left_join(:pg_constraint___con, :conname=>:indc__relname).
372
- filter(:indc__relkind=>'i', :ind__indisprimary=>false, :indexprs=>nil, :indpred=>nil, :indisvalid=>true, :tab__oid=>regclass_oid(table, opts)).
373
- order(:indc__relname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:ind__indkey, [x]), x]}, 32, :att__attnum)).
374
- select(:indc__relname___name, :ind__indisunique___unique, :att__attname___column, :con__condeferrable___deferrable)
375
-
376
- ds.filter!(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
383
+ from{pg_class.as(:tab)}.
384
+ join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
385
+ join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
386
+ join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
387
+ left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
388
+ where{{
389
+ indc[:relkind]=>'i',
390
+ ind[:indisprimary]=>false,
391
+ :indexprs=>nil,
392
+ :indpred=>nil,
393
+ :indisvalid=>true,
394
+ tab[:oid]=>oid}}.
395
+ order{[indc[:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(ind[:indkey], [x]), x]}, 32, att[:attnum])]}.
396
+ select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
397
+
398
+ ds = ds.where(:indisready=>true, :indcheckxmin=>false) if server_version >= 80300
377
399
 
378
400
  indexes = {}
379
401
  ds.each do |r|
@@ -385,7 +407,7 @@ module Sequel
385
407
 
386
408
  # Dataset containing all current database locks
387
409
  def locks
388
- dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select(:pg_class__relname, Sequel::SQL::ColumnAll.new(:pg_locks))
410
+ dataset.from(:pg_class).join(:pg_locks, :relation=>:relfilenode).select{[pg_class[:relname], Sequel::SQL::ColumnAll.new(:pg_locks)]}
389
411
  end
390
412
 
391
413
  # Notifies the given channel. See the PostgreSQL NOTIFY documentation. Options:
@@ -972,7 +994,7 @@ module Sequel
972
994
  else
973
995
  Sequel.function(:any, Sequel.function(:current_schemas, false))
974
996
  end
975
- ds.where(:pg_namespace__nspname=>expr)
997
+ ds.where{{pg_namespace[:nspname]=>expr}}
976
998
  end
977
999
 
978
1000
  # Return a hash with oid keys and callable values, used for converting types.
@@ -1032,7 +1054,7 @@ module Sequel
1032
1054
  if block_given?
1033
1055
  yield(ds)
1034
1056
  elsif opts[:qualify]
1035
- ds.select_append(:pg_namespace__nspname).map{|r| Sequel.qualify(m.call(r[:nspname]), m.call(r[:relname]))}
1057
+ ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
1036
1058
  else
1037
1059
  ds.map{|r| m.call(r[:relname])}
1038
1060
  end
@@ -1098,24 +1120,26 @@ module Sequel
1098
1120
  # The dataset used for parsing table schemas, using the pg_* system catalogs.
1099
1121
  def schema_parse_table(table_name, opts)
1100
1122
  m = output_identifier_meth(opts[:dataset])
1101
- ds = metadata_dataset.select(:pg_attribute__attname___name,
1102
- SQL::Cast.new(:pg_attribute__atttypid, :integer).as(:oid),
1103
- SQL::Cast.new(:basetype__oid, :integer).as(:base_oid),
1104
- SQL::Function.new(:format_type, :basetype__oid, :pg_type__typtypmod).as(:db_base_type),
1105
- SQL::Function.new(:format_type, :pg_type__oid, :pg_attribute__atttypmod).as(:db_type),
1106
- SQL::Function.new(:pg_get_expr, :pg_attrdef__adbin, :pg_class__oid).as(:default),
1107
- SQL::BooleanExpression.new(:NOT, :pg_attribute__attnotnull).as(:allow_null),
1108
- SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(:pg_attribute__attnum => SQL::Function.new(:ANY, :pg_index__indkey)), false).as(:primary_key)).
1123
+ oid = regclass_oid(table_name, opts)
1124
+ ds = metadata_dataset.select{[
1125
+ pg_attribute[:attname].as(:name),
1126
+ SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
1127
+ SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
1128
+ SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
1129
+ SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
1130
+ SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
1131
+ SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
1132
+ SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
1109
1133
  from(:pg_class).
1110
1134
  join(:pg_attribute, :attrelid=>:oid).
1111
1135
  join(:pg_type, :oid=>:atttypid).
1112
- left_outer_join(:pg_type___basetype, :oid=>:typbasetype).
1113
- left_outer_join(:pg_attrdef, :adrelid=>:pg_class__oid, :adnum=>:pg_attribute__attnum).
1114
- left_outer_join(:pg_index, :indrelid=>:pg_class__oid, :indisprimary=>true).
1115
- filter(:pg_attribute__attisdropped=>false).
1116
- filter{|o| o.pg_attribute__attnum > 0}.
1117
- filter(:pg_class__oid=>regclass_oid(table_name, opts)).
1118
- order(:pg_attribute__attnum)
1136
+ left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
1137
+ left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
1138
+ left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
1139
+ where{{pg_attribute[:attisdropped]=>false}}.
1140
+ where{pg_attribute[:attnum] > 0}.
1141
+ where{{pg_class[:oid]=>oid}}.
1142
+ order{pg_attribute[:attnum]}
1119
1143
  ds.map do |row|
1120
1144
  row[:default] = nil if blank_object?(row[:default])
1121
1145
  if row[:base_oid]
@@ -1240,6 +1264,8 @@ module Sequel
1240
1264
  LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze)
1241
1265
  SKIP_LOCKED = " SKIP LOCKED".freeze
1242
1266
 
1267
+ NON_SQL_OPTIONS = (Dataset::NON_SQL_OPTIONS + [:cursor, :insert_conflict]).freeze
1268
+
1243
1269
  Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
1244
1270
  Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
1245
1271
  Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
@@ -1403,6 +1429,7 @@ module Sequel
1403
1429
 
1404
1430
  # Handle uniqueness violations when inserting, by updating the conflicting row, using
1405
1431
  # ON CONFLICT. With no options, uses ON CONFLICT DO NOTHING. Options:
1432
+ # :conflict_where :: The index filter, when using a partial index to determine uniqueness.
1406
1433
  # :constraint :: An explicit constraint name, has precendence over :target.
1407
1434
  # :target :: The column name or expression to handle uniqueness violations on.
1408
1435
  # :update :: A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
@@ -1421,6 +1448,10 @@ module Sequel
1421
1448
  # DB[:table].insert_conflict(:target=>:a).insert(:a=>1, :b=>2)
1422
1449
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1423
1450
  # # ON CONFLICT (a) DO NOTHING
1451
+ #
1452
+ # DB[:table].insert_conflict(:target=>:a, :conflict_where=>{:c=>true}).insert(:a=>1, :b=>2)
1453
+ # # INSERT INTO TABLE (a, b) VALUES (1, 2)
1454
+ # # ON CONFLICT (a) WHERE (c IS TRUE) DO NOTHING
1424
1455
  #
1425
1456
  # DB[:table].insert_conflict(:target=>:a, :update=>{:b=>:excluded__b}).insert(:a=>1, :b=>2)
1426
1457
  # # INSERT INTO TABLE (a, b) VALUES (1, 2)
@@ -1642,19 +1673,22 @@ module Sequel
1642
1673
  elsif target = opts[:target]
1643
1674
  sql << ' '
1644
1675
  identifier_append(sql, Array(target))
1676
+ if conflict_where = opts[:conflict_where]
1677
+ sql << " WHERE "
1678
+ literal_append(sql, conflict_where)
1679
+ end
1645
1680
  end
1646
1681
 
1647
1682
  if values = opts[:update]
1648
1683
  sql << " DO UPDATE SET "
1649
1684
  update_sql_values_hash(sql, values)
1650
- if where = opts[:update_where]
1685
+ if update_where = opts[:update_where]
1651
1686
  sql << " WHERE "
1652
- literal_append(sql, where)
1687
+ literal_append(sql, update_where)
1653
1688
  end
1654
1689
  else
1655
1690
  sql << " DO NOTHING"
1656
1691
  end
1657
-
1658
1692
  end
1659
1693
  end
1660
1694
 
@@ -1720,6 +1754,11 @@ module Sequel
1720
1754
  :values
1721
1755
  end
1722
1756
 
1757
+ # Dataset options that do not affect the generated SQL.
1758
+ def non_sql_options
1759
+ NON_SQL_OPTIONS
1760
+ end
1761
+
1723
1762
  # PostgreSQL requires parentheses around compound datasets if they use
1724
1763
  # CTEs, and using them in other places doesn't hurt.
1725
1764
  def compound_dataset_sql_append(sql, ds)
@@ -57,8 +57,8 @@ module Sequel
57
57
  im = input_identifier_meth(opts[:dataset])
58
58
  metadata_dataset.
59
59
  from{sa_describe_query("select * from #{im.call(table)}").as(:a)}.
60
- join(:syscolumn___b, :table_id=>:base_table_id, :column_id=>:base_column_id).
61
- order(:a__column_number).
60
+ join(Sequel[:syscolumn].as(:b), :table_id=>:base_table_id, :column_id=>:base_column_id).
61
+ order{a[:column_number]}.
62
62
  map do |row|
63
63
  auto_increment = row.delete(:is_autoincrement)
64
64
  row[:auto_increment] = auto_increment == 1 || auto_increment == true
@@ -80,11 +80,15 @@ module Sequel
80
80
  im = input_identifier_meth
81
81
  indexes = {}
82
82
  metadata_dataset.
83
- from(:dbo__sysobjects___z).
84
- select(:z__name___table_name, :i__name___index_name, :si__indextype___type, :si__colnames___columns).
85
- join(:dbo__sysindexes___i, :id___i=> :id___z).
86
- join(:sys__sysindexes___si, :iname=> :name___i).
87
- where(:z__type => 'U', :table_name=>im.call(table)).
83
+ from(Sequel[:dbo][:sysobjects].as(:z)).
84
+ select{[
85
+ z[:name].as(:table_name),
86
+ i[:name].as(:index_name),
87
+ si[:indextype].as(:type),
88
+ si[:colnames].as(:columns)]}.
89
+ join(Sequel[:dbo][:sysindexes].as(:i), :id=>:id).
90
+ join(Sequel[:sys][:sysindexes].as(:si), :iname=> :name).
91
+ where{{z[:type] => 'U', :table_name=>im.call(table)}}.
88
92
  each do |r|
89
93
  indexes[m.call(r[:index_name])] =
90
94
  {:unique=>(r[:type].downcase=='unique'),
@@ -98,11 +102,16 @@ module Sequel
98
102
  im = input_identifier_meth
99
103
  fk_indexes = {}
100
104
  metadata_dataset.
101
- from(:sys__sysforeignkey___fk).
102
- select(:fk__role___name, :fks__columns___column_map, :si__indextype___type, :si__colnames___columns, :fks__primary_tname___table_name).
103
- join(:sys__sysforeignkeys___fks, :role => :role).
104
- join_table(:inner, :sys__sysindexes___si, [:iname=> :fk__role], {:implicit_qualifier => :fk}).
105
- where(:fks__foreign_tname=>im.call(table)).
105
+ from{sys[:sysforeignkey].as(:fk)}.
106
+ select{[
107
+ fk[:role].as(:name),
108
+ fks[:columns].as(:column_map),
109
+ si[:indextype].as(:type),
110
+ si[:colnames].as(:columns),
111
+ fks[:primary_tname].as(:table_name)]}.
112
+ join(Sequel[:sys][:sysforeignkeys].as(:fks), :role => :role).
113
+ join(Sequel[:sys][:sysindexes].as(:si), [:iname=> Sequel[:fk][:role]], {:implicit_qualifier => :fk}).
114
+ where{{fks[:foreign_tname]=>im.call(table)}}.
106
115
  each do |r|
107
116
  unless r[:type].downcase == 'primary key'
108
117
  fk_indexes[r[:name]] =
@@ -142,6 +151,11 @@ module Sequel
142
151
  AUTO_INCREMENT
143
152
  end
144
153
 
154
+ # Sybase does not allow adding primary key constraints to NULLable columns.
155
+ def can_add_primary_key_constraint_on_nullable_columns?
156
+ false
157
+ end
158
+
145
159
  # SQL fragment for marking a table as temporary
146
160
  def temporary_table_sql
147
161
  TEMPORARY
@@ -235,9 +249,9 @@ module Sequel
235
249
  def tables_and_views(type, opts=OPTS)
236
250
  m = output_identifier_meth
237
251
  metadata_dataset.
238
- from(:sysobjects___a).
239
- where(:a__type=>type).
240
- select_map(:a__name).
252
+ from{sysobjects.as(:a)}.
253
+ where{{a[:type]=>type}}.
254
+ select_map{a[:name]}.
241
255
  map{|n| m.call(n)}
242
256
  end
243
257
 
@@ -314,17 +314,17 @@ module Sequel
314
314
  end
315
315
  end
316
316
 
317
+ # SQLite allows adding primary key constraints on NULLABLE columns, but then
318
+ # does not enforce NOT NULL for such columns, so force setting the columns NOT NULL.
319
+ def can_add_primary_key_constraint_on_nullable_columns?
320
+ false
321
+ end
322
+
317
323
  # Surround default with parens to appease SQLite
318
324
  def column_definition_default_sql(sql, column)
319
325
  sql << " DEFAULT (#{literal(column[:default])})" if column.include?(:default)
320
326
  end
321
327
 
322
- # Add null/not null SQL fragment to column creation SQL.
323
- def column_definition_null_sql(sql, column)
324
- column = column.merge(:null=>false) if column[:primary_key]
325
- super(sql, column)
326
- end
327
-
328
328
  # Array of PRAGMA SQL statements based on the Database options that should be applied to
329
329
  # new connections.
330
330
  def connection_pragmas
@@ -24,6 +24,7 @@
24
24
  module Sequel
25
25
  @convert_two_digit_years = true
26
26
  @datetime_class = Time
27
+ @split_symbols = true
27
28
 
28
29
  # Whether Sequel is being run in single threaded mode
29
30
  @single_threaded = false
@@ -234,28 +235,78 @@ module Sequel
234
235
  COLUMN_REF_RE3 = /\A((?:(?!__).)+)__(.+)\z/.freeze
235
236
  SPLIT_SYMBOL_CACHE = {}
236
237
 
237
- # Splits the symbol into three parts. Each part will
238
- # either be a string or nil.
238
+ # Splits the symbol into three parts, if symbol splitting is enabled.
239
+ # Each part will either be a string or nil. If symbol splitting
240
+ # is disabled, returns an array with the first and third parts
241
+ # being nil, and the second part beind a string version of the symbol.
239
242
  #
240
243
  # For columns, these parts are the table, column, and alias.
241
244
  # For tables, these parts are the schema, table, and alias.
242
245
  def self.split_symbol(sym)
243
246
  unless v = Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym]}
244
- v = case s = sym.to_s
245
- when COLUMN_REF_RE1
246
- [$1.freeze, $2.freeze, $3.freeze].freeze
247
- when COLUMN_REF_RE2
248
- [nil, $1.freeze, $2.freeze].freeze
249
- when COLUMN_REF_RE3
250
- [$1.freeze, $2.freeze, nil].freeze
247
+ if split_symbols?
248
+ v = case s = sym.to_s
249
+ when COLUMN_REF_RE1
250
+ [$1.freeze, $2.freeze, $3.freeze].freeze
251
+ when COLUMN_REF_RE2
252
+ [nil, $1.freeze, $2.freeze].freeze
253
+ when COLUMN_REF_RE3
254
+ [$1.freeze, $2.freeze, nil].freeze
255
+ else
256
+ [nil, s.freeze, nil].freeze
257
+ end
251
258
  else
252
- [nil, s.freeze, nil].freeze
259
+ v = [nil,sym.to_s.freeze,nil].freeze
253
260
  end
254
261
  Sequel.synchronize{SPLIT_SYMBOL_CACHE[sym] = v}
255
262
  end
256
263
  v
257
264
  end
258
265
 
266
+ # Sequel by default will split symbols, treating:
267
+ #
268
+ # :table__column # table.column
269
+ # :column___alias # column AS alias
270
+ # :table__column___alias # table.column AS alias
271
+ #
272
+ # This can cause problems if any identifiers in the database use a double
273
+ # or triple underscore. When Sequel was first created, using symbols with
274
+ # double or triple underscores was the only way to represent qualified or
275
+ # aliased identifiers. Sequel now offers many ways to create qualified and
276
+ # aliased identifiers, so there is less of a need for this now. This allows
277
+ # you to turn off symbol splitting, potentially avoiding problems if you
278
+ # have identifiers that use double underscores:
279
+ #
280
+ # Sequel.split_symbols = false
281
+ #
282
+ # Note that Sequel::Database instances do their own caching of literalized
283
+ # symbols, and changing this setting does not affect those caches. It is
284
+ # recommended that if you want to change this setting, you do so directly
285
+ # after requiring Sequel, before creating any Sequel::Database instances.
286
+ #
287
+ # Also note that disabling symbol splitting will also disable the handling
288
+ # of double underscores in virtual row methods, causing such methods to
289
+ # yield regular identifers instead of qualified identifiers. To make sure
290
+ # the code works when splitting symbols is both disabled and enabled, you
291
+ # can use Sequel::SQL::Identifier#[].
292
+ #
293
+ # # Sequel.split_symbols = true
294
+ # Sequel.expr{table__column} # table.column
295
+ # Sequel.expr{table[:column]} # table.column
296
+ #
297
+ # # Sequel.split_symbols = false
298
+ # Sequel.expr{table__column} # table__column
299
+ # Sequel.expr{table[:column]} # table.column
300
+ def self.split_symbols=(v)
301
+ Sequel.synchronize{SPLIT_SYMBOL_CACHE.clear}
302
+ @split_symbols = v
303
+ end
304
+
305
+ # Whether Sequel currently splits symbols into qualified/aliased identifiers.
306
+ def self.split_symbols?
307
+ @split_symbols
308
+ end
309
+
259
310
  # Converts the given +string+ into a +Date+ object.
260
311
  #
261
312
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)