sequel 5.39.0 → 5.64.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +318 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +57 -25
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +13 -13
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/migration.rdoc +12 -6
  10. data/doc/model_hooks.rdoc +1 -1
  11. data/doc/object_model.rdoc +8 -8
  12. data/doc/opening_databases.rdoc +18 -11
  13. data/doc/postgresql.rdoc +16 -8
  14. data/doc/querying.rdoc +5 -3
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/release_notes/5.41.0.txt +25 -0
  17. data/doc/release_notes/5.42.0.txt +136 -0
  18. data/doc/release_notes/5.43.0.txt +98 -0
  19. data/doc/release_notes/5.44.0.txt +32 -0
  20. data/doc/release_notes/5.45.0.txt +34 -0
  21. data/doc/release_notes/5.46.0.txt +87 -0
  22. data/doc/release_notes/5.47.0.txt +59 -0
  23. data/doc/release_notes/5.48.0.txt +14 -0
  24. data/doc/release_notes/5.49.0.txt +59 -0
  25. data/doc/release_notes/5.50.0.txt +78 -0
  26. data/doc/release_notes/5.51.0.txt +47 -0
  27. data/doc/release_notes/5.52.0.txt +87 -0
  28. data/doc/release_notes/5.53.0.txt +23 -0
  29. data/doc/release_notes/5.54.0.txt +27 -0
  30. data/doc/release_notes/5.55.0.txt +21 -0
  31. data/doc/release_notes/5.56.0.txt +51 -0
  32. data/doc/release_notes/5.57.0.txt +23 -0
  33. data/doc/release_notes/5.58.0.txt +31 -0
  34. data/doc/release_notes/5.59.0.txt +73 -0
  35. data/doc/release_notes/5.60.0.txt +22 -0
  36. data/doc/release_notes/5.61.0.txt +43 -0
  37. data/doc/release_notes/5.62.0.txt +132 -0
  38. data/doc/release_notes/5.63.0.txt +33 -0
  39. data/doc/release_notes/5.64.0.txt +50 -0
  40. data/doc/schema_modification.rdoc +1 -1
  41. data/doc/security.rdoc +9 -9
  42. data/doc/sql.rdoc +27 -15
  43. data/doc/testing.rdoc +22 -11
  44. data/doc/transactions.rdoc +6 -6
  45. data/doc/virtual_rows.rdoc +2 -2
  46. data/lib/sequel/adapters/ado/access.rb +1 -1
  47. data/lib/sequel/adapters/ado.rb +17 -17
  48. data/lib/sequel/adapters/amalgalite.rb +3 -5
  49. data/lib/sequel/adapters/ibmdb.rb +2 -2
  50. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  51. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  52. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  53. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  54. data/lib/sequel/adapters/jdbc.rb +16 -18
  55. data/lib/sequel/adapters/mysql.rb +80 -67
  56. data/lib/sequel/adapters/mysql2.rb +54 -49
  57. data/lib/sequel/adapters/odbc.rb +6 -2
  58. data/lib/sequel/adapters/oracle.rb +4 -3
  59. data/lib/sequel/adapters/postgres.rb +83 -40
  60. data/lib/sequel/adapters/shared/access.rb +11 -1
  61. data/lib/sequel/adapters/shared/db2.rb +30 -0
  62. data/lib/sequel/adapters/shared/mssql.rb +58 -7
  63. data/lib/sequel/adapters/shared/mysql.rb +40 -2
  64. data/lib/sequel/adapters/shared/oracle.rb +76 -0
  65. data/lib/sequel/adapters/shared/postgres.rb +418 -174
  66. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  67. data/lib/sequel/adapters/shared/sqlite.rb +103 -11
  68. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  69. data/lib/sequel/adapters/sqlite.rb +60 -18
  70. data/lib/sequel/adapters/tinytds.rb +1 -1
  71. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  72. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  73. data/lib/sequel/ast_transformer.rb +6 -0
  74. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  75. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  76. data/lib/sequel/connection_pool/single.rb +6 -8
  77. data/lib/sequel/connection_pool/threaded.rb +8 -8
  78. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  79. data/lib/sequel/connection_pool.rb +47 -30
  80. data/lib/sequel/core.rb +28 -18
  81. data/lib/sequel/database/connecting.rb +26 -2
  82. data/lib/sequel/database/misc.rb +69 -14
  83. data/lib/sequel/database/query.rb +73 -2
  84. data/lib/sequel/database/schema_generator.rb +45 -52
  85. data/lib/sequel/database/schema_methods.rb +17 -1
  86. data/lib/sequel/dataset/actions.rb +107 -13
  87. data/lib/sequel/dataset/features.rb +20 -0
  88. data/lib/sequel/dataset/misc.rb +1 -1
  89. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  90. data/lib/sequel/dataset/query.rb +118 -16
  91. data/lib/sequel/dataset/sql.rb +177 -47
  92. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  93. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  94. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  95. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  96. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  97. data/lib/sequel/extensions/blank.rb +8 -0
  98. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  99. data/lib/sequel/extensions/core_refinements.rb +36 -11
  100. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  101. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  102. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  103. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  104. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  105. data/lib/sequel/extensions/inflector.rb +9 -1
  106. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  107. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  108. data/lib/sequel/extensions/migration.rb +7 -2
  109. data/lib/sequel/extensions/named_timezones.rb +26 -6
  110. data/lib/sequel/extensions/pagination.rb +1 -1
  111. data/lib/sequel/extensions/pg_array.rb +23 -3
  112. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  113. data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
  114. data/lib/sequel/extensions/pg_enum.rb +1 -1
  115. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  116. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  117. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  118. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  119. data/lib/sequel/extensions/pg_inet.rb +10 -11
  120. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  121. data/lib/sequel/extensions/pg_interval.rb +45 -19
  122. data/lib/sequel/extensions/pg_json.rb +13 -15
  123. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  124. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  125. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  126. data/lib/sequel/extensions/pg_range.rb +10 -23
  127. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  128. data/lib/sequel/extensions/pg_row.rb +19 -13
  129. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  130. data/lib/sequel/extensions/query.rb +2 -0
  131. data/lib/sequel/extensions/s.rb +2 -1
  132. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  133. data/lib/sequel/extensions/server_block.rb +8 -12
  134. data/lib/sequel/extensions/sql_comments.rb +110 -3
  135. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  136. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  137. data/lib/sequel/extensions/string_agg.rb +1 -1
  138. data/lib/sequel/extensions/string_date_time.rb +19 -23
  139. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  140. data/lib/sequel/model/associations.rb +330 -96
  141. data/lib/sequel/model/base.rb +51 -27
  142. data/lib/sequel/model/errors.rb +10 -1
  143. data/lib/sequel/model/inflections.rb +1 -1
  144. data/lib/sequel/model/plugins.rb +5 -0
  145. data/lib/sequel/plugins/association_proxies.rb +2 -0
  146. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  147. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  148. data/lib/sequel/plugins/auto_validations.rb +87 -15
  149. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  150. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  151. data/lib/sequel/plugins/column_encryption.rb +728 -0
  152. data/lib/sequel/plugins/composition.rb +10 -4
  153. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  154. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  155. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  156. data/lib/sequel/plugins/dirty.rb +1 -1
  157. data/lib/sequel/plugins/enum.rb +124 -0
  158. data/lib/sequel/plugins/finder.rb +3 -1
  159. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  160. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  161. data/lib/sequel/plugins/json_serializer.rb +39 -24
  162. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  163. data/lib/sequel/plugins/list.rb +3 -1
  164. data/lib/sequel/plugins/many_through_many.rb +109 -10
  165. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  166. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  167. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  168. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  169. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  170. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  171. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  172. data/lib/sequel/plugins/serialization.rb +9 -3
  173. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  174. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  175. data/lib/sequel/plugins/sql_comments.rb +189 -0
  176. data/lib/sequel/plugins/static_cache.rb +1 -1
  177. data/lib/sequel/plugins/subclasses.rb +28 -11
  178. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  179. data/lib/sequel/plugins/timestamps.rb +1 -1
  180. data/lib/sequel/plugins/unused_associations.rb +521 -0
  181. data/lib/sequel/plugins/update_or_create.rb +1 -1
  182. data/lib/sequel/plugins/validate_associated.rb +22 -12
  183. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  184. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  185. data/lib/sequel/sql.rb +1 -1
  186. data/lib/sequel/timezones.rb +12 -14
  187. data/lib/sequel/version.rb +1 -1
  188. metadata +99 -43
