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
@@ -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?