sequel 5.83.0 → 5.106.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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/lib/sequel/adapters/ado/access.rb +2 -2
  4. data/lib/sequel/adapters/ado.rb +2 -2
  5. data/lib/sequel/adapters/amalgalite.rb +16 -1
  6. data/lib/sequel/adapters/ibmdb.rb +2 -1
  7. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/derby.rb +4 -2
  9. data/lib/sequel/adapters/jdbc/h2.rb +4 -4
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  11. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
  13. data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  15. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
  18. data/lib/sequel/adapters/jdbc.rb +29 -15
  19. data/lib/sequel/adapters/mysql.rb +1 -1
  20. data/lib/sequel/adapters/mysql2.rb +10 -3
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +16 -0
  23. data/lib/sequel/adapters/postgres.rb +2 -1
  24. data/lib/sequel/adapters/shared/access.rb +1 -0
  25. data/lib/sequel/adapters/shared/db2.rb +8 -3
  26. data/lib/sequel/adapters/shared/mssql.rb +9 -8
  27. data/lib/sequel/adapters/shared/mysql.rb +16 -11
  28. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  29. data/lib/sequel/adapters/shared/postgres.rb +438 -47
  30. data/lib/sequel/adapters/shared/sqlite.rb +9 -7
  31. data/lib/sequel/adapters/sqlite.rb +20 -2
  32. data/lib/sequel/adapters/tinytds.rb +2 -2
  33. data/lib/sequel/adapters/trilogy.rb +2 -3
  34. data/lib/sequel/ast_transformer.rb +2 -0
  35. data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
  36. data/lib/sequel/connection_pool/timed_queue.rb +33 -10
  37. data/lib/sequel/connection_pool.rb +8 -2
  38. data/lib/sequel/core.rb +31 -2
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/dataset_defaults.rb +3 -3
  41. data/lib/sequel/database/misc.rb +29 -11
  42. data/lib/sequel/database/query.rb +12 -12
  43. data/lib/sequel/database/schema_generator.rb +117 -16
  44. data/lib/sequel/database/schema_methods.rb +85 -30
  45. data/lib/sequel/dataset/actions.rb +83 -11
  46. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  47. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
  48. data/lib/sequel/dataset/prepared_statements.rb +76 -28
  49. data/lib/sequel/dataset/query.rb +26 -11
  50. data/lib/sequel/dataset/sql.rb +40 -17
  51. data/lib/sequel/extensions/async_thread_pool.rb +1 -1
  52. data/lib/sequel/extensions/caller_logging.rb +1 -3
  53. data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
  54. data/lib/sequel/extensions/connection_expiration.rb +44 -5
  55. data/lib/sequel/extensions/connection_validator.rb +15 -10
  56. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  57. data/lib/sequel/extensions/dataset_run.rb +41 -0
  58. data/lib/sequel/extensions/date_arithmetic.rb +6 -6
  59. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  60. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  61. data/lib/sequel/extensions/inflector.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
  63. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  64. data/lib/sequel/extensions/migration.rb +38 -21
  65. data/lib/sequel/extensions/null_dataset.rb +2 -2
  66. data/lib/sequel/extensions/pg_array_ops.rb +41 -0
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
  68. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  69. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
  70. data/lib/sequel/extensions/pg_enum.rb +4 -4
  71. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  73. data/lib/sequel/extensions/pg_json_ops.rb +674 -18
  74. data/lib/sequel/extensions/pg_multirange.rb +1 -1
  75. data/lib/sequel/extensions/pg_range.rb +14 -2
  76. data/lib/sequel/extensions/pg_row.rb +5 -3
  77. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  78. data/lib/sequel/extensions/provenance.rb +1 -3
  79. data/lib/sequel/extensions/query_blocker.rb +172 -0
  80. data/lib/sequel/extensions/schema_caching.rb +24 -9
  81. data/lib/sequel/extensions/schema_dumper.rb +17 -5
  82. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  83. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  84. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  85. data/lib/sequel/extensions/string_agg.rb +2 -2
  86. data/lib/sequel/extensions/to_dot.rb +5 -0
  87. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  88. data/lib/sequel/model/associations.rb +215 -15
  89. data/lib/sequel/model/base.rb +198 -35
  90. data/lib/sequel/model/plugins.rb +12 -1
  91. data/lib/sequel/model.rb +1 -0
  92. data/lib/sequel/plugins/accessed_columns.rb +4 -0
  93. data/lib/sequel/plugins/active_model.rb +5 -6
  94. data/lib/sequel/plugins/association_pks.rb +2 -0
  95. data/lib/sequel/plugins/auto_validations.rb +2 -0
  96. data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
  97. data/lib/sequel/plugins/columns_updated.rb +4 -0
  98. data/lib/sequel/plugins/composition.rb +3 -1
  99. data/lib/sequel/plugins/constraint_validations.rb +15 -10
  100. data/lib/sequel/plugins/dataset_associations.rb +20 -1
  101. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  102. data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
  103. data/lib/sequel/plugins/dirty.rb +9 -2
  104. data/lib/sequel/plugins/enum.rb +1 -1
  105. data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
  106. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  107. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  108. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  109. data/lib/sequel/plugins/instance_filters.rb +8 -1
  110. data/lib/sequel/plugins/instance_hooks.rb +4 -0
  111. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  112. data/lib/sequel/plugins/json_serializer.rb +6 -10
  113. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  114. data/lib/sequel/plugins/many_through_many.rb +21 -0
  115. data/lib/sequel/plugins/modification_detection.rb +2 -0
  116. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  117. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  118. data/lib/sequel/plugins/paged_operations.rb +5 -2
  119. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  120. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  121. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  122. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  123. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
  124. data/lib/sequel/plugins/rcte_tree.rb +6 -6
  125. data/lib/sequel/plugins/serialization.rb +23 -6
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
  127. data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
  128. data/lib/sequel/plugins/split_values.rb +12 -0
  129. data/lib/sequel/plugins/sql_comments.rb +7 -2
  130. data/lib/sequel/plugins/static_cache.rb +13 -0
  131. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  132. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  133. data/lib/sequel/plugins/subset_static_cache.rb +278 -0
  134. data/lib/sequel/plugins/table_select.rb +7 -0
  135. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
  136. data/lib/sequel/plugins/unused_associations.rb +11 -3
  137. data/lib/sequel/plugins/update_primary_key.rb +4 -0
  138. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  139. data/lib/sequel/plugins/validation_contexts.rb +4 -0
  140. data/lib/sequel/sql.rb +27 -12
  141. data/lib/sequel/version.rb +1 -1
  142. metadata +17 -240
  143. data/CHANGELOG +0 -1393
  144. data/README.rdoc +0 -936
  145. data/doc/advanced_associations.rdoc +0 -884
  146. data/doc/association_basics.rdoc +0 -1859
  147. data/doc/bin_sequel.rdoc +0 -146
  148. data/doc/cheat_sheet.rdoc +0 -255
  149. data/doc/code_order.rdoc +0 -104
  150. data/doc/core_extensions.rdoc +0 -405
  151. data/doc/dataset_basics.rdoc +0 -96
  152. data/doc/dataset_filtering.rdoc +0 -222
  153. data/doc/extensions.rdoc +0 -77
  154. data/doc/fork_safety.rdoc +0 -84
  155. data/doc/mass_assignment.rdoc +0 -98
  156. data/doc/migration.rdoc +0 -660
  157. data/doc/model_dataset_method_design.rdoc +0 -129
  158. data/doc/model_hooks.rdoc +0 -254
  159. data/doc/model_plugins.rdoc +0 -270
  160. data/doc/mssql_stored_procedures.rdoc +0 -43
  161. data/doc/object_model.rdoc +0 -563
  162. data/doc/opening_databases.rdoc +0 -439
  163. data/doc/postgresql.rdoc +0 -611
  164. data/doc/prepared_statements.rdoc +0 -144
  165. data/doc/querying.rdoc +0 -1070
  166. data/doc/reflection.rdoc +0 -120
  167. data/doc/release_notes/5.0.0.txt +0 -159
  168. data/doc/release_notes/5.1.0.txt +0 -31
  169. data/doc/release_notes/5.10.0.txt +0 -84
  170. data/doc/release_notes/5.11.0.txt +0 -83
  171. data/doc/release_notes/5.12.0.txt +0 -141
  172. data/doc/release_notes/5.13.0.txt +0 -27
  173. data/doc/release_notes/5.14.0.txt +0 -63
  174. data/doc/release_notes/5.15.0.txt +0 -39
  175. data/doc/release_notes/5.16.0.txt +0 -110
  176. data/doc/release_notes/5.17.0.txt +0 -31
  177. data/doc/release_notes/5.18.0.txt +0 -69
  178. data/doc/release_notes/5.19.0.txt +0 -28
  179. data/doc/release_notes/5.2.0.txt +0 -33
  180. data/doc/release_notes/5.20.0.txt +0 -89
  181. data/doc/release_notes/5.21.0.txt +0 -87
  182. data/doc/release_notes/5.22.0.txt +0 -48
  183. data/doc/release_notes/5.23.0.txt +0 -56
  184. data/doc/release_notes/5.24.0.txt +0 -56
  185. data/doc/release_notes/5.25.0.txt +0 -32
  186. data/doc/release_notes/5.26.0.txt +0 -35
  187. data/doc/release_notes/5.27.0.txt +0 -21
  188. data/doc/release_notes/5.28.0.txt +0 -16
  189. data/doc/release_notes/5.29.0.txt +0 -22
  190. data/doc/release_notes/5.3.0.txt +0 -121
  191. data/doc/release_notes/5.30.0.txt +0 -20
  192. data/doc/release_notes/5.31.0.txt +0 -148
  193. data/doc/release_notes/5.32.0.txt +0 -46
  194. data/doc/release_notes/5.33.0.txt +0 -24
  195. data/doc/release_notes/5.34.0.txt +0 -40
  196. data/doc/release_notes/5.35.0.txt +0 -56
  197. data/doc/release_notes/5.36.0.txt +0 -60
  198. data/doc/release_notes/5.37.0.txt +0 -30
  199. data/doc/release_notes/5.38.0.txt +0 -28
  200. data/doc/release_notes/5.39.0.txt +0 -19
  201. data/doc/release_notes/5.4.0.txt +0 -80
  202. data/doc/release_notes/5.40.0.txt +0 -40
  203. data/doc/release_notes/5.41.0.txt +0 -25
  204. data/doc/release_notes/5.42.0.txt +0 -136
  205. data/doc/release_notes/5.43.0.txt +0 -98
  206. data/doc/release_notes/5.44.0.txt +0 -32
  207. data/doc/release_notes/5.45.0.txt +0 -34
  208. data/doc/release_notes/5.46.0.txt +0 -87
  209. data/doc/release_notes/5.47.0.txt +0 -59
  210. data/doc/release_notes/5.48.0.txt +0 -14
  211. data/doc/release_notes/5.49.0.txt +0 -59
  212. data/doc/release_notes/5.5.0.txt +0 -61
  213. data/doc/release_notes/5.50.0.txt +0 -78
  214. data/doc/release_notes/5.51.0.txt +0 -47
  215. data/doc/release_notes/5.52.0.txt +0 -87
  216. data/doc/release_notes/5.53.0.txt +0 -23
  217. data/doc/release_notes/5.54.0.txt +0 -27
  218. data/doc/release_notes/5.55.0.txt +0 -21
  219. data/doc/release_notes/5.56.0.txt +0 -51
  220. data/doc/release_notes/5.57.0.txt +0 -23
  221. data/doc/release_notes/5.58.0.txt +0 -31
  222. data/doc/release_notes/5.59.0.txt +0 -73
  223. data/doc/release_notes/5.6.0.txt +0 -31
  224. data/doc/release_notes/5.60.0.txt +0 -22
  225. data/doc/release_notes/5.61.0.txt +0 -43
  226. data/doc/release_notes/5.62.0.txt +0 -132
  227. data/doc/release_notes/5.63.0.txt +0 -33
  228. data/doc/release_notes/5.64.0.txt +0 -50
  229. data/doc/release_notes/5.65.0.txt +0 -21
  230. data/doc/release_notes/5.66.0.txt +0 -24
  231. data/doc/release_notes/5.67.0.txt +0 -32
  232. data/doc/release_notes/5.68.0.txt +0 -61
  233. data/doc/release_notes/5.69.0.txt +0 -26
  234. data/doc/release_notes/5.7.0.txt +0 -108
  235. data/doc/release_notes/5.70.0.txt +0 -35
  236. data/doc/release_notes/5.71.0.txt +0 -21
  237. data/doc/release_notes/5.72.0.txt +0 -33
  238. data/doc/release_notes/5.73.0.txt +0 -66
  239. data/doc/release_notes/5.74.0.txt +0 -45
  240. data/doc/release_notes/5.75.0.txt +0 -35
  241. data/doc/release_notes/5.76.0.txt +0 -86
  242. data/doc/release_notes/5.77.0.txt +0 -63
  243. data/doc/release_notes/5.78.0.txt +0 -67
  244. data/doc/release_notes/5.79.0.txt +0 -28
  245. data/doc/release_notes/5.8.0.txt +0 -170
  246. data/doc/release_notes/5.80.0.txt +0 -40
  247. data/doc/release_notes/5.81.0.txt +0 -31
  248. data/doc/release_notes/5.82.0.txt +0 -61
  249. data/doc/release_notes/5.83.0.txt +0 -56
  250. data/doc/release_notes/5.9.0.txt +0 -99
  251. data/doc/schema_modification.rdoc +0 -679
  252. data/doc/security.rdoc +0 -443
  253. data/doc/sharding.rdoc +0 -286
  254. data/doc/sql.rdoc +0 -648
  255. data/doc/testing.rdoc +0 -204
  256. data/doc/thread_safety.rdoc +0 -15
  257. data/doc/transactions.rdoc +0 -250
  258. data/doc/validations.rdoc +0 -558
  259. data/doc/virtual_rows.rdoc +0 -265