@@ -175,6 +175,15 @@ module Sequel
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
177
177
  end
178
+ if !c[:max_value] && !c[:min_value]
179
+ min_max = case c[:type]
180
+ when :integer
181
+ column_schema_integer_min_max_values(c)
182
+ when :decimal
183
+ column_schema_decimal_min_max_values(c)
184
+ end
185
+ c[:min_value], c[:max_value] = min_max if min_max
186
+ end
178
187
  end
179
188
  schema_post_process(cols)
180
189
 
@@ -236,7 +245,7 @@ module Sequel
236
245
  when :date
237
246
  Sequel.string_to_date(default)
238
247
  when :datetime
239
- DateTime.parse(default)
248
+ Sequel.string_to_datetime(default)
240
249
  when :time
241
250
  Sequel.string_to_time(default)
242
251
  when :decimal
@@ -272,6 +281,68 @@ module Sequel
272
281
  column_schema_default_to_ruby_value(default, type) rescue nil
273
282
  end
274
283
 
284
+ INTEGER1_MIN_MAX = [-128, 127].freeze
285
+ INTEGER2_MIN_MAX = [-32768, 32767].freeze
286
+ INTEGER3_MIN_MAX = [-8388608, 8388607].freeze
287
+ INTEGER4_MIN_MAX = [-2147483648, 2147483647].freeze
288
+ INTEGER8_MIN_MAX = [-9223372036854775808, 9223372036854775807].freeze
289
+ UNSIGNED_INTEGER1_MIN_MAX = [0, 255].freeze
290
+ UNSIGNED_INTEGER2_MIN_MAX = [0, 65535].freeze
291
+ UNSIGNED_INTEGER3_MIN_MAX = [0, 16777215].freeze
292
+ UNSIGNED_INTEGER4_MIN_MAX = [0, 4294967295].freeze
293
+ UNSIGNED_INTEGER8_MIN_MAX = [0, 18446744073709551615].freeze
294
+
295
+ # Look at the db_type and guess the minimum and maximum integer values for
296
+ # the column.
297
+ def column_schema_integer_min_max_values(column)
298
+ db_type = column[:db_type]
299
+ if /decimal|numeric|number/i =~ db_type
300
+ if min_max = column_schema_decimal_min_max_values(column)
301
+ min_max.map!(&:to_i)
302
+ end
303
+ return min_max
304
+ end
305
+
306
+ unsigned = /unsigned/i =~ db_type
307
+ case db_type
308
+ when /big|int8/i
309
+ unsigned ? UNSIGNED_INTEGER8_MIN_MAX : INTEGER8_MIN_MAX
310
+ when /medium/i
311
+ unsigned ? UNSIGNED_INTEGER3_MIN_MAX : INTEGER3_MIN_MAX
312
+ when /small|int2/i
313
+ unsigned ? UNSIGNED_INTEGER2_MIN_MAX : INTEGER2_MIN_MAX
314
+ when /tiny/i
315
+ (unsigned || column_schema_tinyint_type_is_unsigned?) ? UNSIGNED_INTEGER1_MIN_MAX : INTEGER1_MIN_MAX
316
+ else
317
+ unsigned ? UNSIGNED_INTEGER4_MIN_MAX : INTEGER4_MIN_MAX
318
+ end
319
+ end
320
+
321
+ # Look at the db_type and guess the minimum and maximum decimal values for
322
+ # the column.
323
+ def column_schema_decimal_min_max_values(column)
324
+ if column[:column_size] && column[:scale]
325
+ precision = column[:column_size]
326
+ scale = column[:scale]
327
+ elsif /\((\d+)(?:,\s*(-?\d+))?\)/ =~ column[:db_type]
328
+ precision = $1.to_i
329
+ scale = $2.to_i if $2
330
+ end
331
+
332
+ if precision
333
+ limit = BigDecimal("9" * precision)
334
+ if scale
335
+ limit /= 10**(scale)
336
+ end
337
+ [-limit, limit]
338
+ end
339
+ end
340
+
341
+ # Whether the tinyint type (if supported by the database) is unsigned by default.
342
+ def column_schema_tinyint_type_is_unsigned?
343
+ false
344
+ end
345
+
275
346
  # Look at the db_type and guess the maximum length of the column.
