sequel 5.60.1 → 5.81.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 (166) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +280 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +27 -24
  5. data/doc/advanced_associations.rdoc +16 -14
  6. data/doc/association_basics.rdoc +53 -17
  7. data/doc/cheat_sheet.rdoc +3 -3
  8. data/doc/code_order.rdoc +5 -3
  9. data/doc/dataset_basics.rdoc +1 -1
  10. data/doc/mass_assignment.rdoc +1 -1
  11. data/doc/migration.rdoc +15 -0
  12. data/doc/model_hooks.rdoc +1 -1
  13. data/doc/object_model.rdoc +8 -8
  14. data/doc/opening_databases.rdoc +18 -7
  15. data/doc/postgresql.rdoc +8 -8
  16. data/doc/querying.rdoc +7 -2
  17. data/doc/release_notes/5.61.0.txt +43 -0
  18. data/doc/release_notes/5.62.0.txt +132 -0
  19. data/doc/release_notes/5.63.0.txt +33 -0
  20. data/doc/release_notes/5.64.0.txt +50 -0
  21. data/doc/release_notes/5.65.0.txt +21 -0
  22. data/doc/release_notes/5.66.0.txt +24 -0
  23. data/doc/release_notes/5.67.0.txt +32 -0
  24. data/doc/release_notes/5.68.0.txt +61 -0
  25. data/doc/release_notes/5.69.0.txt +26 -0
  26. data/doc/release_notes/5.70.0.txt +35 -0
  27. data/doc/release_notes/5.71.0.txt +21 -0
  28. data/doc/release_notes/5.72.0.txt +33 -0
  29. data/doc/release_notes/5.73.0.txt +66 -0
  30. data/doc/release_notes/5.74.0.txt +45 -0
  31. data/doc/release_notes/5.75.0.txt +35 -0
  32. data/doc/release_notes/5.76.0.txt +86 -0
  33. data/doc/release_notes/5.77.0.txt +63 -0
  34. data/doc/release_notes/5.78.0.txt +67 -0
  35. data/doc/release_notes/5.79.0.txt +28 -0
  36. data/doc/release_notes/5.80.0.txt +40 -0
  37. data/doc/release_notes/5.81.0.txt +31 -0
  38. data/doc/schema_modification.rdoc +3 -3
  39. data/doc/security.rdoc +9 -9
  40. data/doc/sharding.rdoc +3 -1
  41. data/doc/sql.rdoc +14 -14
  42. data/doc/testing.rdoc +16 -12
  43. data/doc/transactions.rdoc +6 -6
  44. data/doc/virtual_rows.rdoc +1 -1
  45. data/lib/sequel/adapters/ibmdb.rb +1 -1
  46. data/lib/sequel/adapters/jdbc/h2.rb +3 -0
  47. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
  48. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
  49. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  50. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  51. data/lib/sequel/adapters/jdbc.rb +10 -6
  52. data/lib/sequel/adapters/mysql.rb +19 -7
  53. data/lib/sequel/adapters/mysql2.rb +2 -2
  54. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  55. data/lib/sequel/adapters/oracle.rb +1 -0
  56. data/lib/sequel/adapters/postgres.rb +10 -5
  57. data/lib/sequel/adapters/shared/access.rb +9 -1
  58. data/lib/sequel/adapters/shared/db2.rb +12 -0
  59. data/lib/sequel/adapters/shared/mssql.rb +71 -9
  60. data/lib/sequel/adapters/shared/mysql.rb +82 -3
  61. data/lib/sequel/adapters/shared/oracle.rb +17 -7
  62. data/lib/sequel/adapters/shared/postgres.rb +469 -160
  63. data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
  64. data/lib/sequel/adapters/shared/sqlite.rb +39 -3
  65. data/lib/sequel/adapters/sqlite.rb +42 -3
  66. data/lib/sequel/adapters/trilogy.rb +117 -0
  67. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  68. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  69. data/lib/sequel/connection_pool/threaded.rb +14 -8
  70. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  71. data/lib/sequel/connection_pool.rb +57 -31
  72. data/lib/sequel/database/connecting.rb +25 -1
  73. data/lib/sequel/database/dataset.rb +16 -6
  74. data/lib/sequel/database/misc.rb +65 -14
  75. data/lib/sequel/database/query.rb +72 -1
  76. data/lib/sequel/database/schema_generator.rb +1 -1
  77. data/lib/sequel/database/schema_methods.rb +12 -5
  78. data/lib/sequel/database/transactions.rb +6 -0
  79. data/lib/sequel/dataset/actions.rb +60 -13
  80. data/lib/sequel/dataset/dataset_module.rb +1 -1
  81. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  82. data/lib/sequel/dataset/features.rb +15 -1
  83. data/lib/sequel/dataset/graph.rb +1 -0
  84. data/lib/sequel/dataset/misc.rb +12 -2
  85. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  86. data/lib/sequel/dataset/query.rb +119 -45
  87. data/lib/sequel/dataset/sql.rb +58 -36
  88. data/lib/sequel/dataset.rb +4 -0
  89. data/lib/sequel/exceptions.rb +5 -0
  90. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  91. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  92. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  93. data/lib/sequel/extensions/async_thread_pool.rb +21 -13
  94. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  95. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  96. data/lib/sequel/extensions/caller_logging.rb +4 -1
  97. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  98. data/lib/sequel/extensions/connection_validator.rb +16 -11
  99. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  100. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  101. data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
  102. data/lib/sequel/extensions/index_caching.rb +5 -1
  103. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  104. data/lib/sequel/extensions/migration.rb +65 -15
  105. data/lib/sequel/extensions/named_timezones.rb +22 -6
  106. data/lib/sequel/extensions/pg_array.rb +33 -4
  107. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  108. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  109. data/lib/sequel/extensions/pg_enum.rb +1 -2
  110. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  111. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  112. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  113. data/lib/sequel/extensions/pg_inet.rb +10 -11
  114. data/lib/sequel/extensions/pg_interval.rb +10 -11
  115. data/lib/sequel/extensions/pg_json.rb +10 -10
  116. data/lib/sequel/extensions/pg_json_ops.rb +3 -3
  117. data/lib/sequel/extensions/pg_multirange.rb +6 -11
  118. data/lib/sequel/extensions/pg_range.rb +9 -14
  119. data/lib/sequel/extensions/pg_row.rb +20 -19
  120. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  121. data/lib/sequel/extensions/provenance.rb +110 -0
  122. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  123. data/lib/sequel/extensions/schema_caching.rb +1 -1
  124. data/lib/sequel/extensions/schema_dumper.rb +32 -9
  125. data/lib/sequel/extensions/server_block.rb +2 -1
  126. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  127. data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
  128. data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
  129. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  130. data/lib/sequel/model/associations.rb +38 -11
  131. data/lib/sequel/model/base.rb +32 -18
  132. data/lib/sequel/model/dataset_module.rb +3 -0
  133. data/lib/sequel/model/exceptions.rb +15 -3
  134. data/lib/sequel/plugins/auto_validations.rb +53 -15
  135. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  136. data/lib/sequel/plugins/column_encryption.rb +28 -7
  137. data/lib/sequel/plugins/composition.rb +2 -2
  138. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  139. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  140. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  141. data/lib/sequel/plugins/dirty.rb +1 -1
  142. data/lib/sequel/plugins/finder.rb +4 -2
  143. data/lib/sequel/plugins/input_transformer.rb +1 -1
  144. data/lib/sequel/plugins/list.rb +5 -2
  145. data/lib/sequel/plugins/many_through_many.rb +1 -1
  146. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  147. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  148. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  149. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  150. data/lib/sequel/plugins/paged_operations.rb +181 -0
  151. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  152. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  153. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  154. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  155. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  156. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  157. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  158. data/lib/sequel/plugins/sql_comments.rb +1 -1
  159. data/lib/sequel/plugins/static_cache.rb +38 -0
  160. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  161. data/lib/sequel/plugins/tactical_eager_loading.rb +14 -14
  162. data/lib/sequel/plugins/validate_associated.rb +22 -12
  163. data/lib/sequel/plugins/validation_helpers.rb +29 -2
  164. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  165. data/lib/sequel/version.rb +2 -2
  166. metadata +79 -6
