sequel 5.39.0 → 5.72.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +408 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +13 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +26 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.40.0.txt +40 -0
  17. data/doc/release_notes/5.41.0.txt +25 -0
  18. data/doc/release_notes/5.42.0.txt +136 -0
  19. data/doc/release_notes/5.43.0.txt +98 -0
  20. data/doc/release_notes/5.44.0.txt +32 -0
  21. data/doc/release_notes/5.45.0.txt +34 -0
  22. data/doc/release_notes/5.46.0.txt +87 -0
  23. data/doc/release_notes/5.47.0.txt +59 -0
  24. data/doc/release_notes/5.48.0.txt +14 -0
  25. data/doc/release_notes/5.49.0.txt +59 -0
  26. data/doc/release_notes/5.50.0.txt +78 -0
  27. data/doc/release_notes/5.51.0.txt +47 -0
  28. data/doc/release_notes/5.52.0.txt +87 -0
  29. data/doc/release_notes/5.53.0.txt +23 -0
  30. data/doc/release_notes/5.54.0.txt +27 -0
  31. data/doc/release_notes/5.55.0.txt +21 -0
  32. data/doc/release_notes/5.56.0.txt +51 -0
  33. data/doc/release_notes/5.57.0.txt +23 -0
  34. data/doc/release_notes/5.58.0.txt +31 -0
  35. data/doc/release_notes/5.59.0.txt +73 -0
  36. data/doc/release_notes/5.60.0.txt +22 -0
  37. data/doc/release_notes/5.61.0.txt +43 -0
  38. data/doc/release_notes/5.62.0.txt +132 -0
  39. data/doc/release_notes/5.63.0.txt +33 -0
  40. data/doc/release_notes/5.64.0.txt +50 -0
  41. data/doc/release_notes/5.65.0.txt +21 -0
  42. data/doc/release_notes/5.66.0.txt +24 -0
  43. data/doc/release_notes/5.67.0.txt +32 -0
  44. data/doc/release_notes/5.68.0.txt +61 -0
  45. data/doc/release_notes/5.69.0.txt +26 -0
  46. data/doc/release_notes/5.70.0.txt +35 -0
  47. data/doc/release_notes/5.71.0.txt +21 -0
  48. data/doc/release_notes/5.72.0.txt +33 -0
  49. data/doc/schema_modification.rdoc +1 -1
  50. data/doc/security.rdoc +9 -9
  51. data/doc/sharding.rdoc +3 -1
  52. data/doc/sql.rdoc +28 -16
  53. data/doc/testing.rdoc +22 -11
  54. data/doc/transactions.rdoc +6 -6
  55. data/doc/virtual_rows.rdoc +2 -2
  56. data/lib/sequel/adapters/ado/access.rb +1 -1
  57. data/lib/sequel/adapters/ado.rb +17 -17
  58. data/lib/sequel/adapters/amalgalite.rb +3 -5
  59. data/lib/sequel/adapters/ibmdb.rb +2 -2
  60. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  61. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  62. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  64. data/lib/sequel/adapters/jdbc.rb +16 -18
  65. data/lib/sequel/adapters/mysql.rb +92 -67
  66. data/lib/sequel/adapters/mysql2.rb +54 -49
  67. data/lib/sequel/adapters/odbc.rb +6 -2
  68. data/lib/sequel/adapters/oracle.rb +4 -3
  69. data/lib/sequel/adapters/postgres.rb +83 -40
  70. data/lib/sequel/adapters/shared/access.rb +11 -1
  71. data/lib/sequel/adapters/shared/db2.rb +30 -0
  72. data/lib/sequel/adapters/shared/mssql.rb +90 -9
  73. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  74. data/lib/sequel/adapters/shared/oracle.rb +82 -1
  75. data/lib/sequel/adapters/shared/postgres.rb +496 -178
  76. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  77. data/lib/sequel/adapters/shared/sqlite.rb +116 -11
  78. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  79. data/lib/sequel/adapters/sqlite.rb +60 -18
  80. data/lib/sequel/adapters/tinytds.rb +1 -1
  81. data/lib/sequel/adapters/trilogy.rb +117 -0
  82. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  83. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  84. data/lib/sequel/ast_transformer.rb +6 -0
  85. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  86. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  87. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  88. data/lib/sequel/connection_pool/single.rb +6 -8
  89. data/lib/sequel/connection_pool/threaded.rb +14 -8
  90. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  91. data/lib/sequel/connection_pool.rb +55 -31
  92. data/lib/sequel/core.rb +28 -18
  93. data/lib/sequel/database/connecting.rb +27 -3
  94. data/lib/sequel/database/dataset.rb +16 -6
  95. data/lib/sequel/database/misc.rb +69 -14
  96. data/lib/sequel/database/query.rb +73 -2
  97. data/lib/sequel/database/schema_generator.rb +46 -53
  98. data/lib/sequel/database/schema_methods.rb +18 -2
  99. data/lib/sequel/dataset/actions.rb +108 -14
  100. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  101. data/lib/sequel/dataset/features.rb +20 -0
  102. data/lib/sequel/dataset/misc.rb +12 -2
  103. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  104. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  105. data/lib/sequel/dataset/query.rb +171 -44
  106. data/lib/sequel/dataset/sql.rb +182 -47
  107. data/lib/sequel/dataset.rb +4 -0
  108. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  109. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  110. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  111. data/lib/sequel/extensions/async_thread_pool.rb +439 -0
  112. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  113. data/lib/sequel/extensions/blank.rb +8 -0
  114. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  115. data/lib/sequel/extensions/connection_validator.rb +16 -11
  116. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  117. data/lib/sequel/extensions/core_refinements.rb +36 -11
  118. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  119. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  120. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  121. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  122. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  123. data/lib/sequel/extensions/index_caching.rb +5 -1
  124. data/lib/sequel/extensions/inflector.rb +9 -1
  125. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  126. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  127. data/lib/sequel/extensions/migration.rb +11 -2
  128. data/lib/sequel/extensions/named_timezones.rb +26 -6
  129. data/lib/sequel/extensions/pagination.rb +1 -1
  130. data/lib/sequel/extensions/pg_array.rb +32 -4
  131. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  132. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  133. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  134. data/lib/sequel/extensions/pg_enum.rb +2 -3
  135. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  136. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  137. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  138. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  139. data/lib/sequel/extensions/pg_inet.rb +10 -11
  140. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  141. data/lib/sequel/extensions/pg_interval.rb +45 -19
  142. data/lib/sequel/extensions/pg_json.rb +13 -15
  143. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  144. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +11 -24
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +21 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/query.rb +2 -0
  151. data/lib/sequel/extensions/s.rb +2 -1
  152. data/lib/sequel/extensions/schema_caching.rb +1 -1
  153. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  154. data/lib/sequel/extensions/server_block.rb +10 -13
  155. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  156. data/lib/sequel/extensions/sql_comments.rb +110 -3
  157. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  158. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  159. data/lib/sequel/extensions/string_agg.rb +1 -1
  160. data/lib/sequel/extensions/string_date_time.rb +19 -23
  161. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  162. data/lib/sequel/model/associations.rb +345 -101
  163. data/lib/sequel/model/base.rb +51 -27
  164. data/lib/sequel/model/dataset_module.rb +3 -0
  165. data/lib/sequel/model/errors.rb +10 -1
  166. data/lib/sequel/model/inflections.rb +1 -1
  167. data/lib/sequel/model/plugins.rb +5 -0
  168. data/lib/sequel/plugins/association_proxies.rb +2 -0
  169. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +87 -15
  172. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  173. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  174. data/lib/sequel/plugins/column_encryption.rb +728 -0
  175. data/lib/sequel/plugins/composition.rb +10 -4
  176. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  177. data/lib/sequel/plugins/constraint_validations.rb +10 -6
  178. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  179. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  180. data/lib/sequel/plugins/dirty.rb +1 -1
  181. data/lib/sequel/plugins/enum.rb +124 -0
  182. data/lib/sequel/plugins/finder.rb +4 -2
  183. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  184. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  185. data/lib/sequel/plugins/json_serializer.rb +39 -24
  186. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  187. data/lib/sequel/plugins/list.rb +3 -1
  188. data/lib/sequel/plugins/many_through_many.rb +109 -10
  189. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  190. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  191. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  192. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  193. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  194. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
  195. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  196. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  197. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  198. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  199. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  200. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  201. data/lib/sequel/plugins/serialization.rb +9 -3
  202. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  203. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  204. data/lib/sequel/plugins/sql_comments.rb +189 -0
  205. data/lib/sequel/plugins/static_cache.rb +39 -1
  206. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  207. data/lib/sequel/plugins/subclasses.rb +28 -11
  208. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  209. data/lib/sequel/plugins/timestamps.rb +1 -1
  210. data/lib/sequel/plugins/unused_associations.rb +521 -0
  211. data/lib/sequel/plugins/update_or_create.rb +1 -1
  212. data/lib/sequel/plugins/validate_associated.rb +22 -12
  213. data/lib/sequel/plugins/validation_helpers.rb +46 -12
  214. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  215. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  216. data/lib/sequel/sql.rb +1 -1
  217. data/lib/sequel/timezones.rb +12 -14
  218. data/lib/sequel/version.rb +1 -1
  219. metadata +132 -38