276
347
  # This assumes types such as varchar(255).
277
348
  def column_schema_max_length(db_type)
@@ -333,7 +404,7 @@ module Sequel
333
404
  :boolean
334
405
  when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
335
406
  :float
336
- 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/io
337
408
  $1 && ['0', 'false'].include?($1) ? :integer : :decimal
338
409
  when /bytea|blob|image|(var)?binary/io
339
410
  :blob
@@ -146,6 +146,9 @@ module Sequel
146
146
  #
147
147
  # :generated_type :: Set the type of column when using :generated_always_as,
148
148
  # should be :virtual or :stored to force a type.
149
+ # :on_update_current_timestamp :: Use ON UPDATE CURRENT TIMESTAMP when defining the column,
150
+ # which will update the column value to CURRENT_TIMESTAMP
151
+ # on every UPDATE.
149
152
  #
150
153
  # Microsoft SQL Server specific options:
151
154
  #
@@ -159,7 +162,7 @@ module Sequel
159
162
  nil
160
163
  end
161
164
 
162
- # Adds a named constraint (or unnamed if name is nil),
165
+ # Adds a named CHECK constraint (or unnamed if name is nil),
163
166
  # with the given block or args. To provide options for the constraint, pass
164
167
  # a hash as the first argument.
165
168
  #