@@ -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.
@@ -39,18 +43,14 @@ module Sequel
39
43
  add_graph_aliases distinct except exclude exclude_having
40
44
  filter for_update from from_self graph grep group group_and_count group_append group_by having intersect invert
41
45
  limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
42
- reverse reverse_order select select_all select_append select_group select_more server
46
+ reverse reverse_order select select_all select_append select_group select_more select_prepend server
43
47
  set_graph_aliases unfiltered ungraphed ungrouped union
44
48
  unlimited unordered where with with_recursive with_sql
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,13 +122,14 @@ 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+.
123
129
  def distinct(*args, &block)
124
130
  virtual_row_columns(args, block)
125
131
  if args.empty?
132
+ return self if opts[:distinct] == EMPTY_ARRAY
126
133
  cached_dataset(:_distinct_ds){clone(:distinct => EMPTY_ARRAY)}
127
134
  else
128
135
  raise(InvalidOperation, "DISTINCT ON not supported") unless supports_distinct_on?
@@ -195,11 +202,15 @@ module Sequel
195
202
  if TRUE_FREEZE
196
203
  # Return a clone of the dataset loaded with the given dataset extensions.
197
204
  # 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
205
+ # specific support for Dataset objects, an error will be raised.
206
+ def extension(*exts)
207
+ Sequel.extension(*exts)
208
+ mods = exts.map{|ext| Sequel.synchronize{EXTENSION_MODULES[ext]}}
209
+ if mods.all?
210
+ with_extend(*mods)
211
+ else
212
+ with_extend(DeprecatedSingletonClassMethods).extension(*exts)
213
+ end
203
214
  end
