sequel 5.33.0 → 5.58.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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +318 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +40 -9
  5. data/doc/association_basics.rdoc +77 -13
  6. data/doc/cheat_sheet.rdoc +13 -5
  7. data/doc/code_order.rdoc +0 -12
  8. data/doc/dataset_filtering.rdoc +2 -2
  9. data/doc/fork_safety.rdoc +84 -0
  10. data/doc/migration.rdoc +12 -6
  11. data/doc/model_plugins.rdoc +1 -1
  12. data/doc/opening_databases.rdoc +15 -3
  13. data/doc/postgresql.rdoc +9 -1
  14. data/doc/querying.rdoc +7 -5
  15. data/doc/release_notes/5.34.0.txt +40 -0
  16. data/doc/release_notes/5.35.0.txt +56 -0
  17. data/doc/release_notes/5.36.0.txt +60 -0
  18. data/doc/release_notes/5.37.0.txt +30 -0
  19. data/doc/release_notes/5.38.0.txt +28 -0
  20. data/doc/release_notes/5.39.0.txt +19 -0
  21. data/doc/release_notes/5.40.0.txt +40 -0
  22. data/doc/release_notes/5.41.0.txt +25 -0
  23. data/doc/release_notes/5.42.0.txt +136 -0
  24. data/doc/release_notes/5.43.0.txt +98 -0
  25. data/doc/release_notes/5.44.0.txt +32 -0
  26. data/doc/release_notes/5.45.0.txt +34 -0
  27. data/doc/release_notes/5.46.0.txt +87 -0
  28. data/doc/release_notes/5.47.0.txt +59 -0
  29. data/doc/release_notes/5.48.0.txt +14 -0
  30. data/doc/release_notes/5.49.0.txt +59 -0
  31. data/doc/release_notes/5.50.0.txt +78 -0
  32. data/doc/release_notes/5.51.0.txt +47 -0
  33. data/doc/release_notes/5.52.0.txt +87 -0
  34. data/doc/release_notes/5.53.0.txt +23 -0
  35. data/doc/release_notes/5.54.0.txt +27 -0
  36. data/doc/release_notes/5.55.0.txt +21 -0
  37. data/doc/release_notes/5.56.0.txt +51 -0
  38. data/doc/release_notes/5.57.0.txt +23 -0
  39. data/doc/release_notes/5.58.0.txt +31 -0
  40. data/doc/sql.rdoc +14 -2
  41. data/doc/testing.rdoc +10 -1
  42. data/doc/transactions.rdoc +0 -8
  43. data/doc/validations.rdoc +1 -1
  44. data/doc/virtual_rows.rdoc +1 -1
  45. data/lib/sequel/adapters/ado/access.rb +1 -1
  46. data/lib/sequel/adapters/ado.rb +17 -17
  47. data/lib/sequel/adapters/amalgalite.rb +3 -5
  48. data/lib/sequel/adapters/ibmdb.rb +2 -2
  49. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  50. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  51. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  52. data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
  53. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  54. data/lib/sequel/adapters/jdbc.rb +29 -19
  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 +8 -6
  58. data/lib/sequel/adapters/oracle.rb +5 -4
  59. data/lib/sequel/adapters/postgres.rb +27 -29
  60. data/lib/sequel/adapters/shared/access.rb +2 -0
  61. data/lib/sequel/adapters/shared/db2.rb +30 -0
  62. data/lib/sequel/adapters/shared/mssql.rb +84 -7
  63. data/lib/sequel/adapters/shared/mysql.rb +33 -2
  64. data/lib/sequel/adapters/shared/oracle.rb +82 -7
  65. data/lib/sequel/adapters/shared/postgres.rb +158 -20
  66. data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
  67. data/lib/sequel/adapters/shared/sqlite.rb +102 -10
  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 +2 -1
  71. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  72. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
  73. data/lib/sequel/ast_transformer.rb +6 -0
  74. data/lib/sequel/connection_pool/sharded_single.rb +9 -8
  75. data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
  76. data/lib/sequel/connection_pool/single.rb +7 -9
  77. data/lib/sequel/connection_pool/threaded.rb +1 -1
  78. data/lib/sequel/core.rb +33 -24
  79. data/lib/sequel/database/connecting.rb +3 -4
  80. data/lib/sequel/database/misc.rb +37 -12
  81. data/lib/sequel/database/query.rb +3 -1
  82. data/lib/sequel/database/schema_generator.rb +50 -53
  83. data/lib/sequel/database/schema_methods.rb +45 -23
  84. data/lib/sequel/database/transactions.rb +9 -6
  85. data/lib/sequel/dataset/actions.rb +61 -8
  86. data/lib/sequel/dataset/features.rb +15 -0
  87. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
  88. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  89. data/lib/sequel/dataset/query.rb +114 -11
  90. data/lib/sequel/dataset/sql.rb +172 -46
  91. data/lib/sequel/deprecated.rb +3 -1
  92. data/lib/sequel/exceptions.rb +2 -0
  93. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  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/blank.rb +8 -0
  97. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  98. data/lib/sequel/extensions/core_refinements.rb +38 -11
  99. data/lib/sequel/extensions/date_arithmetic.rb +36 -24
  100. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  101. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  102. data/lib/sequel/extensions/duplicate_columns_handler.rb +3 -1
  103. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  104. data/lib/sequel/extensions/inflector.rb +9 -1
  105. data/lib/sequel/extensions/is_distinct_from.rb +139 -0
  106. data/lib/sequel/extensions/migration.rb +13 -2
  107. data/lib/sequel/extensions/named_timezones.rb +5 -1
  108. data/lib/sequel/extensions/pagination.rb +1 -1
  109. data/lib/sequel/extensions/pg_array.rb +1 -0
  110. data/lib/sequel/extensions/pg_array_ops.rb +6 -2
  111. data/lib/sequel/extensions/pg_enum.rb +3 -1
  112. data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
  113. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  114. data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
  115. data/lib/sequel/extensions/pg_inet.rb +2 -0
  116. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  117. data/lib/sequel/extensions/pg_interval.rb +35 -8
  118. data/lib/sequel/extensions/pg_json.rb +3 -5
  119. data/lib/sequel/extensions/pg_json_ops.rb +119 -4
  120. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  121. data/lib/sequel/extensions/pg_multirange.rb +372 -0
  122. data/lib/sequel/extensions/pg_range.rb +7 -19
  123. data/lib/sequel/extensions/pg_range_ops.rb +39 -9
  124. data/lib/sequel/extensions/pg_row.rb +1 -1
  125. data/lib/sequel/extensions/pg_row_ops.rb +25 -1
  126. data/lib/sequel/extensions/query.rb +3 -0
  127. data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
  128. data/lib/sequel/extensions/s.rb +4 -1
  129. data/lib/sequel/extensions/schema_dumper.rb +16 -5
  130. data/lib/sequel/extensions/server_block.rb +8 -12
  131. data/lib/sequel/extensions/sql_comments.rb +110 -3
  132. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  133. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  134. data/lib/sequel/extensions/string_agg.rb +1 -1
  135. data/lib/sequel/extensions/string_date_time.rb +19 -23
  136. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  137. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  138. data/lib/sequel/extensions/to_dot.rb +9 -3
  139. data/lib/sequel/model/associations.rb +342 -114
  140. data/lib/sequel/model/base.rb +45 -24
  141. data/lib/sequel/model/errors.rb +10 -1
  142. data/lib/sequel/model/inflections.rb +1 -1
  143. data/lib/sequel/model/plugins.rb +8 -3
  144. data/lib/sequel/model.rb +3 -1
  145. data/lib/sequel/plugins/association_pks.rb +60 -18
  146. data/lib/sequel/plugins/association_proxies.rb +3 -0
  147. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  148. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  149. data/lib/sequel/plugins/auto_validations.rb +39 -5
  150. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  151. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  152. data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
  153. data/lib/sequel/plugins/column_encryption.rb +728 -0
  154. data/lib/sequel/plugins/composition.rb +8 -2
  155. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  156. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  157. data/lib/sequel/plugins/csv_serializer.rb +2 -0
  158. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  159. data/lib/sequel/plugins/dirty.rb +44 -0
  160. data/lib/sequel/plugins/enum.rb +124 -0
  161. data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
  162. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  163. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  164. data/lib/sequel/plugins/json_serializer.rb +39 -24
  165. data/lib/sequel/plugins/lazy_attributes.rb +4 -1
  166. data/lib/sequel/plugins/many_through_many.rb +108 -9
  167. data/lib/sequel/plugins/nested_attributes.rb +8 -3
  168. data/lib/sequel/plugins/pg_array_associations.rb +58 -41
  169. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
  170. data/lib/sequel/plugins/prepared_statements.rb +15 -12
  171. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  172. data/lib/sequel/plugins/rcte_tree.rb +37 -35
  173. data/lib/sequel/plugins/serialization.rb +9 -3
  174. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  175. data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
  176. data/lib/sequel/plugins/sql_comments.rb +189 -0
  177. data/lib/sequel/plugins/static_cache.rb +1 -1
  178. data/lib/sequel/plugins/string_stripper.rb +1 -1
  179. data/lib/sequel/plugins/subclasses.rb +28 -11
  180. data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
  181. data/lib/sequel/plugins/timestamps.rb +1 -1
  182. data/lib/sequel/plugins/tree.rb +9 -4
  183. data/lib/sequel/plugins/unused_associations.rb +521 -0
  184. data/lib/sequel/plugins/update_or_create.rb +1 -1
  185. data/lib/sequel/plugins/validation_class_methods.rb +5 -1
  186. data/lib/sequel/plugins/validation_helpers.rb +18 -11
  187. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  188. data/lib/sequel/sql.rb +1 -1
  189. data/lib/sequel/timezones.rb +20 -17
  190. data/lib/sequel/version.rb +1 -1
  191. metadata +93 -39
