sequel 5.48.0 → 5.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +20 -0
  3. data/README.rdoc +2 -1
  4. data/doc/postgresql.rdoc +8 -0
  5. data/doc/release_notes/5.49.0.txt +59 -0
  6. data/lib/sequel/adapters/ado/access.rb +1 -1
  7. data/lib/sequel/adapters/ado.rb +1 -1
  8. data/lib/sequel/adapters/ibmdb.rb +2 -2
  9. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  10. data/lib/sequel/adapters/jdbc.rb +2 -2
  11. data/lib/sequel/adapters/mysql.rb +1 -1
  12. data/lib/sequel/adapters/mysql2.rb +2 -2
  13. data/lib/sequel/adapters/odbc.rb +1 -1
  14. data/lib/sequel/adapters/oracle.rb +3 -3
  15. data/lib/sequel/adapters/postgres.rb +10 -8
  16. data/lib/sequel/adapters/shared/postgres.rb +24 -3
  17. data/lib/sequel/adapters/shared/sqlite.rb +1 -1
  18. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  19. data/lib/sequel/adapters/tinytds.rb +1 -1
  20. data/lib/sequel/ast_transformer.rb +6 -0
  21. data/lib/sequel/database/connecting.rb +2 -2
  22. data/lib/sequel/dataset/actions.rb +1 -1
  23. data/lib/sequel/dataset/query.rb +3 -2
  24. data/lib/sequel/dataset/sql.rb +9 -4
  25. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  26. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  27. data/lib/sequel/extensions/inflector.rb +1 -1
  28. data/lib/sequel/extensions/pagination.rb +1 -1
  29. data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
  30. data/lib/sequel/extensions/pg_hstore_ops.rb +52 -2
  31. data/lib/sequel/extensions/pg_json_ops.rb +70 -0
  32. data/lib/sequel/extensions/string_agg.rb +1 -1
  33. data/lib/sequel/model/base.rb +1 -1
  34. data/lib/sequel/model/inflections.rb +1 -1
  35. data/lib/sequel/plugins/auto_validations.rb +25 -5
  36. data/lib/sequel/plugins/column_encryption.rb +1 -1
  37. data/lib/sequel/plugins/json_serializer.rb +2 -2
  38. data/lib/sequel/plugins/static_cache.rb +1 -1
  39. data/lib/sequel/plugins/update_or_create.rb +1 -1
  40. data/lib/sequel/plugins/validation_helpers.rb +7 -1
  41. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  42. data/lib/sequel/sql.rb +1 -1
  43. data/lib/sequel/version.rb +1 -1
  44. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b395c399cd3632c44311c05df70fd6cd0fa924331f665dc964e041ec87013c08
4
- data.tar.gz: de423f5ece8a1f212f010d430d671e52cf89e00fa0e952fb69fe8cf9c7931e4c
3
+ metadata.gz: bb8660c16f1da94fc18fcc5e983437d5d7bbebd2484a67af4d266c0245045a0c
4
+ data.tar.gz: 9844b032cae12dba23e2719c87991ef6ffbdf9f0b793dcef201336eda8f4d8d4
5
5
  SHA512:
6
- metadata.gz: 475081e84ac0c0ef2b05e642d02cf3956cf263cebc280ecd469401a9c3ed591c8854fe42e687343e68effe47e6cc0f4f12f73734e5477a8749406a8ae94afe15
7
- data.tar.gz: eb5a432efd4e788461422ee754ace25d14add6a6117d10064446f31b3d719943bd4a935b3e70db10613205cf1d7bb980aaaf850449299a8f1861466aa0dcb50f
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.
@@ -318,7 +318,7 @@ module Sequel
318
318
  conn.OpenSchema(ado_schema.type, ado_schema.criteria)
319
319
  end
320
320
  }
321
- yield(r) if block_given?
321
+ yield(r) if defined?(yield)
322
322
  rescue ::WIN32OLERuntimeError => e
323
323
  raise_error(e)
324
324
  end
@@ -161,7 +161,7 @@ module Sequel
161
161
  begin
162
162
  r = log_connection_yield(sql, conn){conn.Execute(sql)}
163
163
  begin
164
- yield r if block_given?
164
+ yield r if defined?(yield)
165
165
  ensure
166
166
  begin
167
167
  r.close
@@ -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 block_given?
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 block_given?
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 block_given? && data
38
+ if defined?(yield) && data
39
39
  raise Error, "Cannot provide both a :data option and a block to copy_into"
40
- elsif !block_given? && !data
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 block_given?
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 block_given?
80
+ if defined?(yield)
81
81
  while buf = copier.readFromCopy
82
82
  yield(String.from_java_bytes(buf))
83
83
  end
@@ -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 block_given?
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 block_given?
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 block_given?
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 block_given?
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 block_given?
247
+ unless defined?(yield)
248
248
  return enum_for(:paged_each, opts)
249
249
  end
250
250
  stream.each(&block)
@@ -40,7 +40,7 @@ module Sequel
40
40
  synchronize(opts[:server]) do |conn|
41
41
  begin
42
42
  r = log_connection_yield(sql, conn){conn.run(sql)}
43
- yield(r) if block_given?
43
+ yield(r) if defined?(yield)
44
44
  rescue ::ODBC::Error, ArgumentError => e
45
45
  raise_error(e)
46
46
  ensure
@@ -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 block_given?
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 block_given?
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 block_given?
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
- block_given? ? yield(q) : q.cmd_tuples
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 block_given?
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 block_given? && data
403
+ if defined?(yield) && data
404
404
  raise Error, "Cannot provide both a :data option and a block to copy_into"
