sequel 5.48.0 → 5.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +20 -0
- data/README.rdoc +2 -1
- data/doc/postgresql.rdoc +8 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +2 -2
- data/lib/sequel/adapters/mysql.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/postgres.rb +10 -8
- data/lib/sequel/adapters/shared/postgres.rb +24 -3
- data/lib/sequel/adapters/shared/sqlite.rb +1 -1
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/database/connecting.rb +2 -2
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/query.rb +3 -2
- data/lib/sequel/dataset/sql.rb +9 -4
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +52 -2
- data/lib/sequel/extensions/pg_json_ops.rb +70 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/model/base.rb +1 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_validations.rb +25 -5
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_helpers.rb +7 -1
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb8660c16f1da94fc18fcc5e983437d5d7bbebd2484a67af4d266c0245045a0c
|
4
|
+
data.tar.gz: 9844b032cae12dba23e2719c87991ef6ffbdf9f0b793dcef201336eda8f4d8d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73fe26957ca392a3d9f0a88a786bbfd03fe1a81053354882469b7b7f4efc1a24784c792a8ac4bc5f7510519619e01c06bd1abfbb858a307b1559619453588e9a
|
7
|
+
data.tar.gz: 561b46e5e32cbbbb33851133858bb5049d8eeb69a67e86d24009e94c6e089e0c12d1e92769cb77906cd290afcc58e832abce686cce305c92abdb45b18f36781c
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
=== 5.49.0 (2021-10-01)
|
2
|
+
|
3
|
+
* Switch block_given? usage to defined?(yield) (jeremyevans)
|
4
|
+
|
5
|
+
* Support table aliases for JOIN USING columns on PostgreSQL 14+ (jeremyevans)
|
6
|
+
|
7
|
+
* Support calling PostgreSQL procedures without arguments (jeremyevans)
|
8
|
+
|
9
|
+
* Support hstore subscripts in pg_hstore_ops on PostgreSQL 14+, for updating only part of an hstore value (jeremyevans)
|
10
|
+
|
11
|
+
* Support JSONB subscripts in pg_json_ops on PostgreSQL 14+, for updating only part of a JSONB value (jeremyevans)
|
12
|
+
|
13
|
+
* Support SQL::Expression#sequel_ast_transform for custom AST transforms on arbitrary expressions (jeremyevans)
|
14
|
+
|
15
|
+
* Add Database#create_trigger :replace option on PostgreSQL 14+ for CREATE OR REPLACE TRIGGER (jeremyevans)
|
16
|
+
|
17
|
+
* Make auto_validations plugin automatically setup no_null_byte validations (jeremyevans)
|
18
|
+
|
19
|
+
* Add Model#validates_no_null_byte to validation_helpers plugin (jeremyevans)
|
20
|
+
|
1
21
|
=== 5.48.0 (2021-09-01)
|
2
22
|
|
3
23
|
* Make the unused_associations plugin association reflection tracking work correctly when combining coverage runs (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -894,7 +894,8 @@ in the most current release.
|
|
894
894
|
Sequel fully supports the currently supported versions of Ruby (MRI) and JRuby. It may
|
895
895
|
support unsupported versions of Ruby or JRuby, but such support may be dropped in any
|
896
896
|
minor version if keeping it becomes a support issue. The minimum Ruby version
|
897
|
-
required to run the current version of Sequel is 1.9.2
|
897
|
+
required to run the current version of Sequel is 1.9.2, and the minimum JRuby version is
|
898
|
+
9.0.0.0.
|
898
899
|
|
899
900
|
== Maintainer
|
900
901
|
|
data/doc/postgresql.rdoc
CHANGED
@@ -428,6 +428,14 @@ rows that are distinct on just those columns:
|
|
428
428
|
DB[:table].distinct(:id).all
|
429
429
|
# SELECT DISTINCT ON ("id") * FROM "table"
|
430
430
|
|
431
|
+
=== JOIN USING table alias
|
432
|
+
|
433
|
+
Sequel allows passing an SQL::AliasedExpression to join table methods to use a USING
|
434
|
+
join with a table alias for the USING columns:
|
435
|
+
|
436
|
+
DB[:t1].join(:t2, Sequel.as([:c1, :c2], :alias))
|
437
|
+
# SELECT * FROM "t1" INNER JOIN "t2" USING ("c1", "c2") AS "alias"
|
438
|
+
|
431
439
|
=== Calling PostgreSQL 11+ Procedures <tt>postgres only</tt>
|
432
440
|
|
433
441
|
PostgreSQL 11+ added support for procedures, which are different from the user defined
|
@@ -0,0 +1,59 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Model#validates_no_null_byte has been added to the
|
4
|
+
validation_helpers. It checks that the value being validated does
|
5
|
+
not contain an ASCII NUL ('\0') byte. Some databases will return an
|
6
|
+
error if a string contains a NUL byte.
|
7
|
+
|
8
|
+
The auto_validations plugin will now automatically add no_null_byte
|
9
|
+
validations for all string columns in the model's table. This will
|
10
|
+
change exceptions raised by NUL bytes from database errors to
|
11
|
+
validation failures.
|
12
|
+
|
13
|
+
If you are using auto_validations and would like to have a table
|
14
|
+
accept NUL bytes in string columns, use the following code inside
|
15
|
+
the model:
|
16
|
+
|
17
|
+
skip_auto_validations(:no_null_byte)
|
18
|
+
|
19
|
+
* JSONB subscripts are now supported on PostgreSQL 14+ when using the
|
20
|
+
pg_json_ops extension. You can use JSONB subscripts to more easily
|
21
|
+
update part of a JSONB column:
|
22
|
+
|
23
|
+
DB[:table].update(Sequel.pg_jsonb_op(:column)['key'] => 'value')
|
24
|
+
UPDATE "table" SET "column"['key'] = 'value'
|
25
|
+
|
26
|
+
* hstore subscripts are now supported on PostgreSQL 14+ when using the
|
27
|
+
pg_hstore_ops extension. You can use hstore subscripts to more
|
28
|
+
easily update part of an hstore column:
|
29
|
+
|
30
|
+
DB[:table].update(Sequel.hstore_op(:column)['key'] => 'value')
|
31
|
+
UPDATE "table" SET "column"['key'] = 'value'
|
32
|
+
|
33
|
+
* Sequel now supports table aliases for JOIN USING columns on
|
34
|
+
PostgreSQL 14+. These allow you to reference the USING columns in
|
35
|
+
the query using a qualified identifier. To use this support, pass an
|
36
|
+
SQL::AliasedExpression as the expression to join on:
|
37
|
+
|
38
|
+
DB[:t1].join(:t2, Sequel.as([:c1, :c2], :alias))
|
39
|
+
# SELECT * FROM "t1" INNER JOIN "t2" USING ("c1", "c2") AS "alias"
|
40
|
+
|
41
|
+
* Database#create_trigger on PostgreSQL now supports a :replace option
|
42
|
+
for CREATE OR REPLACE TRIGGER (supported in PostgreSQL 14+).
|
43
|
+
|
44
|
+
* SQL::Expression#sequel_ast_transform has been added to support
|
45
|
+
AST transforms of custom expression classes.
|
46
|
+
|
47
|
+
= Other Improvements
|
48
|
+
|
49
|
+
* Sequel now supports calling PostgreSQL procedures without arguments
|
50
|
+
when using Database#call_procedure. Previously, attempts to call
|
51
|
+
procuredures without arguments would call the procedure with a
|
52
|
+
single NULL argument.
|
53
|
+
|
54
|
+
* Sequel now uses defined?(yield) instead of block_given? internally
|
55
|
+
for better performance on CRuby. defined?(yield) is faster as it is
|
56
|
+
built into the VM, while block_given? is a regular method and has
|
57
|
+
the overhead of calling a regular method. Note that defined?(yield)
|
58
|
+
is not implemented correctly on JRuby before 9.0.0.0, so this
|
59
|
+
release of Sequel drops support for JRuby versions before 9.0.0.0.
|
data/lib/sequel/adapters/ado.rb
CHANGED
@@ -246,7 +246,7 @@ module Sequel
|
|
246
246
|
end
|
247
247
|
begin
|
248
248
|
stmt = log_connection_yield(log_sql, conn, args){conn.execute_prepared(ps_name, *args)}
|
249
|
-
if
|
249
|
+
if defined?(yield)
|
250
250
|
yield(stmt)
|
251
251
|
else
|
252
252
|
stmt.affected
|
@@ -268,7 +268,7 @@ module Sequel
|
|
268
268
|
# is given or returning the number of affected rows if not, and ensuring the statement is freed.
|
269
269
|
def _execute(conn, sql, opts)
|
270
270
|
stmt = log_connection_yield(sql, conn){conn.execute(sql)}
|
271
|
-
if
|
271
|
+
if defined?(yield)
|
272
272
|
yield(stmt)
|
273
273
|
else
|
274
274
|
stmt.affected
|
@@ -35,9 +35,9 @@ module Sequel
|
|
35
35
|
data = opts[:data]
|
36
36
|
data = Array(data) if data.is_a?(String)
|
37
37
|
|
38
|
-
if
|
38
|
+
if defined?(yield) && data
|
39
39
|
raise Error, "Cannot provide both a :data option and a block to copy_into"
|
40
|
-
elsif !
|
40
|
+
elsif !defined?(yield) && !data
|
41
41
|
raise Error, "Must provide either a :data option or a block to copy_into"
|
42
42
|
end
|
43
43
|
|
@@ -45,7 +45,7 @@ module Sequel
|
|
45
45
|
begin
|
46
46
|
copy_manager = org.postgresql.copy.CopyManager.new(conn)
|
47
47
|
copier = copy_manager.copy_in(copy_into_sql(table, opts))
|
48
|
-
if
|
48
|
+
if defined?(yield)
|
49
49
|
while buf = yield
|
50
50
|
java_bytes = buf.to_java_bytes
|
51
51
|
copier.writeToCopy(java_bytes, 0, java_bytes.length)
|
@@ -77,7 +77,7 @@ module Sequel
|
|
77
77
|
copy_manager = org.postgresql.copy.CopyManager.new(conn)
|
78
78
|
copier = copy_manager.copy_out(copy_table_sql(table, opts))
|
79
79
|
begin
|
80
|
-
if
|
80
|
+
if defined?(yield)
|
81
81
|
while buf = copier.readFromCopy
|
82
82
|
yield(String.from_java_bytes(buf))
|
83
83
|
end
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
@@ -196,7 +196,7 @@ module Sequel
|
|
196
196
|
args.each{|arg| set_ps_arg(cps, arg, i+=1)}
|
197
197
|
|
198
198
|
begin
|
199
|
-
if
|
199
|
+
if defined?(yield)
|
200
200
|
yield log_connection_yield(sql, conn){cps.executeQuery}
|
201
201
|
else
|
202
202
|
log_connection_yield(sql, conn){cps.executeUpdate}
|
@@ -461,7 +461,7 @@ module Sequel
|
|
461
461
|
msg << ")"
|
462
462
|
end
|
463
463
|
begin
|
464
|
-
if
|
464
|
+
if defined?(yield)
|
465
465
|
yield log_connection_yield(msg, conn, args){cps.executeQuery}
|
466
466
|
else
|
467
467
|
case opts[:type]
|
@@ -167,7 +167,7 @@ module Sequel
|
|
167
167
|
r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn){conn.query(sql)}
|
168
168
|
if opts[:type] == :select
|
169
169
|
yield r if r
|
170
|
-
elsif
|
170
|
+
elsif defined?(yield)
|
171
171
|
yield conn
|
172
172
|
end
|
173
173
|
if conn.respond_to?(:more_results?)
|
@@ -151,7 +151,7 @@ module Sequel
|
|
151
151
|
yield r
|
152
152
|
end
|
153
153
|
end
|
154
|
-
elsif
|
154
|
+
elsif defined?(yield)
|
155
155
|
yield conn
|
156
156
|
end
|
157
157
|
rescue ::Mysql2::Error => e
|
@@ -244,7 +244,7 @@ module Sequel
|
|
244
244
|
# it hasn't been disabled.
|
245
245
|
def paged_each(opts=OPTS, &block)
|
246
246
|
if STREAMING_SUPPORTED && opts[:stream] != false
|
247
|
-
unless
|
247
|
+
unless defined?(yield)
|
248
248
|
return enum_for(:paged_each, opts)
|
249
249
|
end
|
250
250
|
stream.each(&block)
|
data/lib/sequel/adapters/odbc.rb
CHANGED
@@ -88,11 +88,11 @@ module Sequel
|
|
88
88
|
r = conn.parse(sql)
|
89
89
|
args = cursor_bind_params(conn, r, args)
|
90
90
|
nr = log_connection_yield(sql, conn, args){r.exec}
|
91
|
-
r = nr unless
|
91
|
+
r = nr unless defined?(yield)
|
92
92
|
else
|
93
93
|
r = log_connection_yield(sql, conn){conn.exec(sql)}
|
94
94
|
end
|
95
|
-
if
|
95
|
+
if defined?(yield)
|
96
96
|
yield(r)
|
97
97
|
elsif type == :insert
|
98
98
|
last_insert_id(conn, opts)
|
@@ -192,7 +192,7 @@ module Sequel
|
|
192
192
|
log_sql << ")"
|
193
193
|
end
|
194
194
|
r = log_connection_yield(log_sql, conn, args){cursor.exec}
|
195
|
-
if
|
195
|
+
if defined?(yield)
|
196
196
|
yield(cursor)
|
197
197
|
elsif type == :insert
|
198
198
|
last_insert_id(conn, opts)
|
@@ -143,7 +143,7 @@ module Sequel
|
|
143
143
|
args = args.map{|v| @db.bound_variable_arg(v, self)} if args
|
144
144
|
q = check_disconnect_errors{execute_query(sql, args)}
|
145
145
|
begin
|
146
|
-
|
146
|
+
defined?(yield) ? yield(q) : q.cmd_tuples
|
147
147
|
ensure
|
148
148
|
q.clear if q && q.respond_to?(:clear)
|
149
149
|
end
|
@@ -350,7 +350,7 @@ module Sequel
|
|
350
350
|
synchronize(opts[:server]) do |conn|
|
351
351
|
conn.execute(copy_table_sql(table, opts))
|
352
352
|
begin
|
353
|
-
if
|
353
|
+
if defined?(yield)
|
354
354
|
while buf = conn.get_copy_data
|
355
355
|
yield buf
|
356
356
|
end
|
@@ -400,16 +400,16 @@ module Sequel
|
|
400
400
|
data = opts[:data]
|
401
401
|
data = Array(data) if data.is_a?(String)
|
402
402
|
|
403
|
-
if
|
403
|
+
if defined?(yield) && data
|
404
404
|
raise Error, "Cannot provide both a :data option and a block to copy_into"
|
405
|
-
elsif !
|
405
|
+
elsif !defined?(yield) && !data
|
406
406
|
raise Error, "Must provide either a :data option or a block to copy_into"
|
407
407
|
end
|
408
408
|
|
409
409
|
synchronize(opts[:server]) do |conn|
|
410
410
|
conn.execute(copy_into_sql(table, opts))
|
411
411
|
begin
|
412
|
-
if
|
412
|
+
if defined?(yield)
|
413
413
|
while buf = yield
|
414
414
|
conn.put_copy_data(buf)
|
415
415
|
end
|
@@ -590,7 +590,7 @@ module Sequel
|
|
590
590
|
|
591
591
|
q = conn.check_disconnect_errors{log_connection_yield(log_sql, conn, args){_execute_prepared_statement(conn, ps_name, args, opts)}}
|
592
592
|
begin
|
593
|
-
|
593
|
+
defined?(yield) ? yield(q) : q.cmd_tuples
|
594
594
|
ensure
|
595
595
|
q.clear if q && q.respond_to?(:clear)
|
596
596
|
end
|
@@ -616,7 +616,7 @@ module Sequel
|
|
616
616
|
|
617
617
|
# Use a cursor for paging.
|
618
618
|
def paged_each(opts=OPTS, &block)
|
619
|
-
unless
|
619
|
+
unless defined?(yield)
|
620
620
|
return enum_for(:paged_each, opts)
|
621
621
|
end
|
622
622
|
use_cursor(opts).each(&block)
|
@@ -717,7 +717,9 @@ module Sequel
|
|
717
717
|
sql = String.new
|
718
718
|
sql << "CALL "
|
719
719
|
identifier_append(sql, name)
|
720
|
-
|
720
|
+
sql << "("
|
721
|
+
expression_list_append(sql, args)
|
722
|
+
sql << ")"
|
721
723
|
with_sql_first(sql)
|
722
724
|
end
|
723
725
|
|
@@ -479,6 +479,7 @@ module Sequel
|
|
479
479
|
# :each_row :: Calls the trigger for each row instead of for each statement.
|
480
480
|
# :events :: Can be :insert, :update, :delete, or an array of any of those. Calls the trigger whenever that type of statement is used. By default,
|
481
481
|
# the trigger is called for insert, update, or delete.
|
482
|
+
# :replace :: Replace the trigger with the same name if it already exists (PostgreSQL 14+).
|
482
483
|
# :when :: A filter to use for the trigger
|
483
484
|
def create_trigger(table, name, function, opts=OPTS)
|
484
485
|
self << create_trigger_sql(table, name, function, opts)
|
@@ -1237,7 +1238,7 @@ module Sequel
|
|
1237
1238
|
raise Error, "Trigger conditions are not supported for this database" unless supports_trigger_conditions?
|
1238
1239
|
filter = " WHEN #{filter_expr(filter)}"
|
1239
1240
|
end
|
1240
|
-
"CREATE TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
1241
|
+
"CREATE #{'OR REPLACE ' if opts[:replace]}TRIGGER #{name} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} ON #{quote_schema_table(table)}#{' FOR EACH ROW' if opts[:each_row]}#{filter} EXECUTE PROCEDURE #{function}(#{Array(opts[:args]).map{|a| literal(a)}.join(', ')})"
|
1241
1242
|
end
|
1242
1243
|
|
1243
1244
|
# DDL fragment for initial part of CREATE VIEW statement
|
@@ -1335,7 +1336,7 @@ module Sequel
|
|
1335
1336
|
ds = metadata_dataset.from(:pg_class).where(:relkind=>type).select(:relname).server(opts[:server]).join(:pg_namespace, :oid=>:relnamespace)
|
1336
1337
|
ds = filter_schema(ds, opts)
|
1337
1338
|
m = output_identifier_meth
|
1338
|
-
if
|
1339
|
+
if defined?(yield)
|
1339
1340
|
yield(ds)
|
1340
1341
|
elsif opts[:qualify]
|
1341
1342
|
ds.select_append{pg_namespace[:nspname]}.map{|r| Sequel.qualify(m.call(r[:nspname]).to_s, m.call(r[:relname]).to_s)}
|
@@ -1727,13 +1728,22 @@ module Sequel
|
|
1727
1728
|
ds.insert_sql(*values)
|
1728
1729
|
end
|
1729
1730
|
|
1731
|
+
# Support SQL::AliasedExpression as expr to setup a USING join with a table alias for the
|
1732
|
+
# USING columns.
|
1733
|
+
def join_table(type, table, expr=nil, options=OPTS, &block)
|
1734
|
+
if expr.is_a?(SQL::AliasedExpression) && expr.expression.is_a?(Array) && !expr.expression.empty? && expr.expression.all?
|
1735
|
+
options = options.merge(:join_using=>true)
|
1736
|
+
end
|
1737
|
+
super
|
1738
|
+
end
|
1739
|
+
|
1730
1740
|
# Locks all tables in the dataset's FROM clause (but not in JOINs) with
|
1731
1741
|
# the specified mode (e.g. 'EXCLUSIVE'). If a block is given, starts
|
1732
1742
|
# a new transaction, locks the table, and yields. If a block is not given,
|
1733
1743
|
# just locks the tables. Note that PostgreSQL will probably raise an error
|
1734
1744
|
# if you lock the table outside of an existing transaction. Returns nil.
|
1735
1745
|
def lock(mode, opts=OPTS)
|
1736
|
-
if
|
1746
|
+
if defined?(yield) # perform locking inside a transaction and yield to block
|
1737
1747
|
@db.transaction(opts){lock(mode, opts); yield}
|
1738
1748
|
else
|
1739
1749
|
sql = 'LOCK TABLE '.dup
|
@@ -2023,6 +2033,17 @@ module Sequel
|
|
2023
2033
|
end
|
2024
2034
|
end
|
2025
2035
|
|
2036
|
+
# Support table aliases for USING columns
|
2037
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
2038
|
+
if using_columns.is_a?(SQL::AliasedExpression)
|
2039
|
+
super(sql, using_columns.expression)
|
2040
|
+
sql << ' AS '
|
2041
|
+
identifier_append(sql, using_columns.alias)
|
2042
|
+
else
|
2043
|
+
super
|
2044
|
+
end
|
2045
|
+
end
|
2046
|
+
|
2026
2047
|
# Use a generic blob quoting method, hopefully overridden in one of the subadapter methods
|
2027
2048
|
def literal_blob_append(sql, v)
|
2028
2049
|
sql << "'" << v.gsub(/[\000-\037\047\134\177-\377]/n){|b| "\\#{("%o" % b[0..1].unpack("C")[0]).rjust(3, '0')}"} << "'"
|
@@ -393,7 +393,7 @@ module Sequel
|
|
393
393
|
old_columns = def_columns.map{|c| c[:name]}
|
394
394
|
opts[:old_columns_proc].call(old_columns) if opts[:old_columns_proc]
|
395
395
|
|
396
|
-
yield def_columns if
|
396
|
+
yield def_columns if defined?(yield)
|
397
397
|
|
398
398
|
constraints = (opts[:constraints] || []).dup
|
399
399
|
pks = []
|
@@ -80,6 +80,12 @@ module Sequel
|
|
80
80
|
SQL::DelayedEvaluation.new(lambda{|ds| v(o.call(ds))})
|
81
81
|
when SQL::Wrapper
|
82
82
|
SQL::Wrapper.new(v(o.value))
|
83
|
+
when SQL::Expression
|
84
|
+
if o.respond_to?(:sequel_ast_transform)
|
85
|
+
o.sequel_ast_transform(method(:v))
|
86
|
+
else
|
87
|
+
o
|
88
|
+
end
|
83
89
|
else
|
84
90
|
o
|
85
91
|
end
|
@@ -55,11 +55,11 @@ module Sequel
|
|
55
55
|
|
56
56
|
begin
|
57
57
|
db = c.new(opts)
|
58
|
-
if
|
58
|
+
if defined?(yield)
|
59
59
|
return yield(db)
|
60
60
|
end
|
61
61
|
ensure
|
62
|
-
if
|
62
|
+
if defined?(yield)
|
63
63
|
db.disconnect if db
|
64
64
|
Sequel.synchronize{::Sequel::DATABASES.delete(db)}
|
65
65
|
end
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -508,6 +508,7 @@ module Sequel
|
|
508
508
|
# argument.
|
509
509
|
# :implicit_qualifier :: The name to use for qualifying implicit conditions. By default,
|
510
510
|
# the last joined or primary table is used.
|
511
|
+
# :join_using :: Force the using of JOIN USING, even if +expr+ is not an array of symbols.
|
511
512
|
# :reset_implicit_qualifier :: Can set to false to ignore this join when future joins determine qualifier
|
512
513
|
# for implicit conditions.
|
513
514
|
# :qualify :: Can be set to false to not do any implicit qualification. Can be set
|
@@ -541,7 +542,7 @@ module Sequel
|
|
541
542
|
return s.join_table(type, ds, expr, options, &block)
|
542
543
|
end
|
543
544
|
|
544
|
-
using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
|
545
|
+
using_join = options[:join_using] || (expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)})
|
545
546
|
if using_join && !supports_join_using?
|
546
547
|
h = {}
|
547
548
|
expr.each{|e| h[e] = e}
|
@@ -616,7 +617,7 @@ module Sequel
|
|
616
617
|
UNCONDITIONED_JOIN_TYPES.each do |jtype|
|
617
618
|
class_eval(<<-END, __FILE__, __LINE__+1)
|
618
619
|
def #{jtype}_join(table, opts=Sequel::OPTS)
|
619
|
-
raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if
|
620
|
+
raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if defined?(yield)
|
620
621
|
raise(Sequel::Error, '#{jtype}_join 2nd argument should be an options hash, not conditions') unless opts.is_a?(Hash)
|
621
622
|
join_table(:#{jtype}, table, nil, opts)
|
622
623
|
end
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -559,11 +559,9 @@ module Sequel
|
|
559
559
|
# Append literalization of JOIN USING clause to SQL string.
|
560
560
|
def join_using_clause_sql_append(sql, jc)
|
561
561
|
join_clause_sql_append(sql, jc)
|
562
|
-
sql
|
563
|
-
column_list_append(sql, jc.using)
|
564
|
-
sql << ')'
|
562
|
+
join_using_clause_using_sql_append(sql, jc.using)
|
565
563
|
end
|
566
|
-
|
564
|
+
|
567
565
|
# Append literalization of negative boolean constant to SQL string.
|
568
566
|
def negative_boolean_constant_sql_append(sql, constant)
|
569
567
|
sql << 'NOT '
|
@@ -1218,6 +1216,13 @@ module Sequel
|
|
1218
1216
|
"#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
|
1219
1217
|
end
|
1220
1218
|
|
1219
|
+
# Append USING clause for JOIN USING
|
1220
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
1221
|
+
sql << ' USING ('
|
1222
|
+
column_list_append(sql, using_columns)
|
1223
|
+
sql << ')'
|
1224
|
+
end
|
1225
|
+
|
1221
1226
|
# Append a literalization of the array to SQL string.
|
1222
1227
|
# Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
|
1223
1228
|
def literal_array_append(sql, v)
|
@@ -44,7 +44,7 @@ module Sequel
|
|
44
44
|
# :nocov:
|
45
45
|
|
46
46
|
# Customize handling of duplicate columns for this dataset.
|
47
|
-
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless
|
47
|
+
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless defined?(yield); nil), &block)
|
48
48
|
raise Error, "Cannot provide both an argument and a block to on_duplicate_columns" if handler && block
|
49
49
|
clone(:on_duplicate_columns=>handler||block)
|
50
50
|
end
|
@@ -54,7 +54,7 @@ module Sequel
|
|
54
54
|
# an enumerator if no block is given.
|
55
55
|
def each_page(page_size)
|
56
56
|
raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
|
57
|
-
return to_enum(:each_page, page_size) unless
|
57
|
+
return to_enum(:each_page, page_size) unless defined?(yield)
|
58
58
|
record_count = count
|
59
59
|
total_pages = (record_count / page_size.to_f).ceil
|
60
60
|
(1..total_pages).each{|page_no| yield paginate(page_no, page_size, record_count)}
|
@@ -83,7 +83,7 @@ module Sequel
|
|
83
83
|
# If convert_infinite_timestamps is true and the value is infinite, return an appropriate
|
84
84
|
# value based on the convert_infinite_timestamps setting.
|
85
85
|
def to_application_timestamp(value)
|
86
|
-
if value.is_a?(String) && (m =
|
86
|
+
if value.is_a?(String) && (m = /((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/.match(value)) && (m[2] || m[3])
|
87
87
|
if m[3]
|
88
88
|
value = value.sub(' BC', '').sub(' ', ' BC ')
|
89
89
|
conv = defined?(JRUBY_VERSION) && JRUBY_VERSION == '9.2.0.0'
|
@@ -62,6 +62,19 @@
|
|
62
62
|
# # Delete a key
|
63
63
|
# DB[:tab].update(h: Sequel.hstore_op(:h).delete('k1'))
|
64
64
|
#
|
65
|
+
# On PostgreSQL 14+, The hstore <tt>[]</tt> method will use subscripts instead of being
|
66
|
+
# the same as +get+, if the value being wrapped is an identifer:
|
67
|
+
#
|
68
|
+
# Sequel.hstore_op(:hstore_column)['a'] # hstore_column['a']
|
69
|
+
# Sequel.hstore_op(Sequel[:h][:s])['a'] # h.s['a']
|
70
|
+
#
|
71
|
+
# This support allows you to use hstore subscripts in UPDATE statements to update only
|
72
|
+
# part of a column:
|
73
|
+
#
|
74
|
+
# h = Sequel.hstore_op(:h)
|
75
|
+
# DB[:t].update(h['key1'] => 'val1', h['key2'] => 'val2')
|
76
|
+
# # UPDATE "t" SET "h"['key1'] = 'val1', "h"['key2'] = 'val2'
|
77
|
+
#
|
65
78
|
# See the PostgreSQL hstore function and operator documentation for more
|
66
79
|
# details on what these functions and operators do.
|
67
80
|
#
|
@@ -114,10 +127,15 @@ module Sequel
|
|
114
127
|
#
|
115
128
|
# hstore_op['a'] # (hstore -> 'a')
|
116
129
|
def [](key)
|
117
|
-
v = Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)])
|
118
130
|
if key.is_a?(Array) || (defined?(Sequel::Postgres::PGArray) && key.is_a?(Sequel::Postgres::PGArray)) || (defined?(Sequel::Postgres::ArrayOp) && key.is_a?(Sequel::Postgres::ArrayOp))
|
119
|
-
wrap_output_array(
|
131
|
+
wrap_output_array(Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)]))
|
120
132
|
else
|
133
|
+
v = case @value
|
134
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
|
135
|
+
HStoreSubscriptOp.new(self, key)
|
136
|
+
else
|
137
|
+
Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, key])
|
138
|
+
end
|
121
139
|
Sequel::SQL::StringExpression.new(:NOOP, v)
|
122
140
|
end
|
123
141
|
end
|
@@ -304,6 +322,38 @@ module Sequel
|
|
304
322
|
end
|
305
323
|
end
|
306
324
|
|
325
|
+
# Represents hstore subscripts. This is abstracted because the
|
326
|
+
# subscript support depends on the database version.
|
327
|
+
class HStoreSubscriptOp < SQL::Expression
|
328
|
+
SUBSCRIPT = ["".freeze, "[".freeze, "]".freeze].freeze
|
329
|
+
|
330
|
+
# The expression being subscripted
|
331
|
+
attr_reader :expression
|
332
|
+
|
333
|
+
# The subscript to use
|
334
|
+
attr_reader :sub
|
335
|
+
|
336
|
+
# Set the expression and subscript to the given arguments
|
337
|
+
def initialize(expression, sub)
|
338
|
+
@expression = expression
|
339
|
+
@sub = sub
|
340
|
+
freeze
|
341
|
+
end
|
342
|
+
|
343
|
+
# Use subscripts instead of -> operator on PostgreSQL 14+
|
344
|
+
def to_s_append(ds, sql)
|
345
|
+
server_version = ds.db.server_version
|
346
|
+
frag = server_version && server_version >= 140000 ? SUBSCRIPT : HStoreOp::LOOKUP
|
347
|
+
ds.literal_append(sql, Sequel::SQL::PlaceholderLiteralString.new(frag, [@expression, @sub]))
|
348
|
+
end
|
349
|
+
|
350
|
+
# Support transforming of hstore subscripts
|
351
|
+
def sequel_ast_transform(transformer)
|
352
|
+
self.class.new(transformer.call(@expression), transformer.call(@sub))
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
|
307
357
|
module HStoreOpMethods
|
308
358
|
# Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
|
309
359
|
# hstore functions and operators with it.
|
@@ -101,6 +101,27 @@
|
|
101
101
|
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
102
102
|
# errors are not suppressed.
|
103
103
|
#
|
104
|
+
# On PostgreSQL 14+, The JSONB <tt>[]</tt> method will use subscripts instead of being
|
105
|
+
# the same as +get+, if the value being wrapped is an identifer:
|
106
|
+
#
|
107
|
+
# Sequel.pg_jsonb_op(:jsonb_column)[1] # jsonb_column[1]
|
108
|
+
# Sequel.pg_jsonb_op(:jsonb_column)[1][2] # jsonb_column[1][2]
|
109
|
+
# Sequel.pg_jsonb_op(Sequel[:j][:b])[1] # j.b[1]
|
110
|
+
#
|
111
|
+
# This support allows you to use JSONB subscripts in UPDATE statements to update only
|
112
|
+
# part of a column:
|
113
|
+
#
|
114
|
+
# c = Sequel.pg_jsonb_op(:c)
|
115
|
+
# DB[:t].update(c['key1'] => '1', c['key2'] => '"a"')
|
116
|
+
# # UPDATE "t" SET "c"['key1'] = '1', "c"['key2'] = '"a"'
|
117
|
+
#
|
118
|
+
# Note that you have to provide the value of a JSONB subscript as a JSONB value, so this
|
119
|
+
# will update +key1+ to use the number <tt>1</tt>, and +key2+ to use the string <tt>a</tt>.
|
120
|
+
# For this reason it may be simpler to use +to_json+:
|
121
|
+
#
|
122
|
+
# c = Sequel.pg_jsonb_op(:c)
|
123
|
+
# DB[:t].update(c['key1'] => 1.to_json, c['key2'] => "a".to_json)
|
124
|
+
#
|
104
125
|
# If you are also using the pg_json extension, you should load it before
|
105
126
|
# loading this extension. Doing so will allow you to use the #op method on
|
106
127
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
@@ -323,6 +344,24 @@ module Sequel
|
|
323
344
|
PATH_EXISTS = ["(".freeze, " @? ".freeze, ")".freeze].freeze
|
324
345
|
PATH_MATCH = ["(".freeze, " @@ ".freeze, ")".freeze].freeze
|
325
346
|
|
347
|
+
# Support subscript syntax for JSONB.
|
348
|
+
def [](key)
|
349
|
+
if is_array?(key)
|
350
|
+
super
|
351
|
+
else
|
352
|
+
case @value
|
353
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, JSONBSubscriptOp
|
354
|
+
# Only use subscripts for identifiers. In other cases, switching from
|
355
|
+
# the -> operator to [] for subscripts causes SQL syntax issues. You
|
356
|
+
# only need the [] for subscripting when doing assignment, and
|
357
|
+
# assignment is generally done on identifiers.
|
358
|
+
self.class.new(JSONBSubscriptOp.new(self, key))
|
359
|
+
else
|
360
|
+
super
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
326
365
|
# jsonb expression for deletion of the given argument from the
|
327
366
|
# current jsonb.
|
328
367
|
#
|
@@ -582,6 +621,37 @@ module Sequel
|
|
582
621
|
end
|
583
622
|
end
|
584
623
|
|
624
|
+
# Represents JSONB subscripts. This is abstracted because the
|
625
|
+
# subscript support depends on the database version.
|
626
|
+
class JSONBSubscriptOp < SQL::Expression
|
627
|
+
SUBSCRIPT = ["".freeze, "[".freeze, "]".freeze].freeze
|
628
|
+
|
629
|
+
# The expression being subscripted
|
630
|
+
attr_reader :expression
|
631
|
+
|
632
|
+
# The subscript to use
|
633
|
+
attr_reader :sub
|
634
|
+
|
635
|
+
# Set the expression and subscript to the given arguments
|
636
|
+
def initialize(expression, sub)
|
637
|
+
@expression = expression
|
638
|
+
@sub = sub
|
639
|
+
freeze
|
640
|
+
end
|
641
|
+
|
642
|
+
# Use subscripts instead of -> operator on PostgreSQL 14+
|
643
|
+
def to_s_append(ds, sql)
|
644
|
+
server_version = ds.db.server_version
|
645
|
+
frag = server_version && server_version >= 140000 ? SUBSCRIPT : JSONOp::GET
|
646
|
+
ds.literal_append(sql, Sequel::SQL::PlaceholderLiteralString.new(frag, [@expression, @sub]))
|
647
|
+
end
|
648
|
+
|
649
|
+
# Support transforming of jsonb subscripts
|
650
|
+
def sequel_ast_transform(transformer)
|
651
|
+
self.class.new(transformer.call(@expression), transformer.call(@sub))
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
585
655
|
module JSONOpMethods
|
586
656
|
# Wrap the receiver in an JSONOp so you can easily use the PostgreSQL
|
587
657
|
# json functions and operators with it.
|
data/lib/sequel/model/base.rb
CHANGED
@@ -71,12 +71,14 @@ module Sequel
|
|
71
71
|
MAX_LENGTH_OPTIONS = {:from=>:values, :allow_nil=>true}.freeze
|
72
72
|
SCHEMA_TYPES_OPTIONS = NOT_NULL_OPTIONS
|
73
73
|
UNIQUE_OPTIONS = NOT_NULL_OPTIONS
|
74
|
+
NO_NULL_BYTE_OPTIONS = MAX_LENGTH_OPTIONS
|
74
75
|
EMPTY_ARRAY = [].freeze
|
75
76
|
|
76
77
|
def self.apply(model, opts=OPTS)
|
77
78
|
model.instance_exec do
|
78
79
|
plugin :validation_helpers
|
79
80
|
@auto_validate_presence = false
|
81
|
+
@auto_validate_no_null_byte_columns = []
|
80
82
|
@auto_validate_not_null_columns = []
|
81
83
|
@auto_validate_explicit_not_null_columns = []
|
82
84
|
@auto_validate_max_length_columns = []
|
@@ -84,6 +86,7 @@ module Sequel
|
|
84
86
|
@auto_validate_types = true
|
85
87
|
|
86
88
|
@auto_validate_options = {
|
89
|
+
:no_null_byte=>NO_NULL_BYTE_OPTIONS,
|
87
90
|
:not_null=>NOT_NULL_OPTIONS,
|
88
91
|
:explicit_not_null=>EXPLICIT_NOT_NULL_OPTIONS,
|
89
92
|
:max_length=>MAX_LENGTH_OPTIONS,
|
@@ -102,14 +105,14 @@ module Sequel
|
|
102
105
|
end
|
103
106
|
|
104
107
|
h = @auto_validate_options.dup
|
105
|
-
[:not_null, :explicit_not_null, :max_length, :schema_types, :unique].each do |type|
|
108
|
+
[:not_null, :explicit_not_null, :max_length, :no_null_byte, :schema_types, :unique].each do |type|
|
106
109
|
if type_opts = opts[:"#{type}_opts"]
|
107
110
|
h[type] = h[type].merge(type_opts).freeze
|
108
111
|
end
|
109
112
|
end
|
110
113
|
|
111
114
|
if opts[:skip_invalid]
|
112
|
-
[:not_null, :explicit_not_null, :max_length, :schema_types].each do |type|
|
115
|
+
[:not_null, :explicit_not_null, :no_null_byte, :max_length, :schema_types].each do |type|
|
113
116
|
h[type] = h[type].merge(:skip_invalid=>true).freeze
|
114
117
|
end
|
115
118
|
end
|
@@ -119,6 +122,9 @@ module Sequel
|
|
119
122
|
end
|
120
123
|
|
121
124
|
module ClassMethods
|
125
|
+
# The columns with automatic no_null_byte validations
|
126
|
+
attr_reader :auto_validate_no_null_byte_columns
|
127
|
+
|
122
128
|
# The columns with automatic not_null validations
|
123
129
|
attr_reader :auto_validate_not_null_columns
|
124
130
|
|
@@ -135,7 +141,15 @@ module Sequel
|
|
135
141
|
# Inherited options
|
136
142
|
attr_reader :auto_validate_options
|
137
143
|
|
138
|
-
Plugins.inherited_instance_variables(self,
|
144
|
+
Plugins.inherited_instance_variables(self,
|
145
|
+
:@auto_validate_presence=>nil,
|
146
|
+
:@auto_validate_types=>nil,
|
147
|
+
:@auto_validate_no_null_byte_columns=>:dup,
|
148
|
+
:@auto_validate_not_null_columns=>:dup,
|
149
|
+
:@auto_validate_explicit_not_null_columns=>:dup,
|
150
|
+
:@auto_validate_max_length_columns=>:dup,
|
151
|
+
:@auto_validate_unique_columns=>:dup,
|
152
|
+
:@auto_validate_options => :dup)
|
139
153
|
Plugins.after_set_dataset(self, :setup_auto_validations)
|
140
154
|
|
141
155
|
# Whether to use a presence validation for not null columns
|
@@ -150,6 +164,7 @@ module Sequel
|
|
150
164
|
|
151
165
|
# Freeze auto_validation settings when freezing model class.
|
152
166
|
def freeze
|
167
|
+
@auto_validate_no_null_byte_columns.freeze
|
153
168
|
@auto_validate_not_null_columns.freeze
|
154
169
|
@auto_validate_explicit_not_null_columns.freeze
|
155
170
|
@auto_validate_max_length_columns.freeze
|
@@ -158,12 +173,13 @@ module Sequel
|
|
158
173
|
super
|
159
174
|
end
|
160
175
|
|
161
|
-
# Skip automatic validations for the given validation type
|
176
|
+
# Skip automatic validations for the given validation type
|
177
|
+
# (:not_null, :types, :unique, :max_length, :no_null_byte).
|
162
178
|
# If :all is given as the type, skip all auto validations.
|
163
179
|
def skip_auto_validations(type)
|
164
180
|
case type
|
165
181
|
when :all
|
166
|
-
[:not_null, :types, :unique, :max_length].each{|v| skip_auto_validations(v)}
|
182
|
+
[:not_null, :no_null_byte, :types, :unique, :max_length].each{|v| skip_auto_validations(v)}
|
167
183
|
when :not_null
|
168
184
|
auto_validate_not_null_columns.clear
|
169
185
|
auto_validate_explicit_not_null_columns.clear
|
@@ -183,6 +199,7 @@ module Sequel
|
|
183
199
|
explicit_not_null_cols += Array(primary_key)
|
184
200
|
@auto_validate_explicit_not_null_columns = explicit_not_null_cols.uniq
|
185
201
|
@auto_validate_max_length_columns = db_schema.select{|col, sch| sch[:type] == :string && sch[:max_length].is_a?(Integer)}.map{|col, sch| [col, sch[:max_length]]}
|
202
|
+
@auto_validate_no_null_byte_columns = db_schema.select{|_, sch| sch[:type] == :string}.map{|col, _| col}
|
186
203
|
table = dataset.first_source_table
|
187
204
|
@auto_validate_unique_columns = if db.supports_index_parsing? && [Symbol, SQL::QualifiedIdentifier, SQL::Identifier, String].any?{|c| table.is_a?(c)}
|
188
205
|
db.indexes(table).select{|name, idx| idx[:unique] == true}.map{|name, idx| idx[:columns].length == 1 ? idx[:columns].first : idx[:columns]}
|
@@ -209,6 +226,9 @@ module Sequel
|
|
209
226
|
return if skip.include?(:all)
|
210
227
|
opts = model.auto_validate_options
|
211
228
|
|
229
|
+
unless skip.include?(:no_null_byte) || (no_null_byte_columns = model.auto_validate_no_null_byte_columns).empty?
|
230
|
+
validates_no_null_byte(no_null_byte_columns, opts[:no_null_byte])
|
231
|
+
end
|
212
232
|
|
213
233
|
unless skip.include?(:not_null)
|
214
234
|
not_null_method = model.auto_validate_presence? ? :validates_presence : :validates_not_null
|
@@ -365,7 +365,7 @@ module Sequel
|
|
365
365
|
h = {root => h}
|
366
366
|
end
|
367
367
|
|
368
|
-
h = yield h if
|
368
|
+
h = yield h if defined?(yield)
|
369
369
|
Sequel.object_to_json(h, *a)
|
370
370
|
end
|
371
371
|
|
@@ -441,7 +441,7 @@ module Sequel
|
|
441
441
|
end
|
442
442
|
|
443
443
|
res = {collection_root => res} if collection_root
|
444
|
-
res = yield res if
|
444
|
+
res = yield res if defined?(yield)
|
445
445
|
|
446
446
|
Sequel.object_to_json(res, *a)
|
447
447
|
end
|
@@ -87,7 +87,7 @@ module Sequel
|
|
87
87
|
# array containing the number of instances specified (single integer
|
88
88
|
# argument).
|
89
89
|
def first(*args)
|
90
|
-
if
|
90
|
+
if defined?(yield) || args.length > 1 || (args.length == 1 && !args[0].is_a?(Integer))
|
91
91
|
super
|
92
92
|
else
|
93
93
|
@all.first(*args)
|
@@ -85,6 +85,7 @@ module Sequel
|
|
85
85
|
:max_length=>{:message=>lambda{|max| "is longer than #{max} characters"}, :nil_message=>lambda{"is not present"}},
|
86
86
|
:min_length=>{:message=>lambda{|min| "is shorter than #{min} characters"}},
|
87
87
|
:not_null=>{:message=>lambda{"is not present"}},
|
88
|
+
:no_null_byte=>{:message=>lambda{"contains a null byte"}},
|
88
89
|
:numeric=>{:message=>lambda{"is not a number"}},
|
89
90
|
:operator=>{:message=>lambda{|operator, rhs| "is not #{operator} #{rhs}"}},
|
90
91
|
:type=>{:message=>lambda{|klass| klass.is_a?(Array) ? "is not a valid #{klass.join(" or ").downcase}" : "is not a valid #{klass.to_s.downcase}"}},
|
@@ -149,6 +150,11 @@ module Sequel
|
|
149
150
|
def validates_not_null(atts, opts=OPTS)
|
150
151
|
validatable_attributes_for_type(:not_null, atts, opts){|a,v,m| validation_error_message(m) if v.nil?}
|
151
152
|
end
|
153
|
+
|
154
|
+
# Check attribute value(s) does not contain a null ("\0", ASCII NUL) byte.
|
155
|
+
def validates_no_null_byte(atts, opts=OPTS)
|
156
|
+
validatable_attributes_for_type(:no_null_byte, atts, opts){|a,v,m| validation_error_message(m) if String === v && v.include?("\0")}
|
157
|
+
end
|
152
158
|
|
153
159
|
# Check attribute value(s) string representation is a valid float.
|
154
160
|
def validates_numeric(atts, opts=OPTS)
|
@@ -260,7 +266,7 @@ module Sequel
|
|
260
266
|
next if vals.any?(&:nil?)
|
261
267
|
ds.where(arr.zip(vals))
|
262
268
|
end
|
263
|
-
ds = yield(ds) if
|
269
|
+
ds = yield(ds) if defined?(yield)
|
264
270
|
unless new?
|
265
271
|
h = ds.joined_dataset? ? qualified_pk_hash : pk_hash
|
266
272
|
ds = ds.exclude(h)
|
data/lib/sequel/sql.rb
CHANGED
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ 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 = 49
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
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.49.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -192,6 +192,7 @@ extra_rdoc_files:
|
|
192
192
|
- doc/release_notes/5.46.0.txt
|
193
193
|
- doc/release_notes/5.47.0.txt
|
194
194
|
- doc/release_notes/5.48.0.txt
|
195
|
+
- doc/release_notes/5.49.0.txt
|
195
196
|
- doc/release_notes/5.5.0.txt
|
196
197
|
- doc/release_notes/5.6.0.txt
|
197
198
|
- doc/release_notes/5.7.0.txt
|
@@ -268,6 +269,7 @@ files:
|
|
268
269
|
- doc/release_notes/5.46.0.txt
|
269
270
|
- doc/release_notes/5.47.0.txt
|
270
271
|
- doc/release_notes/5.48.0.txt
|
272
|
+
- doc/release_notes/5.49.0.txt
|
271
273
|
- doc/release_notes/5.5.0.txt
|
272
274
|
- doc/release_notes/5.6.0.txt
|
273
275
|
- doc/release_notes/5.7.0.txt
|