@@ -38,7 +38,6 @@ module Sequel
38
38
  @constraints = []
39
39
  @primary_key = nil
40
40
  instance_exec(&block) if block
41
- @columns.unshift(@primary_key) if @primary_key && !has_column?(primary_key_name)
42
41
  end
43
42
 
44
43
  # Use custom Bignum method to use :Bignum instead of Bignum class, to work
@@ -144,8 +143,17 @@ module Sequel
144
143
  # :identity :: Create an identity column.
145
144
  #
146
145
  # MySQL specific options:
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.
152
+ #
153
+ # Microsoft SQL Server specific options:
154
+ #
155
+ # :clustered :: When using :primary_key or :unique, marks the primary key or unique
156
+ # constraint as CLUSTERED (if true), or NONCLUSTERED (if false).
149
157
  def column(name, type, opts = OPTS)
150
158
  columns << {:name => name, :type => type}.merge!(opts)
151
159
  if index_opts = opts[:index]
@@ -154,7 +162,7 @@ module Sequel
154
162
  nil
155
163
  end
156
164
 
157
- # Adds a named constraint (or unnamed if name is nil),
165
+ # Adds a named CHECK constraint (or unnamed if name is nil),
158
166
  # with the given block or args. To provide options for the constraint, pass
159
167
  # a hash as the first argument.
