sequel 5.39.0 → 5.72.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 (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