@@ -167,6 +170,15 @@ module Sequel
167
170
  # # CONSTRAINT blah CHECK num >= 1 AND num <= 5
168
171
  # constraint({name: :blah, deferrable: true}, num: 1..5)
169
172
  # # CONSTRAINT blah CHECK num >= 1 AND num <= 5 DEFERRABLE INITIALLY DEFERRED
173
+ #
174
+ # If the first argument is a hash, the following options are supported:
175
+ #
176
+ # Options:
177
+ # :name :: The name of the CHECK constraint
178
+ # :deferrable :: Whether the CHECK constraint should be marked DEFERRABLE.
179
+ #
180
+ # PostgreSQL specific options:
181
+ # :not_valid :: Whether the CHECK constraint should be marked NOT VALID.
170
182
  def constraint(name, *args, &block)
171
183
  opts = name.is_a?(Hash) ? name : {:name=>name}
172
184
  constraints << opts.merge(:type=>:check, :check=>block || args)
@@ -205,14 +217,12 @@ module Sequel
205
217
  end
206
218
 
207
219
  # Add a full text index on the given columns.
220
+ # See #index for additional options.
208
221
  #
209
222
  # PostgreSQL specific options:
210
223
  # :index_type :: Can be set to :gist to use a GIST index instead of the
211
224
  # default GIN index.
212
225
  # :language :: Set a language to use for the index (default: simple).
213
- #
214
- # Microsoft SQL Server specific options:
215
- # :key_index :: The KEY INDEX to use for the full text index.
216
226
  def full_text_index(columns, opts = OPTS)
217
227
  index(columns, opts.merge(:type => :full_text))
218
228
  end
@@ -222,35 +232,44 @@ module Sequel
222
232
  columns.any?{|c| c[:name] == name}
223
233
  end
224
234
 
225
- # Add an index on the given column(s) with the given options.
235
+ # Add an index on the given column(s) with the given options. Examples:
236
+ #
237
+ # index :name
238
+ # # CREATE INDEX table_name_index ON table (name)
239
+ #
240
+ # index [:artist_id, :name]
241
+ # # CREATE INDEX table_artist_id_name_index ON table (artist_id, name)
242
+ #
243
+ # index [:artist_id, :name], name: :foo
244
+ # # CREATE INDEX foo ON table (artist_id, name)
245
+ #
226
246
  # General options:
227
247
  #
248
+ # :include :: Include additional column values in the index, without
249
+ # actually indexing on those values (only supported by
250
+ # some databases).
228
251
  # :name :: The name to use for the index. If not given, a default name
229
252
  # based on the table and columns is used.
230
- # :type :: The type of index to use (only supported by some databases)
253
+ # :type :: The type of index to use (only supported by some databases,
254
+ # :full_text and :spatial values are handled specially).
231
255
  # :unique :: Make the index unique, so duplicate values are not allowed.
232
- # :where :: Create a partial index (only supported by some databases)
256
+ # :where :: A filter expression, used to create a partial index (only
257
+ # supported by some databases).
233
258
  #
234
259
  # PostgreSQL specific options:
235
260
  #
236
261
  # :concurrently :: Create the index concurrently, so it doesn't block
237
262
  # operations on the table while the index is being
238
263
  # built.
239
- # :opclass :: Use a specific operator class in the index.
240
- # :include :: Include additional column values in the index, without
241
- # actually indexing on those values (PostgreSQL 11+).
264
+ # :if_not_exists :: Only create the index if an index of the same name doesn't already exist.
265
+ # :nulls_distinct :: Set whether separate NULLs should be considered distinct values in unique indexes.
266
+ # :opclass :: Set an opclass to use for all columns (per-column opclasses require
267
+ # custom SQL).
242
268
  # :tablespace :: Specify tablespace for index.
243
269
  #
244
270
  # Microsoft SQL Server specific options:
245
271
  #
246
- # :include :: Include additional column values in the index, without
247
- # actually indexing on those values.
248
- #
249
- # index :name
250
- # # CREATE INDEX table_name_index ON table (name)
251
- #
252
- # index [:artist_id, :name]
253
- # # CREATE INDEX table_artist_id_name_index ON table (artist_id, name)
272
+ # :key_index :: Sets the KEY INDEX to the given value.
254
273
  def index(columns, opts = OPTS)
