sequel 5.57.0 → 5.60.0

Sign up to get free protection for your applications and to get access to all the features.
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?