sequel 5.39.0 → 5.63.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 +308 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +57 -25
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +13 -13
- data/doc/association_basics.rdoc +89 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/migration.rdoc +12 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +18 -11
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +58 -7
- data/lib/sequel/adapters/shared/mysql.rb +40 -2
- data/lib/sequel/adapters/shared/oracle.rb +76 -0
- data/lib/sequel/adapters/shared/postgres.rb +418 -174
- data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +8 -8
- data/lib/sequel/connection_pool/timed_queue.rb +257 -0
- data/lib/sequel/connection_pool.rb +47 -30
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +26 -2
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +38 -1
- data/lib/sequel/database/schema_generator.rb +45 -52
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +107 -13
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +118 -16
- data/lib/sequel/dataset/sql.rb +177 -47
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +7 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +23 -3
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +10 -23
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +19 -13
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_dumper.rb +13 -2
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +325 -96
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +3 -1
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
- data/lib/sequel/plugins/prepared_statements.rb +10 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +38 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +97 -43
|
@@ -19,6 +19,9 @@ module Sequel
|
|
|
19
19
|
module Postgres
|
|
20
20
|
Sequel::Database.set_shared_adapter_scheme(:postgres, self)
|
|
21
21
|
|
|
22
|
+
# Exception class ranged when literalizing integers outside the bigint/int8 range.
|
|
23
|
+
class IntegerOutsideBigintRange < InvalidValue; end
|
|
24
|
+
|
|
22
25
|
NAN = 0.0/0.0
|
|
23
26
|
PLUS_INFINITY = 1.0/0.0
|
|
24
27
|
MINUS_INFINITY = -1.0/0.0
|
|
@@ -83,11 +86,22 @@ module Sequel
|
|
|
83
86
|
def primary_key(table)
|
|
84
87
|
:id
|
|
85
88
|
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# Handle NoMethodErrors when parsing schema due to output_identifier
|
|
93
|
+
# being called with nil when the Database fetch results are not set
|
|
94
|
+
# to what schema parsing expects.
|
|
95
|
+
def schema_parse_table(table, opts=OPTS)
|
|
96
|
+
super
|
|
97
|
+
rescue NoMethodError
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
86
100
|
end
|
|
87
101
|
|
|
88
102
|
def self.mock_adapter_setup(db)
|
|
89
103
|
db.instance_exec do
|
|
90
|
-
@server_version =
|
|
104
|
+
@server_version = 150000
|
|
91
105
|
initialize_postgres_adapter
|
|
92
106
|
extend(MockAdapterDatabaseMethods)
|
|
93
107
|
end
|
|
@@ -230,7 +244,6 @@ module Sequel
|
|
|
230
244
|
module DatabaseMethods
|
|
231
245
|
include UnmodifiedIdentifiers::DatabaseMethods
|
|
232
246
|
|
|
233
|
-
PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
|
|
234
247
|
FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'=>:no_action, 'r'=>:restrict, 'c'=>:cascade, 'n'=>:set_null, 'd'=>:set_default}.freeze
|
|
235
248
|
ON_COMMIT = {:drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS'}.freeze
|
|
236
249
|
ON_COMMIT.each_value(&:freeze)
|
|
@@ -254,7 +267,7 @@ module Sequel
|
|
|
254
267
|
WHERE cons.contype = 'p'
|
|
255
268
|
AND pg_get_expr(def.adbin, attr.attrelid) ~* 'nextval'
|
|
256
269
|
end_sql
|
|
257
|
-
).strip.gsub(/\s+/, ' ').freeze
|
|
270
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
|
258
271
|
|
|
259
272
|
# SQL fragment for determining primary key column for the given table. Only
|
|
260
273
|
# returns the first primary key if the table has a composite primary key.
|
|
@@ -267,7 +280,7 @@ module Sequel
|
|
|
267
280
|
AND pg_index.indkey[0] = pg_attribute.attnum
|
|
268
281
|
AND pg_index.indisprimary = 't'
|
|
269
282
|
end_sql
|
|
270
|
-
).strip.gsub(/\s+/, ' ').freeze
|
|
283
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
|
271
284
|
|
|
272
285
|
# SQL fragment for getting sequence associated with table's
|
|
273
286
|
# primary key, assuming it was a serial primary key column.
|
|
@@ -285,7 +298,7 @@ module Sequel
|
|
|
285
298
|
AND attr.attrelid = t.oid
|
|
286
299
|
AND cons.contype = 'p'
|
|
287
300
|
end_sql
|
|
288
|
-
).strip.gsub(/\s+/, ' ').freeze
|
|
301
|
+
).strip.gsub(/\s+/, ' ').freeze # SEQUEL6: Remove
|
|
289
302
|
|
|
290
303
|
# A hash of conversion procs, keyed by type integer (oid) and
|
|
291
304
|
# having callable values for the conversion proc for that type.
|
|
@@ -319,14 +332,8 @@ module Sequel
|
|
|
319
332
|
def check_constraints(table)
|
|
320
333
|
m = output_identifier_meth
|
|
321
334
|
|
|
322
|
-
rows = metadata_dataset.
|
|
323
|
-
from{pg_constraint.as(:co)}.
|
|
324
|
-
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
325
|
-
where(:conrelid=>regclass_oid(table), :contype=>'c').
|
|
326
|
-
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
|
327
|
-
|
|
328
335
|
hash = {}
|
|
329
|
-
|
|
336
|
+
_check_constraints_ds.where_each(:conrelid=>regclass_oid(table)) do |row|
|
|
330
337
|
constraint = m.call(row[:constraint])
|
|
331
338
|
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
|
332
339
|
entry[:columns] << m.call(row[:column]) if row[:column]
|
|
@@ -365,9 +372,10 @@ module Sequel
|
|
|
365
372
|
|
|
366
373
|
table_oid = regclass_oid(table)
|
|
367
374
|
im = input_identifier_meth
|
|
368
|
-
unless column =
|
|
375
|
+
unless column = (opts[:column] || ((sch = schema(table).find{|_, sc| sc[:primary_key] && sc[:auto_increment]}) && sch[0]))
|
|
369
376
|
raise Error, "could not determine column to convert from serial to identity automatically"
|
|
370
377
|
end
|
|
378
|
+
column = im.call(column)
|
|
371
379
|
|
|
372
380
|
column_num = ds.from(:pg_attribute).
|
|
373
381
|
where(:attrelid=>table_oid, :attname=>column).
|
|
@@ -414,6 +422,7 @@ module Sequel
|
|
|
414
422
|
# 2 :: argument name
|
|
415
423
|
# 3 :: argument mode (e.g. in, out, inout)
|
|
416
424
|
# :behavior :: Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
|
|
425
|
+
# :parallel :: The thread safety attribute of the function. Should be SAFE, UNSAFE, RESTRICTED. PostgreSQL assumes UNSAFE by default.
|
|
417
426
|
# :cost :: The estimated cost of the function, used by the query planner.
|
|
418
427
|
# :language :: The language the function uses. SQL is the default.
|
|
419
428
|
# :link_symbol :: For a dynamically loaded see function, the function's link symbol if different from the definition argument.
|
|
@@ -479,6 +488,7 @@ module Sequel
|
|
|
479
488
|
# :each_row :: Calls the trigger for each row instead of for each statement.
|
|
480
489
|
# :events :: Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
|
|
481
490
|
# the trigger is called for insert, update, or delete.
|
|
491
|
+
# :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
|
|
482
492
|
# :when :: A filter to use for the trigger
|
|
483
493
|
def create_trigger(table, name, function, opts=OPTS)
|
|
484
494
|
self << create_trigger_sql(table, name, function, opts)
|
|
@@ -549,63 +559,12 @@ module Sequel
|
|
|
549
559
|
def foreign_key_list(table, opts=OPTS)
|
|
550
560
|
m = output_identifier_meth
|
|
551
561
|
schema, _ = opts.fetch(:schema, schema_and_table(table))
|
|
552
|
-
oid = regclass_oid(table)
|
|
553
|
-
reverse = opts[:reverse]
|
|
554
|
-
|
|
555
|
-
if reverse
|
|
556
|
-
ctable = Sequel[:att2]
|
|
557
|
-
cclass = Sequel[:cl2]
|
|
558
|
-
rtable = Sequel[:att]
|
|
559
|
-
rclass = Sequel[:cl]
|
|
560
|
-
else
|
|
561
|
-
ctable = Sequel[:att]
|
|
562
|
-
cclass = Sequel[:cl]
|
|
563
|
-
rtable = Sequel[:att2]
|
|
564
|
-
rclass = Sequel[:cl2]
|
|
565
|
-
end
|
|
566
|
-
|
|
567
|
-
if server_version >= 90500
|
|
568
|
-
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
|
569
|
-
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
|
570
|
-
else
|
|
571
|
-
range = 0...32
|
|
572
|
-
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
|
573
|
-
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
|
574
|
-
end
|
|
575
|
-
|
|
576
|
-
ds = metadata_dataset.
|
|
577
|
-
from{pg_constraint.as(:co)}.
|
|
578
|
-
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
|
579
|
-
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
580
|
-
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
|
581
|
-
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
|
582
|
-
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
|
583
|
-
order{[co[:conname], cpos]}.
|
|
584
|
-
where{{
|
|
585
|
-
cl[:relkind]=>'r',
|
|
586
|
-
co[:contype]=>'f',
|
|
587
|
-
cl[:oid]=>oid,
|
|
588
|
-
cpos=>rpos
|
|
589
|
-
}}.
|
|
590
|
-
select{[
|
|
591
|
-
co[:conname].as(:name),
|
|
592
|
-
ctable[:attname].as(:column),
|
|
593
|
-
co[:confupdtype].as(:on_update),
|
|
594
|
-
co[:confdeltype].as(:on_delete),
|
|
595
|
-
cl2[:relname].as(:table),
|
|
596
|
-
rtable[:attname].as(:refcolumn),
|
|
597
|
-
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
|
598
|
-
nsp[:nspname].as(:schema)
|
|
599
|
-
]}
|
|
600
|
-
|
|
601
|
-
if reverse
|
|
602
|
-
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
|
603
|
-
end
|
|
604
562
|
|
|
605
563
|
h = {}
|
|
606
564
|
fklod_map = FOREIGN_KEY_LIST_ON_DELETE_MAP
|
|
565
|
+
reverse = opts[:reverse]
|
|
607
566
|
|
|
608
|
-
|
|
567
|
+
(reverse ? _reverse_foreign_key_list_ds : _foreign_key_list_ds).where_each(Sequel[:cl][:oid]=>regclass_oid(table)) do |row|
|
|
609
568
|
if reverse
|
|
610
569
|
key = [row[:schema], row[:table], row[:name]]
|
|
611
570
|
else
|
|
@@ -640,6 +599,14 @@ module Sequel
|
|
|
640
599
|
def freeze
|
|
641
600
|
server_version
|
|
642
601
|
supports_prepared_transactions?
|
|
602
|
+
_schema_ds
|
|
603
|
+
_select_serial_sequence_ds
|
|
604
|
+
_select_custom_sequence_ds
|
|
605
|
+
_select_pk_ds
|
|
606
|
+
_indexes_ds
|
|
607
|
+
_check_constraints_ds
|
|
608
|
+
_foreign_key_list_ds
|
|
609
|
+
_reverse_foreign_key_list_ds
|
|
643
610
|
@conversion_procs.freeze
|
|
644
611
|
super
|
|
645
612
|
end
|
|
@@ -647,38 +614,11 @@ module Sequel
|
|
|
647
614
|
# Use the pg_* system tables to determine indexes on a table
|
|
648
615
|
def indexes(table, opts=OPTS)
|
|
649
616
|
m = output_identifier_meth
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
if server_version >= 90500
|
|
653
|
-
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
|
654
|
-
else
|
|
655
|
-
range = 0...32
|
|
656
|
-
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
|
657
|
-
end
|
|
658
|
-
|
|
659
|
-
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
|
660
|
-
|
|
661
|
-
ds = metadata_dataset.
|
|
662
|
-
from{pg_class.as(:tab)}.
|
|
663
|
-
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
|
664
|
-
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
|
665
|
-
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
|
666
|
-
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
|
667
|
-
where{{
|
|
668
|
-
indc[:relkind]=>'i',
|
|
669
|
-
ind[:indisprimary]=>false,
|
|
670
|
-
:indexprs=>nil,
|
|
671
|
-
:indisvalid=>true,
|
|
672
|
-
tab[:oid]=>oid}}.
|
|
673
|
-
order(*order).
|
|
674
|
-
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
|
675
|
-
|
|
676
|
-
ds = ds.where(:indpred=>nil) unless opts[:include_partial]
|
|
677
|
-
ds = ds.where(:indisready=>true) if server_version >= 80300
|
|
678
|
-
ds = ds.where(:indislive=>true) if server_version >= 90300
|
|
617
|
+
cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
|
|
618
|
+
cond[:indpred] = nil unless opts[:include_partial]
|
|
679
619
|
|
|
680
620
|
indexes = {}
|
|
681
|
-
|
|
621
|
+
_indexes_ds.where_each(cond) do |r|
|
|
682
622
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
|
|
683
623
|
i[:columns] << m.call(r[:column])
|
|
684
624
|
end
|
|
@@ -711,8 +651,7 @@ module Sequel
|
|
|
711
651
|
def primary_key(table, opts=OPTS)
|
|
712
652
|
quoted_table = quote_schema_table(table)
|
|
713
653
|
Sequel.synchronize{return @primary_keys[quoted_table] if @primary_keys.has_key?(quoted_table)}
|
|
714
|
-
|
|
715
|
-
value = fetch(sql).single_value
|
|
654
|
+
value = _select_pk_ds.where_single_value(Sequel[:pg_class][:oid] => regclass_oid(table, opts))
|
|
716
655
|
Sequel.synchronize{@primary_keys[quoted_table] = value}
|
|
717
656
|
end
|
|
718
657
|
|
|
@@ -720,24 +659,21 @@ module Sequel
|
|
|
720
659
|
def primary_key_sequence(table, opts=OPTS)
|
|
721
660
|
quoted_table = quote_schema_table(table)
|
|
722
661
|
Sequel.synchronize{return @primary_key_sequences[quoted_table] if @primary_key_sequences.has_key?(quoted_table)}
|
|
723
|
-
|
|
724
|
-
if pks =
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
sql = "#{SELECT_CUSTOM_SEQUENCE_SQL} AND t.oid = #{literal(regclass_oid(table, opts))}"
|
|
729
|
-
if pks = fetch(sql).single_record
|
|
730
|
-
value = literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
|
731
|
-
Sequel.synchronize{@primary_key_sequences[quoted_table] = value}
|
|
732
|
-
end
|
|
662
|
+
cond = {Sequel[:t][:oid] => regclass_oid(table, opts)}
|
|
663
|
+
value = if pks = _select_serial_sequence_ds.first(cond)
|
|
664
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], pks[:sequence]))
|
|
665
|
+
elsif pks = _select_custom_sequence_ds.first(cond)
|
|
666
|
+
literal(SQL::QualifiedIdentifier.new(pks[:schema], LiteralString.new(pks[:sequence])))
|
|
733
667
|
end
|
|
668
|
+
|
|
669
|
+
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
|
734
670
|
end
|
|
735
671
|
|
|
736
672
|
# Refresh the materialized view with the given name.
|
|
737
673
|
#
|
|
738
674
|
# DB.refresh_view(:items_view)
|
|
739
675
|
# # REFRESH MATERIALIZED VIEW items_view
|
|
740
|
-
# DB.refresh_view(:items_view, :
|
|
676
|
+
# DB.refresh_view(:items_view, concurrently: true)
|
|
741
677
|
# # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
|
|
742
678
|
def refresh_view(name, opts=OPTS)
|
|
743
679
|
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
|
@@ -756,10 +692,12 @@ module Sequel
|
|
|
756
692
|
seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
|
|
757
693
|
increment_by = :seqincrement
|
|
758
694
|
min_value = :seqmin
|
|
695
|
+
# :nocov:
|
|
759
696
|
else
|
|
760
697
|
seq_ds = metadata_dataset.from(LiteralString.new(seq))
|
|
761
698
|
increment_by = :increment_by
|
|
762
699
|
min_value = :min_value
|
|
700
|
+
# :nocov:
|
|
763
701
|
end
|
|
764
702
|
|
|
765
703
|
get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
|
|
@@ -772,7 +710,9 @@ module Sequel
|
|
|
772
710
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
|
773
711
|
# managing incrementing primary keys.
|
|
774
712
|
def serial_primary_key_options
|
|
713
|
+
# :nocov:
|
|
775
714
|
auto_increment_key = server_version >= 100002 ? :identity : :serial
|
|
715
|
+
# :nocov:
|
|
776
716
|
{:primary_key => true, auto_increment_key => true, :type=>Integer}
|
|
777
717
|
end
|
|
778
718
|
|
|
@@ -883,6 +823,216 @@ module Sequel
|
|
|
883
823
|
|
|
884
824
|
private
|
|
885
825
|
|
|
826
|
+
# Dataset used to retrieve CHECK constraint information
|
|
827
|
+
def _check_constraints_ds
|
|
828
|
+
@_check_constraints_ds ||= metadata_dataset.
|
|
829
|
+
from{pg_constraint.as(:co)}.
|
|
830
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
831
|
+
where(:contype=>'c').
|
|
832
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
# Dataset used to retrieve foreign keys referenced by a table
|
|
836
|
+
def _foreign_key_list_ds
|
|
837
|
+
@_foreign_key_list_ds ||= __foreign_key_list_ds(false)
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
# Dataset used to retrieve foreign keys referencing a table
|
|
841
|
+
def _reverse_foreign_key_list_ds
|
|
842
|
+
@_reverse_foreign_key_list_ds ||= __foreign_key_list_ds(true)
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
# Build dataset used for foreign key list methods.
|
|
846
|
+
def __foreign_key_list_ds(reverse)
|
|
847
|
+
if reverse
|
|
848
|
+
ctable = Sequel[:att2]
|
|
849
|
+
cclass = Sequel[:cl2]
|
|
850
|
+
rtable = Sequel[:att]
|
|
851
|
+
rclass = Sequel[:cl]
|
|
852
|
+
else
|
|
853
|
+
ctable = Sequel[:att]
|
|
854
|
+
cclass = Sequel[:cl]
|
|
855
|
+
rtable = Sequel[:att2]
|
|
856
|
+
rclass = Sequel[:cl2]
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
if server_version >= 90500
|
|
860
|
+
cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
|
|
861
|
+
rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
|
|
862
|
+
# :nocov:
|
|
863
|
+
else
|
|
864
|
+
range = 0...32
|
|
865
|
+
cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
|
|
866
|
+
rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
|
|
867
|
+
# :nocov:
|
|
868
|
+
end
|
|
869
|
+
|
|
870
|
+
ds = metadata_dataset.
|
|
871
|
+
from{pg_constraint.as(:co)}.
|
|
872
|
+
join(Sequel[:pg_class].as(cclass), :oid=>:conrelid).
|
|
873
|
+
join(Sequel[:pg_attribute].as(ctable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
874
|
+
join(Sequel[:pg_class].as(rclass), :oid=>Sequel[:co][:confrelid]).
|
|
875
|
+
join(Sequel[:pg_attribute].as(rtable), :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:confkey])).
|
|
876
|
+
join(Sequel[:pg_namespace].as(:nsp), :oid=>Sequel[:cl2][:relnamespace]).
|
|
877
|
+
order{[co[:conname], cpos]}.
|
|
878
|
+
where{{
|
|
879
|
+
cl[:relkind]=>%w'r p',
|
|
880
|
+
co[:contype]=>'f',
|
|
881
|
+
cpos=>rpos
|
|
882
|
+
}}.
|
|
883
|
+
select{[
|
|
884
|
+
co[:conname].as(:name),
|
|
885
|
+
ctable[:attname].as(:column),
|
|
886
|
+
co[:confupdtype].as(:on_update),
|
|
887
|
+
co[:confdeltype].as(:on_delete),
|
|
888
|
+
cl2[:relname].as(:table),
|
|
889
|
+
rtable[:attname].as(:refcolumn),
|
|
890
|
+
SQL::BooleanExpression.new(:AND, co[:condeferrable], co[:condeferred]).as(:deferrable),
|
|
891
|
+
nsp[:nspname].as(:schema)
|
|
892
|
+
]}
|
|
893
|
+
|
|
894
|
+
if reverse
|
|
895
|
+
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
ds
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
# Dataset used to retrieve index information
|
|
902
|
+
def _indexes_ds
|
|
903
|
+
@_indexes_ds ||= begin
|
|
904
|
+
if server_version >= 90500
|
|
905
|
+
order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
|
|
906
|
+
# :nocov:
|
|
907
|
+
else
|
|
908
|
+
range = 0...32
|
|
909
|
+
order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
|
|
910
|
+
# :nocov:
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
|
|
914
|
+
|
|
915
|
+
ds = metadata_dataset.
|
|
916
|
+
from{pg_class.as(:tab)}.
|
|
917
|
+
join(Sequel[:pg_index].as(:ind), :indrelid=>:oid).
|
|
918
|
+
join(Sequel[:pg_class].as(:indc), :oid=>:indexrelid).
|
|
919
|
+
join(Sequel[:pg_attribute].as(:att), :attrelid=>Sequel[:tab][:oid], :attnum=>attnums).
|
|
920
|
+
left_join(Sequel[:pg_constraint].as(:con), :conname=>Sequel[:indc][:relname]).
|
|
921
|
+
where{{
|
|
922
|
+
indc[:relkind]=>'i',
|
|
923
|
+
ind[:indisprimary]=>false,
|
|
924
|
+
:indexprs=>nil,
|
|
925
|
+
:indisvalid=>true}}.
|
|
926
|
+
order(*order).
|
|
927
|
+
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
|
928
|
+
|
|
929
|
+
# :nocov:
|
|
930
|
+
ds = ds.where(:indisready=>true) if server_version >= 80300
|
|
931
|
+
ds = ds.where(:indislive=>true) if server_version >= 90300
|
|
932
|
+
# :nocov:
|
|
933
|
+
|
|
934
|
+
ds
|
|
935
|
+
end
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
# Dataset used to determine custom serial sequences for tables
|
|
939
|
+
def _select_custom_sequence_ds
|
|
940
|
+
@_select_custom_sequence_ds ||= metadata_dataset.
|
|
941
|
+
from{pg_class.as(:t)}.
|
|
942
|
+
join(:pg_namespace, {:oid => :relnamespace}, :table_alias=>:name).
|
|
943
|
+
join(:pg_attribute, {:attrelid => Sequel[:t][:oid]}, :table_alias=>:attr).
|
|
944
|
+
join(:pg_attrdef, {:adrelid => :attrelid, :adnum => :attnum}, :table_alias=>:def).
|
|
945
|
+
join(:pg_constraint, {:conrelid => :adrelid, Sequel[:cons][:conkey].sql_subscript(1) => :adnum}, :table_alias=>:cons).
|
|
946
|
+
where{{cons[:contype] => 'p', pg_get_expr(self.def[:adbin], attr[:attrelid]) => /nextval/i}}.
|
|
947
|
+
select{
|
|
948
|
+
expr = split_part(pg_get_expr(self.def[:adbin], attr[:attrelid]), "'", 2)
|
|
949
|
+
[
|
|
950
|
+
name[:nspname].as(:schema),
|
|
951
|
+
Sequel.case({{expr => /./} => substr(expr, strpos(expr, '.')+1)}, expr).as(:sequence)
|
|
952
|
+
]
|
|
953
|
+
}
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
# Dataset used to determine normal serial sequences for tables
|
|
957
|
+
def _select_serial_sequence_ds
|
|
958
|
+
@_serial_sequence_ds ||= metadata_dataset.
|
|
959
|
+
from{[
|
|
960
|
+
pg_class.as(:seq),
|
|
961
|
+
pg_attribute.as(:attr),
|
|
962
|
+
pg_depend.as(:dep),
|
|
963
|
+
pg_namespace.as(:name),
|
|
964
|
+
pg_constraint.as(:cons),
|
|
965
|
+
pg_class.as(:t)
|
|
966
|
+
]}.
|
|
967
|
+
where{[
|
|
968
|
+
[seq[:oid], dep[:objid]],
|
|
969
|
+
[seq[:relnamespace], name[:oid]],
|
|
970
|
+
[seq[:relkind], 'S'],
|
|
971
|
+
[attr[:attrelid], dep[:refobjid]],
|
|
972
|
+
[attr[:attnum], dep[:refobjsubid]],
|
|
973
|
+
[attr[:attrelid], cons[:conrelid]],
|
|
974
|
+
[attr[:attnum], cons[:conkey].sql_subscript(1)],
|
|
975
|
+
[attr[:attrelid], t[:oid]],
|
|
976
|
+
[cons[:contype], 'p']
|
|
977
|
+
]}.
|
|
978
|
+
select{[
|
|
979
|
+
name[:nspname].as(:schema),
|
|
980
|
+
seq[:relname].as(:sequence)
|
|
981
|
+
]}
|
|
982
|
+
end
|
|
983
|
+
|
|
984
|
+
# Dataset used to determine primary keys for tables
|
|
985
|
+
def _select_pk_ds
|
|
986
|
+
@_select_pk_ds ||= metadata_dataset.
|
|
987
|
+
from(:pg_class, :pg_attribute, :pg_index, :pg_namespace).
|
|
988
|
+
where{[
|
|
989
|
+
[pg_class[:oid], pg_attribute[:attrelid]],
|
|
990
|
+
[pg_class[:relnamespace], pg_namespace[:oid]],
|
|
991
|
+
[pg_class[:oid], pg_index[:indrelid]],
|
|
992
|
+
[pg_index[:indkey].sql_subscript(0), pg_attribute[:attnum]],
|
|
993
|
+
[pg_index[:indisprimary], 't']
|
|
994
|
+
]}.
|
|
995
|
+
select{pg_attribute[:attname].as(:pk)}
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
# Dataset used to get schema for tables
|
|
999
|
+
def _schema_ds
|
|
1000
|
+
@_schema_ds ||= begin
|
|
1001
|
+
ds = metadata_dataset.select{[
|
|
1002
|
+
pg_attribute[:attname].as(:name),
|
|
1003
|
+
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
|
1004
|
+
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
|
1005
|
+
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
|
1006
|
+
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
|
1007
|
+
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
|
1008
|
+
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
|
1009
|
+
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
|
|
1010
|
+
from(:pg_class).
|
|
1011
|
+
join(:pg_attribute, :attrelid=>:oid).
|
|
1012
|
+
join(:pg_type, :oid=>:atttypid).
|
|
1013
|
+
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
|
1014
|
+
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
|
1015
|
+
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
|
1016
|
+
where{{pg_attribute[:attisdropped]=>false}}.
|
|
1017
|
+
where{pg_attribute[:attnum] > 0}.
|
|
1018
|
+
order{pg_attribute[:attnum]}
|
|
1019
|
+
|
|
1020
|
+
# :nocov:
|
|
1021
|
+
if server_version > 100000
|
|
1022
|
+
# :nocov:
|
|
1023
|
+
ds = ds.select_append{pg_attribute[:attidentity]}
|
|
1024
|
+
|
|
1025
|
+
# :nocov:
|
|
1026
|
+
if server_version > 120000
|
|
1027
|
+
# :nocov:
|
|
1028
|
+
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
|
1029
|
+
end
|
|
1030
|
+
end
|
|
1031
|
+
|
|
1032
|
+
ds
|
|
1033
|
+
end
|
|
1034
|
+
end
|
|
1035
|
+
|
|
886
1036
|
def alter_table_add_column_sql(table, op)
|
|
887
1037
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
|
888
1038
|
end
|
|
@@ -1116,6 +1266,7 @@ module Sequel
|
|
|
1116
1266
|
#{opts[:behavior].to_s.upcase if opts[:behavior]}
|
|
1117
1267
|
#{'STRICT' if opts[:strict]}
|
|
1118
1268
|
#{'SECURITY DEFINER' if opts[:security_definer]}
|
|
1269
|
+
#{"PARALLEL #{opts[:parallel].to_s.upcase}" if opts[:parallel]}
|
|
1119
1270
|
#{"COST #{opts[:cost]}" if opts[:cost]}
|
|
1120
1271
|
#{"ROWS #{opts[:rows]}" if opts[:rows]}
|
|
1121
1272
|
#{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
|
|
@@ -1149,7 +1300,7 @@ module Sequel
|
|
|
1149
1300
|
when :hash
|
|
1150
1301
|
mod, remainder = generator.hash_values
|
|
1151
1302
|
sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
|
|
1152
|
-
when :default
|
|
1303
|
+
else # when :default
|
|
1153
1304
|
sql << " DEFAULT"
|
|
1154
1305
|
end
|
|
1155
1306
|
|
|
@@ -1237,13 +1388,17 @@ module Sequel
|
|
|
1237
1388
|
raise Error, "Trigger conditions are not supported for this database" unless supports_trigger_conditions?
|
|
1238
1389
|
filter = " WHEN #{filter_expr(filter)}"
|
|
1239
1390
|
end
|
|
1240
|
-
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
|
1391
|
+
"CREATE #{'OR REPLACE ' if opts[:replace]}TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
|
1241
1392
|
end
|
|
1242
1393
|
|
|
1243
1394
|
# DDL fragment for initial part of CREATE VIEW statement
|
|
1244
1395
|
def create_view_prefix_sql(name, options)
|
|
1245
1396
|
sql = create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
|
|
1246
1397
|
|
|
1398
|
+
if options[:security_invoker]
|
|
1399
|
+
sql += " WITH (security_invoker)"
|
|
1400
|
+
end
|
|
1401
|
+
|
|
1247
1402
|
if tablespace = options[:tablespace]
|
|
1248
1403
|
sql += " TABLESPACE #{quote_identifier(tablespace)}"
|
|
1249
1404
|
end
|
|
@@ -1301,16 +1456,20 @@ module Sequel
|
|
|
1301
1456
|
def index_definition_sql(table_name, index)
|
|
1302
1457
|
cols = index[:columns]
|
|
1303
1458
|
index_name = index[:name] || default_index_name(table_name, cols)
|
|
1459
|
+
|
|
1304
1460
|
expr = if o = index[:opclass]
|
|
1305
1461
|
"(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
|
|
1306
1462
|
else
|
|
1307
1463
|
literal(Array(cols))
|
|
1308
1464
|
end
|
|
1465
|
+
|
|
1309
1466
|
if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
|
|
1310
1467
|
unique = "UNIQUE " if index[:unique]
|
|
1311
1468
|
index_type = index[:type]
|
|
1312
1469
|
filter = index[:where] || index[:filter]
|
|
1313
1470
|
filter = " WHERE #{filter_expr(filter)}" if filter
|
|
1471
|
+
nulls_distinct = " NULLS#{' NOT' if index[:nulls_distinct] == false} DISTINCT" unless index[:nulls_distinct].nil?
|
|
1472
|
+
|
|
1314
1473
|
case index_type
|
|
1315
1474
|
when :full_text
|
|
1316
1475
|
expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
|
|
@@ -1318,7 +1477,8 @@ module Sequel
|
|
|
1318
1477
|
when :spatial
|
|
1319
1478
|
index_type = :gist
|
|
1320
1479
|
end
|
|
1321
|
-
|
|
1480
|
+
|
|
1481
|
+
"CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
|
|
1322
1482
|
end
|
|
1323
1483
|
|
|
1324
1484
|
# Setup datastructures shared by all postgres adapters.
|
|
@@ -1335,7 +1495,7 @@ module Sequel
|
|
|
1335
1495
|
ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
|
1336
1496
|
ds = filter_schema(ds, opts)
|
|
1337
1497
|
m = output_identifier_meth
|
|
1338
|
-
if
|
|
1498
|
+
if defined?(yield)
|
|
1339
1499
|
yield(ds)
|
|
1340
1500
|
elsif opts[:qualify]
|
|
1341
1501
|
ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
|
|
@@ -1344,11 +1504,6 @@ module Sequel
|
|
|
1344
1504
|
end
|
|
1345
1505
|
end
|
|
1346
1506
|
|
|
1347
|
-
# Use a dollar sign instead of question mark for the argument placeholder.
|
|
1348
|
-
def prepared_arg_placeholder
|
|
1349
|
-
PREPARED_ARG_PLACEHOLDER
|
|
1350
|
-
end
|
|
1351
|
-
|
|
1352
1507
|
# Return an expression the oid for the table expr. Used by the metadata parsing
|
|
1353
1508
|
# code to disambiguate unqualified tables.
|
|
1354
1509
|
def regclass_oid(expr, opts=OPTS)
|
|
@@ -1401,36 +1556,8 @@ module Sequel
|
|
|
1401
1556
|
# The dataset used for parsing table schemas, using the pg_* system catalogs.
|
|
1402
1557
|
def schema_parse_table(table_name, opts)
|
|
1403
1558
|
m = output_identifier_meth(opts[:dataset])
|
|
1404
|
-
oid = regclass_oid(table_name, opts)
|
|
1405
|
-
ds = metadata_dataset.select{[
|
|
1406
|
-
pg_attribute[:attname].as(:name),
|
|
1407
|
-
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
|
1408
|
-
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
|
1409
|
-
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
|
1410
|
-
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
|
1411
|
-
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
|
1412
|
-
SQL::BooleanExpression.new(:NOT, pg_attribute[:attnotnull]).as(:allow_null),
|
|
1413
|
-
SQL::Function.new(:COALESCE, SQL::BooleanExpression.from_value_pairs(pg_attribute[:attnum] => SQL::Function.new(:ANY, pg_index[:indkey])), false).as(:primary_key)]}.
|
|
1414
|
-
from(:pg_class).
|
|
1415
|
-
join(:pg_attribute, :attrelid=>:oid).
|
|
1416
|
-
join(:pg_type, :oid=>:atttypid).
|
|
1417
|
-
left_outer_join(Sequel[:pg_type].as(:basetype), :oid=>:typbasetype).
|
|
1418
|
-
left_outer_join(:pg_attrdef, :adrelid=>Sequel[:pg_class][:oid], :adnum=>Sequel[:pg_attribute][:attnum]).
|
|
1419
|
-
left_outer_join(:pg_index, :indrelid=>Sequel[:pg_class][:oid], :indisprimary=>true).
|
|
1420
|
-
where{{pg_attribute[:attisdropped]=>false}}.
|
|
1421
|
-
where{pg_attribute[:attnum] > 0}.
|
|
1422
|
-
where{{pg_class[:oid]=>oid}}.
|
|
1423
|
-
order{pg_attribute[:attnum]}
|
|
1424
|
-
|
|
1425
|
-
if server_version > 100000
|
|
1426
|
-
ds = ds.select_append{pg_attribute[:attidentity]}
|
|
1427
|
-
|
|
1428
|
-
if server_version > 120000
|
|
1429
|
-
ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
|
|
1430
|
-
end
|
|
1431
|
-
end
|
|
1432
1559
|
|
|
1433
|
-
|
|
1560
|
+
_schema_ds.where_all(Sequel[:pg_class][:oid]=>regclass_oid(table_name, opts)).map do |row|
|
|
1434
1561
|
row[:default] = nil if blank_object?(row[:default])
|
|
1435
1562
|
if row[:base_oid]
|
|
1436
1563
|
row[:domain_oid] = row[:oid]
|
|
@@ -1500,10 +1627,12 @@ module Sequel
|
|
|
1500
1627
|
# disallowed or there is a size specified, use the varchar type.
|
|
1501
1628
|
# Otherwise use the text type.
|
|
1502
1629
|
def type_literal_generic_string(column)
|
|
1503
|
-
if column[:
|
|
1504
|
-
|
|
1505
|
-
elsif column[:
|
|
1506
|
-
"
|
|
1630
|
+
if column[:text]
|
|
1631
|
+
:text
|
|
1632
|
+
elsif column[:fixed]
|
|
1633
|
+
"char(#{column[:size]||default_string_column_size})"
|
|
1634
|
+
elsif column[:text] == false || column[:size]
|
|
1635
|
+
"varchar(#{column[:size]||default_string_column_size})"
|
|
1507
1636
|
else
|
|
1508
1637
|
:text
|
|
1509
1638
|
end
|
|
@@ -1511,7 +1640,9 @@ module Sequel
|
|
|
1511
1640
|
|
|
1512
1641
|
# PostgreSQL 9.4+ supports views with check option.
|
|
1513
1642
|
def view_with_check_option_support
|
|
1643
|
+
# :nocov:
|
|
1514
1644
|
:local if server_version >= 90400
|
|
1645
|
+
# :nocov:
|
|
1515
1646
|
end
|
|
1516
1647
|
end
|
|
1517
1648
|
|
|
@@ -1522,7 +1653,7 @@ module Sequel
|
|
|
1522
1653
|
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
|
|
1523
1654
|
|
|
1524
1655
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
|
1525
|
-
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']])
|
|
1656
|
+
Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns override values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
|
|
1526
1657
|
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']])
|
|
1527
1658
|
Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
|
|
1528
1659
|
|
|
@@ -1725,13 +1856,22 @@ module Sequel
|
|
|
1725
1856
|
ds.insert_sql(*values)
|
|
1726
1857
|
end
|
|
1727
1858
|
|
|
1859
|
+
# Support SQL::AliasedExpression as expr to setup a USING join with a table alias for the
|
|
1860
|
+
# USING columns.
|
|
1861
|
+
def join_table(type, table, expr=nil, options=OPTS, &block)
|
|
1862
|
+
if expr.is_a?(SQL::AliasedExpression) && expr.expression.is_a?(Array) && !expr.expression.empty? && expr.expression.all?
|
|
1863
|
+
options = options.merge(:join_using=>true)
|
|
1864
|
+
end
|
|
1865
|
+
super
|
|
1866
|
+
end
|
|
1867
|
+
|
|
1728
1868
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
|
1729
1869
|
# the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
|
|
1730
1870
|
# a new transaction, locks the table, and yields. If a block is not given,
|
|
1731
1871
|
# just locks the tables. Note that PostgreSQL will probably raise an error
|
|
1732
1872
|
# if you lock the table outside of an existing transaction. Returns nil.
|
|
1733
1873
|
def lock(mode, opts=OPTS)
|
|
1734
|
-
if
|
|
1874
|
+
if defined?(yield) # perform locking inside a transaction and yield to block
|
|
1735
1875
|
@db.transaction(opts){lock(mode, opts); yield}
|
|
1736
1876
|
else
|
|
1737
1877
|
sql = 'LOCK TABLE '.dup
|
|
@@ -1746,6 +1886,41 @@ module Sequel
|
|
|
1746
1886
|
nil
|
|
1747
1887
|
end
|
|
1748
1888
|
|
|
1889
|
+
# Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
|
|
1890
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
|
1891
|
+
# use it as additional conditions for the match.
|
|
1892
|
+
#
|
|
1893
|
+
# merge_do_nothing_when_matched
|
|
1894
|
+
# # WHEN MATCHED THEN DO NOTHING
|
|
1895
|
+
#
|
|
1896
|
+
# merge_do_nothing_when_matched{a > 30}
|
|
1897
|
+
# # WHEN MATCHED AND (a > 30) THEN DO NOTHING
|
|
1898
|
+
def merge_do_nothing_when_matched(&block)
|
|
1899
|
+
_merge_when(:type=>:matched, &block)
|
|
1900
|
+
end
|
|
1901
|
+
|
|
1902
|
+
# Return a dataset with a WHEN NOT MATCHED THEN DO NOTHING clause added to the
|
|
1903
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
|
1904
|
+
# use it as additional conditions for the match.
|
|
1905
|
+
#
|
|
1906
|
+
# merge_do_nothing_when_not_matched
|
|
1907
|
+
# # WHEN NOT MATCHED THEN DO NOTHING
|
|
1908
|
+
#
|
|
1909
|
+
# merge_do_nothing_when_not_matched{a > 30}
|
|
1910
|
+
# # WHEN NOT MATCHED AND (a > 30) THEN DO NOTHING
|
|
1911
|
+
def merge_do_nothing_when_not_matched(&block)
|
|
1912
|
+
_merge_when(:type=>:not_matched, &block)
|
|
1913
|
+
end
|
|
1914
|
+
|
|
1915
|
+
# Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
|
|
1916
|
+
def merge_insert(*values, &block)
|
|
1917
|
+
h = {:type=>:insert, :values=>values}
|
|
1918
|
+
if override = @opts[:override]
|
|
1919
|
+
h[:override] = insert_override_sql(String.new)
|
|
1920
|
+
end
|
|
1921
|
+
_merge_when(h, &block)
|
|
1922
|
+
end
|
|
1923
|
+
|
|
1749
1924
|
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
|
1750
1925
|
# always use the user supplied value, and an error is not raised for identity
|
|
1751
1926
|
# columns that are GENERATED ALWAYS.
|
|
@@ -1813,6 +1988,11 @@ module Sequel
|
|
|
1813
1988
|
true
|
|
1814
1989
|
end
|
|
1815
1990
|
|
|
1991
|
+
# PostgreSQL 15+ supports MERGE.
|
|
1992
|
+
def supports_merge?
|
|
1993
|
+
server_version >= 150000
|
|
1994
|
+
end
|
|
1995
|
+
|
|
1816
1996
|
# PostgreSQL supports NOWAIT.
|
|
1817
1997
|
def supports_nowait?
|
|
1818
1998
|
true
|
|
@@ -1858,6 +2038,8 @@ module Sequel
|
|
|
1858
2038
|
server_version >= 90000
|
|
1859
2039
|
when :groups, :exclude
|
|
1860
2040
|
server_version >= 110000
|
|
2041
|
+
else
|
|
2042
|
+
false
|
|
1861
2043
|
end
|
|
1862
2044
|
end
|
|
1863
2045
|
|
|
@@ -1900,12 +2082,10 @@ module Sequel
|
|
|
1900
2082
|
# Otherwise, return an array of hashes.
|
|
1901
2083
|
def _import(columns, values, opts=OPTS)
|
|
1902
2084
|
if @opts[:returning]
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
statements.map{|st| returning_fetch_rows(st)}
|
|
1908
|
-
end.first.map{|v| v.length == 1 ? v.values.first : v}
|
|
2085
|
+
# no transaction: our multi_insert_sql_strategy should guarantee
|
|
2086
|
+
# that there's only ever a single statement.
|
|
2087
|
+
sql = multi_insert_sql(columns, values)[0]
|
|
2088
|
+
returning_fetch_rows(sql).map{|v| v.length == 1 ? v.values.first : v}
|
|
1909
2089
|
elsif opts[:return] == :primary_key
|
|
1910
2090
|
returning(insert_pk)._import(columns, values, opts)
|
|
1911
2091
|
else
|
|
@@ -1923,6 +2103,22 @@ module Sequel
|
|
|
1923
2103
|
|
|
1924
2104
|
private
|
|
1925
2105
|
|
|
2106
|
+
# Append the INSERT sql used in a MERGE
|
|
2107
|
+
def _merge_insert_sql(sql, data)
|
|
2108
|
+
sql << " THEN INSERT "
|
|
2109
|
+
columns, values = _parse_insert_sql_args(data[:values])
|
|
2110
|
+
_insert_columns_sql(sql, columns)
|
|
2111
|
+
if override = data[:override]
|
|
2112
|
+
sql << override
|
|
2113
|
+
end
|
|
2114
|
+
_insert_values_sql(sql, values)
|
|
2115
|
+
end
|
|
2116
|
+
|
|
2117
|
+
def _merge_matched_sql(sql, data)
|
|
2118
|
+
sql << " THEN DO NOTHING"
|
|
2119
|
+
end
|
|
2120
|
+
alias _merge_not_matched_sql _merge_matched_sql
|
|
2121
|
+
|
|
1926
2122
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
|
1927
2123
|
def _truncate_sql(table)
|
|
1928
2124
|
to = @opts[:truncate_opts] || OPTS
|
|
@@ -1988,25 +2184,23 @@ module Sequel
|
|
|
1988
2184
|
|
|
1989
2185
|
# Return the primary key to use for RETURNING in an INSERT statement
|
|
1990
2186
|
def insert_pk
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
end
|
|
2187
|
+
(f = opts[:from]) && !f.empty? && (t = f.first)
|
|
2188
|
+
case t
|
|
2189
|
+
when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
|
|
2190
|
+
if pk = db.primary_key(t)
|
|
2191
|
+
Sequel::SQL::Identifier.new(pk)
|
|
1997
2192
|
end
|
|
1998
2193
|
end
|
|
1999
2194
|
end
|
|
2000
2195
|
|
|
2001
2196
|
# Support OVERRIDING SYSTEM|USER VALUE in insert statements
|
|
2002
|
-
def
|
|
2197
|
+
def insert_override_sql(sql)
|
|
2003
2198
|
case opts[:override]
|
|
2004
2199
|
when :system
|
|
2005
2200
|
sql << " OVERRIDING SYSTEM VALUE"
|
|
2006
2201
|
when :user
|
|
2007
2202
|
sql << " OVERRIDING USER VALUE"
|
|
2008
2203
|
end
|
|
2009
|
-
super
|
|
2010
2204
|
end
|
|
2011
2205
|
|
|
2012
2206
|
# For multiple table support, PostgreSQL requires at least
|
|
@@ -2021,6 +2215,17 @@ module Sequel
|
|
|
2021
2215
|
end
|
|
2022
2216
|
end
|
|
2023
2217
|
|
|
2218
|
+
# Support table aliases for USING columns
|
|
2219
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
|
2220
|
+
if using_columns.is_a?(SQL::AliasedExpression)
|
|
2221
|
+
super(sql, using_columns.expression)
|
|
2222
|
+
sql << ' AS '
|
|
2223
|
+
identifier_append(sql, using_columns.alias)
|
|
2224
|
+
else
|
|
2225
|
+
super
|
|
2226
|
+
end
|
|
2227
|
+
end
|
|
2228
|
+
|
|
2024
2229
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
|
2025
2230
|
def literal_blob_append(sql, v)
|
|
2026
2231
|
sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
|
|
@@ -2044,6 +2249,22 @@ module Sequel
|
|
|
2044
2249
|
end
|
|
2045
2250
|
end
|
|
2046
2251
|
|
|
2252
|
+
# Handle Ruby integers outside PostgreSQL bigint range specially.
|
|
2253
|
+
def literal_integer(v)
|
|
2254
|
+
if v > 9223372036854775807 || v < -9223372036854775808
|
|
2255
|
+
literal_integer_outside_bigint_range(v)
|
|
2256
|
+
else
|
|
2257
|
+
v.to_s
|
|
2258
|
+
end
|
|
2259
|
+
end
|
|
2260
|
+
|
|
2261
|
+
# Raise IntegerOutsideBigintRange when attempting to literalize Ruby integer
|
|
2262
|
+
# outside PostgreSQL bigint range, so PostgreSQL doesn't treat
|
|
2263
|
+
# the value as numeric.
|
|
2264
|
+
def literal_integer_outside_bigint_range(v)
|
|
2265
|
+
raise IntegerOutsideBigintRange, "attempt to literalize Ruby integer outside PostgreSQL bigint range: #{v}"
|
|
2266
|
+
end
|
|
2267
|
+
|
|
2047
2268
|
# Assume that SQL standard quoting is on, per Sequel's defaults
|
|
2048
2269
|
def literal_string_append(sql, v)
|
|
2049
2270
|
sql << "'" << v.gsub("'", "''") << "'"
|
|
@@ -2139,15 +2360,38 @@ module Sequel
|
|
|
2139
2360
|
opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
|
|
2140
2361
|
end
|
|
2141
2362
|
|
|
2142
|
-
# Support
|
|
2143
|
-
def
|
|
2363
|
+
# Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
|
|
2364
|
+
def select_with_sql_cte(sql, cte)
|
|
2144
2365
|
super
|
|
2366
|
+
select_with_sql_cte_search_cycle(sql, cte)
|
|
2367
|
+
end
|
|
2368
|
+
|
|
2369
|
+
def select_with_sql_cte_search_cycle(sql, cte)
|
|
2370
|
+
if search_opts = cte[:search]
|
|
2371
|
+
sql << if search_opts[:type] == :breadth
|
|
2372
|
+
" SEARCH BREADTH FIRST BY "
|
|
2373
|
+
else
|
|
2374
|
+
" SEARCH DEPTH FIRST BY "
|
|
2375
|
+
end
|
|
2145
2376
|
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
sql
|
|
2149
|
-
|
|
2150
|
-
|
|
2377
|
+
identifier_list_append(sql, Array(search_opts[:by]))
|
|
2378
|
+
sql << " SET "
|
|
2379
|
+
identifier_append(sql, search_opts[:set] || :ordercol)
|
|
2380
|
+
end
|
|
2381
|
+
|
|
2382
|
+
if cycle_opts = cte[:cycle]
|
|
2383
|
+
sql << " CYCLE "
|
|
2384
|
+
identifier_list_append(sql, Array(cycle_opts[:columns]))
|
|
2385
|
+
sql << " SET "
|
|
2386
|
+
identifier_append(sql, cycle_opts[:cycle_column] || :is_cycle)
|
|
2387
|
+
if cycle_opts.has_key?(:cycle_value)
|
|
2388
|
+
sql << " TO "
|
|
2389
|
+
literal_append(sql, cycle_opts[:cycle_value])
|
|
2390
|
+
sql << " DEFAULT "
|
|
2391
|
+
literal_append(sql, cycle_opts.fetch(:noncycle_value, false))
|
|
2392
|
+
end
|
|
2393
|
+
sql << " USING "
|
|
2394
|
+
identifier_append(sql, cycle_opts[:path_column] || :path)
|
|
2151
2395
|
end
|
|
2152
2396
|
end
|
|
2153
2397
|
|