sequel 5.21.0 → 5.26.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 +80 -0
- data/README.rdoc +1 -1
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +3 -0
- data/doc/postgresql.rdoc +2 -2
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/release_notes/5.25.0.txt +32 -0
- data/doc/release_notes/5.26.0.txt +35 -0
- data/doc/testing.rdoc +1 -0
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +0 -1
- data/lib/sequel/adapters/shared/mssql.rb +11 -9
- data/lib/sequel/adapters/shared/postgres.rb +42 -12
- data/lib/sequel/adapters/shared/sqlite.rb +16 -2
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/schema_generator.rb +11 -2
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/dataset.rb +4 -2
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/dataset/query.rb +4 -0
- data/lib/sequel/dataset/sql.rb +11 -7
- data/lib/sequel/extensions/named_timezones.rb +51 -9
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +88 -17
- data/lib/sequel/extensions/pg_json_ops.rb +124 -0
- data/lib/sequel/extensions/pg_range.rb +12 -2
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/sql_comments.rb +2 -2
- data/lib/sequel/model/base.rb +12 -5
- data/lib/sequel/plugins/association_multi_add_remove.rb +83 -0
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/caching.rb +3 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -0
- data/lib/sequel/plugins/csv_serializer.rb +26 -9
- data/lib/sequel/plugins/dirty.rb +3 -9
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +89 -30
- data/lib/sequel/plugins/sharding.rb +11 -5
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/typecast_on_load.rb +3 -2
- data/lib/sequel/sql.rb +18 -4
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +174 -0
- data/spec/bin_spec.rb +2 -2
- data/spec/core/database_spec.rb +50 -0
- data/spec/core/dataset_spec.rb +33 -1
- data/spec/core/expression_filters_spec.rb +32 -3
- data/spec/core/schema_spec.rb +18 -0
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core_extensions_spec.rb +1 -1
- data/spec/extensions/association_multi_add_remove_spec.rb +1041 -0
- data/spec/extensions/dirty_spec.rb +33 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/nested_attributes_spec.rb +48 -0
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +37 -0
- data/spec/extensions/pg_json_ops_spec.rb +67 -0
- data/spec/extensions/pg_json_spec.rb +12 -0
- data/spec/extensions/pg_range_spec.rb +90 -9
- data/spec/extensions/sharding_spec.rb +8 -0
- data/spec/extensions/spec_helper.rb +9 -2
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/guards_helper.rb +1 -1
- data/spec/integration/dataset_test.rb +24 -8
- data/spec/integration/plugin_test.rb +27 -0
- data/spec/integration/schema_test.rb +16 -2
- data/spec/model/spec_helper.rb +1 -1
- metadata +32 -2
@@ -340,14 +340,18 @@ module Sequel
|
|
340
340
|
raise Sequel::Error, "invalid array, empty string" if eos?
|
341
341
|
raise Sequel::Error, "invalid array, doesn't start with {" unless scan(/((\[\d+:\d+\])+=)?\{/)
|
342
342
|
|
343
|
+
# :nocov:
|
343
344
|
while !eos?
|
345
|
+
# :nocov:
|
344
346
|
char = scan(/[{}",]|[^{}",]+/)
|
345
347
|
if char == ','
|
346
348
|
# Comma outside quoted string indicates end of current entry
|
347
349
|
new_entry
|
348
350
|
elsif char == '"'
|
349
351
|
raise Sequel::Error, "invalid array, opening quote with existing recorded data" unless @recorded.empty?
|
352
|
+
# :nocov:
|
350
353
|
while true
|
354
|
+
# :nocov:
|
351
355
|
char = scan(/["\\]|[^"\\]+/)
|
352
356
|
if char == '\\'
|
353
357
|
@recorded << getch
|
@@ -12,7 +12,7 @@
|
|
12
12
|
#
|
13
13
|
# Note that wrapping JSON primitives changes the behavior for
|
14
14
|
# JSON false and null values. Because only +false+ and +nil+
|
15
|
-
# in Ruby are considered
|
15
|
+
# in Ruby are considered falsey, wrapping these objects results
|
16
16
|
# in unexpected behavior if you use the values directly in
|
17
17
|
# conditionals:
|
18
18
|
#
|
@@ -208,11 +208,19 @@ module Sequel
|
|
208
208
|
JSONB_PRIMITIVE_WRAPPER_MAPPING.freeze
|
209
209
|
|
210
210
|
JSON_COMBINED_WRAPPER_MAPPING =JSON_WRAPPER_MAPPING.merge(JSON_PRIMITIVE_WRAPPER_MAPPING).freeze
|
211
|
-
JSON_WRAP_CLASSES = JSON_COMBINED_WRAPPER_MAPPING.keys.freeze
|
212
|
-
|
213
211
|
JSONB_COMBINED_WRAPPER_MAPPING =JSONB_WRAPPER_MAPPING.merge(JSONB_PRIMITIVE_WRAPPER_MAPPING).freeze
|
214
212
|
JSONB_WRAP_CLASSES = JSONB_COMBINED_WRAPPER_MAPPING.keys.freeze
|
215
213
|
|
214
|
+
Sequel::Deprecation.deprecate_constant(self, :JSON_WRAPPER_MAPPING)
|
215
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_WRAPPER_MAPPING)
|
216
|
+
Sequel::Deprecation.deprecate_constant(self, :JSON_PRIMITIVE_WRAPPER_MAPPING)
|
217
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_PRIMITIVE_WRAPPER_MAPPING)
|
218
|
+
Sequel::Deprecation.deprecate_constant(self, :JSON_COMBINED_WRAPPER_MAPPING)
|
219
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_COMBINED_WRAPPER_MAPPING)
|
220
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_WRAP_CLASSES)
|
221
|
+
|
222
|
+
JSON_WRAP_CLASSES = [Hash, Array, String, Integer, Float, NilClass, TrueClass, FalseClass].freeze
|
223
|
+
|
216
224
|
# Methods enabling Database object integration with the json type.
|
217
225
|
module JSONDatabaseMethods
|
218
226
|
def self.extended(db)
|
@@ -228,6 +236,69 @@ module Sequel
|
|
228
236
|
end
|
229
237
|
end
|
230
238
|
|
239
|
+
# Return the wrapper class for the json type if value is Hash or Array.
|
240
|
+
def self.json_wrapper(value)
|
241
|
+
case value
|
242
|
+
when ::Hash
|
243
|
+
JSONHash
|
244
|
+
when ::Array
|
245
|
+
JSONArray
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Return the wrapper class for the jsonb type if value is Hash or Array.
|
250
|
+
def self.jsonb_wrapper(value)
|
251
|
+
case value
|
252
|
+
when ::Hash
|
253
|
+
JSONBHash
|
254
|
+
when ::Array
|
255
|
+
JSONBArray
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Return the wrapper class for the json type if value is a supported type.
|
260
|
+
def self.json_primitive_wrapper(value)
|
261
|
+
case value
|
262
|
+
when ::Hash
|
263
|
+
JSONHash
|
264
|
+
when ::Array
|
265
|
+
JSONArray
|
266
|
+
when ::String
|
267
|
+
JSONString
|
268
|
+
when ::Integer
|
269
|
+
JSONInteger
|
270
|
+
when ::Float
|
271
|
+
JSONFloat
|
272
|
+
when ::NilClass
|
273
|
+
JSONNull
|
274
|
+
when ::TrueClass
|
275
|
+
JSONTrue
|
276
|
+
when ::FalseClass
|
277
|
+
JSONFalse
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Return the wrapper class for the jsonb type if value is a supported type.
|
282
|
+
def self.jsonb_primitive_wrapper(value)
|
283
|
+
case value
|
284
|
+
when ::Hash
|
285
|
+
JSONBHash
|
286
|
+
when ::Array
|
287
|
+
JSONBArray
|
288
|
+
when ::String
|
289
|
+
JSONBString
|
290
|
+
when ::Integer
|
291
|
+
JSONBInteger
|
292
|
+
when ::Float
|
293
|
+
JSONBFloat
|
294
|
+
when ::NilClass
|
295
|
+
JSONBNull
|
296
|
+
when ::TrueClass
|
297
|
+
JSONBTrue
|
298
|
+
when ::FalseClass
|
299
|
+
JSONBFalse
|
300
|
+
end
|
301
|
+
end
|
231
302
|
|
232
303
|
# Deprecated
|
233
304
|
def self.db_parse_json(s)
|
@@ -326,9 +397,9 @@ module Sequel
|
|
326
397
|
# Wrap the parsed JSON value in the appropriate JSON wrapper class.
|
327
398
|
# Only wrap primitive values if wrap_json_primitives is set.
|
328
399
|
def _wrap_json(value)
|
329
|
-
if klass =
|
400
|
+
if klass = JSONDatabaseMethods.json_wrapper(value)
|
330
401
|
klass.new(value)
|
331
|
-
elsif klass =
|
402
|
+
elsif klass = JSONDatabaseMethods.json_primitive_wrapper(value)
|
332
403
|
if wrap_json_primitives
|
333
404
|
klass.new(value)
|
334
405
|
else
|
@@ -342,9 +413,9 @@ module Sequel
|
|
342
413
|
# Wrap the parsed JSON value in the appropriate JSONB wrapper class.
|
343
414
|
# Only wrap primitive values if wrap_json_primitives is set.
|
344
415
|
def _wrap_jsonb(value)
|
345
|
-
if klass =
|
416
|
+
if klass = JSONDatabaseMethods.jsonb_wrapper(value)
|
346
417
|
klass.new(value)
|
347
|
-
elsif klass =
|
418
|
+
elsif klass = JSONDatabaseMethods.jsonb_primitive_wrapper(value)
|
348
419
|
if wrap_json_primitives
|
349
420
|
klass.new(value)
|
350
421
|
else
|
@@ -413,10 +484,10 @@ module Sequel
|
|
413
484
|
_wrap_json(_parse_json(value))
|
414
485
|
end
|
415
486
|
when *JSON_WRAP_CLASSES
|
416
|
-
|
487
|
+
JSONDatabaseMethods.json_primitive_wrapper(value).new(value)
|
417
488
|
when JSONBObject
|
418
489
|
value = value.__getobj__
|
419
|
-
|
490
|
+
JSONDatabaseMethods.json_primitive_wrapper(value).new(value)
|
420
491
|
else
|
421
492
|
raise Sequel::InvalidValue, "invalid value for json: #{value.inspect}"
|
422
493
|
end
|
@@ -433,11 +504,11 @@ module Sequel
|
|
433
504
|
else
|
434
505
|
_wrap_jsonb(_parse_json(value))
|
435
506
|
end
|
436
|
-
when *
|
437
|
-
|
507
|
+
when *JSON_WRAP_CLASSES
|
508
|
+
JSONDatabaseMethods.jsonb_primitive_wrapper(value).new(value)
|
438
509
|
when JSONObject
|
439
510
|
value = value.__getobj__
|
440
|
-
|
511
|
+
JSONDatabaseMethods.jsonb_primitive_wrapper(value).new(value)
|
441
512
|
else
|
442
513
|
raise Sequel::InvalidValue, "invalid value for jsonb: #{value.inspect}"
|
443
514
|
end
|
@@ -460,7 +531,7 @@ module Sequel
|
|
460
531
|
Postgres::JSONHash.new(v)
|
461
532
|
when Postgres::JSONBObject
|
462
533
|
v = v.__getobj__
|
463
|
-
Postgres::
|
534
|
+
Postgres::JSONDatabaseMethods.json_primitive_wrapper(v).new(v)
|
464
535
|
else
|
465
536
|
Sequel.pg_json_op(v)
|
466
537
|
end
|
@@ -472,7 +543,7 @@ module Sequel
|
|
472
543
|
def pg_json_wrap(v)
|
473
544
|
case v
|
474
545
|
when *Postgres::JSON_WRAP_CLASSES
|
475
|
-
Postgres::
|
546
|
+
Postgres::JSONDatabaseMethods.json_primitive_wrapper(v).new(v)
|
476
547
|
else
|
477
548
|
raise Error, "invalid value passed to Sequel.pg_json_wrap: #{v.inspect}"
|
478
549
|
end
|
@@ -492,7 +563,7 @@ module Sequel
|
|
492
563
|
Postgres::JSONBHash.new(v)
|
493
564
|
when Postgres::JSONObject
|
494
565
|
v = v.__getobj__
|
495
|
-
Postgres::
|
566
|
+
Postgres::JSONDatabaseMethods.jsonb_primitive_wrapper(v).new(v)
|
496
567
|
else
|
497
568
|
Sequel.pg_jsonb_op(v)
|
498
569
|
end
|
@@ -503,8 +574,8 @@ module Sequel
|
|
503
574
|
# other types.
|
504
575
|
def pg_jsonb_wrap(v)
|
505
576
|
case v
|
506
|
-
when *Postgres::
|
507
|
-
Postgres::
|
577
|
+
when *Postgres::JSON_WRAP_CLASSES
|
578
|
+
Postgres::JSONDatabaseMethods.jsonb_primitive_wrapper(v).new(v)
|
508
579
|
else
|
509
580
|
raise Error, "invalid value passed to Sequel.pg_jsonb_wrap: #{v.inspect}"
|
510
581
|
end
|
@@ -73,6 +73,23 @@
|
|
73
73
|
# j.pretty # jsonb_pretty(jsonb_column)
|
74
74
|
# j.set(%w'0 a', :h) # jsonb_set(jsonb_column, ARRAY['0','a'], h, true)
|
75
75
|
#
|
76
|
+
# On PostgreSQL 12+ SQL/JSON functions and operators are supported:
|
77
|
+
#
|
78
|
+
# j.path_exists('$.foo') # (jsonb_column @? '$.foo')
|
79
|
+
# j.path_match('$.foo') # (jsonb_column @@ '$.foo')
|
80
|
+
#
|
81
|
+
# j.path_exists!('$.foo') # jsonb_path_exists(jsonb_column, '$.foo')
|
82
|
+
# j.path_match!('$.foo') # jsonb_path_match(jsonb_column, '$.foo')
|
83
|
+
# j.path_query('$.foo') # jsonb_path_query(jsonb_column, '$.foo')
|
84
|
+
# j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo')
|
85
|
+
# j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo')
|
86
|
+
#
|
87
|
+
# For the PostgreSQL 12+ SQL/JSON functions, one argument is required (+path+) and
|
88
|
+
# two more arguments are optional (+vars+ and +silent+). +path+ specifies the JSON path.
|
89
|
+
# +vars+ specifies a hash or a string in JSON format of named variables to be
|
90
|
+
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
91
|
+
# errors are not suppressed.
|
92
|
+
#
|
76
93
|
# If you are also using the pg_json extension, you should load it before
|
77
94
|
# loading this extension. Doing so will allow you to use the #op method on
|
78
95
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
@@ -292,6 +309,8 @@ module Sequel
|
|
292
309
|
CONTAINED_BY = ["(".freeze, " <@ ".freeze, ")".freeze].freeze
|
293
310
|
DELETE_PATH = ["(".freeze, " #- ".freeze, ")".freeze].freeze
|
294
311
|
HAS_KEY = ["(".freeze, " ? ".freeze, ")".freeze].freeze
|
312
|
+
PATH_EXISTS = ["(".freeze, " @? ".freeze, ")".freeze].freeze
|
313
|
+
PATH_MATCH = ["(".freeze, " @@ ".freeze, ")".freeze].freeze
|
295
314
|
|
296
315
|
# jsonb expression for deletion of the given argument from the
|
297
316
|
# current jsonb.
|
@@ -362,6 +381,95 @@ module Sequel
|
|
362
381
|
self.class.new(function(:insert, wrap_input_array(path), wrap_input_jsonb(other), insert_after))
|
363
382
|
end
|
364
383
|
|
384
|
+
# Returns whether the JSON path returns any item for the json object.
|
385
|
+
#
|
386
|
+
# json_op.path_exists("$.foo") # (json @? '$.foo')
|
387
|
+
def path_exists(path)
|
388
|
+
bool_op(PATH_EXISTS, path)
|
389
|
+
end
|
390
|
+
|
391
|
+
# Returns whether the JSON path returns any item for the json object.
|
392
|
+
#
|
393
|
+
# json_op.path_exists!("$.foo")
|
394
|
+
# # jsonb_path_exists(json, '$.foo')
|
395
|
+
#
|
396
|
+
# json_op.path_exists!("$.foo ? ($ > $x)", x: 2)
|
397
|
+
# # jsonb_path_exists(json, '$.foo ? ($ > $x)', '{"x":2}')
|
398
|
+
#
|
399
|
+
# json_op.path_exists!("$.foo ? ($ > $x)", {x: 2}, true)
|
400
|
+
# # jsonb_path_exists(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
401
|
+
def path_exists!(path, vars=nil, silent=nil)
|
402
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists, path, vars, silent))
|
403
|
+
end
|
404
|
+
|
405
|
+
# Returns the first item of the result of JSON path predicate check for the json object.
|
406
|
+
# Returns nil if the first item is not true or false.
|
407
|
+
#
|
408
|
+
# json_op.path_match("$.foo") # (json @@ '$.foo')
|
409
|
+
def path_match(path)
|
410
|
+
bool_op(PATH_MATCH, path)
|
411
|
+
end
|
412
|
+
|
413
|
+
# Returns the first item of the result of JSON path predicate check for the json object.
|
414
|
+
# Returns nil if the first item is not true or false and silent is true.
|
415
|
+
#
|
416
|
+
# json_op.path_match!("$.foo")
|
417
|
+
# # jsonb_path_match(json, '$.foo')
|
418
|
+
#
|
419
|
+
# json_op.path_match!("$.foo ? ($ > $x)", x: 2)
|
420
|
+
# # jsonb_path_match(json, '$.foo ? ($ > $x)', '{"x":2}')
|
421
|
+
#
|
422
|
+
# json_op.path_match!("$.foo ? ($ > $x)", {x: 2}, true)
|
423
|
+
# # jsonb_path_match(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
424
|
+
def path_match!(path, vars=nil, silent=nil)
|
425
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match, path, vars, silent))
|
426
|
+
end
|
427
|
+
|
428
|
+
# Returns a set of all jsonb values specified by the JSON path
|
429
|
+
# for the json object.
|
430
|
+
#
|
431
|
+
# json_op.path_query("$.foo")
|
432
|
+
# # jsonb_path_query(json, '$.foo')
|
433
|
+
#
|
434
|
+
# json_op.path_query("$.foo ? ($ > $x)", x: 2)
|
435
|
+
# # jsonb_path_query(json, '$.foo ? ($ > $x)', '{"x":2}')
|
436
|
+
#
|
437
|
+
# json_op.path_query("$.foo ? ($ > $x)", {x: 2}, true)
|
438
|
+
# # jsonb_path_query(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
439
|
+
def path_query(path, vars=nil, silent=nil)
|
440
|
+
_path_function(:jsonb_path_query, path, vars, silent)
|
441
|
+
end
|
442
|
+
|
443
|
+
# Returns a jsonb array of all values specified by the JSON path
|
444
|
+
# for the json object.
|
445
|
+
#
|
446
|
+
# json_op.path_query_array("$.foo")
|
447
|
+
# # jsonb_path_query_array(json, '$.foo')
|
448
|
+
#
|
449
|
+
# json_op.path_query_array("$.foo ? ($ > $x)", x: 2)
|
450
|
+
# # jsonb_path_query_array(json, '$.foo ? ($ > $x)', '{"x":2}')
|
451
|
+
#
|
452
|
+
# json_op.path_query_array("$.foo ? ($ > $x)", {x: 2}, true)
|
453
|
+
# # jsonb_path_query_array(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
454
|
+
def path_query_array(path, vars=nil, silent=nil)
|
455
|
+
JSONBOp.new(_path_function(:jsonb_path_query_array, path, vars, silent))
|
456
|
+
end
|
457
|
+
|
458
|
+
# Returns the first item of the result specified by the JSON path
|
459
|
+
# for the json object.
|
460
|
+
#
|
461
|
+
# json_op.path_query_first("$.foo")
|
462
|
+
# # jsonb_path_query_first(json, '$.foo')
|
463
|
+
#
|
464
|
+
# json_op.path_query_first("$.foo ? ($ > $x)", x: 2)
|
465
|
+
# # jsonb_path_query_first(json, '$.foo ? ($ > $x)', '{"x":2}')
|
466
|
+
#
|
467
|
+
# json_op.path_query_first("$.foo ? ($ > $x)", {x: 2}, true)
|
468
|
+
# # jsonb_path_query_first(json, '$.foo ? ($ > $x)', '{"x":2}', true)
|
469
|
+
def path_query_first(path, vars=nil, silent=nil)
|
470
|
+
JSONBOp.new(_path_function(:jsonb_path_query_first, path, vars, silent))
|
471
|
+
end
|
472
|
+
|
365
473
|
# Return the receiver, since it is already a JSONBOp.
|
366
474
|
def pg_jsonb
|
367
475
|
self
|
@@ -386,6 +494,22 @@ module Sequel
|
|
386
494
|
|
387
495
|
private
|
388
496
|
|
497
|
+
# Internals of the jsonb SQL/JSON path functions.
|
498
|
+
def _path_function(func, path, vars, silent)
|
499
|
+
args = []
|
500
|
+
if vars
|
501
|
+
if vars.is_a?(Hash)
|
502
|
+
vars = vars.to_json
|
503
|
+
end
|
504
|
+
args << vars
|
505
|
+
|
506
|
+
unless silent.nil?
|
507
|
+
args << silent
|
508
|
+
end
|
509
|
+
end
|
510
|
+
SQL::Function.new(func, self, path, *args)
|
511
|
+
end
|
512
|
+
|
389
513
|
# Return a placeholder literal with the given str and args, wrapped
|
390
514
|
# in a boolean expression, used by operators that return booleans.
|
391
515
|
def bool_op(str, other)
|
@@ -402,6 +402,15 @@ module Sequel
|
|
402
402
|
end
|
403
403
|
alias == eql?
|
404
404
|
|
405
|
+
# Make sure equal ranges have the same hash.
|
406
|
+
def hash
|
407
|
+
if @empty
|
408
|
+
@db_type.hash
|
409
|
+
else
|
410
|
+
[@begin, @end, @exclude_begin, @exclude_end, @db_type].hash
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
405
414
|
# Allow PGRange values in case statements, where they return true if they
|
406
415
|
# are equal to each other using eql?, or if this PGRange can be converted
|
407
416
|
# to a Range, delegating to that range.
|
@@ -453,13 +462,14 @@ module Sequel
|
|
453
462
|
end
|
454
463
|
|
455
464
|
ENDLESS_RANGE_NOT_SUPPORTED = RUBY_VERSION < '2.6'
|
465
|
+
STARTLESS_RANGE_NOT_SUPPORTED = RUBY_VERSION < '2.7'
|
456
466
|
|
457
467
|
# Return a ruby Range object for this instance, if one can be created.
|
458
468
|
def to_range
|
459
469
|
return @range if @range
|
460
470
|
raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
|
461
471
|
raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
|
462
|
-
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning")
|
472
|
+
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") if STARTLESS_RANGE_NOT_SUPPORTED && !self.begin
|
463
473
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") if ENDLESS_RANGE_NOT_SUPPORTED && !self.end
|
464
474
|
@range = Range.new(self.begin, self.end, exclude_end?)
|
465
475
|
end
|
@@ -468,7 +478,7 @@ module Sequel
|
|
468
478
|
# it must have a beginning and an ending (no unbounded ranges), and it cannot exclude
|
469
479
|
# the beginning element.
|
470
480
|
def valid_ruby_range?
|
471
|
-
!(empty? || exclude_begin? || !self.begin || (ENDLESS_RANGE_NOT_SUPPORTED && !self.end))
|
481
|
+
!(empty? || exclude_begin? || (STARTLESS_RANGE_NOT_SUPPORTED && !self.begin) || (ENDLESS_RANGE_NOT_SUPPORTED && !self.end))
|
472
482
|
end
|
473
483
|
|
474
484
|
# Whether the beginning of the range is unbounded.
|
@@ -7,7 +7,7 @@
|
|
7
7
|
# that when composite fields are retrieved, they are parsed and returned
|
8
8
|
# as instances of Sequel::Postgres::PGRow::(HashRow|ArrayRow), or
|
9
9
|
# optionally a custom type. HashRow and ArrayRow are DelegateClasses of
|
10
|
-
#
|
10
|
+
# Hash and Array, so they mostly act like a hash or array, but not
|
11
11
|
# completely (is_a?(Hash) and is_a?(Array) are false). If you want the
|
12
12
|
# actual hash for a HashRow, call HashRow#to_hash, and if you want the
|
13
13
|
# actual array for an ArrayRow, call ArrayRow#to_a. This is done so
|
@@ -228,7 +228,9 @@ module Sequel
|
|
228
228
|
if skip(/\)/)
|
229
229
|
values << nil
|
230
230
|
else
|
231
|
+
# :nocov:
|
231
232
|
until eos?
|
233
|
+
# :nocov:
|
232
234
|
if skip(/"/)
|
233
235
|
values << scan(/(\\.|""|[^"])*/).gsub(/\\(.)|"(")/, '\1\2')
|
234
236
|
skip(/"[,)]/)
|
@@ -9,8 +9,8 @@
|
|
9
9
|
# #
|
10
10
|
#
|
11
11
|
# As you can see, this uses single line SQL comments (--) suffixed
|
12
|
-
# by a newline. This
|
13
|
-
#
|
12
|
+
# by a newline. This plugin transforms all consecutive whitespace
|
13
|
+
# in the comment to a single string:
|
14
14
|
#
|
15
15
|
# ds = DB[:table].comment("Some\r\nComment Here").all
|
16
16
|
# # SELECT * FROM table -- Some Comment Here
|
data/lib/sequel/model/base.rb
CHANGED
@@ -1069,7 +1069,7 @@ module Sequel
|
|
1069
1069
|
@new = true
|
1070
1070
|
@modified = true
|
1071
1071
|
initialize_set(values)
|
1072
|
-
|
1072
|
+
_clear_changed_columns(:initialize)
|
1073
1073
|
yield self if block_given?
|
1074
1074
|
end
|
1075
1075
|
|
@@ -1626,6 +1626,13 @@ module Sequel
|
|
1626
1626
|
def _changed_columns
|
1627
1627
|
@changed_columns ||= []
|
1628
1628
|
end
|
1629
|
+
|
1630
|
+
# Clear the changed columns. Reason is the reason for clearing
|
1631
|
+
# the columns, and should be one of: :initialize, :refresh, :create
|
1632
|
+
# or :update.
|
1633
|
+
def _clear_changed_columns(_reason)
|
1634
|
+
_changed_columns.clear
|
1635
|
+
end
|
1629
1636
|
|
1630
1637
|
# Do the deletion of the object's dataset, and check that the row
|
1631
1638
|
# was actually deleted.
|
@@ -1716,7 +1723,7 @@ module Sequel
|
|
1716
1723
|
# is used for reading newly inserted values from the database
|
1717
1724
|
def _refresh(dataset)
|
1718
1725
|
_refresh_set_values(_refresh_get(dataset) || raise(NoExistingObject, "Record not found"))
|
1719
|
-
|
1726
|
+
_clear_changed_columns(:refresh)
|
1720
1727
|
end
|
1721
1728
|
|
1722
1729
|
# Get the row of column data from the database.
|
@@ -1754,7 +1761,7 @@ module Sequel
|
|
1754
1761
|
@this = nil
|
1755
1762
|
@new = false
|
1756
1763
|
@modified = false
|
1757
|
-
pk ? _save_refresh :
|
1764
|
+
pk ? _save_refresh : _clear_changed_columns(:create)
|
1758
1765
|
after_create
|
1759
1766
|
true
|
1760
1767
|
end
|
@@ -1771,7 +1778,7 @@ module Sequel
|
|
1771
1778
|
cc.clear
|
1772
1779
|
else
|
1773
1780
|
columns_updated = _save_update_all_columns_hash
|
1774
|
-
|
1781
|
+
_clear_changed_columns(:update)
|
1775
1782
|
end
|
1776
1783
|
else # update only the specified columns
|
1777
1784
|
columns = Array(columns)
|
@@ -1798,7 +1805,7 @@ module Sequel
|
|
1798
1805
|
# can be overridden to avoid the refresh.
|
1799
1806
|
def _save_refresh
|
1800
1807
|
_save_set_values(_refresh_get(this.server?(:default)) || raise(NoExistingObject, "Record not found"))
|
1801
|
-
|
1808
|
+
_clear_changed_columns(:create)
|
1802
1809
|
end
|
1803
1810
|
|
1804
1811
|
# Set values to the provided hash. Called after a create,
|