@@ -170,7 +170,7 @@ module Sequel
170
170
  c[:ruby_default] = column_schema_to_ruby_default(c[:default], c[:type]) unless c.has_key?(:ruby_default)
171
171
  if c[:primary_key] && !auto_increment_set
172
172
  # If adapter didn't set it, assume that integer primary keys are auto incrementing
173
- c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/io)
173
+ c[:auto_increment] = primary_keys == 1 && !!(c[:db_type] =~ /int/i)
174
174
  end
175
175
  if !c[:max_length] && c[:type] == :string && (max_length = column_schema_max_length(c[:db_type]))
176
176
  c[:max_length] = max_length
@@ -269,7 +269,7 @@ module Sequel
269
269
  def column_schema_to_ruby_default(default, type)
270
270
  return default unless default.is_a?(String)
271
271
  if COLUMN_SCHEMA_DATETIME_TYPES.include?(type)
272
- if /now|today|CURRENT|getdate|\ADate\(\)\z/i.match(default)
272
+ if /now|today|CURRENT|getdate|\ADate\(\)\z/i =~ default
273
273
  if type == :date
274
274
  return Sequel::CURRENT_DATE
275
275
  else
@@ -390,25 +390,25 @@ module Sequel
390
390
  # such as :integer or :string.
391
391
  def schema_column_type(db_type)