160
168
  #
@@ -162,6 +170,15 @@ module Sequel
162
170
  # # CONSTRAINT blah CHECK num >= 1 AND num <= 5
163
171
  # constraint({name: :blah, deferrable: true}, num: 1..5)
164
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.
165
182
  def constraint(name, *args, &block)
166
183
  opts = name.is_a?(Hash) ? name : {:name=>name}
167
184
  constraints << opts.merge(:type=>:check, :check=>block || args)
@@ -200,14 +217,12 @@ module Sequel
200
217
  end
201
218
 
202
219
  # Add a full text index on the given columns.
220
+ # See #index for additional options.
203
221
  #
204
222
  # PostgreSQL specific options:
205
223
  # :index_type :: Can be set to :gist to use a GIST index instead of the
206
224
  # default GIN index.
207
225
  # :language :: Set a language to use for the index (default: simple).
208
- #
209
- # Microsoft SQL Server specific options:
210
- # :key_index :: The KEY INDEX to use for the full text index.
211
226
  def full_text_index(columns, opts = OPTS)
212
227
  index(columns, opts.merge(:type => :full_text))
213
228
  end
@@ -217,35 +232,43 @@ module Sequel
217
232
  columns.any?{|c| c[:name] == name}
218
233
  end
219
234
 
220
- # 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
+ #
221
246
  # General options:
