sequel 5.58.0 → 5.60.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f02f6a35677e8af2ea2b164f655cc869213864e5b27f67cdd3e17c7db28c546
4
- data.tar.gz: ee09f1b4d5b3f4decbf4d4af3b8ae16ef7e488af40011808dac94f8ca05da89f
3
+ metadata.gz: cd8695696d6b55edf6337870f3ff172aeb6fe2ef9ba4bbc23daae0af26908649
4
+ data.tar.gz: 2483bbb3549adbcb9f179165119051b7bb234d6e714e4afa116c0ffddf379dc6
5
5
  SHA512:
6
- metadata.gz: 89efe5f1a7f2c2eedba5ac1cf336adee1abf489278f79413756e70a41e4ba073ea818562aaf176c1f092e7ab9e763693473a0d96edc6782d9da037c759afd05d
7
- data.tar.gz: 84986009ccb30a7a0fd15a555262631f539b95ef9ec8d74d6ca2a4078bd07c33ffda725092bad5afbc93b27901e67dc6d06ca735481448174ac695ef14b6ee99
6
+ metadata.gz: 0527cba1fcc06d8001cbd55473b3af5c23a9f5c004113fc24aa6f9e029c5ed2cf4f938bf184d79922164cdc3ea502d12860d46d64237bdbd306bf5ce50f59ffa
7
+ data.tar.gz: e13c17553592773e70c0e9a6753438444a5245f3cd0664c6c5a6c0a08a271d1d92180b110c7b7e5981f7ca6dc2b18d90da3feeb05a14ae863fcfd99d5673650e
data/CHANGELOG CHANGED
@@ -1,3 +1,35 @@
1
+ === 5.60.1 (2022-09-02)
2
+
3
+ * Revert conversion of respond_to? to defined?, as it breaks with unused refinements on Ruby 2 (jeremyevans) (#1919)
4
+
5
+ === 5.60.0 (2022-09-01)
6
+
7
+ * Support arbitrary expressions for date_arithmetic interval values on PostgreSQL 9.4+ (jeremyevans)
8
+
9
+ * Support native IS DISTINCT FROM on SQLite 3.39+ instead of emulating support in the is_distinct_from extension (jeremyevans)
10
+
11
+ * Support HAVING without GROUP BY on SQLite 3.39+ (jeremyevans)
12
+
13
+ * Convert most respond_to? calls to equivalent defined? for better performance (jeremyevans)
14
+
15
+ === 5.59.0 (2022-08-01)
16
+
17
+ * Set :allow_eager association option to false for instance specific associations without eager loaders (jeremyevans)
18
+
19
+ * Add require_valid_schema plugin for checking that model classes have schema parsed as expected (jeremyevans)
20
+
21
+ * Model classes created from aliased expressions and literal strings no longer use the simple table optimization (jeremyevans)
22
+
23
+ * Model code that does not swallow connection errors will now also not swallow disconnect errors (jeremyevans) (#1892)
24
+
25
+ * Add is_json and is_not_json methods to the pg_json_ops extension, for the PostgreSQL 15+ IS [NOT] JSON operator (jeremyevans)
26
+
27
+ * Support :security_invoker view option on PostgreSQL 15+, for views where access uses permissions of user instead of owner (jeremyevans)
28
+
29
+ * Support :nulls_distinct index option on PostgreSQL 15+, for NULLS [NOT] DISTINCT (jeremyevans)
30
+
31
+ * Support sequel-postgres-pr driver in the postgres adapter (jeremyevans)
32
+
1
33
  === 5.58.0 (2022-07-01)
2
34
 
3
35
  * Support :disable_split_materialized Database option on MySQL to work around optimizer bug in MariaDB 10.5+ affecting association tests (jeremyevans)
data/README.rdoc CHANGED
@@ -416,7 +416,7 @@ As with +delete+, +update+ affects all rows in the dataset, so +where+ first,
416
416
 
417
417
  === Merging records
418
418
 
419
- Merging records using the SQL MERGE statment is done using <tt>merge*</tt> methods.
419
+ Merging records using the SQL MERGE statement is done using <tt>merge*</tt> methods.
420
420
  You use +merge_using+ to specify the merge source and join conditions.
421
421
  You can use +merge_insert+, +merge_delete+, and/or +merge_update+ to set the
422
422
  INSERT, DELETE, and UPDATE clauses for the merge. +merge_insert+ takes the same
data/bin/sequel CHANGED
@@ -194,7 +194,11 @@ begin
194
194
  TO_DB = connect_proc[db2]
195
195
  same_db = DB.database_type==TO_DB.database_type
196
196
  index_opts = {:same_db=>same_db}
197
+
198
+ # :nocov:
197
199
  index_opts[:index_names] = :namespace if !DB.global_index_namespace? && TO_DB.global_index_namespace?
200
+ # :nocov:
201
+
198
202
  if DB.database_type == :sqlite && !same_db
199
203
  # SQLite integer types allows 64-bit integers
200
204
  TO_DB.extension :integer64
@@ -212,18 +216,20 @@ begin
212
216
  puts "Begin copying data"
213
217
  DB.transaction do
214
218
  TO_DB.transaction do
219
+ all_status_lines = ENV['SEQUEL_BIN_STATUS_ALL_LINES']
220
+
215
221
  DB.tables.each do |table|
216
222
  puts "Begin copying records for table: #{table}"
217
223
  time = Time.now
218
224
  to_ds = TO_DB.from(table)
219
225
  j = 0
220
226
  DB.from(table).each do |record|
221
- if Time.now - time > 5
227
+ to_ds.insert(record)
228
+ j += 1
229
+ if Time.now - time > 5 || all_status_lines
222
230
  puts "Status: #{j} records copied"
223
231
  time = Time.now
224
232
  end
225
- to_ds.insert(record)
226
- j += 1
227
233
  end
228
234
  puts "Finished copying #{j} records for table: #{table}"
229
235
  end
@@ -260,8 +266,10 @@ if !ARGV.empty?
260
266
  ARGV.each{|v| load(v)}
261
267
  elsif !$stdin.isatty
262
268
  eval($stdin.read)
269
+ # :nocov:
263
270
  else
264
271
  require 'irb'
265
272
  puts "Your database is stored in DB..."
266
273
  IRB.start
267
274
  end
275
+ # :nocov:
@@ -312,15 +312,14 @@ The following additional options are supported:
312
312
 
313
313
  === postgres
314
314
 
315
- Requires: pg (or postgres-pr/postgres-compat if pg is not available)
315
+ Requires: pg (or sequel/postgres-pr or postgres-pr/postgres-compat if pg is not available)
316
316
 
317
- The Sequel postgres adapter works with the pg and postgres-pr ruby libraries.
317
+ The Sequel postgres adapter works with the pg, sequel-postgres-pr, jeremyevans-postgres-pr, and postgres-pr ruby libraries.
318
318
  The pg library is the best supported, as it supports real bound variables and prepared statements.
319
319
  If the pg library is being used, Sequel will also attempt to load the sequel_pg library, which is
320
320
  a C extension that optimizes performance when Sequel is used with pg. All users of Sequel who
321
- use pg are encouraged to install sequel_pg. For users who want to use postgres-pr to avoid issues
322
- with C extensions, it is recommended to use jeremyevans-postgres-pr, which fixes many issues in
323
- the upstream postgres-pr gem, and is regularly tested with Sequel.
321
+ use pg are encouraged to install sequel_pg. For users who want to use one of the postgres-pr
322
+ libraries to avoid issues with C extensions, it is recommended to use sequel-postgres-pr.
324
323
 
325
324
  The following additional options are supported:
326
325
 
@@ -0,0 +1,73 @@
1
+ = New Features
2
+
3
+ * A require_valid_schema plugin has been added, for checking that
4
+ model classes have schema parsed as expected. By default, model
5
+ classes are not required to have valid schema, because it is
6
+ allowed to have model classes based on arbitrary datasets (such
7
+ as those using joins or set-returning functions), and it is not
8
+ possible to determine the schema for arbitary datasets.
9
+
10
+ Sequel swallows non-connection errors when trying to parse schema
11
+ for a model's dataset, but if schema parsing fails when you would
12
+ expect it to succeed, it results in a model where typecasting does
13
+ not work as expected.
14
+
15
+ The require_valid_schema plugin will raise an error when setting
16
+ the dataset for a model if schema parsing fails and the dataset
17
+ uses a simple table where you would expect schema parsing to
18
+ succeed. You can also provide an argument of :warn when loading
19
+ the plugin, to warn instead of raising an error.
20
+
21
+ This plugin may not work correctly in all cases for all adapters,
22
+ especially external adapters. Adapters are not required to support
23
+ schema parsing. Even if supported, adapters may not support
24
+ parsing schema for qualified tables, or parsing schema for views.
25
+ You should consider this plugin as a possible safety net. Users
26
+ are encouraged to try using it and report any unexpected breakage,
27
+ as that may help improve schema parsing in adapters that Sequel
28
+ ships.
29
+
30
+ * is_json and is_not_json methods have been added to the pg_json_ops
31
+ extension, for the IS [NOT] JSON operator supported in PostgreSQL
32
+ 15+.
33
+
34
+ * Index creation methods on PostgreSQL 15+ now support a
35
+ :nulls_distinct option, for NULLS [NOT] DISTINCT. This allows you
36
+ to create unique indexes where NULL values are not considered
37
+ distinct.
38
+
39
+ * View creation methods on PostgreSQL 15+ now support a
40
+ :security_invoker option to create a view where access is
41
+ determined by the permissions of the role that is accessing the
42
+ view, instead of the role that created the view.
43
+
44
+ = Other Improvements
45
+
46
+ * The :allow_eager association option is now set to false by default
47
+ for associations explicitly marked as :instance_specific, if the
48
+ :eager_loader association is not given.
49
+
50
+ * The postgres adapter now supports the sequel-postgres-pr driver.
51
+ The sequel-postgres-pr driver is a slimmed down fork of the
52
+ postgres-pr driver designed specifically for use by Sequel.
53
+
54
+ * Model code that explicitly does not swallow connection errors
55
+ will also now not swallow disconnect errors. This can fix issues
56
+ where model classes are being loaded at runtime, and the query to
57
+ get the columns/schema for the model uses a connection that has
58
+ been disconnected.
59
+
60
+ * Model classes created from aliased expressions and literal
61
+ strings no longer use the simple_table optimization, as there
62
+ are cases where doing so is not safe.
63
+
64
+ = Backwards Compatibility
65
+
66
+ * The change to not swallow disconnect errors when not swallowing
67
+ connection errors can result in exceptions being raised which
68
+ weren't raised previously. In most cases, this will alert you
69
+ to issues in your application that should be fixed, but it
70
+ potentially it can result in regressions if you were OK with
71
+ the errors being swallowed. If this does result in regressions
72
+ in your application, please file an issue and we can probably
73
+ add a setting controlling this feature.
@@ -0,0 +1,22 @@
1
+ = New Features
2
+
3
+ * The date_arithmetic extension now supports arbitrary expressions
4
+ as interval values on PostgreSQL 9.4+. Previously, only integers
5
+ were supported for the interval values.
6
+
7
+ = Other Improvements
8
+
9
+ * Most Kernel#respond_to? calls have been converted to equivalent
10
+ defined? calls for better performance. defined? is a keyword
11
+ and is about 50% faster for the same behavior.
12
+
13
+ * The is_distinct_from extension now supports the IS DISTINCT FROM
14
+ syntax natively on SQLite 3.39+, instead of emulating it.
15
+
16
+ * HAVING without GROUP BY is now supported on SQLite 3.39+.
17
+
18
+ * Coverage testing has been significantly expanded. Previously,
19
+ the core, model, plugin, and extension code had 100% line/branch
20
+ coverage. 100% line/branch coverage has been added for the
21
+ core extensions, bin/sequel, and the postgres adapter with the
22
+ pg driver.
@@ -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
@@ -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,12 +252,11 @@ 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
261
  if conn.respond_to?(:set_client_encoding)
237
262
  conn.set_client_encoding(encoding)
@@ -239,6 +264,7 @@ module Sequel
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
@@ -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)
584
+ # :nocov:
549
585
  if exception.respond_to?(:result) && (result = exception.result)
586
+ # :nocov:
550
587
  result.error_field(PGresult::PG_DIAG_SQLSTATE)
551
588
  end
552
589
  end
@@ -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:
@@ -230,7 +230,6 @@ module Sequel
230
230
  module DatabaseMethods
231
231
  include UnmodifiedIdentifiers::DatabaseMethods
232
232
 
233
- PREPARED_ARG_PLACEHOLDER = LiteralString.new('$').freeze
234
233
  FOREIGN_KEY_LIST_ON_DELETE_MAP = {'a'=>:no_action, 'r'=>:restrict, 'c'=>:cascade, 'n'=>:set_null, 'd'=>:set_default}.freeze
235
234
  ON_COMMIT = {:drop => 'DROP', :delete_rows => 'DELETE ROWS', :preserve_rows => 'PRESERVE ROWS'}.freeze
236
235
  ON_COMMIT.each_value(&:freeze)
@@ -365,9 +364,10 @@ module Sequel
365
364
 
366
365
  table_oid = regclass_oid(table)
367
366
  im = input_identifier_meth
368
- unless column = im.call(opts[:column] || ((sch = schema(table).find{|_, sc| sc[:primary_key] && sc[:auto_increment]}) && sch[0]))
367
+ unless column = (opts[:column] || ((sch = schema(table).find{|_, sc| sc[:primary_key] && sc[:auto_increment]}) && sch[0]))
369
368
  raise Error, "could not determine column to convert from serial to identity automatically"
370
369
  end
370
+ column = im.call(column)
371
371
 
372
372
  column_num = ds.from(:pg_attribute).
373
373
  where(:attrelid=>table_oid, :attname=>column).
@@ -569,10 +569,12 @@ module Sequel
569
569
  if server_version >= 90500
570
570
  cpos = Sequel.expr{array_position(co[:conkey], ctable[:attnum])}
571
571
  rpos = Sequel.expr{array_position(co[:confkey], rtable[:attnum])}
572
+ # :nocov:
572
573
  else
573
574
  range = 0...32
574
575
  cpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:conkey], [x]), x]}, 32, ctable[:attnum])}