392
392
  case db_type
393
- when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/io
393
+ when /\A(character( varying)?|n?(var)?char|n?text|string|clob)/i
394
394
  :string
395
- when /\A(int(eger)?|(big|small|tiny)int)/io
395
+ when /\A(int(eger)?|(big|small|tiny)int)/i
396
396
  :integer
397
- when /\Adate\z/io
397
+ when /\Adate\z/i
398
398
  :date
399
- when /\A((small)?datetime|timestamp(\(\d\))?( with(out)? time zone)?)\z/io
399
+ when /\A((small)?datetime(\(\d\))?|timestamp(\(\d\))?( with(out)? time zone)?)\z/i
400
400
  :datetime
401
- when /\Atime( with(out)? time zone)?\z/io
401
+ when /\Atime( with(out)? time zone)?\z/i
402
402
  :time
403
- when /\A(bool(ean)?)\z/io
403
+ when /\A(bool(ean)?)\z/i
404
404
  :boolean
405
- when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
405
+ when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/i
406
406
  :float
407
- when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/io
407
+ when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/i
408
408
  $1 && ['0', 'false'].include?($1) ? :integer : :decimal
409
- when /bytea|blob|image|(var)?binary/io
409
+ when /bytea|blob|image|(var)?binary/i
410
410
  :blob
411
- when /\Aenum/io
411
+ when /\Aenum/i
412
412
  :enum
413
413
  end
414
414
  end
@@ -3,6 +3,33 @@
3
3
  module Sequel
4
4
  # The Schema module holds the schema generators.
5
5
  module Schema
6
+ module ColumnOptionMerger
7
+ private
8
+
9
+ # Merge given options into the column's default options. For backwards compatibility,
10
+ # the options take priority, but in cases where the option value overrides the argument
11
+ # value, and the values are different, we warn as this is likely to be an error in the
12
+ # code.
13
+ def _merge_column_options(defaults, opts)
14
+ defaults.merge!(opts) do |k, defv, v|
15
+ unless defv == v
16
+ # :nocov:
17
+ if RUBY_VERSION >= "3.2"
18
+ # :nocov:
19
+ caller_loc = Thread.each_caller_location do |loc|
20
+ break loc unless loc.path == __FILE__
21
+ end
22
+ caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
23
+ end
24
+ warn("#{caller_loc}#{k.inspect} option value (#{v.inspect}) overrides argument value (#{defv.inspect})")
25
+ end
26
+
27
+ v
28
+ end
29
+ end
30
+ end
31
+ private_constant :ColumnOptionMerger
32
+
6
33
  # Schema::CreateTableGenerator is an internal class that the user is not expected
7
34
  # to instantiate directly. Instances are created by Database#create_table.
