sequel 5.45.0 → 5.77.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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +434 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +27 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +28 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.46.0.txt +87 -0
  17. data/doc/release_notes/5.47.0.txt +59 -0
  18. data/doc/release_notes/5.48.0.txt +14 -0
  19. data/doc/release_notes/5.49.0.txt +59 -0
  20. data/doc/release_notes/5.50.0.txt +78 -0
  21. data/doc/release_notes/5.51.0.txt +47 -0
  22. data/doc/release_notes/5.52.0.txt +87 -0
  23. data/doc/release_notes/5.53.0.txt +23 -0
  24. data/doc/release_notes/5.54.0.txt +27 -0
  25. data/doc/release_notes/5.55.0.txt +21 -0
  26. data/doc/release_notes/5.56.0.txt +51 -0
  27. data/doc/release_notes/5.57.0.txt +23 -0
  28. data/doc/release_notes/5.58.0.txt +31 -0
  29. data/doc/release_notes/5.59.0.txt +73 -0
  30. data/doc/release_notes/5.60.0.txt +22 -0
  31. data/doc/release_notes/5.61.0.txt +43 -0
  32. data/doc/release_notes/5.62.0.txt +132 -0
  33. data/doc/release_notes/5.63.0.txt +33 -0
  34. data/doc/release_notes/5.64.0.txt +50 -0
  35. data/doc/release_notes/5.65.0.txt +21 -0
  36. data/doc/release_notes/5.66.0.txt +24 -0
  37. data/doc/release_notes/5.67.0.txt +32 -0
  38. data/doc/release_notes/5.68.0.txt +61 -0
  39. data/doc/release_notes/5.69.0.txt +26 -0
  40. data/doc/release_notes/5.70.0.txt +35 -0
  41. data/doc/release_notes/5.71.0.txt +21 -0
  42. data/doc/release_notes/5.72.0.txt +33 -0
  43. data/doc/release_notes/5.73.0.txt +66 -0
  44. data/doc/release_notes/5.74.0.txt +45 -0
  45. data/doc/release_notes/5.75.0.txt +35 -0
  46. data/doc/release_notes/5.76.0.txt +86 -0
  47. data/doc/release_notes/5.77.0.txt +63 -0
  48. data/doc/schema_modification.rdoc +1 -1
  49. data/doc/security.rdoc +9 -9
  50. data/doc/sharding.rdoc +3 -1
  51. data/doc/sql.rdoc +27 -15
  52. data/doc/testing.rdoc +23 -13
  53. data/doc/transactions.rdoc +6 -6
  54. data/doc/virtual_rows.rdoc +1 -1
  55. data/lib/sequel/adapters/ado/access.rb +1 -1
  56. data/lib/sequel/adapters/ado.rb +1 -1
  57. data/lib/sequel/adapters/amalgalite.rb +3 -5
  58. data/lib/sequel/adapters/ibmdb.rb +3 -3
  59. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  60. data/lib/sequel/adapters/jdbc/h2.rb +63 -10
  61. data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
  62. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  63. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  64. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  65. data/lib/sequel/adapters/jdbc.rb +24 -22
  66. data/lib/sequel/adapters/mysql.rb +92 -67
  67. data/lib/sequel/adapters/mysql2.rb +56 -51
  68. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  69. data/lib/sequel/adapters/odbc.rb +1 -1
  70. data/lib/sequel/adapters/oracle.rb +4 -3
  71. data/lib/sequel/adapters/postgres.rb +89 -45
  72. data/lib/sequel/adapters/shared/access.rb +11 -1
  73. data/lib/sequel/adapters/shared/db2.rb +42 -0
  74. data/lib/sequel/adapters/shared/mssql.rb +91 -10
  75. data/lib/sequel/adapters/shared/mysql.rb +78 -3
  76. data/lib/sequel/adapters/shared/oracle.rb +86 -7
  77. data/lib/sequel/adapters/shared/postgres.rb +576 -171
  78. data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
  79. data/lib/sequel/adapters/shared/sqlite.rb +92 -8
  80. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  81. data/lib/sequel/adapters/sqlite.rb +99 -18
  82. data/lib/sequel/adapters/tinytds.rb +1 -1
  83. data/lib/sequel/adapters/trilogy.rb +117 -0
  84. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  85. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  86. data/lib/sequel/ast_transformer.rb +6 -0
  87. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  88. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  89. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  90. data/lib/sequel/connection_pool/single.rb +6 -8
  91. data/lib/sequel/connection_pool/threaded.rb +14 -8
  92. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  93. data/lib/sequel/connection_pool.rb +57 -31
  94. data/lib/sequel/core.rb +17 -18
  95. data/lib/sequel/database/connecting.rb +27 -3
  96. data/lib/sequel/database/dataset.rb +16 -6
  97. data/lib/sequel/database/misc.rb +70 -14
  98. data/lib/sequel/database/query.rb +73 -2
  99. data/lib/sequel/database/schema_generator.rb +11 -6
  100. data/lib/sequel/database/schema_methods.rb +23 -4
  101. data/lib/sequel/database/transactions.rb +6 -0
  102. data/lib/sequel/dataset/actions.rb +111 -15
  103. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  104. data/lib/sequel/dataset/features.rb +20 -1
  105. data/lib/sequel/dataset/misc.rb +12 -2
  106. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  107. data/lib/sequel/dataset/query.rb +170 -41
  108. data/lib/sequel/dataset/sql.rb +190 -71
  109. data/lib/sequel/dataset.rb +4 -0
  110. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  111. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  112. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  113. data/lib/sequel/extensions/async_thread_pool.rb +14 -13
  114. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  115. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  116. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  117. data/lib/sequel/extensions/connection_validator.rb +16 -11
  118. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  119. data/lib/sequel/extensions/core_refinements.rb +36 -11
  120. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  121. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  122. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  123. data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
  124. data/lib/sequel/extensions/index_caching.rb +5 -1
  125. data/lib/sequel/extensions/inflector.rb +1 -1
  126. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  127. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  128. data/lib/sequel/extensions/migration.rb +57 -15
  129. data/lib/sequel/extensions/named_timezones.rb +22 -6
  130. data/lib/sequel/extensions/pagination.rb +1 -1
  131. data/lib/sequel/extensions/pg_array.rb +33 -4
  132. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  133. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  134. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  135. data/lib/sequel/extensions/pg_enum.rb +1 -2
  136. data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
  137. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  138. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  139. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  140. data/lib/sequel/extensions/pg_inet.rb +10 -11
  141. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  142. data/lib/sequel/extensions/pg_interval.rb +11 -11
  143. data/lib/sequel/extensions/pg_json.rb +13 -15
  144. data/lib/sequel/extensions/pg_json_ops.rb +125 -2
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +13 -26
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +20 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  151. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  152. data/lib/sequel/extensions/s.rb +2 -1
  153. data/lib/sequel/extensions/schema_caching.rb +1 -1
  154. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  155. data/lib/sequel/extensions/server_block.rb +10 -13
  156. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  157. data/lib/sequel/extensions/sql_comments.rb +110 -3
  158. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  159. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  160. data/lib/sequel/extensions/string_agg.rb +1 -1
  161. data/lib/sequel/extensions/string_date_time.rb +19 -23
  162. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  163. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  164. data/lib/sequel/model/associations.rb +286 -92
  165. data/lib/sequel/model/base.rb +53 -33
  166. data/lib/sequel/model/dataset_module.rb +3 -0
  167. data/lib/sequel/model/errors.rb +10 -1
  168. data/lib/sequel/model/exceptions.rb +15 -3
  169. data/lib/sequel/model/inflections.rb +1 -1
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +74 -16
  172. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  173. data/lib/sequel/plugins/column_encryption.rb +29 -8
  174. data/lib/sequel/plugins/composition.rb +3 -2
  175. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  176. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  177. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  178. data/lib/sequel/plugins/dirty.rb +1 -1
  179. data/lib/sequel/plugins/enum.rb +124 -0
  180. data/lib/sequel/plugins/finder.rb +4 -2
  181. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  182. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  183. data/lib/sequel/plugins/json_serializer.rb +2 -2
  184. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  185. data/lib/sequel/plugins/list.rb +8 -3
  186. data/lib/sequel/plugins/many_through_many.rb +109 -10
  187. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  188. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  189. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  190. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  191. data/lib/sequel/plugins/paged_operations.rb +181 -0
  192. data/lib/sequel/plugins/pg_array_associations.rb +46 -34
  193. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  194. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  195. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  196. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  197. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  198. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  199. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  200. data/lib/sequel/plugins/serialization.rb +1 -0
  201. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
  202. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  203. data/lib/sequel/plugins/sql_comments.rb +189 -0
  204. data/lib/sequel/plugins/static_cache.rb +39 -1
  205. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  206. data/lib/sequel/plugins/subclasses.rb +28 -11
  207. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  208. data/lib/sequel/plugins/timestamps.rb +1 -1
  209. data/lib/sequel/plugins/unused_associations.rb +521 -0
  210. data/lib/sequel/plugins/update_or_create.rb +1 -1
  211. data/lib/sequel/plugins/validate_associated.rb +22 -12
  212. data/lib/sequel/plugins/validation_helpers.rb +41 -11
  213. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  214. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  215. data/lib/sequel/sql.rb +1 -1
  216. data/lib/sequel/timezones.rb +12 -14
  217. data/lib/sequel/version.rb +1 -1
  218. metadata +109 -19
