sequel 4.39.0 → 4.40.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|