255
274
  indexes << {:columns => Array(columns)}.merge!(opts)
256
275
  nil
@@ -316,6 +335,7 @@ module Sequel
316
335
  end
317
336
 
318
337
  # Add a spatial index on the given columns.
338
+ # See #index for additional options.
319
339
  def spatial_index(columns, opts = OPTS)
320
340
  index(columns, opts.merge(:type => :spatial))
321
341
  end
@@ -371,8 +391,7 @@ module Sequel
371
391
  end
372
392
 
373
393
  # Add a column with the given name, type, and opts.
374
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
375
- # separate +add_index+ call to add an index for the column).
394
+ # See CreateTableGenerator#column for the available options.
376
395
  #
377
396
  # add_column(:name, String) # ADD COLUMN name varchar(255)
378
397
  #
@@ -385,7 +404,10 @@ module Sequel
385
404
  # :after :: The name of an existing column that the new column should be positioned after
386
405
  # :first :: Create this new column before all other existing columns
387
406
  def add_column(name, type, opts = OPTS)
388
- @operations << {:op => :add_column, :name => name, :type => type}.merge!(opts)
407
+ op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
408
+ index_opts = op.delete(:index)
409
+ @operations << op
410
+ add_index(name, index_opts.is_a?(Hash) ? index_opts : OPTS) if index_opts
389
411
  nil
390
412
  end
391
413
 
@@ -414,8 +436,7 @@ module Sequel
414
436
  end
415
437
 
416
438
  # Add a foreign key with the given name and referencing the given table.
417
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
418
- # separate +add_index+ call to add an index for the column).
439
+ # See CreateTableGenerator#column for the available options.
419
440
  #
420
441
  # You can also pass an array of column names for creating composite foreign
421
442
  # keys. In this case, it will assume the columns exist and will only add
@@ -442,7 +463,7 @@ module Sequel
442
463
  end
443
464
 
444
465
  # Add a full text index on the given columns.
445
- # See CreateTableGenerator#index for available options.
466
+ # See CreateTableGenerator#full_text_index for available options.
446
467
  def add_full_text_index(columns, opts = OPTS)
447
468
  add_index(columns, {:type=>:full_text}.merge!(opts))
448
469
  end
@@ -451,34 +472,6 @@ module Sequel
451
472
  # CreateTableGenerator#index for available options.
452
473
  #
453
474
  # add_index(:artist_id) # CREATE INDEX table_artist_id_index ON table (artist_id)
454
- #
455
- # Options:
456
- #
457
- # :name :: Give a specific name for the index. Highly recommended if you plan on
458
- # dropping the index later.
459
- # :where :: A filter expression, used to setup a partial index (if supported).
460
- # :unique :: Create a unique index.
461
- #
462
- # PostgreSQL specific options:
463
- #
464
- # :concurrently :: Create the index concurrently, so it doesn't require an exclusive lock
465
- # on the table.
466
- # :index_type :: The underlying index type to use for a full_text index, gin by default).
467
- # :language :: The language to use for a full text index (simple by default).
468
- # :opclass :: Set an opclass to use for all columns (per-column opclasses require
469
- # custom SQL).
470
- # :type :: Set the index type (e.g. full_text, spatial, hash, gin, gist, btree).
471
- # :if_not_exists :: Only create the index if an index of the same name doesn't already exists
472
- #
473
- # MySQL specific options:
474
- #
475
- # :type :: Set the index type, with full_text and spatial indexes handled specially.
476
- #
477
- # Microsoft SQL Server specific options:
478
- #
479
- # :include :: Includes additional columns in the index.
480
- # :key_index :: Sets the KEY INDEX to the given value.
481
- # :type :: clustered uses a clustered index, full_text uses a full text index.
482
475
  def add_index(columns, opts = OPTS)
483
476
  @operations << {:op => :add_index, :columns => Array(columns)}.merge!(opts)
484
477
  nil
@@ -63,7 +63,7 @@ module Sequel
63
63
  # definitions using <tt>create_table</tt>, and +add_index+ accepts all the options
64
64
  # available for index definition.
65
65
  #
66
- # See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[rdoc-ref:doc/migration.rdoc].
66
+ # See <tt>Schema::AlterTableGenerator</tt> and the {Migrations guide}[rdoc-ref:doc/migration.rdoc].
67
67
  def alter_table(name, &block)
68
68
  generator = alter_table_generator(&block)
69
69
  remove_cached_schema(name)
@@ -183,6 +183,15 @@ module Sequel
183
183
  # keys.
184
184
  # :tablespace :: The tablespace to use for the table.