575
576
  rpos = Sequel.expr{SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(co[:confkey], [x]), x]}, 32, rtable[:attnum])}
577
+ # :nocov:
576
578
  end
577
579
 
578
580
  ds = metadata_dataset.
@@ -653,9 +655,11 @@ module Sequel
653
655
 
654
656
  if server_version >= 90500
655
657
  order = [Sequel[:indc][:relname], Sequel.function(:array_position, Sequel[:ind][:indkey], Sequel[:att][:attnum])]
658
+ # :nocov:
656
659
  else
657
660
  range = 0...32
658
661
  order = [Sequel[:indc][:relname], SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(Sequel[:ind][:indkey], [x]), x]}, 32, Sequel[:att][:attnum])]
662
+ # :nocov:
659
663
  end
660
664
 
661
665
  attnums = SQL::Function.new(:ANY, Sequel[:ind][:indkey])
@@ -676,8 +680,10 @@ module Sequel
676
680
  select{[indc[:relname].as(:name), ind[:indisunique].as(:unique), att[:attname].as(:column), con[:condeferrable].as(:deferrable)]}
677
681
 
678
682
  ds = ds.where(:indpred=>nil) unless opts[:include_partial]
683
+ # :nocov:
679
684
  ds = ds.where(:indisready=>true) if server_version >= 80300
