sequel 5.33.0 → 5.58.0

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