204
215
  else
205
216
  # :nocov:
@@ -220,6 +231,7 @@ module Sequel
220
231
  #
221
232
  # DB[:table].for_update # SELECT * FROM table FOR UPDATE
222
233
  def for_update
234
+ return self if opts[:lock] == :update
223
235
  cached_dataset(:_for_update_ds){lock_style(:update)}
224
236
  end
225
237
 
@@ -631,6 +643,7 @@ module Sequel
631
643
  # DB.from(:a, DB[:b].where(Sequel[:a][:c]=>Sequel[:b][:d]).lateral)
632
644
  # # SELECT * FROM a, LATERAL (SELECT * FROM b WHERE (a.c = b.d))
633
645
  def lateral
646
+ return self if opts[:lateral]
634
647
  cached_dataset(:_lateral_ds){clone(:lateral=>true)}
635
648
  end
636
649
 
@@ -734,6 +747,7 @@ module Sequel
734
747
  # ds.all # => [{2=>:id}]
735
748
  # ds.naked.all # => [{:id=>2}]
736
749
  def naked
750
+ return self unless opts[:row_proc]
737
751
  cached_dataset(:_naked_ds){with_row_proc(nil)}
738
752
  end
739
753
 
@@ -743,6 +757,7 @@ module Sequel
743
757
  # DB[:items].for_update.nowait
744
758
  # # SELECT * FROM items FOR UPDATE NOWAIT
745
759
  def nowait
760
+ return self if opts[:nowait]
746
761
  cached_dataset(:_nowait_ds) do
747
762
  raise(Error, 'This dataset does not support raises errors instead of waiting for locked rows') unless supports_nowait?
748
763
  clone(:nowait=>true)
@@ -787,7 +802,7 @@ module Sequel
787
802
  # DB[:items].order(Sequel.lit('a + b')) # SELECT * FROM items ORDER BY a + b
788
803
  # DB[:items].order(Sequel[:a] + :b) # SELECT * FROM items ORDER BY (a + b)
789
804
  # DB[:items].order(Sequel.desc(:name)) # SELECT * FROM items ORDER BY name DESC
790
- # DB[:items].order(Sequel.asc(:name, :nulls=>:last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
805
+ # DB[:items].order(Sequel.asc(:name, nulls: :last)) # SELECT * FROM items ORDER BY name ASC NULLS LAST
791
806
  # DB[:items].order{sum(name).desc} # SELECT * FROM items ORDER BY sum(name) DESC
792
807
  # DB[:items].order(nil) # SELECT * FROM items
793
808
  def order(*columns, &block)
@@ -857,17 +872,18 @@ module Sequel
857
872
  # DB[:items].returning(nil) # RETURNING NULL
858
873
  # DB[:items].returning(:id, :name) # RETURNING id, name
859
874
  #
860
- # DB[:items].returning.insert(:a=>1) do |hash|
875
+ # DB[:items].returning.insert(a: 1) do |hash|
861
876
  # # hash for each row inserted, with values for all columns
862
877
  # end
863
- # DB[:items].returning.update(:a=>1) do |hash|
878
+ # DB[:items].returning.update(a: 1) do |hash|
864
879
  # # hash for each row updated, with values for all columns
865
880
  # end
866
- # DB[:items].returning.delete(:a=>1) do |hash|
881
+ # DB[:items].returning.delete(a: 1) do |hash|
867
882
  # # hash for each row deleted, with values for all columns
868
883
  # end
869
884
  def returning(*values)
870
885
  if values.empty?
886
+ return self if opts[:returning] == EMPTY_ARRAY
871
887
  cached_dataset(:_returning_ds) do
872
888
  raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
873
889
  clone(:returning=>EMPTY_ARRAY)
@@ -920,6 +936,7 @@ module Sequel
920
936
  # DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
921
937
  def select_all(*tables)
922
938
  if tables.empty?
939
+ return self unless opts[:select]
923
940
  cached_dataset(:_select_all_ds){clone(:select => nil)}
924
941
  else
925
942
  select(*tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze)
@@ -934,14 +951,8 @@ module Sequel
934
951
  # DB[:items].select(:a).select_append(:b) # SELECT a, b FROM items
935
952
  # DB[:items].select_append(:b) # SELECT *, b FROM items
936
953
  def select_append(*columns, &block)
937
- cur_sel = @opts[:select]
938
- if !cur_sel || cur_sel.empty?
939
- unless supports_select_all_and_column?
940
- return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_append(*columns, &block)
941
- end
942
- cur_sel = [WILDCARD]
943
- end
944
- select(*(cur_sel + columns), &block)
954
+ virtual_row_columns(columns, block)
955
+ select(*(_current_select(true) + columns))
945
956
  end