@@ -187,6 +187,15 @@ module Sequel
187
187
  def views(opts=OPTS)
188
188
  full_tables('VIEW', opts)
189
189
  end
190
+
191
+ # Renames multiple tables in a single call.
192
+ #
193
+ # DB.rename_tables [:items, :old_items], [:other_items, :old_other_items]
194
+ # # RENAME TABLE items TO old_items, other_items TO old_other_items
195
+ def rename_tables(*renames)
196
+ execute_ddl(rename_tables_sql(renames))
197
+ renames.each{|from,| remove_cached_schema(from)}
198
+ end
190
199
 
191
200
  private
192
201
 
@@ -324,6 +333,12 @@ module Sequel
324
333
  sqls << "SET sql_mode = '#{sql_mode}'"
325
334
  end
326
335
 
336
+ # Disable the use of split_materialized in the optimizer. This is
337
+ # needed to pass association tests on MariaDB 10.5+.
338
+ if opts[:disable_split_materialized] && typecast_value_boolean(opts[:disable_split_materialized])
339
+ sqls << "SET SESSION optimizer_switch='split_materialized=off'"
340
+ end
341
+
327
342
  sqls
328
343
  end
329
344
 
@@ -347,6 +362,12 @@ module Sequel
347
362
  end
348
363
  end