222
247
  #
248
+ # :include :: Include additional column values in the index, without
249
+ # actually indexing on those values (only supported by
250
+ # some databases).
223
251
  # :name :: The name to use for the index. If not given, a default name
224
252
  # based on the table and columns is used.
225
- # :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).
226
255
  # :unique :: Make the index unique, so duplicate values are not allowed.
227
- # :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).
228
258
  #
229
259
  # PostgreSQL specific options:
230
260
  #
231
261
  # :concurrently :: Create the index concurrently, so it doesn't block
232
262
  # operations on the table while the index is being
233
263
  # built.
234
- # :opclass :: Use a specific operator class in the index.
235
- # :include :: Include additional column values in the index, without
236
- # 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
+ # :opclass :: Set an opclass to use for all columns (per-column opclasses require
266
+ # custom SQL).
237
267
  # :tablespace :: Specify tablespace for index.
238
268
  #
239
269
  # Microsoft SQL Server specific options:
240
270
  #
241
- # :include :: Include additional column values in the index, without
242
- # actually indexing on those values.
243
- #
244
- # index :name
245
- # # CREATE INDEX table_name_index ON table (name)
246
- #
247
- # index [:artist_id, :name]
248
- # # CREATE INDEX table_artist_id_name_index ON table (artist_id, name)
271
+ # :key_index :: Sets the KEY INDEX to the given value.
249
272
  def index(columns, opts = OPTS)
250
273
  indexes << {:columns => Array(columns)}.merge!(opts)
251
274
  nil
@@ -311,6 +334,7 @@ module Sequel
311
334
  end
312
335
 
313
336
  # Add a spatial index on the given columns.
337
+ # See #index for additional options.
314
338
  def spatial_index(columns, opts = OPTS)
315
339
  index(columns, opts.merge(:type => :spatial))
316
340
  end
@@ -366,8 +390,7 @@ module Sequel
366
390
  end
367
391
 
368
392
  # Add a column with the given name, type, and opts.
369
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
370
- # separate +add_index+ call to add an index for the column).
393
+ # See CreateTableGenerator#column for the available options.
371
394
  #
372
395
  # add_column(:name, String) # ADD COLUMN name varchar(255)
373
396
  #
@@ -380,7 +403,10 @@ module Sequel
380
403
  # :after :: The name of an existing column that the new column should be positioned after
381
404
  # :first :: Create this new column before all other existing columns
382
405
  def add_column(name, type, opts = OPTS)
383
- @operations << {:op => :add_column, :name => name, :type => type}.merge!(opts)
406
+ op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
407
+ index_opts = op.delete(:index)
408
+ @operations << op
409
+ add_index(name, index_opts.is_a?(Hash) ? index_opts : OPTS) if index_opts
384
410
  nil
385
411
  end
386
412
 
@@ -409,8 +435,7 @@ module Sequel
409
435
  end
410
436
 
411
437
  # Add a foreign key with the given name and referencing the given table.
412
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
413
- # separate +add_index+ call to add an index for the column).
438
+ # See CreateTableGenerator#column for the available options.
414
439
  #
415
440
  # You can also pass an array of column names for creating composite foreign
416
441
  # keys. In this case, it will assume the columns exist and will only add
@@ -437,7 +462,7 @@ module Sequel
437
462
  end
