sequel 5.57.0 → 5.60.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +34 -0
  3. data/README.rdoc +25 -0
  4. data/bin/sequel +11 -3
  5. data/doc/cheat_sheet.rdoc +8 -0
  6. data/doc/opening_databases.rdoc +10 -6
  7. data/doc/release_notes/5.58.0.txt +31 -0
  8. data/doc/release_notes/5.59.0.txt +73 -0
  9. data/doc/release_notes/5.60.0.txt +22 -0
  10. data/doc/testing.rdoc +1 -1
  11. data/lib/sequel/adapters/jdbc/derby.rb +5 -0
  12. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  13. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
  15. data/lib/sequel/adapters/jdbc.rb +5 -5
  16. data/lib/sequel/adapters/mock.rb +1 -1
  17. data/lib/sequel/adapters/mysql.rb +3 -3
  18. data/lib/sequel/adapters/oracle.rb +1 -1
  19. data/lib/sequel/adapters/postgres.rb +58 -17
  20. data/lib/sequel/adapters/shared/db2.rb +28 -0
  21. data/lib/sequel/adapters/shared/mssql.rb +35 -1
  22. data/lib/sequel/adapters/shared/mysql.rb +6 -0
  23. data/lib/sequel/adapters/shared/oracle.rb +70 -1
  24. data/lib/sequel/adapters/shared/postgres.rb +94 -18
  25. data/lib/sequel/adapters/shared/sqlite.rb +1 -1
  26. data/lib/sequel/adapters/sqlite.rb +1 -1
  27. data/lib/sequel/ast_transformer.rb +1 -1
  28. data/lib/sequel/database/misc.rb +2 -2
  29. data/lib/sequel/database/schema_generator.rb +1 -0
  30. data/lib/sequel/database/schema_methods.rb +3 -0
  31. data/lib/sequel/dataset/actions.rb +49 -0
  32. data/lib/sequel/dataset/features.rb +5 -0
  33. data/lib/sequel/dataset/query.rb +62 -0
  34. data/lib/sequel/dataset/sql.rb +114 -27
  35. data/lib/sequel/extensions/date_arithmetic.rb +35 -7
  36. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  37. data/lib/sequel/extensions/is_distinct_from.rb +3 -1
  38. data/lib/sequel/extensions/pg_array.rb +2 -2
  39. data/lib/sequel/extensions/pg_array_ops.rb +1 -1
  40. data/lib/sequel/extensions/pg_enum.rb +1 -1
  41. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  42. data/lib/sequel/extensions/pg_hstore_ops.rb +3 -3
  43. data/lib/sequel/extensions/pg_inet.rb +2 -2
  44. data/lib/sequel/extensions/pg_interval.rb +1 -1
  45. data/lib/sequel/extensions/pg_json.rb +1 -1
  46. data/lib/sequel/extensions/pg_json_ops.rb +55 -3
  47. data/lib/sequel/extensions/pg_multirange.rb +2 -2
  48. data/lib/sequel/extensions/pg_range.rb +2 -2
  49. data/lib/sequel/extensions/pg_row.rb +2 -2
  50. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
  51. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  52. data/lib/sequel/model/associations.rb +18 -6
  53. data/lib/sequel/model/base.rb +17 -7
  54. data/lib/sequel/model/exceptions.rb +1 -1
  55. data/lib/sequel/model/inflections.rb +6 -6
  56. data/lib/sequel/plugins/auto_validations.rb +1 -1
  57. data/lib/sequel/plugins/defaults_setter.rb +1 -1
  58. data/lib/sequel/plugins/dirty.rb +1 -1
  59. data/lib/sequel/plugins/insert_conflict.rb +1 -1
  60. data/lib/sequel/plugins/json_serializer.rb +1 -1
  61. data/lib/sequel/plugins/list.rb +3 -1
  62. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  63. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +1 -1
  64. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  65. data/lib/sequel/plugins/serialization.rb +1 -1
  66. data/lib/sequel/plugins/sharding.rb +1 -1
  67. data/lib/sequel/plugins/sql_comments.rb +4 -4
  68. data/lib/sequel/plugins/subclasses.rb +1 -1
  69. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -0
  70. data/lib/sequel/plugins/validation_class_methods.rb +3 -3
  71. data/lib/sequel/plugins/validation_helpers.rb +1 -1
  72. data/lib/sequel/sql.rb +1 -1
  73. data/lib/sequel/version.rb +1 -1
  74. metadata +9 -16
@@ -5,6 +5,7 @@ require_relative 'shared/postgres'
5
5
  begin
6
6
  require 'pg'
7
7
 