349
364
 
365
+ # Support :on_update_current_timestamp option.
366
+ def column_definition_default_sql(sql, column)
367
+ super
368
+ sql << " ON UPDATE CURRENT_TIMESTAMP" if column[:on_update_current_timestamp]
369
+ end
370
+
350
371
  # Add generation clause SQL fragment to column creation SQL.
351
372
  def column_definition_generated_sql(sql, column)
352
373
  if (generated_expression = column[:generated_always_as])
@@ -473,6 +494,14 @@ module Sequel
473
494
  schema(table).select{|a| a[1][:primary_key]}.map{|a| a[0]}
474
495
  end
475
496
 
497
+ # SQL statement for renaming multiple tables.
498
+ def rename_tables_sql(renames)
499
+ rename_tos = renames.map do |from, to|
500
+ "#{quote_schema_table(from)} TO #{quote_schema_table(to)}"
501
+ end.join(', ')
502
+ "RENAME TABLE #{rename_tos}"
503
+ end
504
+
476
505
  # Rollback the currently open XA transaction
477
506
  def rollback_transaction(conn, opts=OPTS)
478
507
  if (s = opts[:prepare]) && savepoint_level(conn) <= 1
@@ -516,19 +545,35 @@ module Sequel
516
545
  row[:default] = row.delete(:Default)
517
546
  row[:db_type] = row.delete(:Type)
518
547
  row[:type] = schema_column_type(row[:db_type])
548
+ row[:extra] = extra
519
549
  [m.call(row.delete(:Field)), row]
520
550
  end
521
551
  end
522
552
 
553
+ # Return nil if CHECK constraints are not supported, because
554
+ # versions that don't support check constraints don't raise
555
+ # errors for values outside of range.
556
+ def column_schema_integer_min_max_values(column)
557
+ super if supports_check_constraints?
558
+ end
559
+
560
+ # Return nil if CHECK constraints are not supported, because
561
+ # versions that don't support check constraints don't raise
562
+ # errors for values outside of range.
563
+ def column_schema_decimal_min_max_values(column)
564
+ super if supports_check_constraints?
565
+ end
566
+
523
567
  # Split DROP INDEX ops on MySQL 5.6+, as dropping them in the same
524
568
  # statement as dropping a related foreign key causes an error.
525
569
  def split_alter_table_op?(op)
526
570
  server_version >= 50600 && (op[:op] == :drop_index || (op[:op] == :drop_constraint && op[:type] == :unique))
527
571
  end
528
572
 
