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
@@ -81,7 +81,7 @@ module Sequel
81
81
  # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
82
82
  # columns are deleted. This method should generally not be called
83
83
  # directly by user code.
84
- def clone(opts = (return self; nil))
84
+ def clone(opts = nil || (return self))
85
85
  # return self used above because clone is called by almost all
86
86
  # other query methods, and it is the fastest approach
87
87
  c = super(:freeze=>false)
@@ -330,16 +330,17 @@ module Sequel
330
330
  # # SELECT * FROM a WHERE ((a LIKE '%foo%' ESCAPE '\') AND (b LIKE '%foo%' ESCAPE '\')
331
331
  # # AND (a LIKE '%bar%' ESCAPE '\') AND (b LIKE '%bar%' ESCAPE '\'))
332
332
  def grep(columns, patterns, opts=OPTS)
333
+ column_op = opts[:all_columns] ? :AND : :OR
333
334
  if opts[:all_patterns]
334
335
  conds = Array(patterns).map do |pat|
335
- SQL::BooleanExpression.new(opts[:all_columns] ? :AND : :OR, *Array(columns).map{|c| SQL::StringExpression.like(c, pat, opts)})
336
+ SQL::BooleanExpression.new(column_op, *Array(columns).map{|c| SQL::StringExpression.like(c, pat, opts)})
336
337
  end
337
- where(SQL::BooleanExpression.new(opts[:all_patterns] ? :AND : :OR, *conds))
338
+ where(SQL::BooleanExpression.new(:AND, *conds))
338
339
  else
339
340
  conds = Array(columns).map do |c|
340
341
  SQL::BooleanExpression.new(:OR, *Array(patterns).map{|pat| SQL::StringExpression.like(c, pat, opts)})
341
342
  end
342
- where(SQL::BooleanExpression.new(opts[:all_columns] ? :AND : :OR, *conds))
343
+ where(SQL::BooleanExpression.new(column_op, *conds))
343
344
  end
344
345
  end
345
346
 
@@ -507,6 +508,7 @@ module Sequel
507
508
  # argument.
508
509
  # :implicit_qualifier :: The name to use for qualifying implicit conditions. By default,
509
510
  # the last joined or primary table is used.
511
+ # :join_using :: Force the using of JOIN USING, even if +expr+ is not an array of symbols.
510
512
  # :reset_implicit_qualifier :: Can set to false to ignore this join when future joins determine qualifier
511
513
  # for implicit conditions.
512
514
  # :qualify :: Can be set to false to not do any implicit qualification. Can be set
@@ -540,7 +542,7 @@ module Sequel
540
542
  return s.join_table(type, ds, expr, options, &block)
541
543
  end
542
544
 
543
- using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
545
+ using_join = options[:join_using] || (expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)})
544
546
  if using_join && !supports_join_using?
545
547
  h = {}
546
548
  expr.each{|e| h[e] = e}
@@ -615,7 +617,7 @@ module Sequel
615
617
  UNCONDITIONED_JOIN_TYPES.each do |jtype|
616
618
  class_eval(<<-END, __FILE__, __LINE__+1)
617
619
  def #{jtype}_join(table, opts=Sequel::OPTS)
618
- raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if block_given?
620
+ raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if defined?(yield)
619
621
  raise(Sequel::Error, '#{jtype}_join 2nd argument should be an options hash, not conditions') unless opts.is_a?(Hash)