8
35
  # It is used to specify table creation parameters. It takes a Database
@@ -17,6 +44,8 @@ module Sequel
17
44
  # For more information on Sequel's support for schema modification, see
18
45
  # the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
19
46
  class CreateTableGenerator
47
+ include ColumnOptionMerger
48
+
20
49
  # Classes specifying generic types that Sequel will convert to database-specific types.
21
50
  GENERIC_TYPES=%w'String Integer Float Numeric BigDecimal Date DateTime Time File TrueClass FalseClass'.freeze
22
51
 
@@ -118,6 +147,11 @@ module Sequel
118
147
  # that this column references. Unnecessary if this column
119
148
  # references the primary key of the associated table, except if you are
120
149
  # using MySQL.
150
+ # :not_null :: Similar to setting <tt>null: false</tt>, but you can provide a hash value
151
+ # to specify options for the NOT NULL constraint on PostgreSQL 18+:
152
+ # :name :: The name of the NOT NULL constraint
153
+ # :no_inherit :: Set NO INHERIT on the constraint, so it will not propogate to
154
+ # child tables.
121
155
  # :null :: Mark the column as allowing NULL values (if true),
122
156
  # or not allowing NULL values (if false). The default is to allow NULL values.
123
157
  # :on_delete :: Specify the behavior of this column when being deleted
@@ -126,21 +160,34 @@ module Sequel
126
160
  # (:restrict, :cascade, :set_null, :set_default, :no_action).
127
161
  # :primary_key :: Make the column as a single primary key column. This should not
128
162
  # be used if you want a single autoincrementing primary key column
129
- # (use the primary_key method in that case).
130
- # :primary_key_constraint_name :: The name to give the primary key constraint
131
- # :primary_key_deferrable :: Similar to :deferrable, but for the primary key constraint
132
- # if :primary_key is used.
163
+ # (use the primary_key method in that case). Can be a hash to provide
164
+ # options for the constraint:
165
+ # :name :: The name to give the primary key constraint.
166
+ # :deferrable :: Sets whether the primary key constraint is deferrable.
167
+ # :include :: Include additional columns in the underlying index, to
168
+ # allow for index-only scans in more cases (PostgreSQL 11+).
169
+ # :primary_key_constraint_name :: Older option to name primary key constraint.
170
+ # :primary_key_deferrable :: Older option to set primary key constraint as deferrable.
133
171
  # :type :: Overrides the type given as the argument. Generally not used by column
134
172
  # itself, but can be passed as an option to other methods that call column.
135
173
  # :unique :: Mark the column as unique, generally has the same effect as
136
- # creating a unique index on the column.
137
- # :unique_constraint_name :: The name to give the unique key constraint
138
- # :unique_deferrable :: Similar to :deferrable, but for the unique constraint if :unique
139
- # is used.
174
+ # creating a unique index on the column. Can be a hash to provide options for
175
+ # the constraint:
176
+ # :name :: The name to give the unique constraint.
177
+ # :deferrable :: Sets whether the unique constraint is deferrable.
178
+ # :include :: Include additional columns in the underlying index, to
179
+ # allow for index-only scans in more cases (PostgreSQL 11+).
180
+ # :nulls_not_distinct :: Use NULLS NOT DISTINCT to setup a constraint where NULL
181
+ # entries are considered distinct (PostgreSQL 15+)
182
+ # :unique_constraint_name :: Older option to name unique constraint.
183
+ # :unique_deferrable :: Older option to set unique constraint as deferrable.
140
184
  #
141
185
  # PostgreSQL specific options:
142
186
  #
143
187
  # :identity :: Create an identity column.
188
+ # :virtual :: When using :generated_always_as, create a VIRTUAL column instead of a STORED
189
+ # column (PostgreSQL 18+). VIRTUAL is the default if not specified on
190
+ # PostgreSQL 18+, but for backwards compatibility, Sequel's default is STORED.
144
191
  #
145
192
  # MySQL specific options:
146
193
  #
@@ -155,13 +202,13 @@ module Sequel
155
202
  # :clustered :: When using :primary_key or :unique, marks the primary key or unique
156
203
  # constraint as CLUSTERED (if true), or NONCLUSTERED (if false).
157
204
  def column(name, type, opts = OPTS)
158
- columns << {:name => name, :type => type}.merge!(opts)
205
+ columns << _merge_column_options({:name => name, :type => type}, opts)
159
206
  if index_opts = opts[:index]
160
207
  index(name, index_opts.is_a?(Hash) ? index_opts : OPTS)
161
208
  end
162
209
  nil
163
210
  end
164
-
211
+
165
212
  # Adds a named CHECK constraint (or unnamed if name is nil),
166
213
  # with the given block or args. To provide options for the constraint, pass
167
214
  # a hash as the first argument.
@@ -178,6 +225,9 @@ module Sequel
178
225
  # :deferrable :: Whether the CHECK constraint should be marked DEFERRABLE.
179
226
  #
180
227
  # PostgreSQL specific options:
228
+ # :no_inherit :: Set NO INHERIT on the constraint, so it will not propogate to
229
+ # child tables.
230
+ # :not_enforced :: Whether the CHECK constraint should be marked NOT ENFORCED.
181
231
  # :not_valid :: Whether the CHECK constraint should be marked NOT VALID.
182
232
  def constraint(name, *args, &block)
183
233
  opts = name.is_a?(Hash) ? name : {:name=>name}
@@ -197,9 +247,21 @@ module Sequel
197
247
  #
198
248
  # :foreign_key_constraint_name :: The name to give the foreign key constraint
199
249
  #
250
+ # PostgreSQL specific options:
251
+ #
252
+ # :not_enforced :: Whether the foreign key constraint should be marked NOT ENFORCED.
253
+ #
200
254
  # If you want a foreign key constraint without adding a column (usually because it is a