680
685
  ds = ds.where(:indislive=>true) if server_version >= 90300
686
+ # :nocov:
681
687
 
682
688
  indexes = {}
683
689
  ds.each do |r|
@@ -758,10 +764,12 @@ module Sequel
758
764
  seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
759
765
  increment_by = :seqincrement
760
766
  min_value = :seqmin
767
+ # :nocov:
761
768
  else
762
769
  seq_ds = metadata_dataset.from(LiteralString.new(seq))
763
770
  increment_by = :increment_by
764
771
  min_value = :min_value
772
+ # :nocov:
765
773
  end
766
774
 
767
775
  get{setval(seq, db[table].select(coalesce(max(pk)+seq_ds.select(increment_by), seq_ds.select(min_value))), false)}
@@ -774,7 +782,9 @@ module Sequel
774
782
  # PostgreSQL uses SERIAL psuedo-type instead of AUTOINCREMENT for
775
783
  # managing incrementing primary keys.
776
784
  def serial_primary_key_options
785
+ # :nocov:
777
786
  auto_increment_key = server_version >= 100002 ? :identity : :serial
787
+ # :nocov:
778
788
  {:primary_key => true, auto_increment_key => true, :type=>Integer}
779
789
  end
780
790
 
@@ -1152,7 +1162,7 @@ module Sequel
1152
1162
  when :hash