8
+ # :nocov:
8
9
  Sequel::Postgres::PGError = PG::Error if defined?(PG::Error)
9
10
  Sequel::Postgres::PGconn = PG::Connection if defined?(PG::Connection)
10
11
  Sequel::Postgres::PGresult = PG::Result if defined?(PG::Result)
@@ -14,30 +15,40 @@ begin
14
15
  raise LoadError unless defined?(PGconn::CONNECTION_OK)
15
16
  end
16
17
 
17
- Sequel::Postgres::USES_PG = true
18
18
  if defined?(PG::TypeMapByClass)
19
+ # :nocov:
19
20
  type_map = Sequel::Postgres::PG_QUERY_TYPE_MAP = PG::TypeMapByClass.new
20
21
  type_map[Integer] = PG::TextEncoder::Integer.new
21
22
  type_map[FalseClass] = type_map[TrueClass] = PG::TextEncoder::Boolean.new
22
23
  type_map[Float] = PG::TextEncoder::Float.new
23
24
  end
25
+
26
+ Sequel::Postgres::USES_PG = true
24
27
  rescue LoadError => e
28
+ # :nocov:
25
29
  begin
26
- require 'postgres-pr/postgres-compat'
27
- Sequel::Postgres::USES_PG = false
30
+ require 'sequel/postgres-pr'
28
31
  rescue LoadError
29
- raise e
32
+ begin
33
+ require 'postgres-pr/postgres-compat'
34
+ rescue LoadError
35
+ raise e
36
+ end
30
37
  end
38
+ Sequel::Postgres::USES_PG = false
39
+ # :nocov:
31
40
  end
32
41
 
33
42
  module Sequel
34
43
  module Postgres
35
- if Sequel::Postgres::USES_PG
44
+ # :nocov:
45
+ if USES_PG
36
46
  # Whether the given sequel_pg version integer is supported.
37
47
  def self.sequel_pg_version_supported?(version)
38
48
  version >= 10617
39
49
  end
40
50
  end
51
+ # :nocov:
41
52
 
42
53
  # PGconn subclass for connection specific methods used with the
43
54
  # pg or postgres-pr driver.
@@ -45,7 +56,9 @@ module Sequel
45
56
  # The underlying exception classes to reraise as disconnect errors
46
57
  # instead of regular database errors.
47
58
  DISCONNECT_ERROR_CLASSES = [IOError, Errno::EPIPE, Errno::ECONNRESET]
59
+ # :nocov:
48
60
  if defined?(::PG::ConnectionBad)
61
+ # :nocov:
49
62
  DISCONNECT_ERROR_CLASSES << ::PG::ConnectionBad
50
63
  end
51
64
  DISCONNECT_ERROR_CLASSES.freeze
@@ -71,11 +84,14 @@ module Sequel
71
84
  # are SQL strings.
72
85
  attr_reader :prepared_statements
73
86
 
87
+ # :nocov:
74
88
  unless public_method_defined?(:async_exec_params)
75
89
  alias async_exec_params async_exec
76
90
  end
77
- else
78
- # Make postgres-pr look like pg
91
+ elsif !const_defined?(:CONNECTION_OK)
92
+ # Handle old postgres-pr
93
+ # sequel-postgres-pr already implements this API
94
+
79
95
  CONNECTION_OK = -1
80
96
 
81
97
  # Escape bytea values. Uses historical format instead of hex
@@ -111,6 +127,7 @@ module Sequel
111
127
  alias cmd_tuples cmdtuples
112
128
  end
113
129
  end
130
+ # :nocov:
114
131
 
115
132
  # Raise a Sequel::DatabaseDisconnectError if a one of the disconnect
116
133
  # error classes is raised, or a PGError is raised and the connection
@@ -143,7 +160,7 @@ module Sequel
143
160
  begin
144
161
  defined?(yield) ? yield(q) : q.cmd_tuples
145
162
  ensure
146
- q.clear if q && q.respond_to?(:clear)
163
+ q.clear if q && defined?(q.clear)
147
164
  end
148
165
  end
149
166
 
@@ -204,7 +221,9 @@ module Sequel
204
221
  :sslmode => opts[:sslmode],
205
222
  :sslrootcert => opts[:sslrootcert]
206
223
  }.delete_if { |key, value| blank_object?(value) }
224
+ # :nocov:
207
225
  connection_params.merge!(opts[:driver_options]) if opts[:driver_options]
226
+ # :nocov:
208
227
  conn = Adapter.connect(opts[:conn_str] || connection_params)
209
228
 
210
229
  conn.instance_variable_set(:@prepared_statements, {})