201
- # composite foreign key), you can provide an array of columns as the first argument, and
202
- # you can provide the :name option to name the constraint:
255
+ # composite foreign key), you can provide an array of columns as the first argument.
256
+ # This changes the method to accept constraint options instead of column options.
257
+ # You can provide the :name option to name the constraint.
258
+ #
259
+ # PostgreSQL specific options:
260
+ #
261
+ # :not_enforced :: Whether the foreign key constraint should be marked NOT ENFORCED.
262
+ # :period :: Use PERIOD to setup a constraint where the final column is a range
263
+ # or multirange column, and the range or multirange is covered by
264
+ # existing ranges in the referenced table (which can be in separate rows).
203
265
  #
204
266
  # foreign_key([:artist_name, :artist_location], :artists, name: :artist_fk)
205
267
  # # ADD CONSTRAINT artist_fk FOREIGN KEY (artist_name, artist_location) REFERENCES artists
@@ -213,7 +275,7 @@ module Sequel
213
275
  opts.merge(:table=>table)
214
276
  end
215
277
  return composite_foreign_key(name, opts) if name.is_a?(Array)
216
- column(name, Integer, opts)
278
+ column(name, opts.fetch(:type, Integer), opts)
217
279
  end
218
280
 
219
281
  # Add a full text index on the given columns.
@@ -266,6 +328,7 @@ module Sequel
266
328
  # :opclass :: Set an opclass to use for all columns (per-column opclasses require
267
329
  # custom SQL).
268
330
  # :tablespace :: Specify tablespace for index.
331
+ # :only :: Create index only for given table, not for any child tables (PostgreSQL 11+)
269
332
  #
270
333
  # Microsoft SQL Server specific options:
271
334
  #
@@ -303,6 +366,13 @@ module Sequel
303
366
  # :keep_order :: For non-composite primary keys, respects the existing order of
304
367
  # columns, overriding the default behavior of making the primary
305
368
  # key the first column.
369
+ #
370
+ # PostgreSQL specific options:
371
+ #
372
+ # :include :: Include additional columns in the underlying index, to
373
+ # allow for index-only scans in more cases (PostgreSQL 11+).
374
+ # :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
375
+ # on the final column (PostgreSQL 18+, composite primary keys only).
306
376
  #
307
377
  # Examples:
308
378
  # primary_key(:id)
@@ -346,6 +416,15 @@ module Sequel
346
416
  #
347
417
  # Supports the same :deferrable option as #column. The :name option can be used
348
418
  # to name the constraint.
419
+ #
420
+ # PostgreSQL specific options:
421
+ #
422
+ # :include :: Include additional columns in the underlying index, to
423
+ # allow for index-only scans in more cases (PostgreSQL 11+).
424
+ # :nulls_not_distinct :: Use NULLS NOT DISTINCT to setup a constraint where NULL
425
+ # entries are considered distinct (PostgreSQL 15+)
426
+ # :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
427
+ # on the final column (PostgreSQL 18+, composite unique only).
349
428
  def unique(columns, opts = OPTS)
350
429
  constraints << {:type => :unique, :columns => Array(columns)}.merge!(opts)
351
430
  nil
@@ -379,6 +458,8 @@ module Sequel
379
458
  # For more information on Sequel's support for schema modification, see
380
459
  # the {"Schema Modification" guide}[link:files/doc/schema_modification_rdoc.html].
381
460
  class AlterTableGenerator
461
+ include ColumnOptionMerger
462
+
382
463
  # An array of operations to perform
383
464
  attr_reader :operations
384
465
 
@@ -404,7 +485,7 @@ module Sequel
404
485
  # :after :: The name of an existing column that the new column should be positioned after
405
486
  # :first :: Create this new column before all other existing columns
406
487
  def add_column(name, type, opts = OPTS)
407
- op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
488
+ op = _merge_column_options({:op => :add_column, :name => name, :type => type}, opts)
408
489
  index_opts = op.delete(:index)
409
490
  @operations << op
410
491
  add_index(name, index_opts.is_a?(Hash) ? index_opts : OPTS) if index_opts
@@ -430,6 +511,16 @@ module Sequel
430
511
  # add_unique_constraint(:name, name: :unique_name) # ADD CONSTRAINT unique_name UNIQUE (name)
431
512
  #
432
513
  # Supports the same :deferrable option as CreateTableGenerator#column.
514
+ #
515
+ # PostgreSQL specific options:
516
+ #
517
+ # :include :: Include additional columns in the underlying index, to
518
+ # allow for index-only scans in more cases (PostgreSQL 11+).
519
+ # :nulls_not_distinct :: Use NULLS NOT DISTINCT to setup a constraint where NULL
520
+ # entries are considered distinct (PostgreSQL 15+)
521
+ # :using_index :: Use the USING INDEX clause to specify an existing unique index
522
+ # :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
523
+ # on the final column (PostgreSQL 18+, composite unique constraints only).
433
524
  def add_unique_constraint(columns, opts = OPTS)
434
525
  @operations << {:op => :add_constraint, :type => :unique, :columns => Array(columns)}.merge!(opts)
435
526
  nil
@@ -459,7 +550,7 @@ module Sequel
459
550
  # sense when using an array of columns.
460
551
  def add_foreign_key(name, table, opts = OPTS)
461
552
  return add_composite_foreign_key(name, table, opts) if name.is_a?(Array)
462
- add_column(name, Integer, {:table=>table}.merge!(opts))
553
+ add_column(name, opts.fetch(:type, Integer), {:table=>table}.merge!(opts))
463
554
  end
464
555
 
465
556
  # Add a full text index on the given columns.
@@ -483,6 +574,14 @@ module Sequel
483
574
  #
484
575
  # add_primary_key(:id) # ADD COLUMN id serial PRIMARY KEY
485
576
  # add_primary_key([:artist_id, :name]) # ADD PRIMARY KEY (artist_id, name)
577
+ #
578
+ # PostgreSQL specific options:
579
+ #
580
+ # :include :: Include additional columns in the underlying index, to
581
+ # allow for index-only scans in more cases (PostgreSQL 11+).
582
+ # :using_index :: Use the USING INDEX clause to specify an existing unique index
583
+ # :without_overlaps :: Use WITHOUT OVERLAPS clause to specify an exclusion constraint
584
+ # on the final column (PostgreSQL 18+, composite primary keys only).
486
585
  def add_primary_key(name, opts = OPTS)