1153
1163
  mod, remainder = generator.hash_values
1154
1164
  sql << " FOR VALUES WITH (MODULUS #{literal(mod)}, REMAINDER #{literal(remainder)})"
1155
- when :default
1165
+ else # when :default
1156
1166
  sql << " DEFAULT"
1157
1167
  end
1158
1168
 
@@ -1247,6 +1257,10 @@ module Sequel
1247
1257
  def create_view_prefix_sql(name, options)
1248
1258
  sql = create_view_sql_append_columns("CREATE #{'OR REPLACE 'if options[:replace]}#{'TEMPORARY 'if options[:temp]}#{'RECURSIVE ' if options[:recursive]}#{'MATERIALIZED ' if options[:materialized]}VIEW #{quote_schema_table(name)}", options[:columns] || options[:recursive])
1249
1259
 
1260
+ if options[:security_invoker]
1261
+ sql += " WITH (security_invoker)"
1262
+ end
1263
+
1250
1264
  if tablespace = options[:tablespace]
1251
1265
  sql += " TABLESPACE #{quote_identifier(tablespace)}"
1252
1266
  end
@@ -1304,16 +1318,20 @@ module Sequel
1304
1318
  def index_definition_sql(table_name, index)
1305
1319
  cols = index[:columns]
1306
1320
  index_name = index[:name] || default_index_name(table_name, cols)
1321
+
1307
1322
  expr = if o = index[:opclass]
1308
1323
  "(#{Array(cols).map{|c| "#{literal(c)} #{o}"}.join(', ')})"
1309
1324
  else
1310
1325
  literal(Array(cols))
1311
1326
  end
1327
+
1312
1328
  if_not_exists = " IF NOT EXISTS" if index[:if_not_exists]
1313
1329
  unique = "UNIQUE " if index[:unique]
1314
1330
  index_type = index[:type]
1315
1331
  filter = index[:where] || index[:filter]
1316
1332
  filter = " WHERE #{filter_expr(filter)}" if filter
1333
+ nulls_distinct = " NULLS#{' NOT' if index[:nulls_distinct] == false} DISTINCT" unless index[:nulls_distinct].nil?
1334
+
1317
1335
  case index_type
1318
1336
  when :full_text
1319
1337
  expr = "(to_tsvector(#{literal(index[:language] || 'simple')}::regconfig, #{literal(dataset.send(:full_text_string_join, cols))}))"
@@ -1321,7 +1339,8 @@ module Sequel
1321
1339
  when :spatial
1322
1340
  index_type = :gist
1323
1341
  end
1324
- "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
1342
+
1343
+ "CREATE #{unique}INDEX#{' CONCURRENTLY' if index[:concurrently]}#{if_not_exists} #{quote_identifier(index_name)} ON #{quote_schema_table(table_name)} #{"USING #{index_type} " if index_type}#{expr}#{" INCLUDE #{literal(Array(index[:include]))}" if index[:include]}#{nulls_distinct}#{" TABLESPACE #{quote_identifier(index[:tablespace])}" if index[:tablespace]}#{filter}"
1325
1344
  end
1326
1345
 
1327
1346
  # Setup datastructures shared by all postgres adapters.
@@ -1347,11 +1366,6 @@ module Sequel
1347
1366
  end
1348
1367
  end
1349
1368
 
1350
- # Use a dollar sign instead of question mark for the argument placeholder.
1351
- def prepared_arg_placeholder
1352
- PREPARED_ARG_PLACEHOLDER
1353
- end
1354
-
1355
1369
  # Return an expression the oid for the table expr. Used by the metadata parsing
1356
1370
  # code to disambiguate unqualified tables.
1357
1371
  def regclass_oid(expr, opts=OPTS)
@@ -1425,10 +1439,14 @@ module Sequel
1425
1439
  where{{pg_class[:oid]=>oid}}.
1426
1440
  order{pg_attribute[:attnum]}
1427
1441
 
1442
+ # :nocov:
1428
1443
  if server_version > 100000
1444
+ # :nocov:
1429
1445
  ds = ds.select_append{pg_attribute[:attidentity]}
1430
1446
 
1447
+ # :nocov:
1431
1448
  if server_version > 120000
1449
+ # :nocov:
1432
1450
  ds = ds.select_append{Sequel.~(pg_attribute[:attgenerated]=>'').as(:generated)}
1433
1451
  end
1434
1452
  end
@@ -1516,7 +1534,9 @@ module Sequel
1516
1534
 
1517
1535
  # PostgreSQL 9.4+ supports views with check option.
1518
1536
  def view_with_check_option_support
1537
+ # :nocov:
1519
1538
  :local if server_version >= 90400
1539
+ # :nocov:
1520
1540
  end
1521
1541
  end
1522
1542
 
@@ -1912,6 +1932,8 @@ module Sequel
1912
1932
  server_version >= 90000
1913
1933
  when :groups, :exclude
1914
1934
  server_version >= 110000
1935
+ else
1936
+ false
1915
1937
  end
1916
1938
  end
1917
1939
 