946
957
 
947
958
  # Set both the select and group clauses with the given +columns+.
@@ -963,6 +974,18 @@ module Sequel
963
974
  select_append(*columns, &block)
964
975
  end
965
976
 
977
+ # Returns a copy of the dataset with the given columns added
978
+ # to the existing selected columns. If no columns are currently selected,
979
+ # it will select the columns given in addition to *.
980
+ #
981
+ # DB[:items].select(:a).select(:b) # SELECT b FROM items
982
+ # DB[:items].select(:a).select_prepend(:b) # SELECT b, a FROM items
983
+ # DB[:items].select_prepend(:b) # SELECT b, * FROM items
984
+ def select_prepend(*columns, &block)
985
+ virtual_row_columns(columns, block)
986
+ select(*(columns + _current_select(false)))
987
+ end
988
+
966
989
  # Set the server for this dataset to use. Used to pick a specific database
967
990
  # shard to run a query against, or to override the default (where SELECT uses
968
991
  # :read_only database and all other queries use the :default database). This
@@ -989,6 +1012,7 @@ module Sequel
989
1012
 
990
1013
  # Specify that the check for limits/offsets when updating/deleting be skipped for the dataset.
991
1014
  def skip_limit_check
1015
+ return self if opts[:skip_limit_check]
992
1016
  cached_dataset(:_skip_limit_check_ds) do
993
1017
  clone(:skip_limit_check=>true)
994
1018
  end
@@ -996,6 +1020,7 @@ module Sequel
996
1020
 
997
1021
  # Skip locked rows when returning results from this dataset.
998
1022
  def skip_locked
1023
+ return self if opts[:skip_locked]
999
1024
  cached_dataset(:_skip_locked_ds) do
1000
1025
  raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
1001
1026
  clone(:skip_locked=>true)
@@ -1007,6 +1032,7 @@ module Sequel
1007
1032
  # DB[:items].group(:a).having(a: 1).where(:b).unfiltered
1008
1033
  # # SELECT * FROM items GROUP BY a
1009
1034
  def unfiltered
1035
+ return self unless opts[:where] || opts[:having]
1010
1036
  cached_dataset(:_unfiltered_ds){clone(:where => nil, :having => nil)}
1011
1037
  end
1012
1038
 
@@ -1015,6 +1041,7 @@ module Sequel
1015
1041
  # DB[:items].group(:a).having(a: 1).where(:b).ungrouped
1016
1042
  # # SELECT * FROM items WHERE b
1017
1043
  def ungrouped
1044
+ return self unless opts[:group] || opts[:having]
1018
1045
  cached_dataset(:_ungrouped_ds){clone(:group => nil, :having => nil)}
1019
1046
  end
1020
1047
 
@@ -1042,6 +1069,7 @@ module Sequel
1042
1069
  #
1043
1070
  # DB[:items].limit(10, 20).unlimited # SELECT * FROM items
1044
1071
  def unlimited
1072
+ return self unless opts[:limit] || opts[:offset]
1045
1073
  cached_dataset(:_unlimited_ds){clone(:limit=>nil, :offset=>nil)}
1046
1074
  end
1047
1075
 
@@ -1049,6 +1077,7 @@ module Sequel
1049
1077
  #
1050
1078
  # DB[:items].order(:a).unordered # SELECT * FROM items
1051
1079
  def unordered
1080
+ return self unless opts[:order]
1052
1081
  cached_dataset(:_unordered_ds){clone(:order=>nil)}
1053
1082
  end
1054
1083
 
@@ -1102,7 +1131,7 @@ module Sequel
1102
1131
  # referenced in window functions. See Sequel::SQL::Window for a list of
1103
1132
  # options that can be passed in. Example:
1104
1133
  #
1105
- # DB[:items].window(:w, :partition=>:c1, :order=>:c2)
1134
+ # DB[:items].window(:w, partition: :c1, order: :c2)
1106
1135
  # # SELECT * FROM items WINDOW w AS (PARTITION BY c1 ORDER BY c2)
1107
1136
  def window(name, opts)
1108
1137
  clone(:window=>((@opts[:window]||EMPTY_ARRAY) + [[name, SQL::Window.new(opts)].freeze]).freeze)
@@ -1163,7 +1192,7 @@ module Sequel
1163
1192
  # DB[:t].with_recursive(:t,
1164
1193
  # DB[:i1].select(:id, :parent_id).where(parent_id: nil),
1165
1194
  # DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
1166
- # :args=>[:id, :parent_id])
1195
+ # args: [:id, :parent_id])
1167
1196
  #
1168
1197
  # # WITH RECURSIVE t(id, parent_id) AS (
1169
1198
  # # SELECT id, parent_id FROM i1 WHERE (parent_id IS NULL)
@@ -1199,16 +1228,27 @@ module Sequel
1199
1228
  end