@@ -212,6 +231,13 @@ module Sequel
212
231
  if receiver = opts[:notice_receiver]
213
232
  conn.set_notice_receiver(&receiver)
214
233
  end
234
+
235
+ # :nocov:
236
+ if conn.respond_to?(:type_map_for_queries=) && defined?(PG_QUERY_TYPE_MAP)
237
+ # :nocov:
238
+ conn.type_map_for_queries = PG_QUERY_TYPE_MAP
239
+ end
240
+ # :nocov:
215
241
  else
216
242
  unless typecast_value_boolean(@opts.fetch(:force_standard_strings, true))
217
243
  raise Error, "Cannot create connection using postgres-pr unless force_standard_strings is set"
@@ -226,19 +252,19 @@ module Sequel
226
252
  opts[:password]
227
253
  )
228
254
  end
255
+ # :nocov:
229
256
 
230
257
  conn.instance_variable_set(:@db, self)
231
- if USES_PG && conn.respond_to?(:type_map_for_queries=) && defined?(PG_QUERY_TYPE_MAP)
232
- conn.type_map_for_queries = PG_QUERY_TYPE_MAP
233
- end
234
258
 
259
+ # :nocov:
235
260
  if encoding = opts[:encoding] || opts[:charset]
236
- if conn.respond_to?(:set_client_encoding)
261
+ if defined?(conn.set_client_encoding)
237
262
  conn.set_client_encoding(encoding)
238
263
  else
239
264
  conn.async_exec("set client_encoding to '#{encoding}'")
240
265
  end
241
266
  end
267
+ # :nocov:
242
268
 
243
269
  connection_configuration_sqls(opts).each{|sql| conn.execute(sql)}
244
270
  conn
@@ -265,7 +291,9 @@ module Sequel
265
291
  nil
266
292
  end
267
293
 
294
+ # :nocov:
268
295
  if USES_PG && Object.const_defined?(:PG) && ::PG.const_defined?(:Constants) && ::PG::Constants.const_defined?(:PG_DIAG_SCHEMA_NAME)
296
+ # :nocov:
269
297
  # Return a hash of information about the related PGError (or Sequel::DatabaseError that
270
298
  # wraps a PGError), with the following entries (any of which may be +nil+):
271
299
  #
@@ -316,7 +344,9 @@ module Sequel
316
344
  synchronize(opts[:server]){|conn| check_database_errors{_execute(conn, sql, opts, &block)}}
317
345
  end
318
346
 
347
+ # :nocov:
319
348
  if USES_PG
349
+ # :nocov:
320
350
  # +copy_table+ uses PostgreSQL's +COPY TO STDOUT+ SQL statement to return formatted
321
351
  # results directly to the caller. This method is only supported if pg is the
322
352
  # underlying ruby driver. This method should only be called if you want
@@ -462,12 +492,12 @@ module Sequel
462
492
  opts[:after_listen].call(conn) if opts[:after_listen]
463
493
  timeout = opts[:timeout]
464
494
  if timeout
465
- timeout_block = timeout.respond_to?(:call) ? timeout : proc{timeout}
495
+ timeout_block = defined?(timeout.call) ? timeout : proc{timeout}
466
496
  end
467
497
 
468
498
  if l = opts[:loop]
469
499
  raise Error, 'calling #listen with :loop requires a block' unless block
470
- loop_call = l.respond_to?(:call)
500
+ loop_call = defined?(l.call)
471
501
  catch(:stop) do
472
502
  while true
473
503
  t = timeout_block ? [timeout_block.call] : []
@@ -509,8 +539,10 @@ module Sequel
509
539
  def adapter_initialize
510
540
  @use_iso_date_format = typecast_value_boolean(@opts.fetch(:use_iso_date_format, true))
511
541
  initialize_postgres_adapter
542
+ # :nocov:
512
543
  add_conversion_proc(17, method(:unescape_bytea)) if USES_PG
513
544
  add_conversion_proc(1082, TYPE_TRANSLATOR_DATE) if @use_iso_date_format
545
+ # :nocov:
514
546
  self.convert_infinite_timestamps = @opts[:convert_infinite_timestamps]
515
547
  end
516
548
 
@@ -520,19 +552,22 @@ module Sequel
520
552
  rescue => e
521
553
  raise_error(e, :classes=>database_error_classes)
522
554
  end
523
-
524
555
  # Set the DateStyle to ISO if configured, for faster date parsing.
525
556
  def connection_configuration_sqls(opts=@opts)
526
557
  sqls = super
558
+ # :nocov:
527
559
  sqls << "SET DateStyle = 'ISO'" if @use_iso_date_format