@@ -2058,12 +2080,11 @@ module Sequel
2058
2080
 
2059
2081
  # Return the primary key to use for RETURNING in an INSERT statement
2060
2082
  def insert_pk
2061
- if (f = opts[:from]) && !f.empty?
2062
- case t = f.first
2063
- when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
2064
- if pk = db.primary_key(t)
2065
- Sequel::SQL::Identifier.new(pk)
2066
- end
2083
+ (f = opts[:from]) && !f.empty? && (t = f.first)
2084
+ case t
2085
+ when Symbol, String, SQL::Identifier, SQL::QualifiedIdentifier
2086
+ if pk = db.primary_key(t)
2087
+ Sequel::SQL::Identifier.new(pk)
2067
2088
  end
2068
2089
  end
2069
2090
  end
@@ -663,7 +663,7 @@ module Sequel
663
663
 
664
664
  # HAVING requires GROUP BY on SQLite
665
665
  def having(*cond)
666
- raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") unless @opts[:group]
666
+ raise(InvalidOperation, "Can only specify a HAVING clause on a grouped dataset") if !@opts[:group] && db.sqlite_version < 33900
667
667
  super
668
668
  end
669
669
 
@@ -262,6 +262,7 @@ module Sequel
262
262
  # operations on the table while the index is being
263
263
  # built.
264
264
  # :if_not_exists :: Only create the index if an index of the same name doesn't already exist.
265
+ # :nulls_distinct :: Set whether separate NULLs should be considered distinct values in unique indexes.
265
266
  # :opclass :: Set an opclass to use for all columns (per-column opclasses require
266
267
  # custom SQL).
267
268
  # :tablespace :: Specify tablespace for index.
@@ -295,6 +295,9 @@ module Sequel
295
295
  # in a subquery, if you are providing a Dataset as the source
296
296
  # argument, if should probably call the union method with the
297
297
  # all: true and from_self: false options.
298
+ # :security_invoker :: Set the security_invoker property on the view, making
299
+ # the access to the view use the current user's permissions,
300
+ # instead of the view owner's permissions.
298
301
  # :tablespace :: The tablespace to use for materialized views.
299
302
  def create_view(name, source, options = OPTS)
300
303
  execute_ddl(create_view_sql(name, source, options))
@@ -32,6 +32,10 @@
32
32
  #
33
33
  # DB[:table].select(add.as(:d)).where(sub > Sequel::CURRENT_TIMESTAMP)
34
34
  #
35
+ # On most databases, the values you provide for years/months/days/etc. must
36
+ # be numeric values and not arbitrary SQL expressions. However, on PostgreSQL
37
+ # 9.4+, use of arbitrary SQL expressions is supported.
38
+ #
35
39
  # Related module: Sequel::SQL::DateAdd
36
40
 
37
41
  #
@@ -54,7 +58,16 @@ module Sequel
54
58
  interval = interval.parts
55
59
  end
56
60
  parts = {}
57
- interval.each{|k,v| parts[k] = -v unless v.nil?}
61
+ interval.each do |k,v|
62
+ case v
63
+ when nil
64
+ # ignore
65
+ when Numeric
66
+ parts[k] = -v
67
+ else
68
+ parts[k] = Sequel::SQL::NumericExpression.new(:*, v, -1)
69
+ end
70
+ end
58
71
  DateAdd.new(expr, parts, opts)
59
72
  end
60
73
  end
@@ -68,6 +81,7 @@ module Sequel
68
81
  module DatasetMethods
69
82
  DURATION_UNITS = [:years, :months, :days, :hours, :minutes, :seconds].freeze
70
83
  DEF_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s.freeze}).freeze
84
+ POSTGRES_DURATION_UNITS = DURATION_UNITS.zip([:years, :months, :days, :hours, :mins, :secs].map{|s| s.to_s.freeze}).freeze
71
85
  MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
72
86
  MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
73
87
  H2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| s.to_s[0...-1].freeze}).freeze
@@ -87,14 +101,28 @@ module Sequel
87
101
 
88
102
  cast = case db_type = db.database_type
89
103
  when :postgres
90
- interval = String.new
91
- each_valid_interval_unit(h, DEF_DURATION_UNITS) do |value, sql_unit|
92
- interval << "#{value} #{sql_unit} "
104
+ casted = Sequel.cast(expr, cast_type)
105
+
106
+ if db.server_version >= 90400
107
+ placeholder = []
108
+ vals = []
109
+ each_valid_interval_unit(h, POSTGRES_DURATION_UNITS) do |value, sql_unit|
110
+ placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
111
+ vals << value
112
+ end
113
+ interval = Sequel.function(:make_interval, Sequel.lit(placeholder, *vals)) unless vals.empty?
114
+ else
115
+ parts = String.new
116
+ each_valid_interval_unit(h, DEF_DURATION_UNITS) do |value, sql_unit|
117
+ parts << "#{value} #{sql_unit} "
118
+ end
119
+ interval = Sequel.cast(parts, :interval) unless parts.empty?
93
120
  end
94
- if interval.empty?
95
- return literal_append(sql, Sequel.cast(expr, cast_type))
121
+
122
+ if interval
123
+ return complex_expression_sql_append(sql, :+, [casted, interval])
96
124
  else
97
- return complex_expression_sql_append(sql, :+, [Sequel.cast(expr, cast_type), Sequel.cast(interval, :interval)])
125
+ return literal_append(sql, casted)
98
126
  end
99
127
  when :sqlite
100
128
  args = [expr]
@@ -3,7 +3,7 @@
3
3
  # The is_distinct_from extension adds the ability to use the