1200
1229
 
1201
1230
  if TRUE_FREEZE
1202
- # Return a clone of the dataset extended with the given modules.
1231
+ # Create a subclass of the receiver's class, and include the given modules
1232
+ # into it. If a block is provided, a DatasetModule is created using the block and
1233
+ # is included into the subclass. Create an instance of the subclass using the
1234
+ # same db and opts, so that the returned dataset operates similarly to a clone
1235
+ # extended with the given modules. This approach is used to avoid singleton
1236
+ # classes, which significantly improves performance.
1237
+ #
1203
1238
  # Note that like Object#extend, when multiple modules are provided
1204
- # as arguments the cloned dataset is extended with the modules in reverse
1205
- # order. If a block is provided, a DatasetModule is created using the block and
1206
- # the clone is extended with that module after any modules given as arguments.
1239
+ # as arguments the subclass includes the modules in reverse order.
1207
1240
  def with_extend(*mods, &block)
1208
- c = _clone(:freeze=>false)
1209
- c.extend(*mods) unless mods.empty?
1210
- c.extend(DatasetModule.new(&block)) if block
1211
- c.freeze
1241
+ c = Class.new(self.class)
1242
+ c.include(*mods) unless mods.empty?
1243
+ c.include(DatasetModule.new(&block)) if block
1244
+ o = c.freeze.allocate
1245
+ o.instance_variable_set(:@db, @db)
1246
+ o.instance_variable_set(:@opts, @opts)
1247
+ o.instance_variable_set(:@cache, {})
1248
+ if cols = cache_get(:_columns)
1249
+ o.send(:columns=, cols)
1250
+ end
1251
+ o.freeze
1212
1252
  end
1213
1253
  else
1214
1254
  # :nocov:
@@ -1241,7 +1281,7 @@ module Sequel
1241
1281
  #
1242
1282
  # You can also provide a method name and arguments to call to get the SQL:
1243
1283
  #
1244
- # DB[:items].with_sql(:insert_sql, :b=>1) # INSERT INTO items (b) VALUES (1)
1284
+ # DB[:items].with_sql(:insert_sql, b: 1) # INSERT INTO items (b) VALUES (1)
1245
1285
  #
1246
1286
  # Note that datasets that specify custom SQL using this method will generally
1247
1287
  # ignore future dataset methods that modify the SQL used, as specifying custom SQL
@@ -1315,17 +1355,51 @@ module Sequel
1315
1355
 
1316
1356
  private
1317
1357
 
1318
- # Load the extensions into the receiver, without checking if the receiver is frozen.
1319
- def _extension!(exts)
1320
- Sequel.extension(*exts)
1321
- exts.each do |ext|
1322
- if pr = Sequel.synchronize{EXTENSIONS[ext]}
1323
- pr.call(self)
1358
+ # :nocov:
1359
+ unless TRUE_FREEZE
1360
+ # Load the extensions into the receiver, without checking if the receiver is frozen.
1361
+ def _extension!(exts)
1362
+ Sequel.extension(*exts)
1363
+ exts.each do |ext|
1364
+ if pr = Sequel.synchronize{EXTENSIONS[ext]}
1365
+ pr.call(self)
1366
+ else
1367
+ raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
1368
+ end
1369
+ end
1370
+ self
1371
+ end
1372
+ end
1373
+ # :nocov:
1374
+
1375
+ # A frozen array for the currently selected columns.
1376
+ def _current_select(allow_plain_wildcard)
1377
+ cur_sel = @opts[:select]
1378
+
1379
+ if !cur_sel || cur_sel.empty?
1380
+ cur_sel = if allow_plain_wildcard && supports_select_all_and_column?
1381
+ [WILDCARD].freeze
1324
1382
  else
1325
- raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
1383
+ _current_select_column_all
1326
1384
  end
1385
+ elsif !allow_plain_wildcard && cur_sel.include?(WILDCARD)
1386
+ cur_sel = cur_sel.dup
1387
+ index = cur_sel.index(WILDCARD)
1388
+ cur_sel.delete(WILDCARD)
1389
+ _current_select_column_all.each_with_index do |ca, i|
1390
+ cur_sel.insert(index+i, ca)
1391
+ end
1392
+ cur_sel.freeze
1327
1393
  end
1328
- self
1394
+
1395
+ cur_sel
1396
+ end
1397
+
1398
+ # An array of SQL::ColumnAll objects for all FROM and JOIN tables. Used for select_append
1399
+ # and select_prepend.
1400
+ def _current_select_column_all
1401
+ tables = Array(@opts[:from]) + Array(@opts[:join])
1402
+ tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze
1329
1403
  end
1330
1404
 
1331
1405
  # If invert is true, invert the condition.
@@ -82,7 +82,7 @@ module Sequel
82
82
  when DateTime
83
83
  literal_datetime_append(sql, v)