487
586
  return add_composite_primary_key(name, opts) if name.is_a?(Array)
488
587
  opts = @db.serial_primary_key_options.merge(opts)
@@ -589,9 +688,11 @@ module Sequel
589
688
  nil
590
689
  end
591
690
 
592
- # Modify the type of one of the table's column.
691
+ # Modify the type of one of the table's column. The given options are considered when
692
+ # determining the new type.
593
693
  #
594
694
  # set_column_type(:artist_name, 'char(10)') # ALTER COLUMN artist_name TYPE char(10)
695
+ # set_column_type(:artist_name, String, size: 10) # ALTER COLUMN artist_name TYPE varchar(10)
595
696
  #
596
697
  # PostgreSQL specific options:
597
698
  #
@@ -191,6 +191,8 @@ module Sequel
191
191
  # The +any+ type is treated like a SQLite column in a non-strict table,
192
192
  # allowing any type of data to be stored. This option is supported on
193
193
  # SQLite 3.37.0+.
194
+ # :using :: Create a VIRTUAL table with the given USING clause. The value should be
195
+ # a string, as it is used directly in the SQL query.
194
196
  # :without_rowid :: Create a WITHOUT ROWID table. Every row in SQLite has a special
195
197
  # 'rowid' column, that uniquely identifies that row within the table.
196
198
  # If this option is used, the 'rowid' column is omitted, which can
@@ -484,7 +486,9 @@ module Sequel
484
486
  end
485
487
 
486
488
  def alter_table_set_column_type_sql(table, op)
487
- "ALTER COLUMN #{quote_identifier(op[:name])} TYPE #{type_literal(op)}"
489
+ sql = "ALTER COLUMN #{quote_identifier(op[:name])} TYPE #{type_literal(op)}".dup
490
+ column_definition_collate_sql(sql, op)
491
+ sql
488
492
  end
489
493
 
490
494
  def alter_table_set_column_default_sql(table, op)
@@ -590,7 +594,12 @@ module Sequel
590
594
 
591
595
  # Add null/not null SQL fragment to column creation SQL.
592
596
  def column_definition_null_sql(sql, column)
593
- null = column.fetch(:null, column[:allow_null])
597
+ null = if column[:not_null]
598
+ false
599
+ else
600
+ column.fetch(:null, column[:allow_null])
601
+ end
602
+
594
603
  if null.nil? && !can_add_primary_key_constraint_on_nullable_columns? && column[:primary_key]
595
604
  null = false
596
605
  end
@@ -603,36 +612,71 @@ module Sequel
603
612
  end
604
613
  end
605
614
 
606
- # Add primary key SQL fragment to column creation SQL.
615
+ # Add primary key SQL fragment to column creation SQL if column is a primary key.
607
616
  def column_definition_primary_key_sql(sql, column)
608
- if column[:primary_key]
609
- if name = column[:primary_key_constraint_name]
610
- sql << " CONSTRAINT #{quote_identifier(name)}"
611
- end
612
- sql << " " << primary_key_constraint_sql_fragment(column)
613
- constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
614
- end
617
+ column_definition_add_primary_key_sql(sql, column) if column[:primary_key]
618
+ end
619
+
620
+ # Add primary key SQL fragment to column creation SQL (column should be a primary key).
621
+ def column_definition_add_primary_key_sql(sql, column)
622
+ constraint = column_definition_constraint_hash(column, :primary_key)
623
+ append_named_constraint_prefix_sql(sql, constraint[:name])
624
+ column_definition_append_primary_key_sql(sql, constraint)
625
+ constraint_deferrable_sql_append(sql, constraint[:deferrable])
626
+ end
627
+
628
+ def column_definition_append_primary_key_sql(sql, constraint)
629
+ sql << " " << primary_key_constraint_sql_fragment(constraint)
615
630
  end
616
631
 
617
- # Add foreign key reference SQL fragment to column creation SQL.
632
+ # Add foreign key reference SQL fragment to column creation SQL if column is a foreign key.
618
633
  def column_definition_references_sql(sql, column)
619
- if column[:table]
620
- if name = column[:foreign_key_constraint_name]
621
- sql << " CONSTRAINT #{quote_identifier(name)}"
622
- end
623
- sql << column_references_column_constraint_sql(column)
624
- end
634
+ column_definition_add_references_sql(sql, column) if column[:table]
635
+ end
636
+
637
+ # Add foreign key reference SQL fragment to column creation SQL (column should be a foreign key).
638
+ def column_definition_add_references_sql(sql, column)
639
+ append_named_constraint_prefix_sql(sql, column[:foreign_key_constraint_name])
640
+ sql << column_references_column_constraint_sql(column)
625
641
  end
626
642
 
627
- # Add unique constraint SQL fragment to column creation SQL.
643
+ # Add unique constraint SQL fragment to column creation SQL if column has a unique constraint.
628
644
  def column_definition_unique_sql(sql, column)
629
- if column[:unique]
630
- if name = column[:unique_constraint_name]
631
- sql << " CONSTRAINT #{quote_identifier(name)}"
632
- end
633
- sql << ' ' << unique_constraint_sql_fragment(column)
634
- constraint_deferrable_sql_append(sql, column[:unique_deferrable])
645
+ column_definition_add_unique_sql(sql, column) if column[:unique]
646
+ end
647
+
648
+ # Add unique constraint SQL fragment to column creation SQL (column should have unique constraint).
649
+ def column_definition_add_unique_sql(sql, column)
650
+ constraint = column_definition_constraint_hash(column, :unique)
651
+ append_named_constraint_prefix_sql(sql, constraint[:name])
652
+ column_definition_append_unique_sql(sql, constraint)
653
+ constraint_deferrable_sql_append(sql, constraint[:deferrable])
654
+ end
655
+
656
+ def column_definition_append_unique_sql(sql, constraint)
657
+ sql << ' ' << unique_constraint_sql_fragment(constraint)
658
+ end
659
+
660
+ # Add the name of the constraint to the column creation SQL.
661
+ def append_named_constraint_prefix_sql(sql, name)
662
+ sql << " CONSTRAINT #{quote_identifier(name)}" if name
663
+ end
664
+
665
+ # Return a hash of constraint options for the primary key or column
666
+ # unique constraint.
667
+ def column_definition_constraint_hash(column, prefix)
668
+ constraint = column[prefix]
669
+ constraint = constraint.is_a?(Hash) ? constraint.dup : {}
670
+
671
+ if name = column[:"#{prefix}_constraint_name"]
672
+ constraint[:name] = name
673
+ end
674
+
675
+ if column.has_key?(:"#{prefix}_deferrable")
676
+ constraint[:deferrable] = column[:"#{prefix}_deferrable"]
635
677
  end
