sequel 4.39.0 → 4.40.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.
- checksums.yaml +4 -4
- data/CHANGELOG +34 -0
- data/README.rdoc +8 -4
- data/doc/active_record.rdoc +1 -1
- data/doc/advanced_associations.rdoc +7 -7
- data/doc/association_basics.rdoc +7 -7
- data/doc/cheat_sheet.rdoc +5 -3
- data/doc/core_extensions.rdoc +3 -3
- data/doc/dataset_filtering.rdoc +1 -1
- data/doc/object_model.rdoc +16 -7
- data/doc/postgresql.rdoc +3 -3
- data/doc/querying.rdoc +3 -3
- data/doc/release_notes/4.40.0.txt +179 -0
- data/doc/security.rdoc +2 -1
- data/doc/sql.rdoc +34 -18
- data/doc/testing.rdoc +1 -0
- data/doc/virtual_rows.rdoc +11 -2
- data/lib/sequel/adapters/jdbc/derby.rb +7 -1
- data/lib/sequel/adapters/jdbc/h2.rb +15 -1
- data/lib/sequel/adapters/oracle.rb +9 -5
- data/lib/sequel/adapters/postgres.rb +0 -1
- data/lib/sequel/adapters/shared/cubrid.rb +11 -11
- data/lib/sequel/adapters/shared/db2.rb +4 -8
- data/lib/sequel/adapters/shared/mssql.rb +41 -28
- data/lib/sequel/adapters/shared/mysql.rb +9 -6
- data/lib/sequel/adapters/shared/oracle.rb +16 -5
- data/lib/sequel/adapters/shared/postgres.rb +84 -45
- data/lib/sequel/adapters/shared/sqlanywhere.rb +29 -15
- data/lib/sequel/adapters/shared/sqlite.rb +6 -6
- data/lib/sequel/core.rb +61 -10
- data/lib/sequel/database/connecting.rb +2 -1
- data/lib/sequel/database/features.rb +7 -0
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_methods.rb +30 -3
- data/lib/sequel/database/transactions.rb +4 -2
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/graph.rb +6 -1
- data/lib/sequel/dataset/query.rb +14 -7
- data/lib/sequel/dataset/sql.rb +2 -2
- data/lib/sequel/extensions/core_extensions.rb +2 -1
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/s.rb +57 -0
- data/lib/sequel/extensions/set_overrides.rb +5 -1
- data/lib/sequel/extensions/sql_expr.rb +1 -0
- data/lib/sequel/extensions/symbol_aref.rb +71 -0
- data/lib/sequel/extensions/symbol_aref_refinement.rb +41 -0
- data/lib/sequel/extensions/symbol_as.rb +23 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +35 -0
- data/lib/sequel/model/base.rb +3 -3
- data/lib/sequel/plugins/class_table_inheritance.rb +14 -3
- data/lib/sequel/plugins/column_select.rb +4 -2
- data/lib/sequel/plugins/dataset_associations.rb +12 -4
- data/lib/sequel/plugins/insert_returning_select.rb +1 -1
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/prepared_statements.rb +1 -0
- data/lib/sequel/sql.rb +40 -8
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/firebird_spec.rb +3 -3
- data/spec/adapters/mssql_spec.rb +40 -40
- data/spec/adapters/mysql_spec.rb +5 -5
- data/spec/adapters/oracle_spec.rb +4 -4
- data/spec/adapters/postgres_spec.rb +135 -124
- data/spec/adapters/spec_helper.rb +1 -0
- data/spec/adapters/sqlite_spec.rb +6 -6
- data/spec/core/dataset_spec.rb +2 -2
- data/spec/core/expression_filters_spec.rb +43 -2
- data/spec/core/schema_spec.rb +35 -1
- data/spec/core_extensions_spec.rb +27 -0
- data/spec/extensions/class_table_inheritance_spec.rb +8 -0
- data/spec/extensions/column_select_spec.rb +8 -0
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +9 -0
- data/spec/extensions/insert_returning_select_spec.rb +20 -0
- data/spec/extensions/prepared_statements_spec.rb +7 -0
- data/spec/extensions/s_spec.rb +60 -0
- data/spec/extensions/symbol_aref_refinement_spec.rb +28 -0
- data/spec/extensions/symbol_as_refinement_spec.rb +21 -0
- data/spec/integration/associations_test.rb +62 -57
- data/spec/integration/dataset_test.rb +54 -54
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/plugin_test.rb +20 -20
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +21 -0
- data/spec/integration/spec_helper.rb +1 -0
- 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(:
|
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(:
|
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(:
|
51
|
-
where
|
52
|
-
|
53
|
-
|
54
|
-
|
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(:
|
315
|
-
join(:
|
316
|
-
where
|
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(:
|
326
|
-
order
|
327
|
-
|
328
|
-
|
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(:
|
332
|
-
join(:
|
333
|
-
order
|
334
|
-
|
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(:
|
340
|
-
select_more(:
|
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, :
|
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(:
|
368
|
-
join(:
|
369
|
-
join(:
|
370
|
-
join(:
|
371
|
-
left_join(:
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
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
|
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
|
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
|
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
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
SQL::
|
1105
|
-
SQL::
|
1106
|
-
SQL::Function.new(:
|
1107
|
-
SQL::
|
1108
|
-
SQL::Function.new(:
|
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(:
|
1113
|
-
left_outer_join(:pg_attrdef, :adrelid
|
1114
|
-
left_outer_join(:pg_index, :indrelid
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
order
|
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
|
1685
|
+
if update_where = opts[:update_where]
|
1651
1686
|
sql << " WHERE "
|
1652
|
-
literal_append(sql,
|
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(:
|
61
|
-
order
|
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(:
|
84
|
-
select
|
85
|
-
|
86
|
-
|
87
|
-
|
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(:
|
102
|
-
select
|
103
|
-
|
104
|
-
|
105
|
-
|
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(:
|
239
|
-
where
|
240
|
-
select_map
|
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
|
data/lib/sequel/core.rb
CHANGED
@@ -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
|
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
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
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,
|
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)
|