84
84
  when Date
85
- sql << literal_date(v)
85
+ literal_date_append(sql, v)
86
86
  when Dataset
87
87
  literal_dataset_append(sql, v)
88
88
  else
@@ -115,6 +115,33 @@ module Sequel
115
115
  sql
116
116
  end
117
117
 
118
+ # Literalize a date or time value, as a SQL string value with no
119
+ # typecasting. If +raw+ is true, remove the surrounding single
120
+ # quotes. This is designed for usage by bound argument code that
121
+ # can work even if the auto_cast_date_and_time extension is
122
+ # used (either manually or implicitly in the related adapter).
123
+ def literal_date_or_time(dt, raw=false)
124
+ value = case dt
125
+ when SQLTime
126
+ literal_sqltime(dt)
127
+ when Time
128
+ literal_time(dt)
129
+ when DateTime
130
+ literal_datetime(dt)
131
+ when Date
132
+ literal_date(dt)
133
+ else
134
+ raise TypeError, "unsupported type: #{dt.inspect}"
135
+ end
136
+
137
+ if raw
138
+ value.sub!(/\A'/, '')
139
+ value.sub!(/'\z/, '')
140
+ end
141
+
142
+ value
143
+ end
144
+
118
145
  # Returns an array of insert statements for inserting multiple records.
119
146
  # This method is used by +multi_insert+ to format insert statements and
120
147
  # expects a keys array and and an array of value arrays.
@@ -977,7 +1004,12 @@ module Sequel
977
1004
  # order if not. Also removes the row_proc, which isn't needed
978
1005
  # for aggregate calculations.
979
1006
  def aggregate_dataset
980
- (options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered).naked
1007
+ (aggreate_dataset_use_from_self? ? from_self : unordered).naked
1008
+ end
1009
+
1010
+ # Whether to use from_self for an aggregate dataset.
1011
+ def aggreate_dataset_use_from_self?
1012
+ options_overlap(COUNT_FROM_SELF_OPTS)
981
1013
  end
982
1014
 
983
1015
  # Append aliasing expression to SQL string.
@@ -1099,9 +1131,14 @@ module Sequel
1099
1131
  :"t#{number}"
1100
1132
  end
1101
1133
 
1102
- # The strftime format to use when literalizing the time.
1134
+ # The strftime format to use when literalizing time (Sequel::SQLTime) values.
1135
+ def default_time_format
1136
+ "'%H:%M:%S.%6N'"
1137
+ end
1138
+
1139
+ # The strftime format to use when literalizing timestamp (Time/DateTime) values.
1103
1140
  def default_timestamp_format
1104
- requires_sql_standard_datetimes? ? "TIMESTAMP '%Y-%m-%d %H:%M:%S%N%z'" : "'%Y-%m-%d %H:%M:%S%N%z'"
1141
+ "'%Y-%m-%d %H:%M:%S.%6N'"
1105
1142
  end
1106
1143
 
1107
1144
  def delete_delete_sql(sql)
@@ -1164,43 +1201,23 @@ module Sequel
1164
1201
  {1 => ((op == :IN) ? 0 : 1)}
1165
1202
  end
1166
1203
 
1167
- # Format the timestamp based on the default_timestamp_format, with a couple
1168
- # of modifiers. First, allow %N to be used for fractions seconds (if the
1169
- # database supports them), and override %z to always use a numeric offset
1170
- # of hours and minutes.
1204
+ # Format the timestamp based on the default_timestamp_format.
1171
1205
  def format_timestamp(v)
1172
- v2 = db.from_application_timestamp(v)
1173
- fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
1174
- if m == '%N'
1175
- # Ruby 1.9 supports %N in timestamp formats, but Sequel has supported %N
1176
- # for longer in a different way, where the . is already appended and only 6
1177
- # decimal places are used by default.
1178
- format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(1000000) : v.usec) if supports_timestamp_usecs?
1179
- else
1180
- if supports_timestamp_timezones?
1181
- # Would like to just use %z format, but it doesn't appear to work on Windows
1182
- # Instead, the offset fragment is constructed manually
1183
- minutes = (v2.is_a?(DateTime) ? v2.offset * 1440 : v2.utc_offset/60).to_i
1184
- format_timestamp_offset(*minutes.divmod(60))
1185
- end
1186
- end
1187
- end
1188
- v2.strftime(fmt)
1206
+ db.from_application_timestamp(v).strftime(default_timestamp_format)
1189
1207
  end
1190
1208
 
1191
- # Return the SQL timestamp fragment to use for the timezone offset.
1192
- def format_timestamp_offset(hour, minute)
1193
- sprintf("%+03i%02i", hour, minute)
1194
- end
1209
+ # :nocov:
1195
1210
 
1196
1211
  # Return the SQL timestamp fragment to use for the fractional time part.
1197
1212
  # Should start with the decimal point. Uses 6 decimal places by default.
1198
1213
  def format_timestamp_usec(usec, ts=timestamp_precision)
1214
+ # SEQUEL6: Remove
1199
1215
  unless ts == 6
1200
1216
  usec = usec/(10 ** (6 - ts))
1201
1217
  end
1202
1218
  sprintf(".%0#{ts}d", usec)
1203
1219
  end
1220
+ # :nocov:
1204
1221
 
1205
1222
  # Append literalization of identifier to SQL string, considering regular strings
1206
1223
  # as SQL identifiers instead of SQL strings.
@@ -1342,11 +1359,12 @@ module Sequel
1342
1359
 
1343
1360
  # SQL fragment for Date, using the ISO8601 format.
1344
1361
  def literal_date(v)
1345
- if requires_sql_standard_datetimes?
1346
- v.strftime("DATE '%Y-%m-%d'")
1347
- else
1348
- v.strftime("'%Y-%m-%d'")
1349
- end
1362
+ v.strftime("'%Y-%m-%d'")
1363
+ end
1364
+
1365
+ # Append literalization of date to SQL string.
1366
+ def literal_date_append(sql, v)
1367
+ sql << literal_date(v)
1350
1368
  end
1351
1369
 
1352
1370
  # SQL fragment for DateTime
@@ -1409,7 +1427,7 @@ module Sequel
1409
1427
 
1410
1428
  # SQL fragment for Sequel::SQLTime, containing just the time part
1411
1429
  def literal_sqltime(v)
1412
- v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec, sqltime_precision) if supports_timestamp_usecs?}'")
1430
+ v.strftime(default_time_format)
1413
1431
  end