@@ -12,6 +12,10 @@ module Sequel
12
12
  # in the extension).
13
13
  EXTENSIONS = {}
14
14
 
15
+ # Hash of extension name symbols to modules to load to implement the extension.
16
+ EXTENSION_MODULES = {}
17
+ private_constant :EXTENSION_MODULES
18
+
15
19
  EMPTY_ARRAY = [].freeze
16
20
 
17
21
  # The dataset options that require the removal of cached columns if changed.
@@ -45,12 +49,8 @@ module Sequel
45
49
  METHS
46
50
 
47
51
  # Register an extension callback for Dataset objects. ext should be the
48
- # extension name symbol, and mod should either be a Module that the
49
- # dataset is extended with, or a callable object called with the database
50
- # object. If mod is not provided, a block can be provided and is treated
51
- # as the mod object.
52
- #
53
- # If mod is a module, this also registers a Database extension that will
52
+ # extension name symbol, and mod should be a Module that will be
53
+ # included in the dataset's class. This also registers a Database extension that will
54
54
  # extend all of the database's datasets.
55
55
  def self.register_extension(ext, mod=nil, &block)
56
56
  if mod
@@ -58,14 +58,20 @@ module Sequel
58
58
  if mod.is_a?(Module)
59
59
  block = proc{|ds| ds.extend(mod)}
