sequel 5.58.0 → 5.60.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG +32 -0
- data/README.rdoc +1 -1
- data/bin/sequel +11 -3
- data/doc/opening_databases.rdoc +4 -5
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/lib/sequel/adapters/postgres.rb +52 -11
- data/lib/sequel/adapters/shared/postgres.rb +36 -15
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +3 -0
- data/lib/sequel/extensions/date_arithmetic.rb +35 -7
- data/lib/sequel/extensions/is_distinct_from.rb +3 -1
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +12 -0
- data/lib/sequel/model/base.rb +14 -4
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/sql_comments.rb +4 -4
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -0
- data/lib/sequel/version.rb +2 -2
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd8695696d6b55edf6337870f3ff172aeb6fe2ef9ba4bbc23daae0af26908649
|
4
|
+
data.tar.gz: 2483bbb3549adbcb9f179165119051b7bb234d6e714e4afa116c0ffddf379dc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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:
|
data/doc/opening_databases.rdoc
CHANGED
@@ -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
|
322
|
-
with C extensions, it is recommended to use
|
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
|
27
|
-
Sequel::Postgres::USES_PG = false
|
30
|
+
require 'sequel/postgres-pr'
|
28
31
|
rescue LoadError
|
29
|
-
|
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
|
-
|
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
|
-
|
78
|
-
#
|
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 =
|
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
|
-
|
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
|
-
|
2062
|
-
|
2063
|
-
|
2064
|
-
|
2065
|
-
|
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")
|
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
|
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
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
95
|
-
|
121
|
+
|
122
|
+
if interval
|
123
|
+
return complex_expression_sql_append(sql, :+, [casted, interval])
|
96
124
|
else
|
97
|
-
return
|
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.
|
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)
|
@@ -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)
|
data/lib/sequel/model/base.rb
CHANGED
@@ -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
|
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
|
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 =
|
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
|
data/lib/sequel/plugins/list.rb
CHANGED
@@ -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
|
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,
|
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,
|
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,
|
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)
|
data/lib/sequel/version.rb
CHANGED
@@ -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 =
|
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 =
|
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.
|
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-
|
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
|