529
- # Whether the database supports CHECK constraints
573
+ # CHECK constraints only supported on MariaDB 10.2+ and MySQL 8.0.19+
574
+ # (at least MySQL documents DROP CONSTRAINT was supported in 8.0.19+).
530
575
  def supports_check_constraints?
531
- mariadb? && server_version >= 100200
576
+ server_version >= (mariadb? ? 100200 : 80019)
532
577
  end
533
578
 
534
579
  # MySQL can combine multiple alter table ops into a single query.
@@ -601,7 +646,7 @@ module Sequel
601
646
  MATCH_AGAINST_BOOLEAN = ["MATCH ".freeze, " AGAINST (".freeze, " IN BOOLEAN MODE)".freeze].freeze
602
647
 
603
648
  Dataset.def_sql_method(self, :delete, %w'with delete from where order limit')
604
- Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update')
649
+ Dataset.def_sql_method(self, :insert, %w'insert ignore into columns values on_duplicate_key_update returning')
605
650
  Dataset.def_sql_method(self, :select, %w'with select distinct calc_found_rows columns from join where group having window compounds order limit lock')
606
651
  Dataset.def_sql_method(self, :update, %w'with update ignore table set where order limit')
607
652
 
@@ -729,6 +774,21 @@ module Sequel
729
774
  clone(:insert_ignore=>true)
730
775
  end
731
776
 
777
+ # Support insert select for associations, so that the model code can use
778
+ # returning instead of a separate query.
779
+ def insert_select(*values)
780
+ return unless supports_insert_select?
781
+ # Handle case where query does not return a row
782
+ server?(:default).with_sql_first(insert_select_sql(*values)) || false
783
+ end
784
+
785
+ # The SQL to use for an insert_select, adds a RETURNING clause to the insert
786
+ # unless the RETURNING clause is already present.
787
+ def insert_select_sql(*values)
788
+ ds = opts[:returning] ? self : returning
789
+ ds.insert_sql(*values)
790
+ end
791
+
732
792
  # Sets up the insert methods to use ON DUPLICATE KEY UPDATE
733
793
  # If you pass no arguments, ALL fields will be
734
794
  # updated with the new values. If you pass the fields you
@@ -826,6 +886,11 @@ module Sequel
826
886
  true
827
887
  end
828
888
 
889
+ # MariaDB 10.5.0 supports INSERT RETURNING.
890
+ def supports_returning?(type)
891
+ (type == :insert && db.mariadb? && db.adapter_scheme != :jdbc) ? (db.server_version >= 100500) : false
892
+ end
893
+
829
894
  # MySQL 8+ supports SKIP LOCKED.
830
895
  def supports_skip_locked?
831
896
  !db.mariadb? && db.server_version >= 80000
@@ -864,6 +929,16 @@ module Sequel
864
929
  super if type == :truncate || @opts[:offset]
865
930
  end
866
931
 
932
+ # The strftime format to use when literalizing time (Sequel::SQLTime) values.
933
+ def default_time_format
934
+ db.supports_timestamp_usecs? ? super : "'%H:%M:%S'"
935
+ end
936
+
937
+ # The strftime format to use when literalizing timestamp (Time/DateTime) values.
938
+ def default_timestamp_format
939
+ db.supports_timestamp_usecs? ? super : "'%Y-%m-%d %H:%M:%S'"
940
+ end
941
+
867
942
  # Consider the first table in the joined dataset is the table to delete
868
943
  # from, but include the others for the purposes of selecting rows.
869
944
  def delete_from_sql(sql)
@@ -1,6 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  require_relative '../utils/emulate_offset_with_row_number'
4
+ require_relative '../../extensions/auto_cast_date_and_time'
4
5
 
5
6
  module Sequel
6
7
  module Oracle
@@ -178,6 +179,13 @@ module Sequel
178
179
  ''
179
180
  end
180
181
 
182
+ # Support min/max integer values on Oracle only if
183
+ # they use a NUMBER column with a fixed precision
184
+ # and no scale.
185
+ def column_schema_integer_min_max_values(column)
186
+ super if column[:db_type] =~ /NUMBER\(\d+\)/i || (column[:db_type] == 'NUMBER' && column[:column_size].is_a?(Integer) && column[:scale] == 0)
187
+ end
188
+
181
189
  def create_sequence_sql(name, opts=OPTS)
182
190
  "CREATE SEQUENCE #{quote_identifier(name)} start with #{opts [:start_with]||1} increment by #{opts[:increment_by]||1} nomaxvalue"
183
191
  end
@@ -319,6 +327,8 @@ module Sequel
319
327
  end
320
328
 
