sequel 5.83.0 → 5.106.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/MIT-LICENSE +1 -1
- data/lib/sequel/adapters/ado/access.rb +2 -2
- data/lib/sequel/adapters/ado.rb +2 -2
- data/lib/sequel/adapters/amalgalite.rb +16 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -1
- data/lib/sequel/adapters/jdbc/db2.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +4 -2
- data/lib/sequel/adapters/jdbc/h2.rb +4 -4
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
- data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
- data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
- data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
- data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
- data/lib/sequel/adapters/jdbc.rb +29 -15
- data/lib/sequel/adapters/mysql.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +10 -3
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +16 -0
- data/lib/sequel/adapters/postgres.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/db2.rb +8 -3
- data/lib/sequel/adapters/shared/mssql.rb +9 -8
- data/lib/sequel/adapters/shared/mysql.rb +16 -11
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +438 -47
- data/lib/sequel/adapters/shared/sqlite.rb +9 -7
- data/lib/sequel/adapters/sqlite.rb +20 -2
- data/lib/sequel/adapters/tinytds.rb +2 -2
- data/lib/sequel/adapters/trilogy.rb +2 -3
- data/lib/sequel/ast_transformer.rb +2 -0
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
- data/lib/sequel/connection_pool/timed_queue.rb +33 -10
- data/lib/sequel/connection_pool.rb +8 -2
- data/lib/sequel/core.rb +31 -2
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +29 -11
- data/lib/sequel/database/query.rb +12 -12
- data/lib/sequel/database/schema_generator.rb +117 -16
- data/lib/sequel/database/schema_methods.rb +85 -30
- data/lib/sequel/dataset/actions.rb +83 -11
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
- data/lib/sequel/dataset/prepared_statements.rb +76 -28
- data/lib/sequel/dataset/query.rb +26 -11
- data/lib/sequel/dataset/sql.rb +40 -17
- data/lib/sequel/extensions/async_thread_pool.rb +1 -1
- data/lib/sequel/extensions/caller_logging.rb +1 -3
- data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
- data/lib/sequel/extensions/connection_expiration.rb +44 -5
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/constraint_validations.rb +3 -3
- data/lib/sequel/extensions/dataset_run.rb +41 -0
- data/lib/sequel/extensions/date_arithmetic.rb +6 -6
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
- data/lib/sequel/extensions/eval_inspect.rb +3 -1
- data/lib/sequel/extensions/inflector.rb +2 -2
- data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -12
- data/lib/sequel/extensions/migration.rb +38 -21
- data/lib/sequel/extensions/null_dataset.rb +2 -2
- data/lib/sequel/extensions/pg_array_ops.rb +41 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
- data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
- data/lib/sequel/extensions/pg_enum.rb +4 -4
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
- data/lib/sequel/extensions/pg_json_ops.rb +674 -18
- data/lib/sequel/extensions/pg_multirange.rb +1 -1
- data/lib/sequel/extensions/pg_range.rb +14 -2
- data/lib/sequel/extensions/pg_row.rb +5 -3
- data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
- data/lib/sequel/extensions/provenance.rb +1 -3
- data/lib/sequel/extensions/query_blocker.rb +172 -0
- data/lib/sequel/extensions/schema_caching.rb +24 -9
- data/lib/sequel/extensions/schema_dumper.rb +17 -5
- data/lib/sequel/extensions/set_literalizer.rb +20 -39
- data/lib/sequel/extensions/split_array_nil.rb +12 -2
- data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
- data/lib/sequel/extensions/string_agg.rb +2 -2
- data/lib/sequel/extensions/to_dot.rb +5 -0
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +215 -15
- data/lib/sequel/model/base.rb +198 -35
- data/lib/sequel/model/plugins.rb +12 -1
- data/lib/sequel/model.rb +1 -0
- data/lib/sequel/plugins/accessed_columns.rb +4 -0
- data/lib/sequel/plugins/active_model.rb +5 -6
- data/lib/sequel/plugins/association_pks.rb +2 -0
- data/lib/sequel/plugins/auto_validations.rb +2 -0
- data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
- data/lib/sequel/plugins/columns_updated.rb +4 -0
- data/lib/sequel/plugins/composition.rb +3 -1
- data/lib/sequel/plugins/constraint_validations.rb +15 -10
- data/lib/sequel/plugins/dataset_associations.rb +20 -1
- data/lib/sequel/plugins/deprecated_associations.rb +151 -0
- data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
- data/lib/sequel/plugins/dirty.rb +9 -2
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/insert_returning_select.rb +10 -1
- data/lib/sequel/plugins/inspect_pk.rb +44 -0
- data/lib/sequel/plugins/instance_filters.rb +8 -1
- data/lib/sequel/plugins/instance_hooks.rb +4 -0
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/json_serializer.rb +6 -10
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +21 -0
- data/lib/sequel/plugins/modification_detection.rb +2 -0
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +10 -5
- data/lib/sequel/plugins/paged_operations.rb +5 -2
- data/lib/sequel/plugins/pg_array_associations.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
- data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +6 -6
- data/lib/sequel/plugins/serialization.rb +23 -6
- data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
- data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
- data/lib/sequel/plugins/split_values.rb +12 -0
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/static_cache.rb +13 -0
- data/lib/sequel/plugins/static_cache_cache.rb +50 -13
- data/lib/sequel/plugins/subset_conditions.rb +85 -5
- data/lib/sequel/plugins/subset_static_cache.rb +278 -0
- data/lib/sequel/plugins/table_select.rb +7 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
- data/lib/sequel/plugins/unused_associations.rb +11 -3
- data/lib/sequel/plugins/update_primary_key.rb +4 -0
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/validation_contexts.rb +4 -0
- data/lib/sequel/sql.rb +27 -12
- data/lib/sequel/version.rb +1 -1
- metadata +17 -240
- data/CHANGELOG +0 -1393
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -104
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -439
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.81.0.txt +0 -31
- data/doc/release_notes/5.82.0.txt +0 -61
- data/doc/release_notes/5.83.0.txt +0 -56
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -204
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- data/doc/virtual_rows.rdoc +0 -265
|
@@ -48,7 +48,7 @@ module Sequel
|
|
|
48
48
|
TYPE_TRANSLATOR_DATE = date.freeze
|
|
49
49
|
bytea = Object.new
|
|
50
50
|
def bytea.call(str)
|
|
51
|
-
str = if str
|
|
51
|
+
str = if str.start_with?('\\x')
|
|
52
52
|
# PostgreSQL 9.0+ bytea hex format
|
|
53
53
|
str[2..-1].gsub(/(..)/){|s| s.to_i(16).chr}
|
|
54
54
|
else
|
|
@@ -101,7 +101,7 @@ module Sequel
|
|
|
101
101
|
|
|
102
102
|
def self.mock_adapter_setup(db)
|
|
103
103
|
db.instance_exec do
|
|
104
|
-
@server_version =
|
|
104
|
+
@server_version = 170000
|
|
105
105
|
initialize_postgres_adapter
|
|
106
106
|
extend(MockAdapterDatabaseMethods)
|
|
107
107
|
end
|
|
@@ -124,6 +124,8 @@ module Sequel
|
|
|
124
124
|
#
|
|
125
125
|
# Options supported:
|
|
126
126
|
#
|
|
127
|
+
# :include :: Include additional columns in the underlying index, to
|
|
128
|
+
# allow for index-only scans in more cases (PostgreSQL 11+).
|
|
127
129
|
# :name :: Name the constraint with the given name (useful if you may
|
|
128
130
|
# need to drop the constraint later)
|
|
129
131
|
# :using :: Override the index_method for the exclusion constraint (defaults to gist).
|
|
@@ -141,6 +143,22 @@ module Sequel
|
|
|
141
143
|
@operations << {:op => :add_constraint, :type => :exclude, :elements => elements}.merge!(opts)
|
|
142
144
|
end
|
|
143
145
|
|
|
146
|
+
# Alter an existing constraint. Options:
|
|
147
|
+
# :deferrable :: Modify deferrable setting for constraint (PostgreSQL 9.4+):
|
|
148
|
+
# true :: DEFERRABLE INITIALLY DEFERRED
|
|
149
|
+
# false :: NOT DEFERRABLE
|
|
150
|
+
# :immediate :: DEFERRABLE INITIALLY IMMEDIATE
|
|
151
|
+
# :enforced :: Set true to use ENFORCED, or false to use NOT ENFORCED (PostgreSQL 18+)
|
|
152
|
+
# :inherit :: Set true to use INHERIT, or false to use NO INHERIT (PostgreSQL 18+)
|
|
153
|
+
def alter_constraint(name, opts=OPTS)
|
|
154
|
+
@operations << {:op => :alter_constraint, :name => name}.merge!(opts)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# :inherit :: Set true to use INHERIT, or false to use NO INHERIT (PostgreSQL 18+)
|
|
158
|
+
def rename_constraint(name, new_name)
|
|
159
|
+
@operations << {:op => :rename_constraint, :name => name, :new_name => new_name}
|
|
160
|
+
end
|
|
161
|
+
|
|
144
162
|
# Validate the constraint with the given name, which should have
|
|
145
163
|
# been added previously with NOT VALID.
|
|
146
164
|
def validate_constraint(name)
|
|
@@ -321,7 +339,7 @@ module Sequel
|
|
|
321
339
|
end
|
|
322
340
|
|
|
323
341
|
def commit_prepared_transaction(transaction_id, opts=OPTS)
|
|
324
|
-
run("COMMIT PREPARED #{literal(transaction_id)}", opts)
|
|
342
|
+
run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
|
|
325
343
|
end
|
|
326
344
|
|
|
327
345
|
# A hash of metadata for CHECK constraints on the table.
|
|
@@ -335,7 +353,7 @@ module Sequel
|
|
|
335
353
|
hash = {}
|
|
336
354
|
_check_constraints_ds.where_each(:conrelid=>regclass_oid(table)) do |row|
|
|
337
355
|
constraint = m.call(row[:constraint])
|
|
338
|
-
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
|
356
|
+
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[], :validated=>row[:validated], :enforced=>row[:enforced]}
|
|
339
357
|
entry[:columns] << m.call(row[:column]) if row[:column]
|
|
340
358
|
end
|
|
341
359
|
|
|
@@ -398,7 +416,7 @@ module Sequel
|
|
|
398
416
|
return if already_identity
|
|
399
417
|
|
|
400
418
|
transaction(server_hash) do
|
|
401
|
-
run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
|
|
419
|
+
run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
|
|
402
420
|
|
|
403
421
|
ds.from(:pg_depend).
|
|
404
422
|
where(:classid=>pg_class, :objid=>seq_oid, :objsubid=>0, :deptype=>'a').
|
|
@@ -435,7 +453,7 @@ module Sequel
|
|
|
435
453
|
# often used here if :security_definer is used.
|
|
436
454
|
# :strict :: Makes the function return NULL when any argument is NULL.
|
|
437
455
|
def create_function(name, definition, opts=OPTS)
|
|
438
|
-
self << create_function_sql(name, definition, opts)
|
|
456
|
+
self << create_function_sql(name, definition, opts).freeze
|
|
439
457
|
end
|
|
440
458
|
|
|
441
459
|
# Create the procedural language in the database. Arguments:
|
|
@@ -446,7 +464,7 @@ module Sequel
|
|
|
446
464
|
# :trusted :: Marks the language being created as trusted, allowing unprivileged users to create functions using this language.
|
|
447
465
|
# :validator :: The name of previously registered function used as a validator of functions defined in this language.
|
|
448
466
|
def create_language(name, opts=OPTS)
|
|
449
|
-
self << create_language_sql(name, opts)
|
|
467
|
+
self << create_language_sql(name, opts).freeze
|
|
450
468
|
end
|
|
451
469
|
|
|
452
470
|
# Create a schema in the database. Arguments:
|
|
@@ -455,7 +473,7 @@ module Sequel
|
|
|
455
473
|
# :if_not_exists :: Don't raise an error if the schema already exists (PostgreSQL 9.3+)
|
|
456
474
|
# :owner :: The owner to set for the schema (defaults to current user if not specified)
|
|
457
475
|
def create_schema(name, opts=OPTS)
|
|
458
|
-
self << create_schema_sql(name, opts)
|
|
476
|
+
self << create_schema_sql(name, opts).freeze
|
|
459
477
|
end
|
|
460
478
|
|
|
461
479
|
# Support partitions of tables using the :partition_of option.
|
|
@@ -491,7 +509,7 @@ module Sequel
|
|
|
491
509
|
# :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
|
|
492
510
|
# :when :: A filter to use for the trigger
|
|
493
511
|
def create_trigger(table, name, function, opts=OPTS)
|
|
494
|
-
self << create_trigger_sql(table, name, function, opts)
|
|
512
|
+
self << create_trigger_sql(table, name, function, opts).freeze
|
|
495
513
|
end
|
|
496
514
|
|
|
497
515
|
def database_type
|
|
@@ -524,7 +542,7 @@ module Sequel
|
|
|
524
542
|
# default is plpgsql. Can be specified as a string or a symbol.
|
|
525
543
|
def do(code, opts=OPTS)
|
|
526
544
|
language = opts[:language]
|
|
527
|
-
run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
|
|
545
|
+
run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
|
|
528
546
|
end
|
|
529
547
|
|
|
530
548
|
# Drops the function from the database. Arguments:
|
|
@@ -534,7 +552,7 @@ module Sequel
|
|
|
534
552
|
# :cascade :: Drop other objects depending on this function.
|
|
535
553
|
# :if_exists :: Don't raise an error if the function doesn't exist.
|
|
536
554
|
def drop_function(name, opts=OPTS)
|
|
537
|
-
self << drop_function_sql(name, opts)
|
|
555
|
+
self << drop_function_sql(name, opts).freeze
|
|
538
556
|
end
|
|
539
557
|
|
|
540
558
|
# Drops a procedural language from the database. Arguments:
|
|
@@ -543,7 +561,7 @@ module Sequel
|
|
|
543
561
|
# :cascade :: Drop other objects depending on this function.
|
|
544
562
|
# :if_exists :: Don't raise an error if the function doesn't exist.
|
|
545
563
|
def drop_language(name, opts=OPTS)
|
|
546
|
-
self << drop_language_sql(name, opts)
|
|
564
|
+
self << drop_language_sql(name, opts).freeze
|
|
547
565
|
end
|
|
548
566
|
|
|
549
567
|
# Drops a schema from the database. Arguments:
|
|
@@ -552,7 +570,8 @@ module Sequel
|
|
|
552
570
|
# :cascade :: Drop all objects in this schema.
|
|
553
571
|
# :if_exists :: Don't raise an error if the schema doesn't exist.
|
|
554
572
|
def drop_schema(name, opts=OPTS)
|
|
555
|
-
self << drop_schema_sql(name, opts)
|
|
573
|
+
self << drop_schema_sql(name, opts).freeze
|
|
574
|
+
remove_all_cached_schemas
|
|
556
575
|
end
|
|
557
576
|
|
|
558
577
|
# Drops a trigger from the database. Arguments:
|
|
@@ -562,7 +581,7 @@ module Sequel
|
|
|
562
581
|
# :cascade :: Drop other objects depending on this function.
|
|
563
582
|
# :if_exists :: Don't raise an error if the function doesn't exist.
|
|
564
583
|
def drop_trigger(table, name, opts=OPTS)
|
|
565
|
-
self << drop_trigger_sql(table, name, opts)
|
|
584
|
+
self << drop_trigger_sql(table, name, opts).freeze
|
|
566
585
|
end
|
|
567
586
|
|
|
568
587
|
# Return full foreign key information using the pg system tables, including
|
|
@@ -601,6 +620,8 @@ module Sequel
|
|
|
601
620
|
:on_update=>fklod_map[row[:on_update]],
|
|
602
621
|
:on_delete=>fklod_map[row[:on_delete]],
|
|
603
622
|
:deferrable=>row[:deferrable],
|
|
623
|
+
:validated=>row[:validated],
|
|
624
|
+
:enforced=>row[:enforced],
|
|
604
625
|
:table=>schema ? SQL::QualifiedIdentifier.new(m.call(row[:schema]), m.call(row[:table])) : m.call(row[:table]),
|
|
605
626
|
}
|
|
606
627
|
|
|
@@ -648,12 +669,26 @@ module Sequel
|
|
|
648
669
|
_set_constraints(' IMMEDIATE', opts)
|
|
649
670
|
end
|
|
650
671
|
|
|
651
|
-
# Use the pg_* system tables to determine indexes on a table
|
|
672
|
+
# Use the pg_* system tables to determine indexes on a table. Options:
|
|
673
|
+
#
|
|
674
|
+
# :include_partial :: Set to true to include partial indexes
|
|
675
|
+
# :invalid :: Set to true or :only to only return invalid indexes.
|
|
676
|
+
# Set to :include to also return both valid and invalid indexes.
|
|
677
|
+
# When not set or other value given, does not return invalid indexes.
|
|
652
678
|
def indexes(table, opts=OPTS)
|
|
653
679
|
m = output_identifier_meth
|
|
654
680
|
cond = {Sequel[:tab][:oid]=>regclass_oid(table, opts)}
|
|
655
681
|
cond[:indpred] = nil unless opts[:include_partial]
|
|
656
682
|
|
|
683
|
+
case opts[:invalid]
|
|
684
|
+
when true, :only
|
|
685
|
+
cond[:indisvalid] = false
|
|
686
|
+
when :include
|
|
687
|
+
# nothing
|
|
688
|
+
else
|
|
689
|
+
cond[:indisvalid] = true
|
|
690
|
+
end
|
|
691
|
+
|
|
657
692
|
indexes = {}
|
|
658
693
|
_indexes_ds.where_each(cond) do |r|
|
|
659
694
|
i = indexes[m.call(r[:name])] ||= {:columns=>[], :unique=>r[:unique], :deferrable=>r[:deferrable]}
|
|
@@ -706,6 +741,14 @@ module Sequel
|
|
|
706
741
|
Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
|
|
707
742
|
end
|
|
708
743
|
|
|
744
|
+
# Rename a schema in the database. Arguments:
|
|
745
|
+
# name :: Current name of the schema
|
|
746
|
+
# opts :: New name for the schema
|
|
747
|
+
def rename_schema(name, new_name)
|
|
748
|
+
self << rename_schema_sql(name, new_name).freeze
|
|
749
|
+
remove_all_cached_schemas
|
|
750
|
+
end
|
|
751
|
+
|
|
709
752
|
# Refresh the materialized view with the given name.
|
|
710
753
|
#
|
|
711
754
|
# DB.refresh_view(:items_view)
|
|
@@ -713,7 +756,7 @@ module Sequel
|
|
|
713
756
|
# DB.refresh_view(:items_view, concurrently: true)
|
|
714
757
|
# # REFRESH MATERIALIZED VIEW CONCURRENTLY items_view
|
|
715
758
|
def refresh_view(name, opts=OPTS)
|
|
716
|
-
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
|
|
759
|
+
run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
|
|
717
760
|
end
|
|
718
761
|
|
|
719
762
|
# Reset the primary key sequence for the given table, basing it on the
|
|
@@ -726,7 +769,7 @@ module Sequel
|
|
|
726
769
|
table = Sequel.qualify(s, t) if s
|
|
727
770
|
|
|
728
771
|
if server_version >= 100000
|
|
729
|
-
seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
|
|
772
|
+
seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
|
|
730
773
|
increment_by = :seqincrement
|
|
731
774
|
min_value = :seqmin
|
|
732
775
|
# :nocov:
|
|
@@ -741,7 +784,7 @@ module Sequel
|
|
|
741
784
|
end
|
|
742
785
|
|
|
743
786
|
def rollback_prepared_transaction(transaction_id, opts=OPTS)
|
|
744
|
-
run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
|
|
787
|
+
run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
|
|
745
788
|
end
|
|
746
789
|
|
|
747
790
|
# PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
|
|
@@ -898,11 +941,15 @@ module Sequel
|
|
|
898
941
|
|
|
899
942
|
# Dataset used to retrieve CHECK constraint information
|
|
900
943
|
def _check_constraints_ds
|
|
901
|
-
@_check_constraints_ds ||=
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
944
|
+
@_check_constraints_ds ||= begin
|
|
945
|
+
ds = metadata_dataset.
|
|
946
|
+
from{pg_constraint.as(:co)}.
|
|
947
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
|
948
|
+
where(:contype=>'c').
|
|
949
|
+
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
|
950
|
+
|
|
951
|
+
_add_validated_enforced_constraint_columns(ds)
|
|
952
|
+
end
|
|
906
953
|
end
|
|
907
954
|
|
|
908
955
|
# Dataset used to retrieve foreign keys referenced by a table
|
|
@@ -968,6 +1015,28 @@ module Sequel
|
|
|
968
1015
|
ds = ds.order_append(Sequel[:nsp][:nspname], Sequel[:cl2][:relname])
|
|
969
1016
|
end
|
|
970
1017
|
|
|
1018
|
+
_add_validated_enforced_constraint_columns(ds)
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
def _add_validated_enforced_constraint_columns(ds)
|
|
1022
|
+
validated_cond = if server_version >= 90100
|
|
1023
|
+
Sequel[:convalidated]
|
|
1024
|
+
# :nocov:
|
|
1025
|
+
else
|
|
1026
|
+
Sequel.cast(true, TrueClass)
|
|
1027
|
+
# :nocov:
|
|
1028
|
+
end
|
|
1029
|
+
ds = ds.select_append(validated_cond.as(:validated))
|
|
1030
|
+
|
|
1031
|
+
enforced_cond = if server_version >= 180000
|
|
1032
|
+
Sequel[:conenforced]
|
|
1033
|
+
# :nocov:
|
|
1034
|
+
else
|
|
1035
|
+
Sequel.cast(true, TrueClass)
|
|
1036
|
+
# :nocov:
|
|
1037
|
+
end
|
|
1038
|
+
ds = ds.select_append(enforced_cond.as(:enforced))
|
|
1039
|
+
|
|
971
1040
|
ds
|
|
972
1041
|
end
|
|
973
1042
|
|
|
@@ -994,8 +1063,7 @@ module Sequel
|
|
|
994
1063
|
where{{
|
|
995
1064
|
indc[:relkind]=>%w'i I',
|
|
996
1065
|
ind[:indisprimary]=>false,
|
|
997
|
-
:indexprs=>nil
|
|
998
|
-
:indisvalid=>true}}.
|
|
1066
|
+
:indexprs=>nil}}.
|
|
999
1067
|
order(*order).
|
|
1000
1068
|
select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
|
|
1001
1069
|
|
|
@@ -1075,6 +1143,7 @@ module Sequel
|
|
|
1075
1143
|
pg_attribute[:attname].as(:name),
|
|
1076
1144
|
SQL::Cast.new(pg_attribute[:atttypid], :integer).as(:oid),
|
|
1077
1145
|
SQL::Cast.new(basetype[:oid], :integer).as(:base_oid),
|
|
1146
|
+
SQL::Function.new(:col_description, pg_class[:oid], pg_attribute[:attnum]).as(:comment),
|
|
1078
1147
|
SQL::Function.new(:format_type, basetype[:oid], pg_type[:typtypmod]).as(:db_base_type),
|
|
1079
1148
|
SQL::Function.new(:format_type, pg_type[:oid], pg_attribute[:atttypmod]).as(:db_type),
|
|
1080
1149
|
SQL::Function.new(:pg_get_expr, pg_attrdef[:adbin], pg_class[:oid]).as(:default),
|
|
@@ -1139,10 +1208,39 @@ module Sequel
|
|
|
1139
1208
|
"ADD COLUMN#{' IF NOT EXISTS' if op[:if_not_exists]} #{column_definition_sql(op)}"
|
|
1140
1209
|
end
|
|
1141
1210
|
|
|
1211
|
+
def alter_table_alter_constraint_sql(table, op)
|
|
1212
|
+
sql = String.new
|
|
1213
|
+
sql << "ALTER CONSTRAINT #{quote_identifier(op[:name])}"
|
|
1214
|
+
|
|
1215
|
+
constraint_deferrable_sql_append(sql, op[:deferrable])
|
|
1216
|
+
|
|
1217
|
+
case op[:enforced]
|
|
1218
|
+
when nil
|
|
1219
|
+
when false
|
|
1220
|
+
sql << " NOT ENFORCED"
|
|
1221
|
+
else
|
|
1222
|
+
sql << " ENFORCED"
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
case op[:inherit]
|
|
1226
|
+
when nil
|
|
1227
|
+
when false
|
|
1228
|
+
sql << " NO INHERIT"
|
|
1229
|
+
else
|
|
1230
|
+
sql << " INHERIT"
|
|
1231
|
+
end
|
|
1232
|
+
|
|
1233
|
+
sql
|
|
1234
|
+
end
|
|
1235
|
+
|
|
1142
1236
|
def alter_table_generator_class
|
|
1143
1237
|
Postgres::AlterTableGenerator
|
|
1144
1238
|
end
|
|
1145
1239
|
|
|
1240
|
+
def alter_table_rename_constraint_sql(table, op)
|
|
1241
|
+
"RENAME CONSTRAINT #{quote_identifier(op[:name])} TO #{quote_identifier(op[:new_name])}"
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1146
1244
|
def alter_table_set_column_type_sql(table, op)
|
|
1147
1245
|
s = super
|
|
1148
1246
|
if using = op[:using]
|
|
@@ -1189,6 +1287,22 @@ module Sequel
|
|
|
1189
1287
|
end
|
|
1190
1288
|
end
|
|
1191
1289
|
|
|
1290
|
+
def column_definition_append_include_sql(sql, constraint)
|
|
1291
|
+
if include_cols = constraint[:include]
|
|
1292
|
+
sql << " INCLUDE " << literal(Array(include_cols))
|
|
1293
|
+
end
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
def column_definition_append_primary_key_sql(sql, constraint)
|
|
1297
|
+
super
|
|
1298
|
+
column_definition_append_include_sql(sql, constraint)
|
|
1299
|
+
end
|
|
1300
|
+
|
|
1301
|
+
def column_definition_append_unique_sql(sql, constraint)
|
|
1302
|
+
super
|
|
1303
|
+
column_definition_append_include_sql(sql, constraint)
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1192
1306
|
# Literalize non-String collate options. This is because unquoted collatations
|
|
1193
1307
|
# are folded to lowercase, and PostgreSQL used mixed case or capitalized collations.
|
|
1194
1308
|
def column_definition_collate_sql(sql, column)
|
|
@@ -1208,7 +1322,7 @@ module Sequel
|
|
|
1208
1322
|
sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
|
|
1209
1323
|
sql << " AS IDENTITY"
|
|
1210
1324
|
elsif (generated = column[:generated_always_as])
|
|
1211
|
-
sql << " GENERATED ALWAYS AS (#{literal(generated)}) STORED"
|
|
1325
|
+
sql << " GENERATED ALWAYS AS (#{literal(generated)}) #{column[:virtual] ? 'VIRTUAL' : 'STORED'}"
|
|
1212
1326
|
end
|
|
1213
1327
|
end
|
|
1214
1328
|
end
|
|
@@ -1232,9 +1346,9 @@ module Sequel
|
|
|
1232
1346
|
end
|
|
1233
1347
|
|
|
1234
1348
|
# PostgreSQL can't combine rename_column operations, and it can combine
|
|
1235
|
-
#
|
|
1349
|
+
# validate_constraint and alter_constraint operations.
|
|
1236
1350
|
def combinable_alter_table_op?(op)
|
|
1237
|
-
(super || op[:op] == :validate_constraint) && op[:op] != :rename_column
|
|
1351
|
+
(super || op[:op] == :validate_constraint || op[:op] == :alter_constraint) && op[:op] != :rename_column
|
|
1238
1352
|
end
|
|
1239
1353
|
|
|
1240
1354
|
VALID_CLIENT_MIN_MESSAGES = %w'DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC'.freeze.each(&:freeze)
|
|
@@ -1268,26 +1382,97 @@ module Sequel
|
|
|
1268
1382
|
sqls
|
|
1269
1383
|
end
|
|
1270
1384
|
|
|
1271
|
-
# Handle
|
|
1385
|
+
# Handle PostgreSQL-specific constraint features.
|
|
1272
1386
|
def constraint_definition_sql(constraint)
|
|
1273
|
-
case constraint[:type]
|
|
1387
|
+
case type = constraint[:type]
|
|
1274
1388
|
when :exclude
|
|
1275
1389
|
elements = constraint[:elements].map{|c, op| "#{literal(c)} WITH #{op}"}.join(', ')
|
|
1276
1390
|
sql = String.new
|
|
1277
|
-
sql << "
|
|
1391
|
+
sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
|
|
1392
|
+
sql << "EXCLUDE USING #{constraint[:using]||'gist'} (#{elements})"
|
|
1393
|
+
column_definition_append_include_sql(sql, constraint)
|
|
1394
|
+
sql << " WHERE #{filter_expr(constraint[:where])}" if constraint[:where]
|
|
1395
|
+
constraint_deferrable_sql_append(sql, constraint[:deferrable])
|
|
1396
|
+
sql
|
|
1397
|
+
when :primary_key, :unique
|
|
1398
|
+
sql = String.new
|
|
1399
|
+
sql << "CONSTRAINT #{quote_identifier(constraint[:name])} " if constraint[:name]
|
|
1400
|
+
|
|
1401
|
+
if type == :primary_key
|
|
1402
|
+
sql << primary_key_constraint_sql_fragment(constraint)
|
|
1403
|
+
else
|
|
1404
|
+
sql << unique_constraint_sql_fragment(constraint)
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
if using_index = constraint[:using_index]
|
|
1408
|
+
sql << " USING INDEX " << quote_identifier(using_index)
|
|
1409
|
+
else
|
|
1410
|
+
cols = literal(constraint[:columns])
|
|
1411
|
+
cols.insert(-2, " WITHOUT OVERLAPS") if constraint[:without_overlaps]
|
|
1412
|
+
sql << " " << cols
|
|
1413
|
+
|
|
1414
|
+
if include_cols = constraint[:include]
|
|
1415
|
+
sql << " INCLUDE " << literal(Array(include_cols))
|
|
1416
|
+
end
|
|
1417
|
+
end
|
|
1418
|
+
|
|
1278
1419
|
constraint_deferrable_sql_append(sql, constraint[:deferrable])
|
|
1279
1420
|
sql
|
|
1280
|
-
when :foreign_key, :check
|
|
1421
|
+
else # when :foreign_key, :check
|
|
1281
1422
|
sql = super
|
|
1423
|
+
if constraint[:no_inherit]
|
|
1424
|
+
sql << " NO INHERIT"
|
|
1425
|
+
end
|
|
1426
|
+
if constraint[:not_enforced]
|
|
1427
|
+
sql << " NOT ENFORCED"
|
|
1428
|
+
end
|
|
1282
1429
|
if constraint[:not_valid]
|
|
1283
1430
|
sql << " NOT VALID"
|
|
1284
1431
|
end
|
|
1285
1432
|
sql
|
|
1286
|
-
else
|
|
1287
|
-
super
|
|
1288
1433
|
end
|
|
1289
1434
|
end
|
|
1290
1435
|
|
|
1436
|
+
def column_definition_add_references_sql(sql, column)
|
|
1437
|
+
super
|
|
1438
|
+
if column[:not_enforced]
|
|
1439
|
+
sql << " NOT ENFORCED"
|
|
1440
|
+
end
|
|
1441
|
+
end
|
|
1442
|
+
|
|
1443
|
+
def column_definition_null_sql(sql, column)
|
|
1444
|
+
constraint = column[:not_null]
|
|
1445
|
+
constraint = nil unless constraint.is_a?(Hash)
|
|
1446
|
+
if constraint && (name = constraint[:name])
|
|
1447
|
+
sql << " CONSTRAINT #{quote_identifier(name)}"
|
|
1448
|
+
end
|
|
1449
|
+
super
|
|
1450
|
+
if constraint && constraint[:no_inherit]
|
|
1451
|
+
sql << " NO INHERIT"
|
|
1452
|
+
end
|
|
1453
|
+
end
|
|
1454
|
+
|
|
1455
|
+
# Handle :period option
|
|
1456
|
+
def column_references_table_constraint_sql(constraint)
|
|
1457
|
+
sql = String.new
|
|
1458
|
+
sql << "FOREIGN KEY "
|
|
1459
|
+
cols = constraint[:columns]
|
|
1460
|
+
cols = column_references_add_period(cols) if constraint[:period]
|
|
1461
|
+
sql << literal(cols) << column_references_sql(constraint)
|
|
1462
|
+
end
|
|
1463
|
+
|
|
1464
|
+
def column_references_append_key_sql(sql, column)
|
|
1465
|
+
cols = Array(column[:key])
|
|
1466
|
+
cols = column_references_add_period(cols) if column[:period]
|
|
1467
|
+
sql << "(#{cols.map{|x| quote_identifier(x)}.join(', ')})"
|
|
1468
|
+
end
|
|
1469
|
+
|
|
1470
|
+
def column_references_add_period(cols)
|
|
1471
|
+
cols= cols.dup
|
|
1472
|
+
cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
|
|
1473
|
+
cols
|
|
1474
|
+
end
|
|
1475
|
+
|
|
1291
1476
|
def database_specific_error_class_from_sqlstate(sqlstate)
|
|
1292
1477
|
if sqlstate == '23P01'
|
|
1293
1478
|
ExclusionConstraintViolation
|
|
@@ -1357,7 +1542,8 @@ module Sequel
|
|
|
1357
1542
|
# SQL statement to create database function.
|
|
1358
1543
|
def create_function_sql(name, definition, opts=OPTS)
|
|
1359
1544
|
args = opts[:args]
|
|
1360
|
-
|
|
1545
|
+
in_out = %w'OUT INOUT'
|
|
1546
|
+
if (!opts[:args].is_a?(Array) || !opts[:args].any?{|a| Array(a).length == 3 && in_out.include?(a[2].to_s)})
|
|
1361
1547
|
returns = opts[:returns] || 'void'
|
|
1362
1548
|
end
|
|
1363
1549
|
language = opts[:language] || 'SQL'
|
|
@@ -1584,7 +1770,7 @@ module Sequel
|
|
|
1584
1770
|
index_type = :gist
|
|
1585
1771
|
end
|
|
1586
1772
|
|
|
1587
|
-
"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}"
|
|
1773
|
+
"CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON#{' ONLY' if index[:only]} #{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}"
|
|
1588
1774
|
end
|
|
1589
1775
|
|
|
1590
1776
|
# Setup datastructures shared by all postgres adapters.
|
|
@@ -1642,6 +1828,18 @@ module Sequel
|
|
|
1642
1828
|
super
|
|
1643
1829
|
end
|
|
1644
1830
|
|
|
1831
|
+
# Clear all cached schema information
|
|
1832
|
+
def remove_all_cached_schemas
|
|
1833
|
+
@primary_keys.clear
|
|
1834
|
+
@primary_key_sequences.clear
|
|
1835
|
+
@schemas.clear
|
|
1836
|
+
end
|
|
1837
|
+
|
|
1838
|
+
# SQL for renaming a schema.
|
|
1839
|
+
def rename_schema_sql(name, new_name)
|
|
1840
|
+
"ALTER SCHEMA #{quote_identifier(name)} RENAME TO #{quote_identifier(new_name)}"
|
|
1841
|
+
end
|
|
1842
|
+
|
|
1645
1843
|
# SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
|
|
1646
1844
|
# a rename table operation, so specifying a new schema in new_name will not have an effect.
|
|
1647
1845
|
def rename_table_sql(name, new_name)
|
|
@@ -1651,9 +1849,9 @@ module Sequel
|
|
|
1651
1849
|
# Handle interval and citext types.
|
|
1652
1850
|
def schema_column_type(db_type)
|
|
1653
1851
|
case db_type
|
|
1654
|
-
when /\Ainterval\z/
|
|
1852
|
+
when /\Ainterval\z/i
|
|
1655
1853
|
:interval
|
|
1656
|
-
when /\Acitext\z/
|
|
1854
|
+
when /\Acitext\z/i
|
|
1657
1855
|
:string
|
|
1658
1856
|
else
|
|
1659
1857
|
super
|
|
@@ -1804,6 +2002,15 @@ module Sequel
|
|
|
1804
2002
|
end
|
|
1805
2003
|
end
|
|
1806
2004
|
|
|
2005
|
+
# Support :nulls_not_distinct option.
|
|
2006
|
+
def unique_constraint_sql_fragment(constraint)
|
|
2007
|
+
if constraint[:nulls_not_distinct]
|
|
2008
|
+
'UNIQUE NULLS NOT DISTINCT'
|
|
2009
|
+
else
|
|
2010
|
+
'UNIQUE'
|
|
2011
|
+
end
|
|
2012
|
+
end
|
|
2013
|
+
|
|
1807
2014
|
# PostgreSQL 9.4+ supports views with check option.
|
|
1808
2015
|
def view_with_check_option_support
|
|
1809
2016
|
# :nocov:
|
|
@@ -1876,14 +2083,86 @@ module Sequel
|
|
|
1876
2083
|
super
|
|
1877
2084
|
end
|
|
1878
2085
|
|
|
1879
|
-
# Return the results of an EXPLAIN query
|
|
2086
|
+
# Return the results of an EXPLAIN query. Boolean options:
|
|
2087
|
+
#
|
|
2088
|
+
# :analyze :: Use the ANALYZE option.
|
|
2089
|
+
# :buffers :: Use the BUFFERS option.
|
|
2090
|
+
# :costs :: Use the COSTS option.
|
|
2091
|
+
# :generic_plan :: Use the GENERIC_PLAN option.
|
|
2092
|
+
# :memory :: Use the MEMORY option.
|
|
2093
|
+
# :settings :: Use the SETTINGS option.
|
|
2094
|
+
# :summary :: Use the SUMMARY option.
|
|
2095
|
+
# :timing :: Use the TIMING option.
|
|
2096
|
+
# :verbose :: Use the VERBOSE option.
|
|
2097
|
+
# :wal :: Use the WAL option.
|
|
2098
|
+
#
|
|
2099
|
+
# Non boolean options:
|
|
2100
|
+
#
|
|
2101
|
+
# :format :: Use the FORMAT option to change the format of the
|
|
2102
|
+
# returned value. Values can be :text, :xml, :json,
|
|
2103
|
+
# or :yaml.
|
|
2104
|
+
# :serialize :: Use the SERIALIZE option to get timing on
|
|
2105
|
+
# serialization. Values can be :none, :text, or
|
|
2106
|
+
# :binary.
|
|
2107
|
+
#
|
|
2108
|
+
# See the PostgreSQL EXPLAIN documentation for an explanation of
|
|
2109
|
+
# what each option does.
|
|
2110
|
+
#
|
|
2111
|
+
# In most cases, the return value is a single string. However,
|
|
2112
|
+
# using the <tt>format: :json</tt> option can result in the return
|
|
2113
|
+
# value being an array containing a hash.
|
|
1880
2114
|
def explain(opts=OPTS)
|
|
1881
|
-
|
|
2115
|
+
rows = clone(:append_sql=>explain_sql_string_origin(opts)).map(:'QUERY PLAN')
|
|
2116
|
+
|
|
2117
|
+
if rows.length == 1
|
|
2118
|
+
rows[0]
|
|
2119
|
+
elsif rows.all?{|row| String === row}
|
|
2120
|
+
rows.join("\r\n")
|
|
2121
|
+
# :nocov:
|
|
2122
|
+
else
|
|
2123
|
+
# This branch is unreachable in tests, but it seems better to just return
|
|
2124
|
+
# all rows than throw in error if this case actually happens.
|
|
2125
|
+
rows
|
|
2126
|
+
# :nocov:
|
|
2127
|
+
end
|
|
2128
|
+
end
|
|
2129
|
+
|
|
2130
|
+
# Return a cloned dataset which will use FOR KEY SHARE to lock returned rows.
|
|
2131
|
+
# Supported on PostgreSQL 9.3+.
|
|
2132
|
+
def for_key_share
|
|
2133
|
+
cached_lock_style_dataset(:_for_key_share_ds, :key_share)
|
|
2134
|
+
end
|
|
2135
|
+
|
|
2136
|
+
# Set FOR PORTION OF clause for UPDATE and DELETE statements.
|
|
2137
|
+
# The first argument is the range or multirange column. If two arguments
|
|
2138
|
+
# are provided, the second argument is an expression with the same
|
|
2139
|
+
# database type as the first argument. If three arguments are provided,
|
|
2140
|
+
# the second specifies the inclusive start of the portion to update and the third
|
|
2141
|
+
# specifies the exclusive end of portion to update. When using the three argument
|
|
2142
|
+
# form, nil can be provided as the second or third argument to have the start or
|
|
2143
|
+
# end of the portion be unbounded. Supported on PostgreSQL 19+.
|
|
2144
|
+
# Example:
|
|
2145
|
+
#
|
|
2146
|
+
# DB[:t].for_portion_of(:rc, Sequel.function(:int4range, 1, 2)).update(c: 3)
|
|
2147
|
+
# # UPDATE t FOR PORTION OF rc (int4range(1, 2)) SET c = 3
|
|
2148
|
+
#
|
|
2149
|
+
# DB[:t].for_portion_of(:rc, 1, 2).update(c: 3)
|
|
2150
|
+
# # UPDATE t FOR PORTION OF rc FROM 1 TO 2 SET c = 3
|
|
2151
|
+
def for_portion_of(column, range, to=(arg_not_given=true))
|
|
2152
|
+
range = [range, to].freeze unless arg_not_given
|
|
2153
|
+
clone(:for_portion_of => [column, range].freeze)
|
|
2154
|
+
end
|
|
2155
|
+
|
|
2156
|
+
# Return a cloned dataset which will use FOR NO KEY UPDATE to lock returned rows.
|
|
2157
|
+
# This is generally a better choice than using for_update on PostgreSQL, unless
|
|
2158
|
+
# you will be deleting the row or modifying a key column. Supported on PostgreSQL 9.3+.
|
|
2159
|
+
def for_no_key_update
|
|
2160
|
+
cached_lock_style_dataset(:_for_no_key_update_ds, :no_key_update)
|
|
1882
2161
|
end
|
|
1883
2162
|
|
|
1884
2163
|
# Return a cloned dataset which will use FOR SHARE to lock returned rows.
|
|
1885
2164
|
def for_share
|
|
1886
|
-
|
|
2165
|
+
cached_lock_style_dataset(:_for_share_ds, :share)
|
|
1887
2166
|
end
|
|
1888
2167
|
|
|
1889
2168
|
# Run a full text search on PostgreSQL. By default, searching for the inclusion
|
|
@@ -1914,7 +2193,7 @@ module Sequel
|
|
|
1914
2193
|
end
|
|
1915
2194
|
|
|
1916
2195
|
unless opts[:tsquery]
|
|
1917
|
-
phrase_terms = terms.is_a?(Array)
|
|
2196
|
+
phrase_terms = terms.is_a?(Array) || terms.is_a?(Set) ? Sequel.array_or_set_join(terms, ' | ') : terms
|
|
1918
2197
|
|
|
1919
2198
|
query_func = case to_tsquery = opts[:to_tsquery]
|
|
1920
2199
|
when :phrase, :plain
|
|
@@ -2376,10 +2655,11 @@ module Sequel
|
|
|
2376
2655
|
"'%Y-%m-%d %H:%M:%S.%6N%z'"
|
|
2377
2656
|
end
|
|
2378
2657
|
|
|
2379
|
-
# Only include the primary table in the main delete clause
|
|
2658
|
+
# Only include the primary table in the main delete clause.
|
|
2659
|
+
# Support FOR PORTION OF.
|
|
2380
2660
|
def delete_from_sql(sql)
|
|
2381
2661
|
sql << ' FROM '
|
|
2382
|
-
|
|
2662
|
+
table_for_portion_of_sql_append(sql)
|
|
2383
2663
|
end
|
|
2384
2664
|
|
|
2385
2665
|
# Use USING to specify additional tables in a delete query
|
|
@@ -2387,6 +2667,109 @@ module Sequel
|
|
|
2387
2667
|
join_from_sql(:USING, sql)
|
|
2388
2668
|
end
|
|
2389
2669
|
|
|
2670
|
+
# Handle column aliases containing data types, useful for selecting from functions
|
|
2671
|
+
# that return the record data type.
|
|
2672
|
+
def derived_column_list_sql_append(sql, column_aliases)
|
|
2673
|
+
c = false
|
|
2674
|
+
comma = ', '
|
|
2675
|
+
column_aliases.each do |a|
|
|
2676
|
+
sql << comma if c
|
|
2677
|
+
if a.is_a?(Array)
|
|
2678
|
+
raise Error, "column aliases specified as arrays must have only 2 elements, the first is alias name and the second is data type" unless a.length == 2
|
|
2679
|
+
a, type = a
|
|
2680
|
+
identifier_append(sql, a)
|
|
2681
|
+
sql << " " << db.cast_type_literal(type).to_s
|
|
2682
|
+
else
|
|
2683
|
+
identifier_append(sql, a)
|
|
2684
|
+
end
|
|
2685
|
+
c ||= true
|
|
2686
|
+
end
|
|
2687
|
+
end
|
|
2688
|
+
|
|
2689
|
+
EXPLAIN_BOOLEAN_OPTIONS = {}
|
|
2690
|
+
%w[analyze verbose costs settings generic_plan buffers wal timing summary memory].each do |str|
|
|
2691
|
+
EXPLAIN_BOOLEAN_OPTIONS[str.to_sym] = str.upcase.freeze
|
|
2692
|
+
end
|
|
2693
|
+
EXPLAIN_BOOLEAN_OPTIONS.freeze
|
|
2694
|
+
|
|
2695
|
+
EXPLAIN_NONBOOLEAN_OPTIONS = {
|
|
2696
|
+
:serialize => {:none=>"SERIALIZE NONE", :text=>"SERIALIZE TEXT", :binary=>"SERIALIZE BINARY"}.freeze,
|
|
2697
|
+
:format => {:text=>"FORMAT TEXT", :xml=>"FORMAT XML", :json=>"FORMAT JSON", :yaml=>"FORMAT YAML"}.freeze
|
|
2698
|
+
}.freeze
|
|
2699
|
+
|
|
2700
|
+
# A mutable string used as the prefix when explaining a query.
|
|
2701
|
+
def explain_sql_string_origin(opts)
|
|
2702
|
+
origin = String.new
|
|
2703
|
+
origin << 'EXPLAIN '
|
|
2704
|
+
|
|
2705
|
+
# :nocov:
|
|
2706
|
+
if server_version < 90000
|
|
2707
|
+
if opts[:analyze]
|
|
2708
|
+
origin << 'ANALYZE '
|
|
2709
|
+
end
|
|
2710
|
+
|
|
2711
|
+
return origin
|
|
2712
|
+
end
|
|
2713
|
+
# :nocov:
|
|
2714
|
+
|
|
2715
|
+
comma = nil
|
|
2716
|
+
paren = "("
|
|
2717
|
+
|
|
2718
|
+
add_opt = lambda do |str, value|
|
|
2719
|
+
origin << paren if paren
|
|
2720
|
+
origin << comma if comma
|
|
2721
|
+
origin << str
|
|
2722
|
+
origin << " FALSE" unless value
|
|
2723
|
+
comma ||= ', '
|
|
2724
|
+
paren &&= nil
|
|
2725
|
+
end
|
|
2726
|
+
|
|
2727
|
+
EXPLAIN_BOOLEAN_OPTIONS.each do |key, str|
|
|
2728
|
+
unless (value = opts[key]).nil?
|
|
2729
|
+
add_opt.call(str, value)
|
|
2730
|
+
end
|
|
2731
|
+
end
|
|
2732
|
+
|
|
2733
|
+
EXPLAIN_NONBOOLEAN_OPTIONS.each do |key, e_opts|
|
|
2734
|
+
if value = opts[key]
|
|
2735
|
+
if str = e_opts[value]
|
|
2736
|
+
add_opt.call(str, true)
|
|
2737
|
+
else
|
|
2738
|
+
raise Sequel::Error, "unrecognized value for Dataset#explain #{key.inspect} option: #{value.inspect}"
|
|
2739
|
+
end
|
|
2740
|
+
end
|
|
2741
|
+
end
|
|
2742
|
+
|
|
2743
|
+
origin << ') ' unless paren
|
|
2744
|
+
origin
|
|
2745
|
+
end
|
|
2746
|
+
|
|
2747
|
+
# Add FOR PORTION OF SQL if the dataset uses it.
|
|
2748
|
+
def table_for_portion_of_sql_append(sql)
|
|
2749
|
+
fpo_column, fpo_range = @opts[:for_portion_of]
|
|
2750
|
+
if fpo_column
|
|
2751
|
+
table, aliaz = split_alias(@opts[:from].first)
|
|
2752
|
+
source_list_append(sql, [table])
|
|
2753
|
+
sql << ' FOR PORTION OF '
|
|
2754
|
+
literal_append(sql, fpo_column)
|
|
2755
|
+
|
|
2756
|
+
if fpo_range.is_a?(Array)
|
|
2757
|
+
fpo_start, fpo_end = fpo_range
|
|
2758
|
+
sql << ' FROM '
|
|
2759
|
+
literal_append(sql, fpo_start)
|
|
2760
|
+
sql << ' TO '
|
|
2761
|
+
literal_append(sql, fpo_end)
|
|
2762
|
+
else
|
|
2763
|
+
sql << ' ('
|
|
2764
|
+
literal_append(sql, fpo_range)
|
|
2765
|
+
sql << ')'
|
|
2766
|
+
end
|
|
2767
|
+
as_sql_append(sql, aliaz) if aliaz
|
|
2768
|
+
else
|
|
2769
|
+
source_list_append(sql, @opts[:from][0..0])
|
|
2770
|
+
end
|
|
2771
|
+
end
|
|
2772
|
+
|
|
2390
2773
|
# Add ON CONFLICT clause if it should be used
|
|
2391
2774
|
def insert_conflict_sql(sql)
|
|
2392
2775
|
if opts = @opts[:insert_conflict]
|
|
@@ -2430,6 +2813,9 @@ module Sequel
|
|
|
2430
2813
|
# Return the primary key to use for RETURNING in an INSERT statement
|
|
2431
2814
|
def insert_pk
|
|
2432
2815
|
(f = opts[:from]) && !f.empty? && (t = f.first)
|
|
2816
|
+
|
|
2817
|
+
t = t.call(self) if t.is_a? Sequel::SQL::DelayedEvaluation
|
|
2818
|
+
|
|
2433
2819
|
case t
|
|
2434
2820
|
when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
|
|
2435
2821
|
if pk = db.primary_key(t)
|
|
@@ -2579,8 +2965,13 @@ module Sequel
|
|
|
2579
2965
|
# Use SKIP LOCKED if skipping locked rows.
|
|
2580
2966
|
def select_lock_sql(sql)
|
|
2581
2967
|
lock = @opts[:lock]
|
|
2582
|
-
|
|
2968
|
+
case lock
|
|
2969
|
+
when :share
|
|
2583
2970
|
sql << ' FOR SHARE'
|
|
2971
|
+
when :no_key_update
|
|
2972
|
+
sql << ' FOR NO KEY UPDATE'
|
|
2973
|
+
when :key_share
|
|
2974
|
+
sql << ' FOR KEY SHARE'
|
|
2584
2975
|
else
|
|
2585
2976
|
super
|
|
2586
2977
|
end
|
|
@@ -2668,10 +3059,10 @@ module Sequel
|
|
|
2668
3059
|
join_from_sql(:FROM, sql)
|
|
2669
3060
|
end
|
|
2670
3061
|
|
|
2671
|
-
#
|
|
3062
|
+
# Support FOR PORTION OF.
|
|
2672
3063
|
def update_table_sql(sql)
|
|
2673
3064
|
sql << ' '
|
|
2674
|
-
|
|
3065
|
+
table_for_portion_of_sql_append(sql)
|
|
2675
3066
|
end
|
|
2676
3067
|
end
|
|
2677
3068
|
end
|