4
4
  # SQL standard IS DISTINCT FROM operator, which is similar to the
5
5
  # not equals operator, except that NULL values are considered
6
- # equal. Only PostgreSQL and H2 currently support this operator. On
6
+ # equal. PostgreSQL, SQLite 3.39+, and H2 currently support this operator. On
7
7
  # other databases, support is emulated.
8
8
  #
9
9
  # First, you need to load the extension into the database:
@@ -90,6 +90,8 @@ module Sequel
90
90
  case db.database_type
91
91
  when :postgres, :h2
92
92
  true
93
+ when :sqlite
94
+ db.sqlite_version >= 33900
93
95
  else
94
96
  false
95
97
  end
@@ -123,6 +123,15 @@
123
123
  # c = Sequel.pg_jsonb_op(:c)
124
124
  # DB[:t].update(c['key1'] => 1.to_json, c['key2'] => "a".to_json)
125
125
  #
126
+ # On PostgreSQL 15+, the <tt>IS [NOT] JSON</tt> operator is supported:
127
+ #
128
+ # j.is_json # j IS JSON
129
+ # j.is_json(type: :object) # j IS JSON OBJECT
130
+ # j.is_json(type: :object, unique: true) # j IS JSON OBJECT WITH UNIQUE
131
+ # j.is_not_json # j IS NOT JSON
132
+ # j.is_json(type: :array) # j IS NOT JSON ARRAY
133
+ # j.is_json(unique: true) # j IS NOT JSON WITH UNIQUE
134
+ #
126
135
  # If you are also using the pg_json extension, you should load it before
127
136
  # loading this extension. Doing so will allow you to use the #op method on
128
137
  # JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
@@ -151,6 +160,18 @@ module Sequel
151
160
  GET_PATH = ["(".freeze, " #> ".freeze, ")".freeze].freeze
152
161
  GET_PATH_TEXT = ["(".freeze, " #>> ".freeze, ")".freeze].freeze
153
162
 
163
+ IS_JSON = ["(".freeze, " IS JSON".freeze, "".freeze, ")".freeze].freeze
164
+ IS_NOT_JSON = ["(".freeze, " IS NOT JSON".freeze, "".freeze, ")".freeze].freeze
165
+ EMPTY_STRING = Sequel::LiteralString.new('').freeze
166
+ WITH_UNIQUE = Sequel::LiteralString.new(' WITH UNIQUE').freeze
167
+ IS_JSON_MAP = {
168
+ nil => EMPTY_STRING,
169
+ :value => Sequel::LiteralString.new(' VALUE').freeze,
170
+ :scalar => Sequel::LiteralString.new(' SCALAR').freeze,
171
+ :object => Sequel::LiteralString.new(' OBJECT').freeze,
172
+ :array => Sequel::LiteralString.new(' ARRAY').freeze
173
+ }.freeze
174
+
154
175
  # Get JSON array element or object field as json. If an array is given,
155
176
  # gets the object at the specified path.
156
177
  #
@@ -233,6 +254,30 @@ module Sequel
233
254
  end
234
255
  end
235
256
 
257
+ # Return whether the json object can be parsed as JSON.
258
+ #
259
+ # Options:
260
+ # :type :: Check whether the json object can be parsed as a specific type
261
+ # of JSON (:value, :scalar, :object, :array).
262
+ # :unique :: Check JSON objects for unique keys.
263
+ #
264
+ # json_op.is_json # json IS JSON
265
+ # json_op.is_json(type: :object) # json IS JSON OBJECT
266
+ # json_op.is_json(unique: true) # json IS JSON WITH UNIQUE
267
+ def is_json(opts=OPTS)
268
+ _is_json(IS_JSON, opts)
269
+ end
270
+
271
+ # Return whether the json object cannot be parsed as JSON. The opposite
272
+ # of #is_json. See #is_json for options.
273
+ #
274
+ # json_op.is_not_json # json IS NOT JSON
275
+ # json_op.is_not_json(type: :object) # json IS NOT JSON OBJECT
276
+ # json_op.is_not_json(unique: true) # json IS NOT JSON WITH UNIQUE
277
+ def is_not_json(opts=OPTS)
278
+ _is_json(IS_NOT_JSON, opts)
279
+ end
280
+
236
281
  # Returns a set of keys AS text in the json object.
237
282
  #
238
283
  # json_op.keys # json_object_keys(json)
@@ -286,6 +331,13 @@ module Sequel
286
331
 
287
332
  private
288
333
 
334
+ # Internals of IS [NOT] JSON support
335
+ def _is_json(lit_array, opts)
336
+ raise Error, "invalid is_json :type option: #{opts[:type].inspect}" unless type = IS_JSON_MAP[opts[:type]]
337
+ unique = opts[:unique] ? WITH_UNIQUE : EMPTY_STRING
338
+ Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(lit_array, [self, type, unique]))
339
+ end
340
+
289
341
  # Return a placeholder literal with the given str and args, wrapped
290
342
  # in an JSONOp or JSONBOp, used by operators that return json or jsonb.
291
343
  def json_op(str, args)
@@ -35,6 +35,7 @@ if RUBY_VERSION >= '2.0'
35
35
  class Symbol
36
36
  prepend Sequel::SymbolAref
37
37
  end
38
+ # :nocov:
38
39
  else
39
40
  class Symbol
40
41
  if method_defined?(:[])
@@ -51,3 +52,4 @@ else
51
52
  end
52
53
  end
53
54
  end
55
+ # :nocov:
@@ -1717,6 +1717,8 @@ module Sequel
1717
1717
  # :graph_select :: A column or array of columns to select from the associated table