185
185
  #
186
+ # SQLite specific options:
187
+ # :strict :: Create a STRICT table, which checks that the values for the columns
188
+ # are the correct type (similar to all other SQL databases). Note that
189
+ # when using this option, all column types used should be one of the
190
+ # following: +int+, +integer+, +real+, +text+, +blob+, and +any+.
191
+ # The +any+ type is treated like a SQLite column in a non-strict table,
192
+ # allowing any type of data to be stored. This option is supported on
193
+ # SQLite 3.37.0+.
194
+ #
186
195
  # See <tt>Schema::CreateTableGenerator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
187
196
  def create_table(name, options=OPTS, &block)
188
197
  remove_cached_schema(name)
@@ -262,6 +271,10 @@ module Sequel
262
271
  # # SELECT * FROM items WHERE foo
263
272
  # # WITH CHECK OPTION
264
273
  #
274
+ # DB.create_view(:bar_items, DB[:items].select(:foo), columns: [:bar])
275
+ # # CREATE VIEW bar_items (bar) AS
276
+ # # SELECT foo FROM items
277
+ #
265
278
  # Options:
266
279
  # :columns :: The column names to use for the view. If not given,
267
280
  # automatically determined based on the input dataset.
@@ -282,6 +295,9 @@ module Sequel
282
295
  # in a subquery, if you are providing a Dataset as the source
283
296
  # argument, if should probably call the union method with the
284
297
  # all: true and from_self: false options.
298
+ # :security_invoker :: Set the security_invoker property on the view, making
299
+ # the access to the view use the current user's permissions,
300
+ # instead of the view owner's permissions.
285
301
  # :tablespace :: The tablespace to use for materialized views.
286
302
  def create_view(name, source, options = OPTS)
287
303
  execute_ddl(create_view_sql(name, source, options))
@@ -19,7 +19,7 @@ module Sequel
19
19
  METHS
20
20
 
21
21
  # The clone options to use when retrieving columns for a dataset.
22
- COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
22
+ COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 0, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
23
23
 
24
24
  # Inserts the given argument into the database. Returns self so it
25
25
  # can be used safely when chaining:
@@ -127,6 +127,18 @@ module Sequel
127
127
  #
128
128
  # DB[:table].delete # DELETE * FROM table
129
129
  # # => 3
130
+ #
131
+ # Some databases support using multiple tables in a DELETE query. This requires
132
+ # multiple FROM tables (JOINs can also be used). As multiple FROM tables use
133
+ # an implicit CROSS JOIN, you should make sure your WHERE condition uses the
134
+ # appropriate filters for the FROM tables:
135
+ #
136
+ # DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>c[:h]}}.
137
+ # delete
138
+ # # DELETE FROM a
139
+ # # USING b
140
+ # # INNER JOIN c ON (c.d = b.e)
141
+ # # WHERE ((a.f = b.g) AND (a.id = c.h))
130
142
  def delete(&block)
131
143
  sql = delete_sql
132
144
  if uses_returning?(:delete)
@@ -313,14 +325,18 @@ module Sequel
313
325
 
314
326
  # Inserts multiple records into the associated table. This method can be
315
327
  # used to efficiently insert a large number of records into a table in a
316
- # single query if the database supports it. Inserts
317
- # are automatically wrapped in a transaction.
328
+ # single query if the database supports it. Inserts are automatically
329
+ # wrapped in a transaction if necessary.
318
330
  #
319
331
  # This method is called with a columns array and an array of value arrays:
320
332
  #
321
333
  # DB[:table].import([:x, :y], [[1, 2], [3, 4]])
322
334
  # # INSERT INTO table (x, y) VALUES (1, 2)
323
- # # INSERT INTO table (x, y) VALUES (3, 4)
335
+ # # INSERT INTO table (x, y) VALUES (3, 4)
336
+ #
337
+ # or, if the database supports it:
338
+ #
339
+ # # INSERT INTO table (x, y) VALUES (1, 2), (3, 4)
324
340
  #
325
341
  # This method also accepts a dataset instead of an array of value arrays:
326
342
  #
@@ -328,9 +344,13 @@ module Sequel
328
344
  # # INSERT INTO table (x, y) SELECT a, b FROM table2
329
345
  #
330
346
  # Options:
331
- # :commit_every :: Open a new transaction for every given number of records.
332
- # For example, if you provide a value of 50, will commit
333
- # after every 50 records.
347
+ # :commit_every :: Open a new transaction for every given number of
348
+ # records. For example, if you provide a value of 50,
349
+ # will commit after every 50 records. When a
350
+ # transaction is not required, this option controls
351
+ # the maximum number of values to insert with a single
352
+ # statement; it does not force the use of a
353
+ # transaction.
334
354
  # :return :: When this is set to :primary_key, returns an array of