1414
1432
 
1415
1433
  # Append literalization of Sequel::SQLTime to SQL string.
@@ -1725,7 +1743,7 @@ module Sequel
1725
1743
  # Append literalization of the subselect to SQL string.
1726
1744
  def subselect_sql_append(sql, ds)
1727
1745
  sds = subselect_sql_dataset(sql, ds)
1728
- sds.sql
1746
+ subselect_sql_append_sql(sql, sds)
1729
1747
  unless sds.send(:cache_sql?)
1730
1748
  # If subquery dataset does not allow caching SQL,
1731
1749
  # then this dataset should not allow caching SQL.
@@ -1737,6 +1755,10 @@ module Sequel
1737
1755
  ds.clone(:append_sql=>sql)
1738
1756
  end
1739
1757
 
1758
+ def subselect_sql_append_sql(sql, ds)
1759
+ ds.sql
1760
+ end
1761
+
1740
1762
  # The number of decimal digits of precision to use in timestamps.
1741
1763
  def timestamp_precision
1742
1764
  supports_timestamp_usecs? ? 6 : 0
@@ -53,4 +53,8 @@ module Sequel
53
53
  require_relative "dataset/sql"
54
54
  require_relative "dataset/placeholder_literalizer"
55
55
  require_relative "dataset/dataset_module"
56
+
57
+ # :nocov:
58
+ require_relative "dataset/deprecated_singleton_class_methods" if Dataset::TRUE_FREEZE
59
+ # :nocov:
56
60
  end
@@ -18,6 +18,11 @@ module Sequel
18
18
  end
19
19
  end
20
20
  end
21
+
22
+ (
23
+ # Error raised when there is a failed attempt to acquire an advisory lock.
24
+ AdvisoryLockError = Class.new(Error)
25
+ ).name
21
26
 