405
- elsif !block_given? && !data
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 block_given?
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
- block_given? ? yield(q) : q.cmd_tuples
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 block_given?
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
- literal_append(sql, args)
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 block_given?
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 block_given? # perform locking inside a transaction and yield to block
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 block_given?
396
+ yield def_columns if defined?(yield)
397
397
 
398
398
  constraints = (opts[:constraints] || []).dup
399
399
  pks = []
@@ -120,7 +120,7 @@ module Sequel
120
120
 
121
121
  case type
122
122
  when :select
123
- yield rs if block_given?
123
+ yield rs if defined?(yield)
124
124
  when :rows
125
125
  return @api.sqlany_affected_rows(rs)
126
126
  when :insert
@@ -75,7 +75,7 @@ module Sequel
75
75
  return r.public_send(m) if m
76
76
  end
77
77
  end
78
- yield(r) if block_given?
78
+ yield(r) if defined?(yield)
79
79
  rescue TinyTds::Error => e
80
80
  raise_error(e, :disconnect=>!c.active?)
81
81
  ensure
@@ -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 block_given?
58
+ if defined?(yield)
59
59
  return yield(db)
60
60
  end
61
61
  ensure
62
- if block_given?
62
+ if defined?(yield)
63
63
  db.disconnect if db
64
64
  Sequel.synchronize{::Sequel::DATABASES.delete(db)}
65
65
  end
@@ -546,7 +546,7 @@ module Sequel
546
546
  unless @opts[:order]
547
547
  raise Sequel::Error, "Dataset#paged_each requires the dataset be ordered"
548
548
  end
549
- unless block_given?
549
+ unless defined?(yield)
550
550
  return enum_for(:paged_each, opts)
551
551
  end
552
552
 
@@ -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 block_given?
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
@@ -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 << ' USING ('
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)
@@ -33,7 +33,7 @@ module Sequel
33
33
  module AnyNotEmpty
34
34
  # If a block is not given, return whether the dataset is not empty.
35
35
  def any?
36
- if block_given?
36
+ if defined?(yield)
37
37
  super
38
38
  else
39
39
  !empty?
@@ -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 block_given?; nil), &block)
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
@@ -102,7 +102,7 @@ class String
102
102
  # Yield the Inflections module if a block is given, and return
103
103
  # the Inflections module.
104
104
  def self.inflections
105
- yield Inflections if block_given?
105
+ yield Inflections if defined?(yield)
106
106
  Inflections
107
107
  end
108
108
 
@@ -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 block_given?
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 = value.match(/((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/)) && (m[2] || m[3])
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(v)
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.
@@ -147,7 +147,7 @@ module Sequel
147
147
  def initialize(expr, separator=nil)
148
148
  @expr = expr
149
149
  @separator = separator
150
- yield self if block_given?
150
+ yield self if defined?(yield)
151
151
  freeze
152
152
  end
153
153
 
@@ -1093,7 +1093,7 @@ module Sequel
1093
1093
  @modified = true
1094
1094
  initialize_set(values)
1095
1095
  _clear_changed_columns(:initialize)
1096
- yield self if block_given?
1096
+ yield self if defined?(yield)
1097
1097
  end
1098
1098
 
1099
1099
  # Returns value of the column's attribute.
@@ -4,7 +4,7 @@ module Sequel
4
4
  # Yield the Inflections module if a block is given, and return
5
5
  # the Inflections module.
6
6
  def self.inflections
7
- yield Inflections if block_given?
7
+ yield Inflections if defined?(yield)
8
8
  Inflections
9
9
  end
10
10
 
@@ -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, :@auto_validate_presence=>nil, :@auto_validate_types=>nil, :@auto_validate_not_null_columns=>:dup, :@auto_validate_explicit_not_null_columns=>:dup, :@auto_validate_max_length_columns=>:dup, :@auto_validate_unique_columns=>:dup, :@auto_validate_options => :dup)
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 (:not_null, :types, :unique).
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
@@ -608,7 +608,7 @@ module Sequel
608
608
 
609
609
  # Setup encryption for the given column.
610
610
  def _encrypt_column(column, opts)
611
- cryptor ||= if block_given?
611
+ cryptor ||= if defined?(yield)
612
612
  dsl = ColumnDSL.new
613
613
  yield dsl
614
614
  Cryptor.new(dsl.keys)
@@ -365,7 +365,7 @@ module Sequel
365
365
  h = {root => h}
366
366
  end
367
367
 
368
- h = yield h if block_given?
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 block_given?
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 block_given? || args.length > 1 || (args.length == 1 && !args[0].is_a?(Integer))
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)
@@ -55,7 +55,7 @@ module Sequel
55
55
  def find_or_new(attrs, set_attrs=nil)
56
56
  obj = find(attrs) || new(attrs)
57
57
  obj.set(set_attrs) if set_attrs
58
- yield obj if block_given?
58
+ yield obj if defined?(yield)
59
59
  obj
60
60
  end
61
61
  end
@@ -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 block_given?
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)
@@ -359,7 +359,7 @@ module Sequel
359
359
  else
360
360
  Array(inc).each{|i| to_xml_include(x1, i)}
361
361
  end
362
- yield x1 if block_given?
362
+ yield x1 if defined?(yield)
363
363
  end
364
364
  x.to_xml
365
365
  end
data/lib/sequel/sql.rb CHANGED
@@ -467,7 +467,7 @@ module Sequel
467
467
  #
468
468
  # Sequel[1] - :a # SQL: (1 - a)
469
469
  def expr(arg=(no_arg=true), &block)
470
- if block_given?
470
+ if defined?(yield)
471
471
  if no_arg
472
472
  return expr(block)
473
473
  else
@@ -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 = 48
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.48.0
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-09-01 00:00:00.000000000 Z
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