438
463
 
439
464
  # Add a full text index on the given columns.
440
- # See CreateTableGenerator#index for available options.
465
+ # See CreateTableGenerator#full_text_index for available options.
441
466
  def add_full_text_index(columns, opts = OPTS)
442
467
  add_index(columns, {:type=>:full_text}.merge!(opts))
443
468
  end
@@ -446,34 +471,6 @@ module Sequel
446
471
  # CreateTableGenerator#index for available options.
447
472
  #
448
473
  # add_index(:artist_id) # CREATE INDEX table_artist_id_index ON table (artist_id)
449
- #
450
- # Options:
451
- #
452
- # :name :: Give a specific name for the index. Highly recommended if you plan on
453
- # dropping the index later.
454
- # :where :: A filter expression, used to setup a partial index (if supported).
455
- # :unique :: Create a unique index.
456
- #
457
- # PostgreSQL specific options:
458
- #
459
- # :concurrently :: Create the index concurrently, so it doesn't require an exclusive lock
460
- # on the table.
461
- # :index_type :: The underlying index type to use for a full_text index, gin by default).
462
- # :language :: The language to use for a full text index (simple by default).
463
- # :opclass :: Set an opclass to use for all columns (per-column opclasses require
464
- # custom SQL).
465
- # :type :: Set the index type (e.g. full_text, spatial, hash, gin, gist, btree).
466
- # :if_not_exists :: Only create the index if an index of the same name doesn't already exists
467
- #
468
- # MySQL specific options:
469
- #
470
- # :type :: Set the index type, with full_text and spatial indexes handled specially.
471
- #
472
- # Microsoft SQL Server specific options:
473
- #
474
- # :include :: Includes additional columns in the index.
475
- # :key_index :: Sets the KEY INDEX to the given value.
476
- # :type :: clustered uses a clustered index, full_text uses a full text index.
477
474
  def add_index(columns, opts = OPTS)
478
475
  @operations << {:op => :add_index, :columns => Array(columns)}.merge!(opts)
479
476
  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)
@@ -240,7 +249,7 @@ module Sequel
240
249
  if supports_create_or_replace_view?
241
250
  options = options.merge(:replace=>true)
242
251
  else
243
- drop_view(name) rescue nil
252
+ swallow_database_error{drop_view(name)}
244
253
  end
245
254
 
246
255
  create_view(name, source, options)
@@ -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.
@@ -494,7 +507,9 @@ module Sequel
494
507
  when :drop_index
495
508
  drop_index_sql(table, op)
496
509
  else
497
- "ALTER TABLE #{quote_schema_table(table)} #{alter_table_op_sql(table, op)}"
510
+ if sql = alter_table_op_sql(table, op)
511
+ "ALTER TABLE #{quote_schema_table(table)} #{sql}"
512
+ end
498
513
  end
499
514
  end
500
515
 
@@ -578,14 +593,14 @@ module Sequel
578
593
  sql << ' NULL'
579
594
  end
580
595
  end
581
-
596
+
582
597
  # Add primary key SQL fragment to column creation SQL.
583
598
  def column_definition_primary_key_sql(sql, column)
584
599
  if column[:primary_key]
585
600
  if name = column[:primary_key_constraint_name]
586
601
  sql << " CONSTRAINT #{quote_identifier(name)}"
587
602
  end
588
- sql << ' PRIMARY KEY'
603
+ sql << " " << primary_key_constraint_sql_fragment(column)
589
604
  constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
590
605
  end
591
606
  end
@@ -606,7 +621,7 @@ module Sequel
606
621
  if name = column[:unique_constraint_name]
607
622
  sql << " CONSTRAINT #{quote_identifier(name)}"
608
623
  end
609
- sql << ' UNIQUE'
624
+ sql << ' ' << unique_constraint_sql_fragment(column)
610
625
  constraint_deferrable_sql_append(sql, column[:unique_deferrable])
611
626
  end
612
627
  end
