sequel 5.33.0 → 5.58.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.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +318 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +40 -9
  5. data/doc/association_basics.rdoc +77 -13
  6. data/doc/cheat_sheet.rdoc +13 -5
  7. data/doc/code_order.rdoc +0 -12
  8. data/doc/dataset_filtering.rdoc +2 -2
  9. data/doc/fork_safety.rdoc +84 -0
  10. data/doc/migration.rdoc +12 -6
  11. data/doc/model_plugins.rdoc +1 -1
  12. data/doc/opening_databases.rdoc +15 -3
  13. data/doc/postgresql.rdoc +9 -1
  14. data/doc/querying.rdoc +7 -5
  15. data/doc/release_notes/5.34.0.txt +40 -0
  16. data/doc/release_notes/5.35.0.txt +56 -0
  17. data/doc/release_notes/5.36.0.txt +60 -0
  18. data/doc/release_notes/5.37.0.txt +30 -0
  19. data/doc/release_notes/5.38.0.txt +28 -0
  20. data/doc/release_notes/5.39.0.txt +19 -0
  21. data/doc/release_notes/5.40.0.txt +40 -0
  22. data/doc/release_notes/5.41.0.txt +25 -0
  23. data/doc/release_notes/5.42.0.txt +136 -0
  24. data/doc/release_notes/5.43.0.txt +98 -0
  25. data/doc/release_notes/5.44.0.txt +32 -0
  26. data/doc/release_notes/5.45.0.txt +34 -0
  27. data/doc/release_notes/5.46.0.txt +87 -0
  28. data/doc/release_notes/5.47.0.txt +59 -0
  29. data/doc/release_notes/5.48.0.txt +14 -0
  30. data/doc/release_notes/5.49.0.txt +59 -0
  31. data/doc/release_notes/5.50.0.txt +78 -0
  32. data/doc/release_notes/5.51.0.txt +47 -0
  33. data/doc/release_notes/5.52.0.txt +87 -0
  34. data/doc/release_notes/5.53.0.txt +23 -0
  35. data/doc/release_notes/5.54.0.txt +27 -0
  36. data/doc/release_notes/5.55.0.txt +21 -0
  37. data/doc/release_notes/5.56.0.txt +51 -0
  38. data/doc/release_notes/5.57.0.txt +23 -0
  39. data/doc/release_notes/5.58.0.txt +31 -0
  40. data/doc/sql.rdoc +14 -2
  41. data/doc/testing.rdoc +10 -1
  42. data/doc/transactions.rdoc +0 -8
  43. data/doc/validations.rdoc +1 -1
  44. data/doc/virtual_rows.rdoc +1 -1
  45. data/lib/sequel/adapters/ado/access.rb +1 -1
  46. data/lib/sequel/adapters/ado.rb +17 -17
  47. data/lib/sequel/adapters/amalgalite.rb +3 -5
  48. data/lib/sequel/adapters/ibmdb.rb +2 -2
  49. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  50. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  51. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  52. data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
  53. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  54. data/lib/sequel/adapters/jdbc.rb +29 -19
  55. data/lib/sequel/adapters/mysql.rb +80 -67
  56. data/lib/sequel/adapters/mysql2.rb +54 -49
  57. data/lib/sequel/adapters/odbc.rb +8 -6
  58. data/lib/sequel/adapters/oracle.rb +5 -4
  59. data/lib/sequel/adapters/postgres.rb +27 -29
  60. data/lib/sequel/adapters/shared/access.rb +2 -0
  61. data/lib/sequel/adapters/shared/db2.rb +30 -0
  62. data/lib/sequel/adapters/shared/mssql.rb +84 -7
  63. data/lib/sequel/adapters/shared/mysql.rb +33 -2
  64. data/lib/sequel/adapters/shared/oracle.rb +82 -7
  65. data/lib/sequel/adapters/shared/postgres.rb +158 -20
  66. data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
  67. data/lib/sequel/adapters/shared/sqlite.rb +102 -10
  68. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  69. data/lib/sequel/adapters/sqlite.rb +60 -18
  70. data/lib/sequel/adapters/tinytds.rb +2 -1
  71. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  72. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
  73. data/lib/sequel/ast_transformer.rb +6 -0
  74. data/lib/sequel/connection_pool/sharded_single.rb +9 -8
  75. data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
  76. data/lib/sequel/connection_pool/single.rb +7 -9
  77. data/lib/sequel/connection_pool/threaded.rb +1 -1
  78. data/lib/sequel/core.rb +33 -24
  79. data/lib/sequel/database/connecting.rb +3 -4
  80. data/lib/sequel/database/misc.rb +37 -12
  81. data/lib/sequel/database/query.rb +3 -1
  82. data/lib/sequel/database/schema_generator.rb +50 -53
  83. data/lib/sequel/database/schema_methods.rb +45 -23
  84. data/lib/sequel/database/transactions.rb +9 -6
  85. data/lib/sequel/dataset/actions.rb +61 -8
  86. data/lib/sequel/dataset/features.rb +15 -0
  87. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
  88. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  89. data/lib/sequel/dataset/query.rb +114 -11
  90. data/lib/sequel/dataset/sql.rb +172 -46
  91. data/lib/sequel/deprecated.rb +3 -1
  92. data/lib/sequel/exceptions.rb +2 -0
  93. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  94. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  95. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  96. data/lib/sequel/extensions/blank.rb +8 -0
  97. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  98. data/lib/sequel/extensions/core_refinements.rb +38 -11
  99. data/lib/sequel/extensions/date_arithmetic.rb +36 -24
  100. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  101. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  102. data/lib/sequel/extensions/duplicate_columns_handler.rb +3 -1
  103. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  104. data/lib/sequel/extensions/inflector.rb +9 -1
  105. data/lib/sequel/extensions/is_distinct_from.rb +139 -0
  106. data/lib/sequel/extensions/migration.rb +13 -2
  107. data/lib/sequel/extensions/named_timezones.rb +5 -1
  108. data/lib/sequel/extensions/pagination.rb +1 -1
  109. data/lib/sequel/extensions/pg_array.rb +1 -0
  110. data/lib/sequel/extensions/pg_array_ops.rb +6 -2
  111. data/lib/sequel/extensions/pg_enum.rb +3 -1
  112. data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
  113. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  114. data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
  115. data/lib/sequel/extensions/pg_inet.rb +2 -0
  116. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  117. data/lib/sequel/extensions/pg_interval.rb +35 -8
  118. data/lib/sequel/extensions/pg_json.rb +3 -5
  119. data/lib/sequel/extensions/pg_json_ops.rb +119 -4
  120. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  121. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  122. data/lib/sequel/extensions/pg_range.rb +7 -19
  123. data/lib/sequel/extensions/pg_range_ops.rb +39 -9
  124. data/lib/sequel/extensions/pg_row.rb +1 -1
  125. data/lib/sequel/extensions/pg_row_ops.rb +25 -1
  126. data/lib/sequel/extensions/query.rb +3 -0
  127. data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
  128. data/lib/sequel/extensions/s.rb +4 -1
  129. data/lib/sequel/extensions/schema_dumper.rb +16 -5
  130. data/lib/sequel/extensions/server_block.rb +8 -12
  131. data/lib/sequel/extensions/sql_comments.rb +110 -3
  132. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  133. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  134. data/lib/sequel/extensions/string_agg.rb +1 -1
  135. data/lib/sequel/extensions/string_date_time.rb +19 -23
  136. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  137. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  138. data/lib/sequel/extensions/to_dot.rb +9 -3
  139. data/lib/sequel/model/associations.rb +342 -114
  140. data/lib/sequel/model/base.rb +45 -24
  141. data/lib/sequel/model/errors.rb +10 -1
  142. data/lib/sequel/model/inflections.rb +1 -1
  143. data/lib/sequel/model/plugins.rb +8 -3
  144. data/lib/sequel/model.rb +3 -1
  145. data/lib/sequel/plugins/association_pks.rb +60 -18
  146. data/lib/sequel/plugins/association_proxies.rb +3 -0
  147. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  148. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  149. data/lib/sequel/plugins/auto_validations.rb +39 -5
  150. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  151. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  152. data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
  153. data/lib/sequel/plugins/column_encryption.rb +728 -0
  154. data/lib/sequel/plugins/composition.rb +8 -2
  155. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  156. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  157. data/lib/sequel/plugins/csv_serializer.rb +2 -0
  158. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  159. data/lib/sequel/plugins/dirty.rb +44 -0
  160. data/lib/sequel/plugins/enum.rb +124 -0
  161. data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
  162. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  163. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  164. data/lib/sequel/plugins/json_serializer.rb +39 -24
  165. data/lib/sequel/plugins/lazy_attributes.rb +4 -1
  166. data/lib/sequel/plugins/many_through_many.rb +108 -9
  167. data/lib/sequel/plugins/nested_attributes.rb +8 -3
  168. data/lib/sequel/plugins/pg_array_associations.rb +58 -41
  169. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
  170. data/lib/sequel/plugins/prepared_statements.rb +15 -12
  171. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  172. data/lib/sequel/plugins/rcte_tree.rb +37 -35
  173. data/lib/sequel/plugins/serialization.rb +9 -3
  174. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  175. data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
  176. data/lib/sequel/plugins/sql_comments.rb +189 -0
  177. data/lib/sequel/plugins/static_cache.rb +1 -1
  178. data/lib/sequel/plugins/string_stripper.rb +1 -1
  179. data/lib/sequel/plugins/subclasses.rb +28 -11
  180. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
  181. data/lib/sequel/plugins/timestamps.rb +1 -1
  182. data/lib/sequel/plugins/tree.rb +9 -4
  183. data/lib/sequel/plugins/unused_associations.rb +521 -0
  184. data/lib/sequel/plugins/update_or_create.rb +1 -1
  185. data/lib/sequel/plugins/validation_class_methods.rb +5 -1
  186. data/lib/sequel/plugins/validation_helpers.rb +18 -11
  187. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  188. data/lib/sequel/sql.rb +1 -1
  189. data/lib/sequel/timezones.rb +20 -17
  190. data/lib/sequel/version.rb +1 -1
  191. 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 = 90500
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 ||= ds.with_sql("SELECT CAST(current_setting('server_version_num') AS integer) AS v").single_value rescue 0
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 block_given?
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[:fixed]
1504
- "char(#{column[:size]||255})"
1505
- elsif column[:text] == false or column[:size]
1506
- "varchar(#{column[:size]||255})"
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 block_given? # perform locking inside a transaction and yield to block
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 insert_values_sql(sql)
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 WITH AS [NOT] MATERIALIZED if :materialized option is used.
2105
- def select_with_sql_prefix(sql, w)
2222
+ # Support PostgreSQL 14+ CTE SEARCH/CYCLE clauses
2223
+ def select_with_sql_cte(sql, cte)
2106
2224
  super
2107
2225
 
2108
- case w[:materialized]
2109
- when true
2110
- sql << "MATERIALIZED "
2111
- when false
2112
- sql << "NOT MATERIALIZED "
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
- ocp = lambda{|oc| oc.delete_if{|c| c.to_s == op[:name].to_s}}
243
- duplicate_table(table, :old_columns_proc=>ocp){|columns| columns.delete_if{|s| s[:name].to_s == op[:name].to_s}}
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 block_given?
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[:columns].length == 1 && h[:unique]
426
- unique_columns.concat(h[:columns])
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
@@ -120,7 +120,7 @@ module Sequel
120
120
 
121
121
  case type
122
122
  when :select
123
- yield rs if block_given?
123
+ yield rs if defined?(yield)
124
124
  when :rows
125
125
  return @api.sqlany_affected_rows(rs)
126
126
  when :insert