60
60
  Sequel::Database.register_extension(ext){|db| db.extend_datasets(mod)}
61
+ Sequel.synchronize{EXTENSION_MODULES[ext] = mod}
61
62
  else
62
63
  block = mod
63
64
  end
64
65
  end
66
+
67
+ unless mod.is_a?(Module)
68
+ Sequel::Deprecation.deprecate("Providing a block or non-module to Sequel::Dataset.register_extension is deprecated and support for it will be removed in Sequel 6.")
69
+ end
70
+
65
71
  Sequel.synchronize{EXTENSIONS[ext] = block}
66
72
  end
67
73
 
68
- # On Ruby 2.4+, use clone(:freeze=>false) to create clones, because
74
+ # On Ruby 2.4+, use clone(freeze: false) to create clones, because
69
75
  # we use true freezing in that case, and we need to modify the opts
70
76
  # in the frozen copy.
71
77
  #
@@ -116,7 +122,7 @@ module Sequel
116
122
  # DB[:items].order(:id).distinct(:id) # SQL: SELECT DISTINCT ON (id) * FROM items ORDER BY id
117
123
  # DB[:items].order(:id).distinct{func(:id)} # SQL: SELECT DISTINCT ON (func(id)) * FROM items ORDER BY id
118
124
  #
119
- # There is support for emualting the DISTINCT ON support in MySQL, but it
125
+ # There is support for emulating the DISTINCT ON support in MySQL, but it
120
126
  # does not support the ORDER of the dataset, and also doesn't work in many
121
127
  # cases if the ONLY_FULL_GROUP_BY sql_mode is used, which is the default on