321
329
  module DatasetMethods
330
+ include AutoCastDateAndTime
331
+
322
332
  ROW_NUMBER_EXPRESSION = LiteralString.new('ROWNUM').freeze
323
333
  BITAND_PROC = lambda{|a, b| Sequel.lit(["CAST(BITAND(", ", ", ") AS INTEGER)"], a, b)}
324
334
 
@@ -378,7 +388,12 @@ module Sequel
378
388
  # Use a custom expression with EXISTS to determine whether a dataset
379
389
  # is empty.
380
390
  def empty?
381
- db[:dual].where(@opts[:offset] ? exists : unordered.exists).get(1) == nil
391
+ if @opts[:sql]
392
+ naked.each{return false}
393
+ true
394
+ else
395
+ db[:dual].where(@opts[:offset] ? exists : unordered.exists).get(1) == nil
396
+ end
382
397
  end
383
398
 
384
399
  # Oracle requires SQL standard datetimes
@@ -478,6 +493,11 @@ module Sequel
478
493
  false
479
494
  end
480
495
 
496
+ # Oracle supports MERGE
497
+ def supports_merge?
498
+ true
499
+ end
500
+
481
501
  # Oracle supports NOWAIT.
482
502
  def supports_nowait?
483
503
  true
@@ -525,6 +545,70 @@ module Sequel
525
545
 
526
546
  private
527
547
 
548
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
549
+ # type error.
550
+ def _normalize_merge_when_conditions(conditions)
551
+ case conditions
552
+ when nil, false
553
+ {1=>0}
554
+ when true
555
+ {1=>1}
556
+ when Sequel::SQL::DelayedEvaluation
557
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
558
+ else
559
+ conditions
560
+ end
561
+ end
562
+
563
+ # Handle Oracle's non standard MERGE syntax
564
+ def _merge_when_sql(sql)
565
+ raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
566
+ insert = update = delete = nil
567
+ types = merge_when.map{|d| d[:type]}
568
+ raise Error, "Oracle does not support multiple INSERT, UPDATE, or DELETE clauses in MERGE" if types != types.uniq
569
+
570
+ merge_when.each do |data|
571
+ case data[:type]
572
+ when :insert
573
+ insert = data
574
+ when :update
575
+ update = data
576
+ else # when :delete
577
+ delete = data
578
+ end
579
+ end
580
+
581
+ if delete
582
+ raise Error, "Oracle does not support DELETE without UPDATE clause in MERGE" unless update
583
+ raise Error, "Oracle does not support DELETE without conditions clause in MERGE" unless delete.has_key?(:conditions)
584
+ end
585
+
586
+ if update
587
+ sql << " WHEN MATCHED"
588
+ _merge_update_sql(sql, update)
589
+ _merge_when_conditions_sql(sql, update)
590
+
591
+ if delete
592
+ sql << " DELETE"
593
+ _merge_when_conditions_sql(sql, delete)
594
+ end
595
+ end
596
+
597
+ if insert
598
+ sql << " WHEN NOT MATCHED"
599
+ _merge_insert_sql(sql, insert)
600
+ _merge_when_conditions_sql(sql, insert)
601
+ end
602
+ end
603
+
604
+ # Handle Oracle's non-standard MERGE WHEN condition syntax.
605
+ def _merge_when_conditions_sql(sql, data)
606
+ if data.has_key?(:conditions)
607
+ sql << " WHERE "
608
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
609
+ end
610
+ end
611
+
528
612
  # Allow preparing prepared statements, since determining the prepared sql to use for
529
613
  # a prepared statement requires calling prepare on that statement.
530
614
  def allow_preparing_prepared_statements?
@@ -542,7 +626,7 @@ module Sequel
542
626
 
543
627
  # The strftime format to use when literalizing the time.
544
628
  def default_timestamp_format
545
- "TIMESTAMP '%Y-%m-%d %H:%M:%S%N %z'"
629
+ "'%Y-%m-%d %H:%M:%S.%6N %:z'"
546
630
  end
547
631
 
548
632
  def empty_from_sql
@@ -579,11 +663,6 @@ module Sequel
579
663
  super
580
664
  end
581
665
 
582
- # Use a colon for the timestamp offset, since Oracle appears to require it.
583
- def format_timestamp_offset(hour, minute)
584
- sprintf("%+03i:%02i", hour, minute)
585
- end
586
-
587
666
  # Oracle doesn't support empty values when inserting.
588
667
  def insert_supports_empty_values?
589
668
  false