sequel 5.33.0 → 5.58.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +318 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +40 -9
- data/doc/association_basics.rdoc +77 -13
- data/doc/cheat_sheet.rdoc +13 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/fork_safety.rdoc +84 -0
- data/doc/migration.rdoc +12 -6
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +15 -3
- data/doc/postgresql.rdoc +9 -1
- data/doc/querying.rdoc +7 -5
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/release_notes/5.39.0.txt +19 -0
- 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/sql.rdoc +14 -2
- data/doc/testing.rdoc +10 -1
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- 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/mysql.rb +4 -4
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +29 -19
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +8 -6
- data/lib/sequel/adapters/oracle.rb +5 -4
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +84 -7
- data/lib/sequel/adapters/shared/mysql.rb +33 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -7
- data/lib/sequel/adapters/shared/postgres.rb +158 -20
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -10
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +2 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +9 -8
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +7 -9
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +33 -24
- data/lib/sequel/database/connecting.rb +3 -4
- data/lib/sequel/database/misc.rb +37 -12
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +50 -53
- data/lib/sequel/database/schema_methods.rb +45 -23
- data/lib/sequel/database/transactions.rb +9 -6
- data/lib/sequel/dataset/actions.rb +61 -8
- data/lib/sequel/dataset/features.rb +15 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +114 -11
- data/lib/sequel/dataset/sql.rb +172 -46
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +38 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -24
- 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 +3 -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 +139 -0
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/named_timezones.rb +5 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_array_ops.rb +6 -2
- data/lib/sequel/extensions/pg_enum.rb +3 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +35 -8
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +119 -4
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +7 -19
- data/lib/sequel/extensions/pg_range_ops.rb +39 -9
- data/lib/sequel/extensions/pg_row.rb +1 -1
- data/lib/sequel/extensions/pg_row_ops.rb +25 -1
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +4 -1
- data/lib/sequel/extensions/schema_dumper.rb +16 -5
- 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_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model/associations.rb +342 -114
- data/lib/sequel/model/base.rb +45 -24
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +8 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +3 -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 +39 -5
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +8 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +8 -3
- data/lib/sequel/plugins/pg_array_associations.rb +58 -41
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/prepared_statements.rb +15 -12
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +37 -35
- 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 +7 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/plugins/validation_helpers.rb +18 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +20 -17
- data/lib/sequel/version.rb +1 -1
- metadata +93 -39
@@ -87,7 +87,7 @@ module Sequel
|
|
87
87
|
|
88
88
|
def self.mock_adapter_setup(db)
|
89
89
|
db.instance_exec do
|
90
|
-
@server_version =
|
90
|
+
@server_version = 140000
|
91
91
|
initialize_postgres_adapter
|
92
92
|
extend(MockAdapterDatabaseMethods)
|
93
93
|
end
|
@@ -414,6 +414,7 @@ module Sequel
|
|
414
414
|
# 2 :: argument name
|
415
415
|
# 3 :: argument mode (e.g. in, out, inout)
|
416
416
|
# :behavior :: Should be IMMUTABLE, STABLE, or VOLATILE. PostgreSQL assumes VOLATILE by default.
|
417
|
+
# :parallel :: The thread safety attribute of the function. Should be SAFE, UNSAFE, RESTRICTED. PostgreSQL assumes UNSAFE by default.
|
417
418
|
# :cost :: The estimated cost of the function, used by the query planner.
|
418
419
|
# :language :: The language the function uses. SQL is the default.
|
419
420
|
# :link_symbol :: For a dynamically loaded see function, the function's link symbol if different from the definition argument.
|
@@ -479,6 +480,7 @@ module Sequel
|
|
479
480
|
# :each_row :: Calls the trigger for each row instead of for each statement.
|
480
481
|
# :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
482
|
# the trigger is called for insert, update, or delete.
|
483
|
+
# :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
|
482
484
|
# :when :: A filter to use for the trigger
|
483
485
|
def create_trigger(table, name, function, opts=OPTS)
|
484
486
|
self << create_trigger_sql(table, name, function, opts)
|
@@ -781,7 +783,7 @@ module Sequel
|
|
781
783
|
return @server_version if @server_version
|
782
784
|
ds = dataset
|
783
785
|
ds = ds.server(server) if server
|
784
|
-
@server_version
|
786
|
+
@server_version = swallow_database_error{ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value} || 0
|
785
787
|
end
|
786
788
|
|
787
789
|
# PostgreSQL supports CREATE TABLE IF NOT EXISTS on 9.1+
|
@@ -846,7 +848,7 @@ module Sequel
|
|
846
848
|
# :schema :: The schema to search
|
847
849
|
# :server :: The server to use
|
848
850
|
def tables(opts=OPTS, &block)
|
849
|
-
pg_class_relname('r', opts, &block)
|
851
|
+
pg_class_relname(['r', 'p'], opts, &block)
|
850
852
|
end
|
851
853
|
|
852
854
|
# Check whether the given type name string/symbol (e.g. :hstore) is supported by
|
@@ -1116,6 +1118,7 @@ module Sequel
|
|
1116
1118
|
#{opts[:behavior].to_s.upcase if opts[:behavior]}
|
1117
1119
|
#{'STRICT' if opts[:strict]}
|
1118
1120
|
#{'SECURITY DEFINER' if opts[:security_definer]}
|
1121
|
+
#{"PARALLEL #{opts[:parallel].to_s.upcase}" if opts[:parallel]}
|
1119
1122
|
#{"COST #{opts[:cost]}" if opts[:cost]}
|
1120
1123
|
#{"ROWS #{opts[:rows]}" if opts[:rows]}
|
1121
1124
|
#{opts[:set].map{|k,v| " SET #{k} = #{v}"}.join("\n") if opts[:set]}
|
@@ -1237,7 +1240,7 @@ module Sequel
|
|
1237
1240
|
raise Error, "Trigger conditions are not supported for this database" unless supports_trigger_conditions?
|
1238
1241
|
filter = " WHEN #{filter_expr(filter)}"
|
1239
1242
|
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(', ')})"
|
1243
|
+
"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
1244
|
end
|
1242
1245
|
|
1243
1246
|
# DDL fragment for initial part of CREATE VIEW statement
|
@@ -1335,7 +1338,7 @@ module Sequel
|
|
1335
1338
|
ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
1336
1339
|
ds = filter_schema(ds, opts)
|
1337
1340
|
m = output_identifier_meth
|
1338
|
-
if
|
1341
|
+
if defined?(yield)
|
1339
1342
|
yield(ds)
|
1340
1343
|
elsif opts[:qualify]
|
1341
1344
|
ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
|
@@ -1500,10 +1503,12 @@ module Sequel
|
|
1500
1503
|
# disallowed or there is a size specified, use the varchar type.
|
1501
1504
|
# Otherwise use the text type.
|
1502
1505
|
def type_literal_generic_string(column)
|
1503
|
-
if column[:
|
1504
|
-
|
1505
|
-
elsif column[:
|
1506
|
-
"
|
1506
|
+
if column[:text]
|
1507
|
+
:text
|
1508
|
+
elsif column[:fixed]
|
1509
|
+
"char(#{column[:size]||default_string_column_size})"
|
1510
|
+
elsif column[:text] == false || column[:size]
|
1511
|
+
"varchar(#{column[:size]||default_string_column_size})"
|
1507
1512
|
else
|
1508
1513
|
:text
|
1509
1514
|
end
|
@@ -1522,7 +1527,7 @@ module Sequel
|
|
1522
1527
|
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze).freeze
|
1523
1528
|
|
1524
1529
|
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']])
|
1530
|
+
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
1531
|
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
1532
|
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
1533
|
|
@@ -1725,13 +1730,22 @@ module Sequel
|
|
1725
1730
|
ds.insert_sql(*values)
|
1726
1731
|
end
|
1727
1732
|
|
1733
|
+
# Support SQL::AliasedExpression as expr to setup a USING join with a table alias for the
|
1734
|
+
# USING columns.
|
1735
|
+
def join_table(type, table, expr=nil, options=OPTS, &block)
|
1736
|
+
if expr.is_a?(SQL::AliasedExpression) && expr.expression.is_a?(Array) && !expr.expression.empty? && expr.expression.all?
|
1737
|
+
options = options.merge(:join_using=>true)
|
1738
|
+
end
|
1739
|
+
super
|
1740
|
+
end
|
1741
|
+
|
1728
1742
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
1729
1743
|
# the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
|
1730
1744
|
# a new transaction, locks the table, and yields. If a block is not given,
|
1731
1745
|
# just locks the tables. Note that PostgreSQL will probably raise an error
|
1732
1746
|
# if you lock the table outside of an existing transaction. Returns nil.
|
1733
1747
|
def lock(mode, opts=OPTS)
|
1734
|
-
if
|
1748
|
+
if defined?(yield) # perform locking inside a transaction and yield to block
|
1735
1749
|
@db.transaction(opts){lock(mode, opts); yield}
|
1736
1750
|
else
|
1737
1751
|
sql = 'LOCK TABLE '.dup
|
@@ -1746,6 +1760,41 @@ module Sequel
|
|
1746
1760
|
nil
|
1747
1761
|
end
|
1748
1762
|
|
1763
|
+
# Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
|
1764
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
1765
|
+
# use it as additional conditions for the match.
|
1766
|
+
#
|
1767
|
+
# merge_do_nothing_when_matched
|
1768
|
+
# # WHEN MATCHED THEN DO NOTHING
|
1769
|
+
#
|
1770
|
+
# merge_do_nothing_when_matched{a > 30}
|
1771
|
+
# # WHEN MATCHED AND (a > 30) THEN DO NOTHING
|
1772
|
+
def merge_do_nothing_when_matched(&block)
|
1773
|
+
_merge_when(:type=>:matched, &block)
|
1774
|
+
end
|
1775
|
+
|
1776
|
+
# Return a dataset with a WHEN NOT MATCHED THEN DO NOTHING clause added to the
|
1777
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
1778
|
+
# use it as additional conditions for the match.
|
1779
|
+
#
|
1780
|
+
# merge_do_nothing_when_not_matched
|
1781
|
+
# # WHEN NOT MATCHED THEN DO NOTHING
|
1782
|
+
#
|
1783
|
+
# merge_do_nothing_when_not_matched{a > 30}
|
1784
|
+
# # WHEN NOT MATCHED AND (a > 30) THEN DO NOTHING
|
1785
|
+
def merge_do_nothing_when_not_matched(&block)
|
1786
|
+
_merge_when(:type=>:not_matched, &block)
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
# Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
|
1790
|
+
def merge_insert(*values, &block)
|
1791
|
+
h = {:type=>:insert, :values=>values}
|
1792
|
+
if override = @opts[:override]
|
1793
|
+
h[:override] = insert_override_sql(String.new)
|
1794
|
+
end
|
1795
|
+
_merge_when(h, &block)
|
1796
|
+
end
|
1797
|
+
|
1749
1798
|
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
1750
1799
|
# always use the user supplied value, and an error is not raised for identity
|
1751
1800
|
# columns that are GENERATED ALWAYS.
|
@@ -1813,6 +1862,11 @@ module Sequel
|
|
1813
1862
|
true
|
1814
1863
|
end
|
1815
1864
|
|
1865
|
+
# PostgreSQL 15+ supports MERGE.
|
1866
|
+
def supports_merge?
|
1867
|
+
server_version >= 150000
|
1868
|
+
end
|
1869
|
+
|
1816
1870
|
# PostgreSQL supports NOWAIT.
|
1817
1871
|
def supports_nowait?
|
1818
1872
|
true
|
@@ -1885,6 +1939,13 @@ module Sequel
|
|
1885
1939
|
end
|
1886
1940
|
end
|
1887
1941
|
|
1942
|
+
# Use WITH TIES when limiting the result set to also include additional
|
1943
|
+
# rules that have the same results for the order column as the final row.
|
1944
|
+
# Requires PostgreSQL 13.
|
1945
|
+
def with_ties
|
1946
|
+
clone(:limit_with_ties=>true)
|
1947
|
+
end
|
1948
|
+
|
1888
1949
|
protected
|
1889
1950
|
|
1890
1951
|
# If returned primary keys are requested, use RETURNING unless already set on the
|
@@ -1916,6 +1977,22 @@ module Sequel
|
|
1916
1977
|
|
1917
1978
|
private
|
1918
1979
|
|
1980
|
+
# Append the INSERT sql used in a MERGE
|
1981
|
+
def _merge_insert_sql(sql, data)
|
1982
|
+
sql << " THEN INSERT "
|
1983
|
+
columns, values = _parse_insert_sql_args(data[:values])
|
1984
|
+
_insert_columns_sql(sql, columns)
|
1985
|
+
if override = data[:override]
|
1986
|
+
sql << override
|
1987
|
+
end
|
1988
|
+
_insert_values_sql(sql, values)
|
1989
|
+
end
|
1990
|
+
|
1991
|
+
def _merge_matched_sql(sql, data)
|
1992
|
+
sql << " THEN DO NOTHING"
|
1993
|
+
end
|
1994
|
+
alias _merge_not_matched_sql _merge_matched_sql
|
1995
|
+
|
1919
1996
|
# Format TRUNCATE statement with PostgreSQL specific options.
|
1920
1997
|
def _truncate_sql(table)
|
1921
1998
|
to = @opts[:truncate_opts] || OPTS
|
@@ -1992,14 +2069,13 @@ module Sequel
|
|
1992
2069
|
end
|
1993
2070
|
|
1994
2071
|
# Support OVERRIDING SYSTEM|USER VALUE in insert statements
|
1995
|
-
def
|
2072
|
+
def insert_override_sql(sql)
|
1996
2073
|
case opts[:override]
|
1997
2074
|
when :system
|
1998
2075
|
sql << " OVERRIDING SYSTEM VALUE"
|
1999
2076
|
when :user
|
2000
2077
|
sql << " OVERRIDING USER VALUE"
|
2001
2078
|
end
|
2002
|
-
super
|
2003
2079
|
end
|
2004
2080
|
|
2005
2081
|
# For multiple table support, PostgreSQL requires at least
|
@@ -2014,6 +2090,17 @@ module Sequel
|
|
2014
2090
|
end
|
2015
2091
|
end
|
2016
2092
|
|
2093
|
+
# Support table aliases for USING columns
|
2094
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
2095
|
+
if using_columns.is_a?(SQL::AliasedExpression)
|
2096
|
+
super(sql, using_columns.expression)
|
2097
|
+
sql << ' AS '
|
2098
|
+
identifier_append(sql, using_columns.alias)
|
2099
|
+
else
|
2100
|
+
super
|
2101
|
+
end
|
2102
|
+
end
|
2103
|
+
|
2017
2104
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
2018
2105
|
def literal_blob_append(sql, v)
|
2019
2106
|
sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
|
@@ -2071,6 +2158,37 @@ module Sequel
|
|
2071
2158
|
false
|
2072
2159
|
end
|
2073
2160
|
|
2161
|
+
# Support FETCH FIRST WITH TIES on PostgreSQL 13+.
|
2162
|
+
def select_limit_sql(sql)
|
2163
|
+
l = @opts[:limit]
|
2164
|
+
o = @opts[:offset]
|
2165
|
+
|
2166
|
+
return unless l || o
|
2167
|
+
|
2168
|
+
if @opts[:limit_with_ties]
|
2169
|
+
if o
|
2170
|
+
sql << " OFFSET "
|
2171
|
+
literal_append(sql, o)
|
2172
|
+
end
|
2173
|
+
|
2174
|
+
if l
|
2175
|
+
sql << " FETCH FIRST "
|
2176
|
+
literal_append(sql, l)
|
2177
|
+
sql << " ROWS WITH TIES"
|
2178
|
+
end
|
2179
|
+
else
|
2180
|
+
if l
|
2181
|
+
sql << " LIMIT "
|
2182
|
+
literal_append(sql, l)
|
2183
|
+
end
|
2184
|
+
|
2185
|
+
if o
|
2186
|
+
sql << " OFFSET "
|
2187
|
+
literal_append(sql, o)
|
2188
|
+
end
|
2189
|
+
end
|
2190
|
+
end
|
2191
|
+
|
2074
2192
|
# Support FOR SHARE locking when using the :share lock style.
|
2075
2193
|
# Use SKIP LOCKED if skipping locked rows.
|
2076
2194
|
def select_lock_sql(sql)
|
@@ -2101,15 +2219,35 @@ module Sequel
|
|
2101
2219
|
opts[:with].any?{|w| w[:recursive]} ? "WITH RECURSIVE " : super
|
2102
2220
|
end
|
2103
2221
|
|
2104
|
-
# Support
|
2105
|
-
def
|
2222
|
+
# Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
|
2223
|
+
def select_with_sql_cte(sql, cte)
|
2106
2224
|
super
|
2107
2225
|
|
2108
|
-
|
2109
|
-
|
2110
|
-
|
2111
|
-
|
2112
|
-
|
2226
|
+
if search_opts = cte[:search]
|
2227
|
+
sql << if search_opts[:type] == :breadth
|
2228
|
+
" SEARCH BREADTH FIRST BY "
|
2229
|
+
else
|
2230
|
+
" SEARCH DEPTH FIRST BY "
|
2231
|
+
end
|
2232
|
+
|
2233
|
+
identifier_list_append(sql, Array(search_opts[:by]))
|
2234
|
+
sql << " SET "
|
2235
|
+
identifier_append(sql, search_opts[:set] || :ordercol)
|
2236
|
+
end
|
2237
|
+
|
2238
|
+
if cycle_opts = cte[:cycle]
|
2239
|
+
sql << " CYCLE "
|
2240
|
+
identifier_list_append(sql, Array(cycle_opts[:columns]))
|
2241
|
+
sql << " SET "
|
2242
|
+
identifier_append(sql, cycle_opts[:cycle_column] || :is_cycle)
|
2243
|
+
if cycle_opts.has_key?(:cycle_value)
|
2244
|
+
sql << " TO "
|
2245
|
+
literal_append(sql, cycle_opts[:cycle_value])
|
2246
|
+
sql << " DEFAULT "
|
2247
|
+
literal_append(sql, cycle_opts.fetch(:noncycle_value, false))
|
2248
|
+
end
|
2249
|
+
sql << " USING "
|
2250
|
+
identifier_append(sql, cycle_opts[:path_column] || :path)
|
2113
2251
|
end
|
2114
2252
|
end
|
2115
2253
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
+
require_relative '../utils/columns_limit_1'
|
4
|
+
|
3
5
|
module Sequel
|
4
6
|
module SqlAnywhere
|
5
7
|
Sequel::Database.set_shared_adapter_scheme(:sqlanywhere, self)
|
@@ -234,6 +236,7 @@ module Sequel
|
|
234
236
|
module DatasetMethods
|
235
237
|
Dataset.def_sql_method(self, :insert, %w'insert into columns values')
|
236
238
|
Dataset.def_sql_method(self, :select, %w'with select distinct limit columns into from join where group having window compounds order lock')
|
239
|
+
include ::Sequel::Dataset::ColumnsLimit1
|
237
240
|
|
238
241
|
# Whether to convert smallint to boolean arguments for this dataset.
|
239
242
|
# Defaults to the IBMDB module setting.
|
@@ -239,8 +239,12 @@ module Sequel
|
|
239
239
|
super
|
240
240
|
end
|
241
241
|
when :drop_column
|
242
|
-
|
243
|
-
|
242
|
+
if sqlite_version >= 33500
|
243
|
+
super
|
244
|
+
else
|
245
|
+
ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
|
246
|
+
duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
|
247
|
+
end
|
244
248
|
when :rename_column
|
245
249
|
if sqlite_version >= 32500
|
246
250
|
super
|
@@ -269,6 +273,8 @@ module Sequel
|
|
269
273
|
else
|
270
274
|
duplicate_table(table, :no_foreign_keys=>true)
|
271
275
|
end
|
276
|
+
when :unique
|
277
|
+
duplicate_table(table, :no_unique=>true)
|
272
278
|
else
|
273
279
|
duplicate_table(table)
|
274
280
|
end
|
@@ -331,6 +337,11 @@ module Sequel
|
|
331
337
|
ps
|
332
338
|
end
|
333
339
|
|
340
|
+
# Support creating STRICT tables via :strict option
|
341
|
+
def create_table_sql(name, generator, options)
|
342
|
+
"#{super}#{' STRICT' if options[:strict]}"
|
343
|
+
end
|
344
|
+
|
334
345
|
# SQLite support creating temporary views.
|
335
346
|
def create_view_prefix_sql(name, options)
|
336
347
|
create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
|
@@ -341,6 +352,7 @@ module Sequel
|
|
341
352
|
/foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
|
342
353
|
/\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
|
343
354
|
/\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
|
355
|
+
/\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
|
344
356
|
/may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
|
345
357
|
/\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
|
346
358
|
}.freeze
|
@@ -387,7 +399,7 @@ module Sequel
|
|
387
399
|
old_columns = def_columns.map{|c| c[:name]}
|
388
400
|
opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
|
389
401
|
|
390
|
-
yield def_columns if
|
402
|
+
yield def_columns if defined?(yield)
|
391
403
|
|
392
404
|
constraints = (opts[:constraints] || []).dup
|
393
405
|
pks = []
|
@@ -422,8 +434,12 @@ module Sequel
|
|
422
434
|
skip_indexes = []
|
423
435
|
indexes(table, :only_autocreated=>true).each do |name, h|
|
424
436
|
skip_indexes << name
|
425
|
-
if h[:
|
426
|
-
|
437
|
+
if h[:unique] && !opts[:no_unique]
|
438
|
+
if h[:columns].length == 1
|
439
|
+
unique_columns.concat(h[:columns])
|
440
|
+
elsif h[:columns].map(&:to_s) != pks
|
441
|
+
constraints << {:type=>:unique, :columns=>h[:columns]}
|
442
|
+
end
|
427
443
|
end
|
428
444
|
end
|
429
445
|
unique_columns -= pks
|
@@ -552,10 +568,10 @@ module Sequel
|
|
552
568
|
EXTRACT_MAP = {:year=>"'%Y'", :month=>"'%m'", :day=>"'%d'", :hour=>"'%H'", :minute=>"'%M'", :second=>"'%f'"}.freeze
|
553
569
|
EXTRACT_MAP.each_value(&:freeze)
|
554
570
|
|
555
|
-
Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
|
556
|
-
Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
|
571
|
+
Dataset.def_sql_method(self, :delete, [['if db.sqlite_version >= 33500', %w'with delete from where returning'], ['elsif db.sqlite_version >= 30803', %w'with delete from where'], ["else", %w'delete from where']])
|
572
|
+
Dataset.def_sql_method(self, :insert, [['if db.sqlite_version >= 33500', %w'with insert conflict into columns values on_conflict returning'], ['elsif db.sqlite_version >= 30803', %w'with insert conflict into columns values on_conflict'], ["else", %w'insert conflict into columns values']])
|
557
573
|
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'with values compounds'], ['else', %w'with select distinct columns from join where group having window compounds order limit lock']])
|
558
|
-
Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
|
574
|
+
Dataset.def_sql_method(self, :update, [['if db.sqlite_version >= 33500', %w'with update table set from where returning'], ['elsif db.sqlite_version >= 33300', %w'with update table set from where'], ['elsif db.sqlite_version >= 30803', %w'with update table set where'], ["else", %w'update table set where']])
|
559
575
|
|
560
576
|
def cast_sql_append(sql, expr, type)
|
561
577
|
if type == Time or type == DateTime
|
@@ -629,8 +645,8 @@ module Sequel
|
|
629
645
|
# SQLite performs a TRUNCATE style DELETE if no filter is specified.
|
630
646
|
# Since we want to always return the count of records, add a condition
|
631
647
|
# that is always true and then delete.
|
632
|
-
def delete
|
633
|
-
@opts[:where] ? super : where(1=>1).delete
|
648
|
+
def delete(&block)
|
649
|
+
@opts[:where] ? super : where(1=>1).delete(&block)
|
634
650
|
end
|
635
651
|
|
636
652
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
@@ -651,6 +667,21 @@ module Sequel
|
|
651
667
|
super
|
652
668
|
end
|
653
669
|
|
670
|
+
# Support insert select for associations, so that the model code can use
|
671
|
+
# returning instead of a separate query.
|
672
|
+
def insert_select(*values)
|
673
|
+
return unless supports_insert_select?
|
674
|
+
# Handle case where query does not return a row
|
675
|
+
server?(:default).with_sql_first(insert_select_sql(*values)) || false
|
676
|
+
end
|
677
|
+
|
678
|
+
# The SQL to use for an insert_select, adds a RETURNING clause to the insert
|
679
|
+
# unless the RETURNING clause is already present.
|
680
|
+
def insert_select_sql(*values)
|
681
|
+
ds = opts[:returning] ? self : returning
|
682
|
+
ds.insert_sql(*values)
|
683
|
+
end
|
684
|
+
|
654
685
|
# SQLite uses the nonstandard ` (backtick) for quoting identifiers.
|
655
686
|
def quoted_identifier_append(sql, c)
|
656
687
|
sql << '`' << c.to_s.gsub('`', '``') << '`'
|
@@ -732,6 +763,13 @@ module Sequel
|
|
732
763
|
insert_conflict(:ignore)
|
733
764
|
end
|
734
765
|
|
766
|
+
# Automatically add aliases to RETURNING values to work around SQLite bug.
|
767
|
+
def returning(*values)
|
768
|
+
return super if values.empty?
|
769
|
+
raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
|
770
|
+
clone(:returning=>_returning_values(values).freeze)
|
771
|
+
end
|
772
|
+
|
735
773
|
# SQLite 3.8.3+ supports common table expressions.
|
736
774
|
def supports_cte?(type=:select)
|
737
775
|
db.sqlite_version >= 30803
|
@@ -747,6 +785,11 @@ module Sequel
|
|
747
785
|
false
|
748
786
|
end
|
749
787
|
|
788
|
+
# SQLite does not support deleting from a joined dataset
|
789
|
+
def supports_deleting_joins?
|
790
|
+
false
|
791
|
+
end
|
792
|
+
|
750
793
|
# SQLite does not support INTERSECT ALL or EXCEPT ALL
|
751
794
|
def supports_intersect_except_all?
|
752
795
|
false
|
@@ -757,11 +800,21 @@ module Sequel
|
|
757
800
|
false
|
758
801
|
end
|
759
802
|
|
803
|
+
# SQLite 3.33.0 supports modifying joined datasets
|
804
|
+
def supports_modifying_joins?
|
805
|
+
db.sqlite_version >= 33300
|
806
|
+
end
|
807
|
+
|
760
808
|
# SQLite does not support multiple columns for the IN/NOT IN operators
|
761
809
|
def supports_multiple_column_in?
|
762
810
|
false
|
763
811
|
end
|
764
812
|
|
813
|
+
# SQLite 3.35.0 supports RETURNING on INSERT/UPDATE/DELETE.
|
814
|
+
def supports_returning?(_)
|
815
|
+
db.sqlite_version >= 33500
|
816
|
+
end
|
817
|
+
|
765
818
|
# SQLite supports timezones in literal timestamps, since it stores them
|
766
819
|
# as text. But using timezones in timestamps breaks SQLite datetime
|
767
820
|
# functions, so we allow the user to override the default per database.
|
@@ -794,6 +847,21 @@ module Sequel
|
|
794
847
|
|
795
848
|
private
|
796
849
|
|
850
|
+
# Add aliases to symbols and identifiers to work around SQLite bug.
|
851
|
+
def _returning_values(values)
|
852
|
+
values.map do |v|
|
853
|
+
case v
|
854
|
+
when Symbol
|
855
|
+
_, c, a = split_symbol(v)
|
856
|
+
a ? v : Sequel.as(v, c)
|
857
|
+
when SQL::Identifier, SQL::QualifiedIdentifier
|
858
|
+
Sequel.as(v, unqualified_column_for(v))
|
859
|
+
else
|
860
|
+
v
|
861
|
+
end
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
797
865
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
798
866
|
def as_sql_append(sql, aliaz, column_aliases=nil)
|
799
867
|
raise Error, "sqlite does not support derived column lists" if column_aliases
|
@@ -819,6 +887,13 @@ module Sequel
|
|
819
887
|
end
|
820
888
|
end
|
821
889
|
|
890
|
+
# Raise an InvalidOperation exception if insert is not allowed for this dataset.
|
891
|
+
def check_insert_allowed!
|
892
|
+
raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
|
893
|
+
raise(InvalidOperation, "Joined datasets cannot be modified") if joined_dataset?
|
894
|
+
end
|
895
|
+
alias check_delete_allowed! check_insert_allowed!
|
896
|
+
|
822
897
|
# SQLite supports a maximum of 500 rows in a VALUES clause.
|
823
898
|
def default_import_slice
|
824
899
|
500
|
@@ -938,6 +1013,23 @@ module Sequel
|
|
938
1013
|
def _truncate_sql(table)
|
939
1014
|
"DELETE FROM #{table}"
|
940
1015
|
end
|
1016
|
+
|
1017
|
+
# Use FROM to specify additional tables in an update query
|
1018
|
+
def update_from_sql(sql)
|
1019
|
+
if(from = @opts[:from][1..-1]).empty?
|
1020
|
+
raise(Error, 'Need multiple FROM tables if updating/deleting a dataset with JOINs') if @opts[:join]
|
1021
|
+
else
|
1022
|
+
sql << ' FROM '
|
1023
|
+
source_list_append(sql, from)
|
1024
|
+
select_join_sql(sql)
|
1025
|
+
end
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
# Only include the primary table in the main update clause
|
1029
|
+
def update_table_sql(sql)
|
1030
|
+
sql << ' '
|
1031
|
+
source_list_append(sql, @opts[:from][0..0])
|
1032
|
+
end
|
941
1033
|
end
|
942
1034
|
end
|
943
1035
|
end
|