560
+ # :nocov:
528
561
  sqls
529
562
  end
530
563
 
564
+ # :nocov:
531
565
  if USES_PG
532
566
  def unescape_bytea(s)
533
567
  ::Sequel::SQL::Blob.new(Adapter.unescape_bytea(s))
534
568
  end
535
569
  end
570
+ # :nocov:
536
571
 
537
572
  DATABASE_ERROR_CLASSES = [PGError].freeze
538
573
  def database_error_classes
@@ -546,7 +581,9 @@ module Sequel
546
581
  end
547
582
 
548
583
  def database_exception_sqlstate(exception, opts)
549
- if exception.respond_to?(:result) && (result = exception.result)
584
+ # :nocov:
585
+ if defined?(exception.result) && (result = exception.result)
586
+ # :nocov:
550
587
  result.error_field(PGresult::PG_DIAG_SQLSTATE)
551
588
  end
552
589
  end
@@ -588,7 +625,7 @@ module Sequel
588
625
  begin
589
626
  defined?(yield) ? yield(q) : q.cmd_tuples
590
627
  ensure
591
- q.clear if q && q.respond_to?(:clear)
628
+ q.clear if q && defined?(q.clear)
592
629
  end
593
630
  end
594
631
 
@@ -656,7 +693,9 @@ module Sequel
656
693
  clone(:where=>Sequel.lit(['CURRENT OF '], Sequel.identifier(cursor_name)))
657
694
  end
658
695
 
696
+ # :nocov:
659
697
  if USES_PG
698
+ # :nocov:
660
699
  PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
661
700
 
662
701
  # PostgreSQL specific argument mapper used for mapping the named
@@ -803,6 +842,7 @@ module Sequel
803
842
  end
804
843
  end
805
844
 
845
+ # :nocov:
806
846
  if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
807
847
  begin
808
848
  require 'sequel_pg'
@@ -814,3 +854,4 @@ if Sequel::Postgres::USES_PG && !ENV['NO_SEQUEL_PG']
814
854
  rescue LoadError
815
855
  end
816
856
  end
857
+ # :nocov:
@@ -338,6 +338,11 @@ module Sequel
338
338
  true
339
339
  end
340
340
 
341
+ # DB2 supports MERGE
342
+ def supports_merge?
343
+ true
344
+ end
345
+
341
346
  # DB2 does not support multiple columns in IN.
342
347
  def supports_multiple_column_in?
343
348
  false
@@ -360,6 +365,29 @@ module Sequel
360
365
 
361
366
  private
362
367
 
368
+ # Normalize conditions for MERGE WHEN.
369
+ def _merge_when_conditions_sql(sql, data)
370
+ if data.has_key?(:conditions)
371
+ sql << " AND "
372
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
373
+ end
374
+ end
375
+
376
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
377
+ # type error.
378
+ def _normalize_merge_when_conditions(conditions)
379
+ case conditions
380
+ when nil, false
381
+ {1=>0}
382
+ when true
383
+ {1=>1}
384
+ when Sequel::SQL::DelayedEvaluation
385
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
386
+ else
387
+ conditions
388
+ end
389
+ end
390
+
363
391
  def empty_from_sql
364
392
  ' FROM "SYSIBM"."SYSDUMMY1"'
365
393
  end
@@ -205,7 +205,7 @@ module Sequel
205
205
  return @server_version = Integer(@opts[:server_version])
206
206
  end
207
207
  @server_version = synchronize(server) do |conn|
208
- (conn.server_version rescue nil) if conn.respond_to?(:server_version)
208
+ (conn.server_version rescue nil) if defined?(conn.server_version)
209
209
  end
210
210
  unless @server_version
211
211
  m = /^(\d+)\.(\d+)\.(\d+)/.match(fetch("SELECT CAST(SERVERPROPERTY('ProductVersion') AS varchar)").single_value.to_s)
@@ -734,6 +734,11 @@ module Sequel
734
734
  false
735
735
  end
736
736
 
737
+ # MSSQL 2008+ supports MERGE
738
+ def supports_merge?
739
+ is_2008_or_later?
740
+ end
741
+
737
742
  # MSSQL 2005+ supports modifying joined datasets
738
743
  def supports_modifying_joins?
739
744
  is_2005_or_later?
@@ -824,6 +829,35 @@ module Sequel
824
829
 
825
830
  private
826
831
 