@@ -654,11 +669,11 @@ module Sequel
654
669
  check = "(#{check})" unless check[0..0] == '(' && check[-1..-1] == ')'
655
670
  sql << "CHECK #{check}"
656
671
  when :primary_key
657
- sql << "PRIMARY KEY #{literal(constraint[:columns])}"
672
+ sql << "#{primary_key_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
658
673
  when :foreign_key
659
674
  sql << column_references_table_constraint_sql(constraint.merge(:deferrable=>nil))
660
675
  when :unique
661
- sql << "UNIQUE #{literal(constraint[:columns])}"
676
+ sql << "#{unique_constraint_sql_fragment(constraint)} #{literal(constraint[:columns])}"
662
677
  else
663
678
  raise Error, "Invalid constraint type #{constraint[:type]}, should be :check, :primary_key, :foreign_key, or :unique"
664
679
  end
@@ -811,23 +826,20 @@ module Sequel
811
826
  # Proxy the filter_expr call to the dataset, used for creating constraints.
812
827
  # Support passing Proc arguments as blocks, as well as treating plain strings
813
828
  # as literal strings, so that previous migrations that used this API do not break.
814
- def filter_expr(*args, &block)
815
- if args.length == 1
816
- arg = args.first
817
- if arg.is_a?(Proc) && !block
818
- block = args.first
819
- args = nil
820
- elsif arg.is_a?(String)
821
- args = [Sequel.lit(*args)]
822
- elsif arg.is_a?(Array)
823
- if arg.first.is_a?(String)
824
- args = [Sequel.lit(*arg)]
825
- elsif arg.length > 1
826
- args = [Sequel.&(*arg)]
827
- end
829
+ def filter_expr(arg=nil, &block)
830
+ if arg.is_a?(Proc) && !block
831
+ block = arg
832
+ arg = nil
833
+ elsif arg.is_a?(String)
834
+ arg = Sequel.lit(arg)
835
+ elsif arg.is_a?(Array)
836
+ if arg.first.is_a?(String)
837
+ arg = Sequel.lit(*arg)
838
+ elsif arg.length > 1
839
+ arg = Sequel.&(*arg)
828
840
  end
829
841
  end
830
- schema_utility_dataset.literal(schema_utility_dataset.send(:filter_expr, *args, &block))
842
+ schema_utility_dataset.literal(schema_utility_dataset.send(:filter_expr, arg, &block))
831
843
  end
832
844
 
833
845
  # SQL statement for creating an index for the table with the given name
@@ -893,6 +905,11 @@ module Sequel
893
905
  on_delete_clause(action)
894
906
  end
895
907
 
908
+ # Add fragment for primary key specification, separated for easier overridding.
909
+ def primary_key_constraint_sql_fragment(_)
910
+ 'PRIMARY KEY'
911
+ end
912
+
896
913
  # Proxy the quote_schema_table method to the dataset
897
914
  def quote_schema_table(table)
898
915
  schema_utility_dataset.quote_schema_table(table)
@@ -1048,6 +1065,11 @@ module Sequel
1048
1065
  "#{type}#{literal(Array(elements)) if elements}#{' UNSIGNED' if column[:unsigned]}"
1049
1066
  end
1050
1067
 
1068
+ # Add fragment for unique specification, separated for easier overridding.
1069
+ def unique_constraint_sql_fragment(_)
1070
+ 'UNIQUE'
1071
+ end
1072
+
1051
1073
  # Whether clob should be used for String text: true columns.
1052
1074
  def uses_clob_for_text?
1053
1075
  false
@@ -82,7 +82,7 @@ module Sequel
82
82
  # :server :: The server/shard the transaction is being executed on.
83
83
  def rollback_on_exit(opts=OPTS)
84
84
  synchronize(opts[:server]) do |conn|
85
- raise Error, "Cannot call Sequel:: Database#rollback_on_exit! unless inside a transaction" unless h = _trans(conn)
85
+ raise Error, "Cannot call Sequel:: Database#rollback_on_exit unless inside a transaction" unless h = _trans(conn)
86
86
  rollback = !opts[:cancel]