122
128
  # MySQL 5.7.5+.
@@ -195,11 +201,15 @@ module Sequel
195
201
  if TRUE_FREEZE
196
202
  # Return a clone of the dataset loaded with the given dataset extensions.
197
203
  # If no related extension file exists or the extension does not have
198
- # specific support for Dataset objects, an Error will be raised.
199
- def extension(*a)
200
- c = _clone(:freeze=>false)
201
- c.send(:_extension!, a)
202
- c.freeze
204
+ # specific support for Dataset objects, an error will be raised.
205
+ def extension(*exts)
206
+ Sequel.extension(*exts)
207
+ mods = exts.map{|ext| Sequel.synchronize{EXTENSION_MODULES[ext]}}
208
+ if mods.all?
209
+ with_extend(*mods)
210
+ else
211
+ with_extend(DeprecatedSingletonClassMethods).extension(*exts)
212
+ end
203
213
  end
204
214
  else
205
215
  # :nocov:
@@ -508,6 +518,7 @@ module Sequel
508
518
  # argument.
509
519
  # :implicit_qualifier :: The name to use for qualifying implicit conditions. By default,
510
520
  # the last joined or primary table is used.
521
+ # :join_using :: Force the using of JOIN USING, even if +expr+ is not an array of symbols.
511
522
  # :reset_implicit_qualifier :: Can set to false to ignore this join when future joins determine qualifier
512
523
  # for implicit conditions.
513
524
  # :qualify :: Can be set to false to not do any implicit qualification. Can be set
@@ -541,7 +552,7 @@ module Sequel
541
552
  return s.join_table(type, ds, expr, options, &block)
542
553
  end
543
554
 
544
- using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
555
+ using_join = options[:join_using] || (expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)})
545
556
  if using_join && !supports_join_using?
546
557
  h = {}
547
558
  expr.each{|e| h[e] = e}
@@ -616,7 +627,7 @@ module Sequel
616
627
  UNCONDITIONED_JOIN_TYPES.each do |jtype|
617
628
  class_eval(<<-END, __FILE__, __LINE__+1)
618
629
  def #{jtype}_join(table, opts=Sequel::OPTS)
619
- raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if block_given?
630
+ raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if defined?(yield)
620
631
  raise(Sequel::Error, '#{jtype}_join 2nd argument should be an options hash, not conditions') unless opts.is_a?(Hash)