832
+ # Normalize conditions for MERGE WHEN.
833
+ def _merge_when_conditions_sql(sql, data)
834
+ if data.has_key?(:conditions)
835
+ sql << " AND "
836
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
837
+ end
838
+ end
839
+
840
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
841
+ # type error.
842
+ def _normalize_merge_when_conditions(conditions)
843
+ case conditions
844
+ when nil, false
845
+ {1=>0}
846
+ when true
847
+ {1=>1}
848
+ when Sequel::SQL::DelayedEvaluation
849
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
850
+ else
851
+ conditions
852
+ end
853
+ end
854
+
855
+ # MSSQL requires a semicolon at the end of MERGE.
856
+ def _merge_when_sql(sql)
857
+ super
858
+ sql << ';'
859
+ end
860
+
827
861
  # MSSQL does not allow ordering in sub-clauses unless TOP (limit) is specified
828
862
  def aggregate_dataset
829
863
  (options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS) && !options_overlap([:limit])) ? unordered.from_self : super
@@ -333,6 +333,12 @@ module Sequel
333
333
  sqls << "SET sql_mode = '#{sql_mode}'"
334
334
  end
335
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
+
336
342
  sqls
337
343
  end
338
344
 
@@ -121,7 +121,7 @@ module Sequel
121
121
  def server_version(server=nil)
122
122
  return @server_version if @server_version
123
123
  @server_version = synchronize(server) do |conn|
124
- (conn.server_version rescue nil) if conn.respond_to?(:server_version)
124
+ (conn.server_version rescue nil) if defined?(conn.server_version)
125
125
  end
126
126
  unless @server_version
127
127
  @server_version = if m = /(\d+)\.(\d+)\.?(\d+)?\.?(\d+)?/.match(fetch("select version from PRODUCT_COMPONENT_VERSION where lower(product) like 'oracle%'").single_value)
@@ -478,6 +478,11 @@ module Sequel
478
478
  false
479
479
  end
480
480
 
481
+ # Oracle supports MERGE
482
+ def supports_merge?
483
+ true
484
+ end
485
+
481
486
  # Oracle supports NOWAIT.
482
487
  def supports_nowait?
483
488
  true
@@ -525,6 +530,70 @@ module Sequel
525
530
 
526
531
  private
527
532
 
533
+ # Handle nil, false, and true MERGE WHEN conditions to avoid non-boolean
534
+ # type error.
535
+ def _normalize_merge_when_conditions(conditions)
536
+ case conditions
537
+ when nil, false
538
+ {1=>0}
539
+ when true
540
+ {1=>1}
541
+ when Sequel::SQL::DelayedEvaluation
542
+ Sequel.delay{_normalize_merge_when_conditions(conditions.call(self))}
543
+ else
544
+ conditions
545
+ end
546
+ end
547
+
548
+ # Handle Oracle's non standard MERGE syntax
549
+ def _merge_when_sql(sql)
550
+ raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
551
+ insert = update = delete = nil
552
+ types = merge_when.map{|d| d[:type]}
553
+ raise Error, "Oracle does not support multiple INSERT, UPDATE, or DELETE clauses in MERGE" if types != types.uniq
554
+
555
+ merge_when.each do |data|
556
+ case data[:type]
557
+ when :insert
558
+ insert = data
559
+ when :update
560
+ update = data
561
+ else # when :delete
562
+ delete = data
563
+ end
564
+ end
565
+
566
+ if delete
567
+ raise Error, "Oracle does not support DELETE without UPDATE clause in MERGE" unless update
568
+ raise Error, "Oracle does not support DELETE without conditions clause in MERGE" unless delete.has_key?(:conditions)
569
+ end
570
+
571
+ if update
572
+ sql << " WHEN MATCHED"
573
+ _merge_update_sql(sql, update)
574
+ _merge_when_conditions_sql(sql, update)
575
+
576
+ if delete
577
+ sql << " DELETE"
578
+ _merge_when_conditions_sql(sql, delete)
579
+ end
580
+ end
581
+
582
+ if insert
583
+ sql << " WHEN NOT MATCHED"
584
+ _merge_insert_sql(sql, insert)
585
+ _merge_when_conditions_sql(sql, insert)
586
+ end
587
+ end
588
+
589
+ # Handle Oracle's non-standard MERGE WHEN condition syntax.
590
+ def _merge_when_conditions_sql(sql, data)
591
+ if data.has_key?(:conditions)
592
+ sql << " WHERE "
593
+ literal_append(sql, _normalize_merge_when_conditions(data[:conditions]))
594
+ end
595
+ end
596
+
528
597
  # Allow preparing prepared statements, since determining the prepared sql to use for
529
598
  # a prepared statement requires calling prepare on that statement.
530
599
  def allow_preparing_prepared_statements?