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