87
87
 
88
88
  if supports_savepoints?
@@ -154,7 +154,7 @@ module Sequel
154
154
  # Note that this should not be used unless the entire transaction
155
155
  # block is idempotent, as otherwise it can cause non-idempotent
156
156
  # behavior to execute multiple times.
157
- # :rollback :: Can the set to :reraise to reraise any Sequel::Rollback exceptions
157
+ # :rollback :: Can be set to :reraise to reraise any Sequel::Rollback exceptions
158
158
  # raised, or :always to always rollback even if no exceptions occur
159
159
  # (useful for testing).
160
160
  # :server :: The server to use for the transaction. Set to :default, :read_only, or
@@ -205,6 +205,10 @@ module Sequel
205
205
  end
206
206
  end
207
207
 
208
+ if opts[:savepoint] && !supports_savepoints?
209
+ raise Sequel::InvalidOperation, "savepoints not supported on #{database_type}"
210
+ end
211
+
208
212
  if already_in_transaction?(conn, opts)
209
213
  if opts[:rollback] == :always && !opts.has_key?(:savepoint)
210
214
  if supports_savepoints?
@@ -418,11 +422,10 @@ module Sequel
418
422
  end
419
423
 
420
424
  # Retrieve the savepoint hooks that should be run for the given
421
- # connection and commit status.
425
+ # connection and commit status. This expacts that you are
426
+ # already inside a savepoint when calling.
422
427
  def savepoint_hooks(conn, committed)
423
- if in_savepoint?(conn)
424
- _trans(conn)[:savepoints].last[committed ? :after_commit : :after_rollback]
425
- end
428
+ _trans(conn)[:savepoints].last[committed ? :after_commit : :after_rollback]
426
429
  end
427
430
 
428
431
  # Retrieve the transaction hooks that should be run for the given
@@ -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:
@@ -457,6 +457,55 @@ module Sequel
457
457
  _aggregate(:max, arg)
458
458
  end
459
459
 
460
+ # Execute a MERGE statement, which allows for INSERT, UPDATE, and DELETE
461
+ # behavior in a single query, based on whether rows from a source table
462
+ # match rows in the current table, based on the join conditions.
463
+ #
464
+ # Unless the dataset uses static SQL, to use #merge, you must first have
465
+ # called #merge_using to specify the merge source and join conditions.
466
+ # You will then likely to call one or more of the following methods
467
+ # to specify MERGE behavior by adding WHEN [NOT] MATCHED clauses:
468
+ #
469
+ # * #merge_insert
470
+ # * #merge_update
471
+ # * #merge_delete
472
+ #
473
+ # The WHEN [NOT] MATCHED clauses are added to the SQL in the order these
474
+ # methods were called on the dataset. If none of these methods are
475
+ # called, an error is raised.
476
+ #
477
+ # Example:
478
+ #
479
+ # DB[:m1]
480
+ # merge_using(:m2, i1: :i2).
481
+ # merge_insert(i1: :i2, a: Sequel[:b]+11).
482
+ # merge_delete{a > 30}.
483
+ # merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20).
484
+ # merge
485
+ #
486
+ # SQL:
487
+ #
488
+ # MERGE INTO m1 USING m2 ON (i1 = i2)
489
+ # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
490
+ # WHEN MATCHED AND (a > 30) THEN DELETE
491
+ # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
492
+ #
493
+ # On PostgreSQL, two additional merge methods are supported, for the
494
+ # PostgreSQL-specific DO NOTHING syntax.
495
+ #
496
+ # * #merge_do_nothing_when_matched
497
+ # * #merge_do_nothing_when_not_matched
498
+ #
499
+ # This method is supported on Oracle, but Oracle's MERGE support is
500
+ # non-standard, and has the following issues:
501
+ #
502
+ # * DELETE clause requires UPDATE clause
503
+ # * DELETE clause requires a condition
504
+ # * DELETE clause only affects rows updated by UPDATE clause
505
+ def merge
506
+ execute_ddl(merge_sql)
507
+ end
508
+
460
509
  # Returns the minimum value for the given column/expression.