678
+
679
+ constraint
636
680
  end
637
681
 
638
682
  # SQL for all given columns, used inside a CREATE TABLE block.
@@ -649,12 +693,16 @@ module Sequel
649
693
  def column_references_sql(column)
650
694
  sql = String.new
651
695
  sql << " REFERENCES #{quote_schema_table(column[:table])}"
652
- sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(', ')})" if column[:key]
696
+ column_references_append_key_sql(sql, column) if column[:key]
653
697
  sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
654
698
  sql << " ON UPDATE #{on_update_clause(column[:on_update])}" if column[:on_update]
655
699
  constraint_deferrable_sql_append(sql, column[:deferrable])
656
700
  sql
657
701
  end
702
+
703
+ def column_references_append_key_sql(sql, column)
704
+ sql << "(#{Array(column[:key]).map{|x| quote_identifier(x)}.join(', ')})"
705
+ end
658
706
 
659
707
  # SQL fragment for table foreign key references (table constraints)
660
708
  def column_references_table_constraint_sql(constraint)
@@ -675,7 +723,7 @@ module Sequel
675
723
  check = constraint[:check]
676
724
  check = check.first if check.is_a?(Array) && check.length == 1
677
725
  check = filter_expr(check)
678
- check = "(#{check})" unless check[0..0] == '(' && check[-1..-1] == ')'
726
+ check = "(#{check})" unless check.start_with?('(') && check.end_with?(')')
679
727
  sql << "CHECK #{check}"
680
728
  when :primary_key
681
729
  sql << "#{primary_key_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
@@ -731,17 +779,24 @@ module Sequel
731
779
  def create_table_sql(name, generator, options)
732
780
  unless supports_named_column_constraints?
733
781
  # Split column constraints into table constraints if they have a name
782
+ fk_opt_keys = [:key, :on_delete, :on_update, :deferrable]
734
783
  generator.columns.each do |c|
735
784
  if (constraint_name = c.delete(:foreign_key_constraint_name)) && (table = c.delete(:table))
736
785
  opts = {}
737
786
  opts[:name] = constraint_name
738
- [:key, :on_delete, :on_update, :deferrable].each{|k| opts[k] = c[k]}
787
+ fk_opt_keys.each{|k| opts[k] = c[k]}
739
788
  generator.foreign_key([c[:name]], table, opts)
740
789
  end
741
- if (constraint_name = c.delete(:unique_constraint_name)) && c.delete(:unique)
790
+
791
+ if c[:unique].is_a?(Hash) && c[:unique][:name]
792
+ generator.unique(c[:name], c.delete(:unique))
793
+ elsif (constraint_name = c.delete(:unique_constraint_name)) && c.delete(:unique)
742
794
  generator.unique(c[:name], :name=>constraint_name)
743
795
  end
744
- if (constraint_name = c.delete(:primary_key_constraint_name)) && c.delete(:primary_key)
796
+
797
+ if c[:primary_key].is_a?(Hash) && c[:primary_key][:name]
798
+ generator.primary_key([c[:name]], c.delete(:primary_key))
799
+ elsif (constraint_name = c.delete(:primary_key_constraint_name)) && c.delete(:primary_key)
745
800
  generator.primary_key([c[:name]], :name=>constraint_name)
746
801
  end
747
802
  end
@@ -764,7 +819,7 @@ module Sequel
764
819
  # SELECT sql statement.
765
820
  def create_table_as(name, sql, options)
766
821
  sql = sql.sql if sql.is_a?(Sequel::Dataset)
767
- run(create_table_as_sql(name, sql, options))
822
+ run(create_table_as_sql(name, sql, options).freeze)
768
823
  end
769
824
 
770
825
  # SQL statement for creating a table from the result of a SELECT statement.
@@ -11,11 +11,12 @@ module Sequel
11
11
 
12
12
  # Action methods defined by Sequel that execute code on the database.
13
13
  ACTION_METHODS = (<<-METHS).split.map(&:to_sym).freeze
14
- << [] all as_hash avg count columns columns! delete each
14
+ << [] all as_hash as_set avg count columns columns! delete each
15
15
  empty? fetch_rows first first! get import insert last
16
- map max min multi_insert paged_each select_hash select_hash_groups select_map select_order_map
17
- single_record single_record! single_value single_value! sum to_hash to_hash_groups truncate update
18
- where_all where_each where_single_value
16
+ map max min multi_insert paged_each select_hash select_hash_groups
17
+ select_map select_order_map select_set single_record single_record!
18
+ single_value single_value! sum to_hash to_hash_groups
19
+ truncate update where_all where_each where_single_value
19
20
  METHS
20
21
 
21
22
  # The clone options to use when retrieving columns for a dataset.
@@ -51,6 +52,26 @@ module Sequel
51
52
  _all(block){|a| each{|r| a << r}}
52
53
  end
53
54
 
55
+ # Returns sets for column values for each record in the dataset.
56
+ #
57
+ # DB[:table].as_set(:id) # SELECT * FROM table
58
+ # # => Set[1, 2, 3, ...]
59
+ #
60
+ # You can also provide an array of column names, in which case the elements
61
+ # of the returned set are arrays (not sets):
62
+ #
63
+ # DB[:table].as_set([:id, :name]) # SELECT * FROM table
64
+ # # => Set[[1, 'A'], [2, 'B'], [3, 'C'], ...]
65
+ def as_set(column)
66
+ return naked.as_set(column) if row_proc
67
+
68
+ if column.is_a?(Array)
69
+ to_set{|r| r.values_at(*column)}
70
+ else
71
+ to_set{|r| r[column]}
72
+ end
73
+ end
74
+
54
75
  # Returns the average value for the given column/expression.