22
27
  (
23
28
  # Error raised when the adapter requested doesn't exist or can't be loaded.
@@ -23,18 +23,6 @@ module Sequel
23
23
  super
24
24
  end
25
25
  end
26
-
27
- private
28
-
29
- # Handle Sequel::Model instances in bound variable arrays.
30
- def bound_variable_array(arg)
31
- case arg
32
- when Sequel::Model
33
- "\"(#{arg.values.values_at(*arg.columns).map{|v| bound_variable_array(v)}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
34
- else
35
- super
36
- end
37
- end
38
26
  end
39
27
  end
40
28
  end
@@ -39,7 +39,7 @@ module Sequel
39
39
 
40
40
  # Hash of the maximum size of the value for each column
41
41
  def self.column_sizes(records, columns) # :nodoc:
42
- sizes = Hash.new {0}
42
+ sizes = Hash.new(0)
43
43
  columns.each do |c|
44
44
  sizes[c] = c.to_s.size
45
45
  end
@@ -32,8 +32,8 @@
32
32
  module Sequel
33
33
  module AnyNotEmpty
34
34
  # If a block is not given, return whether the dataset is not empty.
35
- def any?
36
- if defined?(yield)
35
+ def any?(*a)
36
+ if !a.empty? || defined?(yield)
37
37
  super
38
38
  else
39
39
  !empty?
@@ -5,9 +5,9 @@
5
5
  # code
6
6
  #
7
7
  # DB.extension :async_thread_pool
8
- # foos = DB[:foos].async.where{:name=>'A'..'M'}.all
8
+ # foos = DB[:foos].async.where(name: 'A'..'M').all
9
9
  # bar_names = DB[:bar].async.select_order_map(:name)
10
- # baz_1 = DB[:bazes].async.first(:id=>1)
10
+ # baz_1 = DB[:bazes].async.first(id: 1)
11
11
  #
12
12
  # All 3 queries will be run in separate threads. +foos+, +bar_names+
13
13
  # and +baz_1+ will be proxy objects. Calling a method on the proxy
@@ -15,9 +15,9 @@
15
15
  # of calling that method on the result of the query method. For example,
16
16
  # if you run:
17
17
  #
18
- # foos = DB[:foos].async.where{:name=>'A'..'M'}.all
18
+ # foos = DB[:foos].async.where(name: 'A'..'M').all
19
19
  # bar_names = DB[:bars].async.select_order_map(:name)
20
- # baz_1 = DB[:bazes].async.first(:id=>1)
20
+ # baz_1 = DB[:bazes].async.first(id: 1)
21
21
  # sleep(1)
22
22
  # foos.size
23
23
  # bar_names.first
@@ -26,9 +26,9 @@
26
26
  # These three queries will generally be run concurrently in separate
27
27
  # threads. If you instead run:
28
28
  #
29
- # DB[:foos].async.where{:name=>'A'..'M'}.all.size
29
+ # DB[:foos].async.where(name: 'A'..'M').all.size
30
30
  # DB[:bars].async.select_order_map(:name).first
31
- # DB[:bazes].async.first(:id=>1).name
31
+ # DB[:bazes].async.first(id: 1).name
32
32
  #
33
33
  # Then will run each query sequentially, since you need the result of
34
34
  # one query before running the next query. The queries will still be
@@ -37,11 +37,11 @@
37
37
  # What is run in the separate thread is the entire method call that
38
38
  # returns results. So with the original example:
39
39
  #
40
- # foos = DB[:foos].async.where{:name=>'A'..'M'}.all
40
+ # foos = DB[:foos].async.where(name: 'A'..'M').all
41
41
  # bar_names = DB[:bars].async.select_order_map(:name)
42
- # baz_1 = DB[:bazes].async.first(:id=>1)
42
+ # baz_1 = DB[:bazes].async.first(id: 1)
43
43
  #
44
- # The +all+, <tt>select_order_map(:name)</tt>, and <tt>first(:id=>1)</tt>
44
+ # The +all+, <tt>select_order_map(:name)</tt>, and <tt>first(id: 1)</tt>
45
45
  # calls are run in separate threads. If a block is passed to a method
46
46
  # such as +all+ or +each+, the block is also run in that thread. If you
47
47
  # have code such as:
@@ -156,10 +156,10 @@
156
156
  # so that the query will run in the current thread instead of waiting
157
157
  # for an async thread to become available. With the following code:
158
158
  #
159
- # foos = DB[:foos].async.where{:name=>'A'..'M'}.all
159
+ # foos = DB[:foos].async.where(name: 'A'..'M').all
160
160
  # bar_names = DB[:bar].async.select_order_map(:name)
161
161
  # if foos.length > 4
162
- # baz_1 = DB[:bazes].async.first(:id=>1)
162
+ # baz_1 = DB[:bazes].async.first(id: 1)
163
163
  # end
164
164
  #
165
165
  # Whether you need the +baz_1+ variable depends on the value of foos.
@@ -176,6 +176,13 @@
176
176
  # +:preempt_async_thread+ Database option before loading the
177
177
  # async_thread_pool extension.
178
178
  #
179
+ # Note that the async_thread_pool extension creates the thread pool
180
+ # when it is loaded into the Database. If you fork after loading
181
+ # the extension, the extension will not work, as fork does not
182
+ # copy the thread pools. If you are using a forking webserver
183
+ # (or any other system that forks worker processes), load this
184
+ # extension in each child process, do not load it before forking.
185
+ #
179
186
  # Related module: Sequel::Database::AsyncThreadPool::DatasetMethods
180
187
 
181
188
 
@@ -338,8 +345,9 @@ module Sequel
338
345
  module DatabaseMethods
339
346
  def self.extended(db)
340
347
  db.instance_exec do
341
- unless pool.pool_type == :threaded || pool.pool_type == :sharded_threaded
342
- raise Error, "can only load async_thread_pool extension if using threaded or sharded_threaded connection pool"
348
+ case pool.pool_type
349
+ when :single, :sharded_single
350
+ raise Error, "cannot load async_thread_pool extension if using single or sharded_single connection pool"
343
351
  end
344
352
 
345
353
  num_async_threads = opts[:num_async_threads] ? typecast_value_integer(opts[:num_async_threads]) : (Integer(opts[:max_connections] || 4))