461
510
  # Uses a virtual row block if no argument is given.
462
511
  #
@@ -546,7 +595,7 @@ module Sequel
546
595
  unless @opts[:order]
547
596
  raise Sequel::Error, "Dataset#paged_each requires the dataset be ordered"
548
597
  end
549
- unless block_given?
598
+ unless defined?(yield)
550
599
  return enum_for(:paged_each, opts)
551
600
  end
552
601
 
@@ -607,14 +656,16 @@ module Sequel
607
656
  # as_hash, it accepts an optional :hash parameter, into which entries will
608
657
  # be merged.
609
658
  #
610
- # DB[:table].select_hash(:id, :name) # SELECT id, name FROM table
659
+ # DB[:table].select_hash(:id, :name)
660
+ # # SELECT id, name FROM table
611
661
  # # => {1=>'a', 2=>'b', ...}
612
662
  #
613
663
  # You can also provide an array of column names for either the key_column,
614
664
  # the value column, or both:
615
665
  #
616
- # DB[:table].select_hash([:id, :foo], [:name, :bar]) # SELECT * FROM table
617
- # # {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
666
+ # DB[:table].select_hash([:id, :foo], [:name, :bar])
667
+ # # SELECT id, foo, name, bar FROM table
668
+ # # => {[1, 3]=>['a', 'c'], [2, 4]=>['b', 'd'], ...}
618
669
  #
619
670
  # When using this method, you must be sure that each expression has an alias
620
671
  # that Sequel can determine.
@@ -626,14 +677,16 @@ module Sequel
626
677
  # Similar to to_hash_groups, but only selects the columns given. Like to_hash_groups,
627
678
  # it accepts an optional :hash parameter, into which entries will be merged.
628
679
  #
629
- # DB[:table].select_hash_groups(:name, :id) # SELECT id, name FROM table
680
+ # DB[:table].select_hash_groups(:name, :id)
681
+ # # SELECT id, name FROM table
630
682
  # # => {'a'=>[1, 4, ...], 'b'=>[2, ...], ...}
631
683
  #
632
684
  # You can also provide an array of column names for either the key_column,
633
685
  # the value column, or both:
634
686
  #
635
- # DB[:table].select_hash_groups([:first, :middle], [:last, :id]) # SELECT * FROM table
636
- # # {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
687
+ # DB[:table].select_hash_groups([:first, :middle], [:last, :id])
688
+ # # SELECT first, middle, last, id FROM table
689
+ # # => {['a', 'b']=>[['c', 1], ['d', 2], ...], ...}
637
690
  #
638
691
  # When using this method, you must be sure that each expression has an alias
639
692
  # that Sequel can determine.
@@ -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
@@ -178,6 +188,11 @@ module Sequel
178
188
  true
179
189
  end
180
190
 
191
+ # Whether updating joined datasets is supported, false by default.
192
+ def supports_updating_joins?
193
+ supports_modifying_joins?
194
+ end
195
+
181
196
  # Whether the dataset supports the WINDOW clause to define windows used by multiple
182
197
  # window functions, false by default.
183
198
  def supports_window_clause?
@@ -114,10 +114,8 @@ module Sequel
114
114
  prepared_sql << sql
115
115
  prepared_sql << "$#{prepared_args[i]}"
116
116
  end
117
- if final_sql
118
- frags << final_sql
119
- prepared_sql << final_sql
120
- end
117
+ frags << final_sql
118
+ prepared_sql << final_sql
121
119
 
122
120
  [prepared_sql, frags]
123
121
  end
@@ -213,9 +211,7 @@ module Sequel
213
211
  end
214
212
  ds.literal_append(s, v)
215
213
  end
216
- if sql = @final_sql
217
- s << sql
218
- end
214
+ s << @final_sql
219
215
  s
220
216
  end
221
217
  end
@@ -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