335
355
  # autoincremented primary key values for the rows inserted.
336
356
  # This does not have an effect if +values+ is a Dataset.
@@ -457,6 +477,55 @@ module Sequel
457
477
  _aggregate(:max, arg)
458
478
  end
459
479
 
480
+ # Execute a MERGE statement, which allows for INSERT, UPDATE, and DELETE
481
+ # behavior in a single query, based on whether rows from a source table
482
+ # match rows in the current table, based on the join conditions.
483
+ #
484
+ # Unless the dataset uses static SQL, to use #merge, you must first have
485
+ # called #merge_using to specify the merge source and join conditions.
486
+ # You will then likely to call one or more of the following methods
487
+ # to specify MERGE behavior by adding WHEN [NOT] MATCHED clauses:
488
+ #
489
+ # * #merge_insert
490
+ # * #merge_update
491
+ # * #merge_delete
492
+ #
493
+ # The WHEN [NOT] MATCHED clauses are added to the SQL in the order these
494
+ # methods were called on the dataset. If none of these methods are
495
+ # called, an error is raised.
496
+ #
497
+ # Example:
498
+ #
499
+ # DB[:m1]
500
+ # merge_using(:m2, i1: :i2).
501
+ # merge_insert(i1: :i2, a: Sequel[:b]+11).
502
+ # merge_delete{a > 30}.
503
+ # merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20).
504
+ # merge
505
+ #
506
+ # SQL:
507
+ #
508
+ # MERGE INTO m1 USING m2 ON (i1 = i2)
509
+ # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
510
+ # WHEN MATCHED AND (a > 30) THEN DELETE
511
+ # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
512
+ #
513
+ # On PostgreSQL, two additional merge methods are supported, for the
514
+ # PostgreSQL-specific DO NOTHING syntax.
515
+ #
516
+ # * #merge_do_nothing_when_matched
517
+ # * #merge_do_nothing_when_not_matched
518
+ #
519
+ # This method is supported on Oracle, but Oracle's MERGE support is
520
+ # non-standard, and has the following issues:
521
+ #
522
+ # * DELETE clause requires UPDATE clause
523
+ # * DELETE clause requires a condition
524
+ # * DELETE clause only affects rows updated by UPDATE clause
525
+ def merge
526
+ execute_ddl(merge_sql)
527
+ end
528
+
460
529
  # Returns the minimum value for the given column/expression.
461
530
  # Uses a virtual row block if no argument is given.
462
531
  #
@@ -527,7 +596,7 @@ module Sequel
527
596
  # # SELECT * FROM table ORDER BY id LIMIT 1000 OFFSET 1000
528
597
  # # ...
529
598
  #
530
- # DB[:table].order(:id).paged_each(:rows_per_fetch=>100){|row| }
599
+ # DB[:table].order(:id).paged_each(rows_per_fetch: 100){|row| }
531
600
  # # SELECT * FROM table ORDER BY id LIMIT 100
532
601
  # # SELECT * FROM table ORDER BY id LIMIT 100 OFFSET 100
533
602
  # # ...
@@ -546,7 +615,7 @@ module Sequel
546
615
  unless @opts[:order]
547
616
  raise Sequel::Error, "Dataset#paged_each requires the dataset be ordered"
548
617
  end
549
- unless block_given?
618
+ unless defined?(yield)
550
619
  return enum_for(:paged_each, opts)
551
620
  end
552
621
 
@@ -869,6 +938,19 @@ module Sequel
869
938
  #
870
939
  # DB[:table].update(x: Sequel[:x]+1, y: 0) # UPDATE table SET x = (x + 1), y = 0
871
940
  # # => 10
941
+ #
942
+ # Some databases support using multiple tables in an UPDATE query. This requires
943
+ # multiple FROM tables (JOINs can also be used). As multiple FROM tables use
944
+ # an implicit CROSS JOIN, you should make sure your WHERE condition uses the
945
+ # appropriate filters for the FROM tables:
946
+ #
947
+ # DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>10}}.
948
+ # update(:f=>Sequel[:c][:h])
949
+ # # UPDATE a
950
+ # # SET f = c.h
951
+ # # FROM b
952
+ # # INNER JOIN c ON (c.d = b.e)
953
+ # # WHERE ((a.f = b.g) AND (a.id = 10))
872
954
  def update(values=OPTS, &block)
873
955
  sql = update_sql(values)