621
632
  join_table(:#{jtype}, table, nil, opts)
622
633
  end
@@ -677,6 +688,56 @@ module Sequel
677
688
  clone(:lock => style)
678
689
  end
679
690
 
691
+ # Return a dataset with a WHEN MATCHED THEN DELETE clause added to the
692
+ # MERGE statement. If a block is passed, treat it as a virtual row and
693
+ # use it as additional conditions for the match.
694
+ #
695
+ # merge_delete
696
+ # # WHEN MATCHED THEN DELETE
697
+ #
698
+ # merge_delete{a > 30}
699
+ # # WHEN MATCHED AND (a > 30) THEN DELETE
700
+ def merge_delete(&block)
701
+ _merge_when(:type=>:delete, &block)
702
+ end
703
+
704
+ # Return a dataset with a WHEN NOT MATCHED THEN INSERT clause added to the
705
+ # MERGE statement. If a block is passed, treat it as a virtual row and
706
+ # use it as additional conditions for the match.
707
+ #
708
+ # The arguments provided can be any arguments that would be accepted by
709
+ # #insert.
710
+ #
711
+ # merge_insert(i1: :i2, a: Sequel[:b]+11)
712
+ # # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
713
+ #
714
+ # merge_insert(:i2, Sequel[:b]+11){a > 30}
715
+ # # WHEN NOT MATCHED AND (a > 30) THEN INSERT VALUES (i2, (b + 11))
716
+ def merge_insert(*values, &block)
717
+ _merge_when(:type=>:insert, :values=>values, &block)
718
+ end
719
+
720
+ # Return a dataset with a WHEN MATCHED THEN UPDATE clause added to the
721
+ # MERGE statement. If a block is passed, treat it as a virtual row and
722
+ # use it as additional conditions for the match.
723
+ #
724
+ # merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
725
+ # # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
726
+ #
727
+ # merge_update(i1: :i2){a > 30}
728
+ # # WHEN MATCHED AND (a > 30) THEN UPDATE SET i1 = i2
729
+ def merge_update(values, &block)
730
+ _merge_when(:type=>:update, :values=>values, &block)
731
+ end
732
+
733
+ # Return a dataset with the source and join condition to use for the MERGE statement.
734
+ #
735
+ # merge_using(:m2, i1: :i2)
736
+ # # USING m2 ON (i1 = i2)
737
+ def merge_using(source, join_condition)
738
+ clone(:merge_using => [source, join_condition].freeze)
739
+ end
740
+
680
741
  # Returns a cloned dataset without a row_proc.
681
742
  #
682
743
  # ds = DB[:items].with_row_proc(:invert.to_proc)
@@ -699,7 +760,7 @@ module Sequel
699
760
  end
700
761
 
701
762
  # Returns a copy of the dataset with a specified order. Can be safely combined with limit.
702
- # If you call limit with an offset, it will override override the offset if you've called
763
+ # If you call limit with an offset, it will override the offset if you've called
703
764
  # offset first.
704
765
  #
705
766
  # DB[:items].offset(10) # SELECT * FROM items OFFSET 10
@@ -736,7 +797,7 @@ module Sequel
736
797
  # DB[:items].order(Sequel.lit('a + b')) # SELECT * FROM items ORDER BY a + b
737
798
  # DB[:items].order(Sequel[:a] + :b) # SELECT * FROM items ORDER BY (a + b)
738
799
  # DB[:items].order(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name DESC
739
- # DB[:items].order(Sequel.asc(:name, :nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
800
+ # DB[:items].order(Sequel.asc(:name, nulls: :last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
740
801
  # DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
741
802
  # DB[:items].order(nil) # SELECT * FROM items
742
803
  def order(*columns, &block)
@@ -806,13 +867,13 @@ module Sequel
806
867
  # DB[:items].returning(nil) # RETURNING NULL
807
868
  # DB[:items].returning(:id, :name) # RETURNING id, name
808
869
  #
809
- # DB[:items].returning.insert(:a=>1) do |hash|
870
+ # DB[:items].returning.insert(a: 1) do |hash|
810
871
  # # hash for each row inserted, with values for all columns
811
872
  # end
812
- # DB[:items].returning.update(:a=>1) do |hash|
873
+ # DB[:items].returning.update(a: 1) do |hash|
813
874
  # # hash for each row updated, with values for all columns
814
875
  # end
815
- # DB[:items].returning.delete(:a=>1) do |hash|
876
+ # DB[:items].returning.delete(a: 1) do |hash|
816
877
  # # hash for each row deleted, with values for all columns
817
878
  # end
818
879
  def returning(*values)
@@ -1051,7 +1112,7 @@ module Sequel
1051
1112
  # referenced in window functions. See Sequel::SQL::Window for a list of
1052
1113
  # options that can be passed in. Example:
1053
1114
  #
1054
- # DB[:items].window(:w, :partition=>:c1, :order=>:c2)
1115
+ # DB[:items].window(:w, partition: :c1, order: :c2)
1055
1116
  # # SELECT * FROM items WINDOW w AS (PARTITION BY c1 ORDER BY c2)
1056
1117
  def window(name, opts)
1057
1118
  clone(:window=>((@opts[:window]||EMPTY_ARRAY) + [[name, SQL::Window.new(opts)].freeze]).freeze)
@@ -1059,13 +1120,12 @@ module Sequel
1059
1120
 
1060
1121
  # Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
1061
1122
  # A common table expression acts as an inline view for the query.
1123
+ #
1062
1124
  # Options:
1063
1125
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
1064
1126
  # :recursive :: Specify that this is a recursive CTE
1065
- #
1066
- # PostgreSQL Specific Options:
1067
1127
  # :materialized :: Set to false to force inlining of the CTE, or true to force not inlining
1068
- # the CTE (PostgreSQL 12+).
1128
+ # the CTE (PostgreSQL 12+/SQLite 3.35+).
1069
1129
  #
1070
1130
  # DB[:items].with(:items, DB[:syx].where(Sequel[:name].like('A%')))
1071
1131
  # # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
@@ -1081,20 +1141,60 @@ module Sequel
1081
1141
 
1082
1142
  # Add a recursive common table expression (CTE) with the given name, a dataset that
1083
1143
  # defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
1084
- # of the CTE. Options:
1144
+ # of the CTE.
1145
+ #
1146
+ # Options:
1085
1147
  # :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
1086
1148
  # :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
1087
1149
  #
1150
+ # PostgreSQL 14+ Options:
1151
+ # :cycle :: Stop recursive searching when a cycle is detected. Includes two columns in the
1152
+ # result of the CTE, a cycle column indicating whether a cycle was detected for
1153
+ # the current row, and a path column for the path traversed to get to the current
1154
+ # row. If given, must be a hash with the following keys:
1155
+ # :columns :: (required) The column or array of columns to use to detect a cycle.
1156
+ # If the value of these columns match columns already traversed, then
1157
+ # a cycle is detected, and recursive searching will not traverse beyond
1158
+ # the cycle (the CTE will include the row where the cycle was detected).
1159
+ # :cycle_column :: The name of the cycle column in the output, defaults to :is_cycle.
1160
+ # :cycle_value :: The value of the cycle column in the output if the current row was
1161
+ # detected as a cycle, defaults to true.
1162
+ # :noncycle_value :: The value of the cycle column in the output if the current row
1163
+ # was not detected as a cycle, defaults to false. Only respected
1164
+ # if :cycle_value is given.
1165
+ # :path_column :: The name of the path column in the output, defaults to :path.
1166
+ # :search :: Include an order column in the result of the CTE that allows for breadth or
1167
+ # depth first searching. If given, must be a hash with the following keys:
1168
+ # :by :: (required) The column or array of columns to search by.
1169
+ # :order_column :: The name of the order column in the output, defaults to :ordercol.
1170
+ # :type :: Set to :breadth to use breadth-first searching (depth-first searching
1171
+ # is the default).
1172
+ #
1088
1173
  # DB[:t].with_recursive(:t,
1089
1174
  # DB[:i1].select(:id, :parent_id).where(parent_id: nil),
1090
1175
  # DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
1091
- # :args=>[:id, :parent_id])
1176
+ # args: [:id, :parent_id])
1092
1177
  #
1093
1178
  # # WITH RECURSIVE t(id, parent_id) AS (
1094
1179
  # # SELECT id, parent_id FROM i1 WHERE (parent_id IS NULL)
1095
1180
  # # UNION ALL
1096
1181
  # # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
1097
1182
  # # ) SELECT * FROM t
1183
+ #
1184
+ # DB[:t].with_recursive(:t,
1185
+ # DB[:i1].where(parent_id: nil),
1186
+ # DB[:i1].join(:t, id: :parent_id).select_all(:i1),
1187
+ # search: {by: :id, type: :breadth},
1188
+ # cycle: {columns: :id, cycle_value: 1, noncycle_value: 2})
1189
+ #
1190
+ # # WITH RECURSIVE t AS (
1191
+ # # SELECT * FROM i1 WHERE (parent_id IS NULL)
1192
+ # # UNION ALL
1193
+ # # (SELECT i1.* FROM i1 INNER JOIN t ON (t.id = i1.parent_id))
1194
+ # # )
1195
+ # # SEARCH BREADTH FIRST BY id SET ordercol
1196
+ # # CYCLE id SET is_cycle TO 1 DEFAULT 2 USING path
1197
+ # # SELECT * FROM t
1098
1198
  def with_recursive(name, nonrecursive, recursive, opts=OPTS)
1099
1199
  raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
1100
1200
  if hoist_cte?(nonrecursive)
@@ -1109,16 +1209,27 @@ module Sequel
1109
1209
  end
1110
1210
 
1111
1211
  if TRUE_FREEZE
1112
- # Return a clone of the dataset extended with the given modules.
1212
+ # Create a subclass of the receiver's class, and include the given modules
1213
+ # into it. If a block is provided, a DatasetModule is created using the block and
1214
+ # is included into the subclass. Create an instance of the subclass using the
1215
+ # same db and opts, so that the returned dataset operates similarly to a clone
1216
+ # extended with the given modules. This approach is used to avoid singleton
1217
+ # classes, which significantly improves performance.
1218
+ #
1113
1219
  # Note that like Object#extend, when multiple modules are provided
1114
- # as arguments the cloned dataset is extended with the modules in reverse
1115
- # order. If a block is provided, a DatasetModule is created using the block and
1116
- # the clone is extended with that module after any modules given as arguments.
1220
+ # as arguments the subclass includes the modules in reverse order.
1117
1221
  def with_extend(*mods, &block)
1118
- c = _clone(:freeze=>false)
1119
- c.extend(*mods) unless mods.empty?
1120
- c.extend(DatasetModule.new(&block)) if block
1121
- c.freeze
1222
+ c = Class.new(self.class)
1223
+ c.include(*mods) unless mods.empty?
1224
+ c.include(DatasetModule.new(&block)) if block
1225
+ o = c.freeze.allocate
1226
+ o.instance_variable_set(:@db, @db)
1227
+ o.instance_variable_set(:@opts, @opts)
1228
+ o.instance_variable_set(:@cache, {})
1229
+ if cols = cache_get(:_columns)
1230
+ o.send(:columns=, cols)
1231
+ end
1232
+ o.freeze
1122
1233
  end
1123
1234
  else
1124
1235
  # :nocov:
@@ -1151,7 +1262,7 @@ module Sequel
1151
1262
  #
1152
1263
  # You can also provide a method name and arguments to call to get the SQL:
1153
1264
  #
1154
- # DB[:items].with_sql(:insert_sql, :b=>1) # INSERT INTO items (b) VALUES (1)
1265
+ # DB[:items].with_sql(:insert_sql, b: 1) # INSERT INTO items (b) VALUES (1)
1155
1266
  #
1156
1267
  # Note that datasets that specify custom SQL using this method will generally
1157
1268
  # ignore future dataset methods that modify the SQL used, as specifying custom SQL
@@ -1225,18 +1336,22 @@ module Sequel
1225
1336
 
1226
1337
  private
1227
1338
 
1228
- # Load the extensions into the receiver, without checking if the receiver is frozen.
1229
- def _extension!(exts)
1230
- Sequel.extension(*exts)
1231
- exts.each do |ext|
1232
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
1233
- pr.call(self)
1234
- else
1235
- raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
1339
+ # :nocov:
1340
+ unless TRUE_FREEZE
1341
+ # Load the extensions into the receiver, without checking if the receiver is frozen.
1342
+ def _extension!(exts)
1343
+ Sequel.extension(*exts)
1344
+ exts.each do |ext|
1345
+ if pr = Sequel.synchronize{EXTENSIONS[ext]}
1346
+ pr.call(self)
1347
+ else
1348
+ raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
1349
+ end
1236
1350
  end
1351
+ self
1237
1352
  end
1238
- self
1239
1353
  end
1354
+ # :nocov:
1240
1355
 
1241
1356
  # If invert is true, invert the condition.
1242
1357
  def _invert_filter(cond, invert)
@@ -1247,6 +1362,18 @@ module Sequel
1247
1362
  end
1248
1363
  end
1249
1364
 
1365
+ # Append to the current MERGE WHEN clauses.
1366
+ # Mutates the hash to add the conditions, if a virtual row block is passed.
1367
+ def _merge_when(hash, &block)
1368
+ hash[:conditions] = Sequel.virtual_row(&block) if block
1369
+
1370
+ if merge_when = @opts[:merge_when]
1371
+ clone(:merge_when => (merge_when.dup << hash.freeze).freeze)
1372
+ else
1373
+ clone(:merge_when => [hash.freeze].freeze)
1374
+ end
1375
+ end
1376
+
1250
1377
  # Add the given filter condition. Arguments:
1251
1378
  # clause :: Symbol or which SQL clause to effect, should be :where or :having
1252
1379
  # cond :: The filter condition to add