1718
1718
  # when eagerly loading the association via +eager_graph+. Defaults to all
1719
1719
  # columns in the associated table.
1720
+ # :instance_specific :: Marks the association as instance specific. Should be used if the association block
1721
+ # uses instance specific state, or transient state (accessing current date/time, etc.).
1720
1722
  # :limit :: Limit the number of records to the provided value. Use
1721
1723
  # an array with two elements for the value to specify a
1722
1724
  # limit (first element) and an offset (second element).
@@ -1856,6 +1858,16 @@ module Sequel
1856
1858
  # in certain places to disable optimizations.
1857
1859
  opts[:instance_specific] = _association_instance_specific_default(name)
1858
1860
  end
1861
+ if (orig_opts[:instance_specific] || orig_opts[:dataset]) && !opts.has_key?(:allow_eager) && !opts[:eager_loader]
1862
+ # For associations explicitly marked as instance specific, or that use the
1863
+ # :dataset option, where :allow_eager is not set, and no :eager_loader is
1864
+ # provided, disallow eager loading. In these cases, eager loading is
1865
+ # unlikely to work. This is not done for implicit setting of :instance_specific,
1866
+ # because implicit use is done by default for all associations with blocks,
1867
+ # and the vast majority of associations with blocks use the block for filtering
1868
+ # in a manner compatible with eager loading.
1869
+ opts[:allow_eager] = false
1870
+ end
1859
1871
  opts = assoc_class.new.merge!(opts)
1860
1872
 
1861
1873
  if opts[:clone] && !opts.cloneable?(cloned_assoc)
@@ -680,10 +680,11 @@ module Sequel
680
680
 
681
681
  private
682
682
 
683
- # Yield to the passed block and if do_raise is false, swallow all errors other than DatabaseConnectionErrors.
683
+ # Yield to the passed block and if do_raise is false, swallow Sequel::Errors other than DatabaseConnectionError
684
+ # and DatabaseDisconnectError.
684
685
  def check_non_connection_error(do_raise=require_valid_table)
685
686
  db.transaction(:savepoint=>:only){yield}
686
- rescue Sequel::DatabaseConnectionError
687
+ rescue Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError
687
688
  raise
688
689
  rescue Sequel::Error
689
690
  raise if do_raise
@@ -693,9 +694,12 @@ module Sequel
693
694
  # this model's dataset.
694
695
  def convert_input_dataset(ds)
695
696
  case ds
696
- when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
697
+ when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
697
698
  self.simple_table = db.literal(ds).freeze
698
699
  ds = db.from(ds)
700
+ when SQL::AliasedExpression, LiteralString
701
+ self.simple_table = nil
702
+ ds = db.from(ds)
699
703
  when Dataset
700
704
  ds = ds.from_self(:alias=>ds.first_source) if ds.joined_dataset?
701
705
 
@@ -784,7 +788,7 @@ module Sequel
784
788
  schema_hash = {}
785
789
  ds_opts = dataset.opts
786
790
  get_columns = proc{check_non_connection_error{columns} || []}