874
956
  if uses_returning?(:update)
@@ -974,18 +1056,19 @@ module Sequel
974
1056
 
975
1057
  # Internals of #import. If primary key values are requested, use
976
1058
  # separate insert commands for each row. Otherwise, call #multi_insert_sql
977
- # and execute each statement it gives separately.
1059
+ # and execute each statement it gives separately. A transaction is only used
1060
+ # if there are multiple statements to execute.
978
1061
  def _import(columns, values, opts)
979
1062
  trans_opts = Hash[opts]
980
1063
  trans_opts[:server] = @opts[:server]
981
1064
  if opts[:return] == :primary_key
982
- @db.transaction(trans_opts){values.map{|v| insert(columns, v)}}
1065
+ _import_transaction(values, trans_opts){values.map{|v| insert(columns, v)}}
983
1066
  else
984
1067
  stmts = multi_insert_sql(columns, values)
985
- @db.transaction(trans_opts){stmts.each{|st| execute_dui(st)}}
1068
+ _import_transaction(stmts, trans_opts){stmts.each{|st| execute_dui(st)}}
986
1069
  end
987
1070
  end
988
-
1071
+
989
1072
  # Return an array of arrays of values given by the symbols in ret_cols.
990
1073
  def _select_map_multiple(ret_cols)
991
1074
  map{|r| r.values_at(*ret_cols)}
@@ -1024,6 +1107,17 @@ module Sequel
1024
1107
  end
1025
1108
  end
1026
1109
 
1110
+ # Use a transaction when yielding to the block if multiple values/statements
1111
+ # are provided. When only a single value or statement is provided, then yield
1112
+ # without using a transaction.
1113
+ def _import_transaction(values, trans_opts, &block)
1114
+ if values.length > 1
1115
+ @db.transaction(trans_opts, &block)
1116
+ else
1117
+ yield
1118
+ end
1119
+ end
1120
+
1027
1121
  # Internals of +select_hash+ and +select_hash_groups+
1028
1122
  def _select_hash(meth, key_column, value_column, opts=OPTS)
1029
1123
  select(*(key_column.is_a?(Array) ? key_column : [key_column]) + (value_column.is_a?(Array) ? value_column : [value_column])).
@@ -51,6 +51,11 @@ module Sequel
51
51
  false
52
52
  end
53
53
 
54
+ # Whether deleting from joined datasets is supported, false by default.
55
+ def supports_deleting_joins?
56
+ supports_modifying_joins?
57
+ end
58
+
54
59
  # Whether the database supports derived column lists (e.g.
55
60
  # "table_expr AS table_alias(column_alias1, column_alias2, ...)"), true by
56
61
  # default.
@@ -120,6 +125,11 @@ module Sequel
120
125
  false
121
126
  end
122
127
 
128
+ # Whether the MERGE statement is supported, false by default.
129
+ def supports_merge?
130
+ false
131
+ end
132
+
123
133
  # Whether modifying joined datasets is supported, false by default.
124
134
  def supports_modifying_joins?
125
135
  false
@@ -142,6 +152,11 @@ module Sequel
142
152
  supports_distinct_on?
143
153
  end
144
154
 
155
+ # Whether placeholder literalizers are supported, true by default.
156
+ def supports_placeholder_literalizer?
157
+ true
158
+ end
159
+
145
160
  # Whether the dataset supports pattern matching by regular expressions, false by default.
146
161
  def supports_regexp?
147
162
  false
@@ -178,6 +193,11 @@ module Sequel
178
193
  true
179
194
  end
180
195
 
196
+ # Whether updating joined datasets is supported, false by default.
197
+ def supports_updating_joins?
198
+ supports_modifying_joins?
199
+ end
200
+
181
201
  # Whether the dataset supports the WINDOW clause to define windows used by multiple
182
202
  # window functions, false by default.
183
203
  def supports_window_clause?
@@ -302,7 +302,7 @@ module Sequel
302
302
  cache_set(key, loader + 1)
303
303
  loader = nil
304
304
  end
305
- elsif cache_sql?
305
+ elsif cache_sql? && supports_placeholder_literalizer?
306
306
  cache_set(key, 1)
307
307
  end
308
308
 
@@ -201,7 +201,9 @@ module Sequel
201
201
  when :insert_pk
202
202
  fetch_rows(prepared_sql){|r| return r.values.first}
203
203
  when Array
204
+ # :nocov:
204
205
  case prepared_type[0]
206
+ # :nocov:
205
207
  when :map, :as_hash, :to_hash, :to_hash_groups
206
208
  public_send(*prepared_type, &block)
207
209
  end