55
76
  # Uses a virtual row block if no argument is given.
56
77
  #
@@ -217,7 +238,7 @@ module Sequel
217
238
  case args.length
218
239
  when 0
219
240
  unless block
220
- return single_record
241
+ return(@opts[:sql] ? single_record! : single_record)
221
242
  end
222
243
  when 1
223
244
  arg = args[0]
@@ -282,6 +303,12 @@ module Sequel
282
303
  #
283
304
  # DB[:table].get{[sum(id).as(sum), name]} # SELECT sum(id) AS sum, name FROM table LIMIT 1
284
305
  # # => [6, 'foo']
306
+ #
307
+ # If called on a dataset with raw SQL, returns the
308
+ # first value in the dataset without changing the selection or setting a limit:
309
+ #
310
+ # DB["SELECT id FROM table"].get # SELECT id FROM table
311
+ # # => 3
285
312
  def get(column=(no_arg=true; nil), &block)
286
313
  ds = naked
287
314
  if block
@@ -289,6 +316,8 @@ module Sequel
289
316
  ds = ds.select(&block)
290
317
  column = ds.opts[:select]
291
318
  column = nil if column.is_a?(Array) && column.length < 2
319
+ elsif no_arg && opts[:sql]
320
+ return ds.single_value!
292
321
  else
293
322
  case column
294
323
  when Array
@@ -343,6 +372,15 @@ module Sequel
343
372
  # DB[:table].import([:x, :y], DB[:table2].select(:a, :b))
344
373
  # # INSERT INTO table (x, y) SELECT a, b FROM table2
345
374
  #
375
+ # The return value of this method is undefined and should not be used,
376
+ # except in two cases:
377
+ #
378
+ # * When the <tt>return: :primary_key</tt> option is used.
379
+ # * On PostgreSQL, when the dataset uses RETURNING. In this case, if
380
+ # a single value is returned per row, the return value is an array
381
+ # of those values. If multiple values are returned per row, the
382
+ # return value is an array of hashes.
383
+ #
346
384
  # Options:
347
385
  # :commit_every :: Open a new transaction for every given number of
348
386
  # records. For example, if you provide a value of 50,
@@ -719,10 +757,7 @@ module Sequel
719
757
  end
720
758
 
721
759
  # Selects the column given (either as an argument or as a block), and
722
- # returns an array of all values of that column in the dataset. If you
723
- # give a block argument that returns an array with multiple entries,
724
- # the contents of the resulting array are undefined. Raises an Error
725
- # if called with both an argument and a block.
760
+ # returns an array of all values of that column in the dataset.
726
761
  #
727
762
  # DB[:table].select_map(:id) # SELECT id FROM table
728
763
  # # => [3, 5, 8, 1, ...]
@@ -760,6 +795,34 @@ module Sequel
760
795
  _select_map(column, true, &block)
761
796
  end
762
797
 
798
+ # Selects the column given (either as an argument or as a block), and
799
+ # returns a set of all values of that column in the dataset.
800
+ #
801
+ # DB[:table].select_set(:id) # SELECT id FROM table
802
+ # # => Set[3, 5, 8, 1, ...]
803
+ #
804
+ # DB[:table].select_set{id * 2} # SELECT (id * 2) FROM table
805
+ # # => Set[6, 10, 16, 2, ...]
806
+ #
807
+ # You can also provide an array of column names, which returns a set
808
+ # with array elements (not set elements):
809
+ #
810
+ # DB[:table].select_map([:id, :name]) # SELECT id, name FROM table
811
+ # # => Set[[1, 'A'], [2, 'B'], [3, 'C'], ...]
812
+ #
813
+ # If you provide an array of expressions, you must be sure that each entry
814
+ # in the array has an alias that Sequel can determine.
815
+ def select_set(column=nil, &block)
816
+ ds = ungraphed.naked
817
+ columns = Array(column)
818
+ virtual_row_columns(columns, block)
819
+ if column.is_a?(Array) || (columns.length > 1)
820
+ ds.select(*columns)._select_set_multiple(hash_key_symbols(columns))
821
+ else
822
+ ds.select(auto_alias_expression(columns.first))._select_set_single
823
+ end
824
+ end
825
+
763
826
  # Limits the dataset to one record, and returns the first record in the dataset,
764
827
  # or nil if the dataset has no records. Users should probably use +first+ instead of
765
828
  # this method. Example:
@@ -1084,6 +1147,17 @@ module Sequel
1084
1147
  map{|r| r[k||=r.keys.first]}
1085
1148
  end
1086
1149
 
1150
+ # Return a set of arrays of values given by the symbols in ret_cols.
1151
+ def _select_set_multiple(ret_cols)
1152
+ to_set{|r| r.values_at(*ret_cols)}
1153
+ end
1154
+
1155
+ # Returns a set of the first value in each row.
1156
+ def _select_set_single
1157
+ k = nil
1158
+ to_set{|r| r[k||=r.keys.first]}
1159
+ end
1160
+
1087
1161
  # A dataset for returning single values from the current dataset.
1088
1162
  def single_value_ds
1089
1163
  clone(:limit=>1).ungraphed.naked
@@ -1248,8 +1322,6 @@ module Sequel
1248
1322
  # receiver's current order. This yields the row and the array of order expressions
1249
1323
  # to the block, which should return an array of values to use.
1250
1324
  def ignore_values_preceding(row)
1251
- @opts[:order].map{|v| v.is_a?(SQL::OrderedExpression) ? v.expression : v}
1252
-
1253
1325
  order_exprs = @opts[:order].map do |v|
1254
1326
  if v.is_a?(SQL::OrderedExpression)
1255
1327
  descending = v.descending