787
- schema_array = check_non_connection_error(false){db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
791
+ schema_array = get_db_schema_array(reload) if db.supports_schema_parsing?
788
792
  if schema_array
789
793
  schema_array.each{|k,v| schema_hash[k] = v}
790
794
 
@@ -821,6 +825,12 @@ module Sequel
821
825
  schema_hash
822
826
  end
823
827
 
828
+ # Get the array of schema information for the dataset. Returns nil if
829
+ # the schema information cannot be determined.
830
+ def get_db_schema_array(reload)
831
+ check_non_connection_error(false){db.schema(dataset, :reload=>reload)}
832
+ end
833
+
824
834
  # Uncached version of setter_methods, to be overridden by plugins
825
835
  # that want to modify the methods used.
826
836
  def get_setter_methods
@@ -33,7 +33,9 @@ module Sequel
33
33
  # You can provide a <tt>:scope</tt> option to scope the list. This option
34
34
  # can be a symbol or array of symbols specifying column name(s), or a proc
35
35
  # that accepts a model instance and returns a dataset representing the list
36
- # the object is in.
36
+ # the object is in. You will need to provide a <tt>:scope</tt> option if
37
+ # the model's dataset uses a subquery (such as when using the class_table_inheritance
38
+ # plugin).
37
39
  #
38
40
  # For example, if each item has a +user_id+ field, and you want every user
39
41
  # to have their own list:
@@ -0,0 +1,67 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The require_valid_schema plugin makes Sequel raise or warn if attempting
6
+ # to set the dataset of a model class to a simple table, where the database
7
+ # supports schema parsing, but schema parsing does not work for the model's
8
+ # table.
9
+ #
10
+ # The plugin's default behavior requires that all models that select from a
11
+ # single identifier have a valid table schema, if the database supports
12
+ # schema parsing. If the schema cannot be determined for such
13
+ # a model, an error is raised:
14
+ #
15
+ # Sequel::Model.plugin :require_valid_schema
16
+ #
17
+ # If you load the plugin with an argument of :warn, Sequel will warn instead
18
+ # of raising for such tables:
19
+ #
20
+ # Sequel::Model.plugin :require_valid_schema, :warn
21
+ #
22
+ # This can catch bugs where you expect models to have valid schema, but
23
+ # they do not. This setting only affects future attempts to set datasets
24
+ # in the current class and subclasses created in the future.
25
+ #
26
+ # If you load the plugin with an argument of false, it will not require valid schema.
27
+ # This can be used in subclasses where you do not want to require valid schema,
28
+ # but the plugin must be loaded before a dataset with invalid schema is set:
29
+ #
30
+ # Sequel::Model.plugin :require_valid_schema
31
+ # InvalidSchemaAllowed = Class.new(Sequel::Model)
32
+ # InvalidSchemaAllowed.plugin :require_valid_schema, false
33
+ # class MyModel < InvalidSchemaAllowed
34
+ # end
35
+ module RequireValidSchema
36
+ # Modify the current model's dataset selection, if the model
37
+ # has a dataset.
38
+ def self.configure(model, setting=true)
39
+ model.instance_variable_set(:@require_valid_schema, setting)
40
+ end
41
+
42
+ module ClassMethods
43
+ Plugins.inherited_instance_variables(self, :@require_valid_schema=>nil)
44
+
45
+ private
46
+
47
+ # If the schema cannot be determined, the model uses a simple table,
48
+ # require_valid_schema is set, and the database supports schema parsing, raise or
49
+ # warn based on the require_valid_schema setting.
50
+ def get_db_schema_array(reload)
51
+ schema_array = super
52
+
53
+ if !schema_array && simple_table && @require_valid_schema
54
+ message = "Not able to parse schema for model: #{inspect}, table: #{simple_table}"
55
+ if @require_valid_schema == :warn
56
+ warn message
57
+ else
58
+ raise Error, message
59
+ end
60
+ end
61
+
62
+ schema_array
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -63,7 +63,7 @@ module Sequel
63
63
  end
64
64
  end
65
65
  # :nocov:
66
- ruby2_keywords(meth) if respond_to?(:ruby2_keywords, false)
66
+ mod.send(:ruby2_keywords, meth) if mod.respond_to?(:ruby2_keywords, true)
67
67
  # :nocov:
68
68
  end
69
69
 
@@ -97,7 +97,7 @@ module Sequel
97
97
  end
98
98
  end
99
99
  # :nocov:
100
- ruby2_keywords(meth) if respond_to?(:ruby2_keywords, false)
100
+ ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
101
101
  # :nocov:
102
102
  end
103
103
 
@@ -129,7 +129,7 @@ module Sequel
129
129
  end
130
130
  end
131
131
  # :nocov:
132
- ruby2_keywords(meth) if respond_to?(:ruby2_keywords, false)
132
+ ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
133
133
  # :nocov:
134
134
  end
135
135
 
@@ -159,7 +159,7 @@ module Sequel
159
159
  end
160
160
  end
161
161
  # :nocov:
162
- ruby2_keywords(meth) if respond_to?(:ruby2_keywords, false)
162
+ ruby2_keywords(meth) if respond_to?(:ruby2_keywords, true)
163
163
  # :nocov:
164
164
  end
165
165
 
@@ -109,6 +109,13 @@ module Sequel
109
109
  # to eagerly set the associated objects, and having separate threads
110
110
  # modify the same model instance is not thread-safe.
111
111
  #
112
+ # Because this plugin will automatically use eager loading for
113
+ # performance, it can break code that defines associations that
114
+ # do not support eager loading, without marking that they do not
115
+ # support eager loading via the <tt>allow_eager: false</tt> option.
116
+ # Make sure to set <tt>allow_eager: false</tt> on any association
117
+ # used with this plugin if the association doesn't support eager loading.
118
+ #
112
119
  # Usage:
113
120
  #
114
121
  # # Make all model subclass instances use tactical eager loading (called before loading subclasses)
@@ -6,11 +6,11 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 58
9
+ MINOR = 60
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
13
- TINY = 0
13
+ TINY = 1
14
14
 
15
15
  # The version of Sequel you are using, as a string (e.g. "2.11.0")
16
16
  VERSION = [MAJOR, MINOR, TINY].join('.').freeze
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.58.0
4
+ version: 5.60.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-01 00:00:00.000000000 Z
11
+ date: 2022-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -189,7 +189,9 @@ extra_rdoc_files:
189
189
  - doc/release_notes/5.56.0.txt
190
190
  - doc/release_notes/5.57.0.txt
191
191
  - doc/release_notes/5.58.0.txt
192
+ - doc/release_notes/5.59.0.txt
192
193
  - doc/release_notes/5.6.0.txt
194
+ - doc/release_notes/5.60.0.txt
193
195
  - doc/release_notes/5.7.0.txt
194
196
  - doc/release_notes/5.8.0.txt
195
197
  - doc/release_notes/5.9.0.txt
@@ -275,7 +277,9 @@ files:
275
277
  - doc/release_notes/5.56.0.txt
276
278
  - doc/release_notes/5.57.0.txt
277
279
  - doc/release_notes/5.58.0.txt
280
+ - doc/release_notes/5.59.0.txt
278
281
  - doc/release_notes/5.6.0.txt
282
+ - doc/release_notes/5.60.0.txt
279
283
  - doc/release_notes/5.7.0.txt
280
284
  - doc/release_notes/5.8.0.txt
281
285
  - doc/release_notes/5.9.0.txt
@@ -527,6 +531,7 @@ files:
527
531
  - lib/sequel/plugins/prepared_statements.rb
528
532
  - lib/sequel/plugins/prepared_statements_safe.rb
529
533
  - lib/sequel/plugins/rcte_tree.rb
534
+ - lib/sequel/plugins/require_valid_schema.rb
530
535
  - lib/sequel/plugins/serialization.rb
531
536
  - lib/sequel/plugins/serialization_modification_detection.rb
532
537
  - lib/sequel/plugins/sharding.rb