620
622
  join_table(:#{jtype}, table, nil, opts)
621
623
  end
@@ -676,6 +678,56 @@ module Sequel
676
678
  clone(:lock => style)
677
679
  end
678
680
 
681
+ # Return a dataset with a WHEN MATCHED THEN DELETE clause added to the
682
+ # MERGE statement. If a block is passed, treat it as a virtual row and
683
+ # use it as additional conditions for the match.
684
+ #
685
+ # merge_delete
686
+ # # WHEN MATCHED THEN DELETE
687
+ #
688
+ # merge_delete{a > 30}
689
+ # # WHEN MATCHED AND (a > 30) THEN DELETE
690
+ def merge_delete(&block)
691
+ _merge_when(:type=>:delete, &block)
692
+ end
693
+
694
+ # Return a dataset with a WHEN NOT MATCHED THEN INSERT clause added to the
695
+ # MERGE statement. If a block is passed, treat it as a virtual row and
696
+ # use it as additional conditions for the match.
697
+ #
698
+ # The arguments provided can be any arguments that would be accepted by
699
+ # #insert.
700
+ #
701
+ # merge_insert(i1: :i2, a: Sequel[:b]+11)
702
+ # # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
703
+ #
704
+ # merge_insert(:i2, Sequel[:b]+11){a > 30}
705
+ # # WHEN NOT MATCHED AND (a > 30) THEN INSERT VALUES (i2, (b + 11))
706
+ def merge_insert(*values, &block)
707
+ _merge_when(:type=>:insert, :values=>values, &block)
708
+ end
709
+
710
+ # Return a dataset with a WHEN MATCHED THEN UPDATE clause added to the
711
+ # MERGE statement. If a block is passed, treat it as a virtual row and
712
+ # use it as additional conditions for the match.
713
+ #
714
+ # merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
715
+ # # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
716
+ #
717
+ # merge_update(i1: :i2){a > 30}
718
+ # # WHEN MATCHED AND (a > 30) THEN UPDATE SET i1 = i2
719
+ def merge_update(values, &block)
720
+ _merge_when(:type=>:update, :values=>values, &block)
721
+ end
722
+
723
+ # Return a dataset with the source and join condition to use for the MERGE statement.
724
+ #
725
+ # merge_using(:m2, i1: :i2)
726
+ # # USING m2 ON (i1 = i2)
727
+ def merge_using(source, join_condition)
728
+ clone(:merge_using => [source, join_condition].freeze)
729
+ end
730
+
679
731
  # Returns a cloned dataset without a row_proc.
680
732
  #
681
733
  # ds = DB[:items].with_row_proc(:invert.to_proc)
@@ -698,7 +750,7 @@ module Sequel
698
750
  end
699
751
 
700
752
  # Returns a copy of the dataset with a specified order. Can be safely combined with limit.
701
- # If you call limit with an offset, it will override override the offset if you've called
753
+ # If you call limit with an offset, it will override the offset if you've called
702
754
  # offset first.
703
755
  #
704
756
  # DB[:items].offset(10) # SELECT * FROM items OFFSET 10
@@ -1058,13 +1110,12 @@ module Sequel
1058
1110
 
1059
1111
  # Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
1060
1112
  # A common table expression acts as an inline view for the query.
1113
+ #
1061
1114
  # Options:
1062
1115
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
1063
1116
  # :recursive :: Specify that this is a recursive CTE
1064
- #
1065
- # PostgreSQL Specific Options:
1066
1117
  # :materialized :: Set to false to force inlining of the CTE, or true to force not inlining
1067
- # the CTE (PostgreSQL 12+).
1118
+ # the CTE (PostgreSQL 12+/SQLite 3.35+).
1068
1119
  #
1069
1120
  # DB[:items].with(:items, DB[:syx].where(Sequel[:name].like('A%')))
1070
1121
  # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
@@ -1080,10 +1131,35 @@ module Sequel
1080
1131
 
1081
1132
  # Add a recursive common table expression (CTE) with the given name, a dataset that
1082
1133
  # defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
1083
- # of the CTE. Options:
1134
+ # of the CTE.
1135
+ #
1136
+ # Options:
1084
1137
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
1085
1138
  # :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
1086
1139
  #
1140
+ # PostgreSQL 14+ Options:
1141
+ # :cycle :: Stop recursive searching when a cycle is detected. Includes two columns in the
1142
+ # result of the CTE, a cycle column indicating whether a cycle was detected for
1143
+ # the current row, and a path column for the path traversed to get to the current
1144
+ # row. If given, must be a hash with the following keys:
1145
+ # :columns :: (required) The column or array of columns to use to detect a cycle.
1146
+ # If the value of these columns match columns already traversed, then
1147
+ # a cycle is detected, and recursive searching will not traverse beyond
1148
+ # the cycle (the CTE will include the row where the cycle was detected).
1149
+ # :cycle_column :: The name of the cycle column in the output, defaults to :is_cycle.
1150
+ # :cycle_value :: The value of the cycle column in the output if the current row was
1151
+ # detected as a cycle, defaults to true.
1152
+ # :noncycle_value :: The value of the cycle column in the output if the current row
1153
+ # was not detected as a cycle, defaults to false. Only respected
1154
+ # if :cycle_value is given.
1155
+ # :path_column :: The name of the path column in the output, defaults to :path.
1156
+ # :search :: Include an order column in the result of the CTE that allows for breadth or
1157
+ # depth first searching. If given, must be a hash with the following keys:
1158
+ # :by :: (required) The column or array of columns to search by.
1159
+ # :order_column :: The name of the order column in the output, defaults to :ordercol.
1160
+ # :type :: Set to :breadth to use breadth-first searching (depth-first searching
1161
+ # is the default).
1162
+ #
1087
1163
  # DB[:t].with_recursive(:t,
1088
1164
  # DB[:i1].select(:id, :parent_id).where(parent_id: nil),
1089
1165
  # DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
@@ -1094,6 +1170,21 @@ module Sequel
1094
1170
  # # UNION ALL
1095
1171
  # # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
1096
1172
  # # ) SELECT * FROM t
1173
+ #
1174
+ # DB[:t].with_recursive(:t,
1175
+ # DB[:i1].where(parent_id: nil),
1176
+ # DB[:i1].join(:t, id: :parent_id).select_all(:i1),
1177
+ # search: {by: :id, type: :breadth},
1178
+ # cycle: {columns: :id, cycle_value: 1, noncycle_value: 2})
1179
+ #
1180
+ # # WITH RECURSIVE t AS (
1181
+ # # SELECT * FROM i1 WHERE (parent_id IS NULL)
1182
+ # # UNION ALL
1183
+ # # (SELECT i1.* FROM i1 INNER JOIN t ON (t.id = i1.parent_id))
1184
+ # # )
1185
+ # # SEARCH BREADTH FIRST BY id SET ordercol
1186
+ # # CYCLE id SET is_cycle TO 1 DEFAULT 2 USING path
1187
+ # # SELECT * FROM t
1097
1188
  def with_recursive(name, nonrecursive, recursive, opts=OPTS)
1098
1189
  raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
1099
1190
  if hoist_cte?(nonrecursive)
@@ -1246,6 +1337,18 @@ module Sequel
1246
1337
  end
1247
1338
  end
1248
1339
 
1340
+ # Append to the current MERGE WHEN clauses.
1341
+ # Mutates the hash to add the conditions, if a virtual row block is passed.
1342
+ def _merge_when(hash, &block)
1343
+ hash[:conditions] = Sequel.virtual_row(&block) if block
1344
+
1345
+ if merge_when = @opts[:merge_when]
1346
+ clone(:merge_when => (merge_when.dup << hash.freeze).freeze)
1347
+ else
1348
+ clone(:merge_when => [hash.freeze].freeze)
1349
+ end
1350
+ end
1351
+
1249
1352
  # Add the given filter condition. Arguments:
1250
1353
  # clause :: Symbol or which SQL clause to effect, should be :where or :having
1251
1354
  # cond :: The filter condition to add
@@ -22,31 +22,9 @@ module Sequel
22
22
  def insert_sql(*values)
23
23
  return static_sql(@opts[:sql]) if @opts[:sql]
24
24
 
25
- check_modification_allowed!
26
-
27
- columns = []
28
-
29
- case values.size
30
- when 0
31
- return insert_sql(OPTS)
32
- when 1
33
- case vals = values[0]
34
- when Hash
35
- values = []
36
- vals.each do |k,v|
37
- columns << k
38
- values << v
39
- end
40
- when Dataset, Array, LiteralString
41
- values = vals
42
- end
43
- when 2
44
- if (v0 = values[0]).is_a?(Array) && ((v1 = values[1]).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
45
- columns, values = v0, v1
46
- raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
47
- end
48
- end
25
+ check_insert_allowed!
49
26
 
27
+ columns, values = _parse_insert_sql_args(values)
50
28
  if values.is_a?(Array) && values.empty? && !insert_supports_empty_values?
51
29
  columns, values = insert_empty_columns_values
52
30
  elsif values.is_a?(Dataset) && hoist_cte?(values) && supports_cte?(:insert)
@@ -112,6 +90,31 @@ module Sequel
112
90
  end
113
91
  end
114
92
 
93
+ # The SQL to use for the MERGE statement.
94
+ def merge_sql
95
+ raise Error, "This database doesn't support MERGE" unless supports_merge?
96
+ if sql = opts[:sql]
97
+ return static_sql(sql)
98
+ end
99
+ if sql = cache_get(:_merge_sql)
100
+ return sql
101
+ end
102
+ source, join_condition = @opts[:merge_using]
103
+ raise Error, "No USING clause for MERGE" unless source
104
+ sql = @opts[:append_sql] || sql_string_origin
105
+
106
+ select_with_sql(sql)
107
+ sql << "MERGE INTO "
108
+ source_list_append(sql, @opts[:from])
109
+ sql << " USING "
110
+ identifier_append(sql, source)
111
+ sql << " ON "
112
+ literal_append(sql, join_condition)
113
+ _merge_when_sql(sql)
114
+ cache_set(:_merge_sql, sql) if cache_sql?
115
+ sql
116
+ end
117
+
115
118
  # Returns an array of insert statements for inserting multiple records.
116
119
  # This method is used by +multi_insert+ to format insert statements and
117
120
  # expects a keys array and and an array of value arrays.
@@ -172,7 +175,7 @@ module Sequel
172
175
  # than one table.
173
176
  def update_sql(values = OPTS)
174
177
  return static_sql(opts[:sql]) if opts[:sql]
175
- check_modification_allowed!
178
+ check_update_allowed!
176
179
  check_not_limited!(:update)
177
180
 
178
181
  case values
@@ -215,7 +218,7 @@ module Sequel
215
218
  lines << "def #{'_' if priv}#{type}_sql"
216
219
  lines << 'if sql = opts[:sql]; return static_sql(sql) end' unless priv
217
220
  lines << "if sql = cache_get(:_#{type}_sql); return sql end" if cacheable
218
- lines << 'check_modification_allowed!' << 'check_not_limited!(:delete)' if type == :delete
221
+ lines << 'check_delete_allowed!' << 'check_not_limited!(:delete)' if type == :delete
219
222
  lines << 'sql = @opts[:append_sql] || sql_string_origin'
220
223
 
221
224
  if clauses.all?{|c| c.is_a?(Array)}
@@ -559,11 +562,9 @@ module Sequel
559
562
  # Append literalization of JOIN USING clause to SQL string.
560
563
  def join_using_clause_sql_append(sql, jc)
561
564
  join_clause_sql_append(sql, jc)
562
- sql << ' USING ('
563
- column_list_append(sql, jc.using)
564
- sql << ')'
565
+ join_using_clause_using_sql_append(sql, jc.using)
565
566
  end
566
-
567
+
567
568
  # Append literalization of negative boolean constant to SQL string.
568
569
  def negative_boolean_constant_sql_append(sql, constant)
569
570
  sql << 'NOT '
@@ -852,6 +853,83 @@ module Sequel
852
853
 
853
854
  private
854
855
 
856
+ # Append the INSERT sql used in a MERGE
857
+ def _merge_insert_sql(sql, data)
858
+ sql << " THEN INSERT"
859
+ columns, values = _parse_insert_sql_args(data[:values])
860
+ _insert_columns_sql(sql, columns)
861
+ _insert_values_sql(sql, values)
862
+ end
863
+
864
+ def _merge_update_sql(sql, data)
865
+ sql << " THEN UPDATE SET "
866
+ update_sql_values_hash(sql, data[:values])
867
+ end
868
+
869
+ def _merge_delete_sql(sql, data)
870
+ sql << " THEN DELETE"
871
+ end
872
+
873
+ # Mapping of merge types to related SQL
874
+ MERGE_TYPE_SQL = {
875
+ :insert => ' WHEN NOT MATCHED',
876
+ :delete => ' WHEN MATCHED',
877
+ :update => ' WHEN MATCHED',
878
+ :matched => ' WHEN MATCHED',
879
+ :not_matched => ' WHEN NOT MATCHED',
880
+ }.freeze
881
+ private_constant :MERGE_TYPE_SQL
882
+
883
+ # Add the WHEN clauses to the MERGE SQL
884
+ def _merge_when_sql(sql)
885
+ raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
886
+ merge_when.each do |data|
887
+ type = data[:type]
888
+ sql << MERGE_TYPE_SQL[type]
889
+ _merge_when_conditions_sql(sql, data)
890
+ send(:"_merge_#{type}_sql", sql, data)
891
+ end
892
+ end
893
+
894
+ # Append MERGE WHEN conditions, if there are conditions provided.
895
+ def _merge_when_conditions_sql(sql, data)
896
+ if data.has_key?(:conditions)
897
+ sql << " AND "
898
+ literal_append(sql, data[:conditions])
899
+ end
900
+ end
901
+
902
+ # Parse the values passed to insert_sql, returning columns and values
903
+ # to use for the INSERT. Returned columns is always an array, but can be empty
904
+ # for an INSERT without explicit column references. Returned values can be an
905
+ # array, dataset, or literal string.
906
+ def _parse_insert_sql_args(values)
907
+ columns = []
908
+
909
+ case values.size
910
+ when 0
911
+ values = []
912
+ when 1
913
+ case vals = values[0]
914
+ when Hash
915
+ values = []
916
+ vals.each do |k,v|
917
+ columns << k
918
+ values << v
919
+ end
920
+ when Dataset, Array, LiteralString
921
+ values = vals
922
+ end
923
+ when 2
924
+ if (v0 = values[0]).is_a?(Array) && ((v1 = values[1]).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
925
+ columns, values = v0, v1
926
+ raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
927
+ end
928
+ end
929
+
930
+ [columns, values]
931
+ end
932
+
855
933
  # Formats the truncate statement. Assumes the table given has already been
856
934
  # literalized.
857
935
  def _truncate_sql(table)
@@ -896,9 +974,10 @@ module Sequel
896
974
  # Clone of this dataset usable in aggregate operations. Does
897
975
  # a from_self if dataset contains any parameters that would
898
976
  # affect normal aggregation, or just removes an existing
899
- # order if not.
977
+ # order if not. Also removes the row_proc, which isn't needed
978
+ # for aggregate calculations.
900
979
  def aggregate_dataset
901
- options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
980
+ (options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered).naked
902
981
  end
903
982
 
904
983
  # Append aliasing expression to SQL string.
@@ -918,10 +997,35 @@ module Sequel
918
997
  !@opts[:no_cache_sql] && !cache_get(:_no_cache_sql)
919
998
  end
920
999
 
921
- # Raise an InvalidOperation exception if deletion is not allowed for this dataset.
1000
+ # Raise an InvalidOperation exception if modification is not allowed for this dataset.
1001
+ # Check whether it is allowed to insert into this dataset.
1002
+ # Only for backwards compatibility with older external adapters.
922
1003
  def check_modification_allowed!
1004
+ # SEQUEL6: Remove
1005
+ Sequel::Deprecation.deprecate("Dataset#check_modification_allowed!", "Use check_{insert,delete,update,truncation}_allowed! instead")
1006
+ _check_modification_allowed!(supports_modifying_joins?)
1007
+ end
1008
+
1009
+ # Check whether it is allowed to insert into this dataset.
1010
+ def check_insert_allowed!
1011
+ _check_modification_allowed!(false)
1012
+ end
1013
+ alias check_truncation_allowed! check_insert_allowed!
1014
+
1015
+ # Check whether it is allowed to delete from this dataset.
1016
+ def check_delete_allowed!
1017
+ _check_modification_allowed!(supports_deleting_joins?)
1018
+ end
1019
+
1020
+ # Check whether it is allowed to update this dataset.
1021
+ def check_update_allowed!
1022
+ _check_modification_allowed!(supports_updating_joins?)
1023
+ end
1024
+
1025
+ # Internals of the check_*_allowed! methods
1026
+ def _check_modification_allowed!(modifying_joins_supported)
923
1027
  raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
924
- raise(InvalidOperation, "Joined datasets cannot be modified") if !supports_modifying_joins? && joined_dataset?
1028
+ raise(InvalidOperation, "Joined datasets cannot be modified") if !modifying_joins_supported && joined_dataset?
925
1029
  end
926
1030
 
927
1031
  # Raise error if the dataset uses limits or offsets.
@@ -930,11 +1034,6 @@ module Sequel
930
1034
  raise InvalidOperation, "Dataset##{type} not supported on datasets with limits or offsets" if opts[:limit] || opts[:offset]
931
1035
  end
932
1036
 
933
- # Alias of check_modification_allowed!
934
- def check_truncation_allowed!
935
- check_modification_allowed!
936
- end
937
-
938
1037
  # Append column list to SQL string.
939
1038
  # If the column list is empty, a wildcard (*) is appended.
940
1039
  def column_list_append(sql, columns)
@@ -971,7 +1070,9 @@ module Sequel
971
1070
  # operators unsupported by some databases. Used by adapters for databases
972
1071
  # that don't support the operators natively.
973
1072
  def complex_expression_emulate_append(sql, op, args)
1073
+ # :nocov:
974
1074
  case op
1075
+ # :nocov:
975
1076
  when :%
976
1077
  complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:MOD, a, b)}
977
1078
  when :>>
@@ -1144,7 +1245,10 @@ module Sequel
1144
1245
  end
1145
1246
 
1146
1247
  def insert_columns_sql(sql)
1147
- columns = opts[:columns]
1248
+ _insert_columns_sql(sql, opts[:columns])
1249
+ end
1250
+
1251
+ def _insert_columns_sql(sql, columns)
1148
1252
  if columns && !columns.empty?
1149
1253
  sql << ' ('
1150
1254
  identifier_list_append(sql, columns)
@@ -1163,7 +1267,11 @@ module Sequel
1163
1267
  end
1164
1268
 
1165
1269
  def insert_values_sql(sql)
1166
- case values = opts[:values]
1270
+ _insert_values_sql(sql, opts[:values])
1271
+ end
1272
+
1273
+ def _insert_values_sql(sql, values)
1274
+ case values
1167
1275
  when Array
1168
1276
  if values.empty?
1169
1277
  sql << " DEFAULT VALUES"
@@ -1196,6 +1304,13 @@ module Sequel
1196
1304
  "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
1197
1305
  end
1198
1306
 
1307
+ # Append USING clause for JOIN USING
1308
+ def join_using_clause_using_sql_append(sql, using_columns)
1309
+ sql << ' USING ('
1310
+ column_list_append(sql, using_columns)
1311
+ sql << ')'
1312
+ end
1313
+
1199
1314
  # Append a literalization of the array to SQL string.
1200
1315
  # Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
1201
1316
  def literal_array_append(sql, v)
@@ -1516,15 +1631,14 @@ module Sequel
1516
1631
 
1517
1632
  def select_with_sql(sql)
1518
1633
  return unless supports_cte?
1519
- ws = opts[:with]
1520
- return if !ws || ws.empty?
1634
+ ctes = opts[:with]
1635
+ return if !ctes || ctes.empty?
1521
1636
  sql << select_with_sql_base
1522
1637
  c = false
1523
1638
  comma = ', '
1524
- ws.each do |w|
1639
+ ctes.each do |cte|
1525
1640
  sql << comma if c
1526
- select_with_sql_prefix(sql, w)
1527
- literal_dataset_append(sql, w[:dataset])
1641
+ select_with_sql_cte(sql, cte)
1528
1642
  c ||= true
1529
1643
  end
1530
1644
  sql << ' '
@@ -1537,6 +1651,11 @@ module Sequel
1537
1651
  "WITH "
1538
1652
  end
1539
1653
 
1654
+ def select_with_sql_cte(sql, cte)
1655
+ select_with_sql_prefix(sql, cte)
1656
+ literal_dataset_append(sql, cte[:dataset])
1657
+ end
1658
+
1540
1659
  def select_with_sql_prefix(sql, w)
1541
1660
  quote_identifier_append(sql, w[:name])
1542
1661
  if args = w[:args]
@@ -1545,6 +1664,13 @@ module Sequel
1545
1664
  sql << ')'
1546
1665
  end
1547
1666
  sql << ' AS '
1667
+
1668
+ case w[:materialized]
1669
+ when true
1670
+ sql << "MATERIALIZED "
1671
+ when false
1672
+ sql << "NOT MATERIALIZED "
1673
+ end
1548
1674
  end
1549
1675
 
1550
1676
  # Whether the symbol cache should be skipped when literalizing the dataset
@@ -39,7 +39,7 @@ module Sequel
39
39
  # Print the message and possibly backtrace to the output.
40
40
  def self.deprecate(method, instead=nil)
41
41
  return unless output
42
- message = instead ? "#{method} is deprecated and will be removed in Sequel 5.1. #{instead}." : method
42
+ message = instead ? "#{method} is deprecated and will be removed in Sequel 6. #{instead}." : method
43
43
  message = "#{prefix}#{message}" if prefix
44
44
  output.puts(message)
45
45
  case b = backtrace_filter
@@ -60,7 +60,9 @@ module Sequel
60
60
  # If using ruby 2.3+, use Module#deprecate_constant to deprecate the constant,
61
61
  # otherwise do nothing as the ruby implementation does not support constant deprecation.
62
62
  def self.deprecate_constant(mod, constant)
63
+ # :nocov:
63
64
  if RUBY_VERSION > '2.3'
65
+ # :nocov:
64
66
  mod.deprecate_constant(constant)
65
67
  end
66
68
  end
@@ -8,7 +8,9 @@ module Sequel
8
8
  # exception is held here.
9
9
  attr_accessor :wrapped_exception
10
10
 
11
+ # :nocov:
11
12
  if RUBY_VERSION >= '2.1'
13
+ # :nocov:
12
14
  # Returned the wrapped exception if one exists, otherwise use
13
15
  # ruby's default behavior.
14
16
  def cause
@@ -41,8 +41,7 @@ module Sequel
41
41
  def self.column_sizes(records, columns) # :nodoc:
42
42
  sizes = Hash.new {0}
43
43
  columns.each do |c|
44
- s = c.to_s.size
45
- sizes[c] = s if s > sizes[c]
44
+ sizes[c] = c.to_s.size
46
45
  end
47
46
  records.each do |r|
48
47
  columns.each do |c|
@@ -33,7 +33,7 @@ module Sequel
33
33
  module AnyNotEmpty
34
34
  # If a block is not given, return whether the dataset is not empty.
35
35
  def any?
36
- if block_given?
36
+ if defined?(yield)
37
37
  super
38
38
  else
39
39
  !empty?