sequel 5.33.0 → 5.58.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 +318 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +40 -9
- data/doc/association_basics.rdoc +77 -13
- data/doc/cheat_sheet.rdoc +13 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/fork_safety.rdoc +84 -0
- data/doc/migration.rdoc +12 -6
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +15 -3
- data/doc/postgresql.rdoc +9 -1
- data/doc/querying.rdoc +7 -5
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/release_notes/5.39.0.txt +19 -0
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/sql.rdoc +14 -2
- data/doc/testing.rdoc +10 -1
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +29 -19
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +8 -6
- data/lib/sequel/adapters/oracle.rb +5 -4
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +84 -7
- data/lib/sequel/adapters/shared/mysql.rb +33 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -7
- data/lib/sequel/adapters/shared/postgres.rb +158 -20
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -10
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +2 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +9 -8
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +7 -9
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +33 -24
- data/lib/sequel/database/connecting.rb +3 -4
- data/lib/sequel/database/misc.rb +37 -12
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +50 -53
- data/lib/sequel/database/schema_methods.rb +45 -23
- data/lib/sequel/database/transactions.rb +9 -6
- data/lib/sequel/dataset/actions.rb +61 -8
- data/lib/sequel/dataset/features.rb +15 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +114 -11
- data/lib/sequel/dataset/sql.rb +172 -46
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +38 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -24
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +3 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +139 -0
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/named_timezones.rb +5 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_array_ops.rb +6 -2
- data/lib/sequel/extensions/pg_enum.rb +3 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +35 -8
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +119 -4
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +7 -19
- data/lib/sequel/extensions/pg_range_ops.rb +39 -9
- data/lib/sequel/extensions/pg_row.rb +1 -1
- data/lib/sequel/extensions/pg_row_ops.rb +25 -1
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +4 -1
- data/lib/sequel/extensions/schema_dumper.rb +16 -5
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model/associations.rb +342 -114
- data/lib/sequel/model/base.rb +45 -24
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +8 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +3 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +39 -5
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +8 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +8 -3
- data/lib/sequel/plugins/pg_array_associations.rb +58 -41
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/prepared_statements.rb +15 -12
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +37 -35
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/plugins/validation_helpers.rb +18 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +20 -17
- data/lib/sequel/version.rb +1 -1
- metadata +93 -39
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
# The pg_json_ops extension adds support to Sequel's DSL to make
|
|
4
4
|
# it easier to call PostgreSQL JSON functions and operators (added
|
|
5
5
|
# first in PostgreSQL 9.3). It also supports the JSONB functions
|
|
6
|
-
# and operators added in PostgreSQL 9.4
|
|
6
|
+
# and operators added in PostgreSQL 9.4, as well as additional
|
|
7
|
+
# functions and operators added in later versions.
|
|
7
8
|
#
|
|
8
9
|
# To load the extension:
|
|
9
10
|
#
|
|
@@ -73,7 +74,10 @@
|
|
|
73
74
|
# j.pretty # jsonb_pretty(jsonb_column)
|
|
74
75
|
# j.set(%w'0 a', :h) # jsonb_set(jsonb_column, ARRAY['0','a'], h, true)
|
|
75
76
|
#
|
|
76
|
-
#
|
|
77
|
+
# j.set_lax(%w'0 a', :h, false, 'raise_exception')
|
|
78
|
+
# # jsonb_set_lax(jsonb_column, ARRAY['0','a'], h, false, 'raise_exception')
|
|
79
|
+
#
|
|
80
|
+
# On PostgreSQL 12+ SQL/JSON path functions and operators are supported:
|
|
77
81
|
#
|
|
78
82
|
# j.path_exists('$.foo') # (jsonb_column @? '$.foo')
|
|
79
83
|
# j.path_match('$.foo') # (jsonb_column @@ '$.foo')
|
|
@@ -84,12 +88,41 @@
|
|
|
84
88
|
# j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo')
|
|
85
89
|
# j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo')
|
|
86
90
|
#
|
|
87
|
-
#
|
|
91
|
+
# On PostgreSQL 13+ timezone-aware SQL/JSON path functions and operators are supported:
|
|
92
|
+
#
|
|
93
|
+
# j.path_exists_tz!('$.foo') # jsonb_path_exists_tz(jsonb_column, '$.foo')
|
|
94
|
+
# j.path_match_tz!('$.foo') # jsonb_path_match_tz(jsonb_column, '$.foo')
|
|
95
|
+
# j.path_query_tz('$.foo') # jsonb_path_query_tz(jsonb_column, '$.foo')
|
|
96
|
+
# j.path_query_array_tz('$.foo') # jsonb_path_query_array_tz(jsonb_column, '$.foo')
|
|
97
|
+
# j.path_query_first_tz('$.foo') # jsonb_path_query_first_tz(jsonb_column, '$.foo')
|
|
98
|
+
#
|
|
99
|
+
# For the PostgreSQL 12+ SQL/JSON path functions, one argument is required (+path+) and
|
|
88
100
|
# two more arguments are optional (+vars+ and +silent+). +path+ specifies the JSON path.
|
|
89
101
|
# +vars+ specifies a hash or a string in JSON format of named variables to be
|
|
90
102
|
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
|
91
103
|
# errors are not suppressed.
|
|
92
104
|
#
|
|
105
|
+
# On PostgreSQL 14+, The JSONB <tt>[]</tt> method will use subscripts instead of being
|
|
106
|
+
# the same as +get+, if the value being wrapped is an identifer:
|
|
107
|
+
#
|
|
108
|
+
# Sequel.pg_jsonb_op(:jsonb_column)[1] # jsonb_column[1]
|
|
109
|
+
# Sequel.pg_jsonb_op(:jsonb_column)[1][2] # jsonb_column[1][2]
|
|
110
|
+
# Sequel.pg_jsonb_op(Sequel[:j][:b])[1] # j.b[1]
|
|
111
|
+
#
|
|
112
|
+
# This support allows you to use JSONB subscripts in UPDATE statements to update only
|
|
113
|
+
# part of a column:
|
|
114
|
+
#
|
|
115
|
+
# c = Sequel.pg_jsonb_op(:c)
|
|
116
|
+
# DB[:t].update(c['key1'] => '1', c['key2'] => '"a"')
|
|
117
|
+
# # UPDATE "t" SET "c"['key1'] = '1', "c"['key2'] = '"a"'
|
|
118
|
+
#
|
|
119
|
+
# Note that you have to provide the value of a JSONB subscript as a JSONB value, so this
|
|
120
|
+
# will update +key1+ to use the number <tt>1</tt>, and +key2+ to use the string <tt>a</tt>.
|
|
121
|
+
# For this reason it may be simpler to use +to_json+:
|
|
122
|
+
#
|
|
123
|
+
# c = Sequel.pg_jsonb_op(:c)
|
|
124
|
+
# DB[:t].update(c['key1'] => 1.to_json, c['key2'] => "a".to_json)
|
|
125
|
+
#
|
|
93
126
|
# If you are also using the pg_json extension, you should load it before
|
|
94
127
|
# loading this extension. Doing so will allow you to use the #op method on
|
|
95
128
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
|
@@ -312,6 +345,24 @@ module Sequel
|
|
|
312
345
|
PATH_EXISTS = ["(".freeze, " @? ".freeze, ")".freeze].freeze
|
|
313
346
|
PATH_MATCH = ["(".freeze, " @@ ".freeze, ")".freeze].freeze
|
|
314
347
|
|
|
348
|
+
# Support subscript syntax for JSONB.
|
|
349
|
+
def [](key)
|
|
350
|
+
if is_array?(key)
|
|
351
|
+
super
|
|
352
|
+
else
|
|
353
|
+
case @value
|
|
354
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, JSONBSubscriptOp
|
|
355
|
+
# Only use subscripts for identifiers. In other cases, switching from
|
|
356
|
+
# the -> operator to [] for subscripts causes SQL syntax issues. You
|
|
357
|
+
# only need the [] for subscripting when doing assignment, and
|
|
358
|
+
# assignment is generally done on identifiers.
|
|
359
|
+
self.class.new(JSONBSubscriptOp.new(self, key))
|
|
360
|
+
else
|
|
361
|
+
super
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
315
366
|
# jsonb expression for deletion of the given argument from the
|
|
316
367
|
# current jsonb.
|
|
317
368
|
#
|
|
@@ -402,6 +453,11 @@ module Sequel
|
|
|
402
453
|
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists, path, vars, silent))
|
|
403
454
|
end
|
|
404
455
|
|
|
456
|
+
# The same as #path_exists!, except that timezone-aware conversions are used for date/time values.
|
|
457
|
+
def path_exists_tz!(path, vars=nil, silent=nil)
|
|
458
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_exists_tz, path, vars, silent))
|
|
459
|
+
end
|
|
460
|
+
|
|
405
461
|
# Returns the first item of the result of JSON path predicate check for the json object.
|
|
406
462
|
# Returns nil if the first item is not true or false.
|
|
407
463
|
#
|
|
@@ -425,6 +481,11 @@ module Sequel
|
|
|
425
481
|
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match, path, vars, silent))
|
|
426
482
|
end
|
|
427
483
|
|
|
484
|
+
# The same as #path_match!, except that timezone-aware conversions are used for date/time values.
|
|
485
|
+
def path_match_tz!(path, vars=nil, silent=nil)
|
|
486
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, _path_function(:jsonb_path_match_tz, path, vars, silent))
|
|
487
|
+
end
|
|
488
|
+
|
|
428
489
|
# Returns a set of all jsonb values specified by the JSON path
|
|
429
490
|
# for the json object.
|
|
430
491
|
#
|
|
@@ -440,6 +501,11 @@ module Sequel
|
|
|
440
501
|
_path_function(:jsonb_path_query, path, vars, silent)
|
|
441
502
|
end
|
|
442
503
|
|
|
504
|
+
# The same as #path_query, except that timezone-aware conversions are used for date/time values.
|
|
505
|
+
def path_query_tz(path, vars=nil, silent=nil)
|
|
506
|
+
_path_function(:jsonb_path_query_tz, path, vars, silent)
|
|
507
|
+
end
|
|
508
|
+
|
|
443
509
|
# Returns a jsonb array of all values specified by the JSON path
|
|
444
510
|
# for the json object.
|
|
445
511
|
#
|
|
@@ -455,6 +521,11 @@ module Sequel
|
|
|
455
521
|
JSONBOp.new(_path_function(:jsonb_path_query_array, path, vars, silent))
|
|
456
522
|
end
|
|
457
523
|
|
|
524
|
+
# The same as #path_query_array, except that timezone-aware conversions are used for date/time values.
|
|
525
|
+
def path_query_array_tz(path, vars=nil, silent=nil)
|
|
526
|
+
JSONBOp.new(_path_function(:jsonb_path_query_array_tz, path, vars, silent))
|
|
527
|
+
end
|
|
528
|
+
|
|
458
529
|
# Returns the first item of the result specified by the JSON path
|
|
459
530
|
# for the json object.
|
|
460
531
|
#
|
|
@@ -470,6 +541,11 @@ module Sequel
|
|
|
470
541
|
JSONBOp.new(_path_function(:jsonb_path_query_first, path, vars, silent))
|
|
471
542
|
end
|
|
472
543
|
|
|
544
|
+
# The same as #path_query_first, except that timezone-aware conversions are used for date/time values.
|
|
545
|
+
def path_query_first_tz(path, vars=nil, silent=nil)
|
|
546
|
+
JSONBOp.new(_path_function(:jsonb_path_query_first_tz, path, vars, silent))
|
|
547
|
+
end
|
|
548
|
+
|
|
473
549
|
# Return the receiver, since it is already a JSONBOp.
|
|
474
550
|
def pg_jsonb
|
|
475
551
|
self
|
|
@@ -492,6 +568,12 @@ module Sequel
|
|
|
492
568
|
self.class.new(function(:set, wrap_input_array(path), wrap_input_jsonb(other), create_missing))
|
|
493
569
|
end
|
|
494
570
|
|
|
571
|
+
# The same as #set, except if +other+ is +nil+, then behaves according to +null_value_treatment+,
|
|
572
|
+
# which can be one of 'raise_exception', 'use_json_null' (default), 'delete_key', or 'return_target'.
|
|
573
|
+
def set_lax(path, other, create_missing=true, null_value_treatment='use_json_null')
|
|
574
|
+
self.class.new(function(:set_lax, wrap_input_array(path), wrap_input_jsonb(other), create_missing, null_value_treatment))
|
|
575
|
+
end
|
|
576
|
+
|
|
495
577
|
private
|
|
496
578
|
|
|
497
579
|
# Internals of the jsonb SQL/JSON path functions.
|
|
@@ -540,6 +622,37 @@ module Sequel
|
|
|
540
622
|
end
|
|
541
623
|
end
|
|
542
624
|
|
|
625
|
+
# Represents JSONB subscripts. This is abstracted because the
|
|
626
|
+
# subscript support depends on the database version.
|
|
627
|
+
class JSONBSubscriptOp < SQL::Expression
|
|
628
|
+
SUBSCRIPT = ["".freeze, "[".freeze, "]".freeze].freeze
|
|
629
|
+
|
|
630
|
+
# The expression being subscripted
|
|
631
|
+
attr_reader :expression
|
|
632
|
+
|
|
633
|
+
# The subscript to use
|
|
634
|
+
attr_reader :sub
|
|
635
|
+
|
|
636
|
+
# Set the expression and subscript to the given arguments
|
|
637
|
+
def initialize(expression, sub)
|
|
638
|
+
@expression = expression
|
|
639
|
+
@sub = sub
|
|
640
|
+
freeze
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# Use subscripts instead of -> operator on PostgreSQL 14+
|
|
644
|
+
def to_s_append(ds, sql)
|
|
645
|
+
server_version = ds.db.server_version
|
|
646
|
+
frag = server_version && server_version >= 140000 ? SUBSCRIPT : JSONOp::GET
|
|
647
|
+
ds.literal_append(sql, Sequel::SQL::PlaceholderLiteralString.new(frag, [@expression, @sub]))
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
# Support transforming of jsonb subscripts
|
|
651
|
+
def sequel_ast_transform(transformer)
|
|
652
|
+
self.class.new(transformer.call(@expression), transformer.call(@sub))
|
|
653
|
+
end
|
|
654
|
+
end
|
|
655
|
+
|
|
543
656
|
module JSONOpMethods
|
|
544
657
|
# Wrap the receiver in an JSONOp so you can easily use the PostgreSQL
|
|
545
658
|
# json functions and operators with it.
|
|
@@ -554,7 +667,9 @@ module Sequel
|
|
|
554
667
|
end
|
|
555
668
|
end
|
|
556
669
|
|
|
670
|
+
# :nocov:
|
|
557
671
|
if defined?(JSONArray)
|
|
672
|
+
# :nocov:
|
|
558
673
|
class JSONArray
|
|
559
674
|
# Wrap the JSONArray instance in an JSONOp, allowing you to easily use
|
|
560
675
|
# the PostgreSQL json functions and operators with literal jsons.
|
|
@@ -630,7 +745,7 @@ end
|
|
|
630
745
|
if defined?(Sequel::CoreRefinements)
|
|
631
746
|
module Sequel::CoreRefinements
|
|
632
747
|
refine Symbol do
|
|
633
|
-
|
|
748
|
+
send INCLUDE_METH, Sequel::Postgres::JSONOpMethods
|
|
634
749
|
end
|
|
635
750
|
end
|
|
636
751
|
end
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
#
|
|
13
13
|
# How accurate this count is depends on the number of rows
|
|
14
14
|
# added/deleted from the table since the last time it was
|
|
15
|
-
# analyzed.
|
|
15
|
+
# analyzed. If the table has not been vacuumed or analyzed
|
|
16
|
+
# yet, this can return 0 or -1 depending on the PostgreSQL
|
|
17
|
+
# version in use.
|
|
16
18
|
#
|
|
17
19
|
# To load the extension into the database:
|
|
18
20
|
#
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The pg_multirange extension adds support for the PostgreSQL 14+ multirange
|
|
4
|
+
# types to Sequel. PostgreSQL multirange types are similar to an array of
|
|
5
|
+
# ranges, where a match against the multirange is a match against any of the
|
|
6
|
+
# ranges in the multirange.
|
|
7
|
+
#
|
|
8
|
+
# When PostgreSQL multirange values are retrieved, they are parsed and returned
|
|
9
|
+
# as instances of Sequel::Postgres::PGMultiRange. PGMultiRange mostly acts
|
|
10
|
+
# like an array of Sequel::Postgres::PGRange (see the pg_range extension).
|
|
11
|
+
#
|
|
12
|
+
# In addition to the parser, this extension comes with literalizers
|
|
13
|
+
# for PGMultiRanges, so they can be used in queries and as bound variables.
|
|
14
|
+
#
|
|
15
|
+
# To turn an existing array of Ranges into a PGMultiRange, use Sequel.pg_multirange.
|
|
16
|
+
# You must provide the type of multirange when creating the multirange:
|
|
17
|
+
#
|
|
18
|
+
# Sequel.pg_multirange(array_of_date_ranges, :datemultirange)
|
|
19
|
+
#
|
|
20
|
+
# To use this extension, load it into the Database instance:
|
|
21
|
+
#
|
|
22
|
+
# DB.extension :pg_multirange
|
|
23
|
+
#
|
|
24
|
+
# See the {schema modification guide}[rdoc-ref:doc/schema_modification.rdoc]
|
|
25
|
+
# for details on using multirange type columns in CREATE/ALTER TABLE statements.
|
|
26
|
+
#
|
|
27
|
+
# This extension makes it easy to add support for other multirange types. In
|
|
28
|
+
# general, you just need to make sure that the subtype is handled and has the
|
|
29
|
+
# appropriate converter installed. For user defined
|
|
30
|
+
# types, you can do this via:
|
|
31
|
+
#
|
|
32
|
+
# DB.add_conversion_proc(subtype_oid){|string| }
|
|
33
|
+
#
|
|
34
|
+
# Then you can call
|
|
35
|
+
# Sequel::Postgres::PGMultiRange::DatabaseMethods#register_multirange_type
|
|
36
|
+
# to automatically set up a handler for the range type. So if you
|
|
37
|
+
# want to support the timemultirange type (assuming the time type is already
|
|
38
|
+
# supported):
|
|
39
|
+
#
|
|
40
|
+
# DB.register_multirange_type('timerange')
|
|
41
|
+
#
|
|
42
|
+
# This extension integrates with the pg_array extension. If you plan
|
|
43
|
+
# to use arrays of multirange types, load the pg_array extension before the
|
|
44
|
+
# pg_multirange extension:
|
|
45
|
+
#
|
|
46
|
+
# DB.extension :pg_array, :pg_multirange
|
|
47
|
+
#
|
|
48
|
+
# The pg_multirange extension will automatically load the pg_range extension.
|
|
49
|
+
#
|
|
50
|
+
# Related module: Sequel::Postgres::PGMultiRange
|
|
51
|
+
|
|
52
|
+
require 'delegate'
|
|
53
|
+
require 'strscan'
|
|
54
|
+
|
|
55
|
+
module Sequel
|
|
56
|
+
module Postgres
|
|
57
|
+
class PGMultiRange < DelegateClass(Array)
|
|
58
|
+
include Sequel::SQL::AliasMethods
|
|
59
|
+
|
|
60
|
+
# Converts strings into PGMultiRange instances.
|
|
61
|
+
class Parser < StringScanner
|
|
62
|
+
def initialize(source, converter)
|
|
63
|
+
super(source)
|
|
64
|
+
@converter = converter
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Parse the multirange type input string into a PGMultiRange value.
|
|
68
|
+
def parse
|
|
69
|
+
raise Sequel::Error, "invalid multirange, doesn't start with {" unless get_byte == '{'
|
|
70
|
+
ranges = []
|
|
71
|
+
|
|
72
|
+
unless scan(/\}/)
|
|
73
|
+
while true
|
|
74
|
+
raise Sequel::Error, "unfinished multirange" unless range_string = scan_until(/[\]\)]/)
|
|
75
|
+
ranges << @converter.call(range_string)
|
|
76
|
+
|
|
77
|
+
case sep = get_byte
|
|
78
|
+
when '}'
|
|
79
|
+
break
|
|
80
|
+
when ','
|
|
81
|
+
# nothing
|
|
82
|
+
else
|
|
83
|
+
raise Sequel::Error, "invalid multirange separator: #{sep.inspect}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
raise Sequel::Error, "invalid multirange, remaining data after }" unless eos?
|
|
89
|
+
ranges
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Callable object that takes the input string and parses it using Parser.
|
|
94
|
+
class Creator
|
|
95
|
+
# The database type to set on the PGMultiRange instances returned.
|
|
96
|
+
attr_reader :type
|
|
97
|
+
|
|
98
|
+
def initialize(type, converter=nil)
|
|
99
|
+
@type = type
|
|
100
|
+
@converter = converter
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Parse the string using Parser with the appropriate
|
|
104
|
+
# converter, and return a PGMultiRange with the appropriate database
|
|
105
|
+
# type.
|
|
106
|
+
def call(string)
|
|
107
|
+
PGMultiRange.new(Parser.new(string, @converter).parse, @type)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
module DatabaseMethods
|
|
112
|
+
# Add the default multirange conversion procs to the database
|
|
113
|
+
def self.extended(db)
|
|
114
|
+
db.instance_exec do
|
|
115
|
+
raise Error, "multiranges not supported on this database" unless server_version >= 140000
|
|
116
|
+
|
|
117
|
+
extension :pg_range
|
|
118
|
+
@pg_multirange_schema_types ||= {}
|
|
119
|
+
|
|
120
|
+
register_multirange_type('int4multirange', :range_oid=>3904, :oid=>4451)
|
|
121
|
+
register_multirange_type('nummultirange', :range_oid=>3906, :oid=>4532)
|
|
122
|
+
register_multirange_type('tsmultirange', :range_oid=>3908, :oid=>4533)
|
|
123
|
+
register_multirange_type('tstzmultirange', :range_oid=>3910, :oid=>4534)
|
|
124
|
+
register_multirange_type('datemultirange', :range_oid=>3912, :oid=>4535)
|
|
125
|
+
register_multirange_type('int8multirange', :range_oid=>3926, :oid=>4536)
|
|
126
|
+
|
|
127
|
+
if respond_to?(:register_array_type)
|
|
128
|
+
register_array_type('int4multirange', :oid=>6150, :scalar_oid=>4451, :scalar_typecast=>:int4multirange)
|
|
129
|
+
register_array_type('nummultirange', :oid=>6151, :scalar_oid=>4532, :scalar_typecast=>:nummultirange)
|
|
130
|
+
register_array_type('tsmultirange', :oid=>6152, :scalar_oid=>4533, :scalar_typecast=>:tsmultirange)
|
|
131
|
+
register_array_type('tstzmultirange', :oid=>6153, :scalar_oid=>4534, :scalar_typecast=>:tstzmultirange)
|
|
132
|
+
register_array_type('datemultirange', :oid=>6155, :scalar_oid=>4535, :scalar_typecast=>:datemultirange)
|
|
133
|
+
register_array_type('int8multirange', :oid=>6157, :scalar_oid=>4536, :scalar_typecast=>:int8multirange)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
[:int4multirange, :nummultirange, :tsmultirange, :tstzmultirange, :datemultirange, :int8multirange].each do |v|
|
|
137
|
+
@schema_type_classes[v] = PGMultiRange
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
procs = conversion_procs
|
|
141
|
+
add_conversion_proc(4533, PGMultiRange::Creator.new("tsmultirange", procs[3908]))
|
|
142
|
+
add_conversion_proc(4534, PGMultiRange::Creator.new("tstzmultirange", procs[3910]))
|
|
143
|
+
|
|
144
|
+
if respond_to?(:register_array_type) && defined?(PGArray::Creator)
|
|
145
|
+
add_conversion_proc(6152, PGArray::Creator.new("tsmultirange", procs[4533]))
|
|
146
|
+
add_conversion_proc(6153, PGArray::Creator.new("tstzmultirange", procs[4534]))
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Handle PGMultiRange values in bound variables
|
|
152
|
+
def bound_variable_arg(arg, conn)
|
|
153
|
+
case arg
|
|
154
|
+
when PGMultiRange
|
|
155
|
+
arg.unquoted_literal(schema_utility_dataset)
|
|
156
|
+
else
|
|
157
|
+
super
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Freeze the pg multirange schema types to prevent adding new ones.
|
|
162
|
+
def freeze
|
|
163
|
+
@pg_multirange_schema_types.freeze
|
|
164
|
+
super
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Register a database specific multirange type. This can be used to support
|
|
168
|
+
# different multirange types per Database. Options:
|
|
169
|
+
#
|
|
170
|
+
# :converter :: A callable object (e.g. Proc), that is called with the PostgreSQL range string,
|
|
171
|
+
# and should return a PGRange instance.
|
|
172
|
+
# :oid :: The PostgreSQL OID for the multirange type. This is used by Sequel to set up automatic type
|
|
173
|
+
# conversion on retrieval from the database.
|
|
174
|
+
# :range_oid :: Should be the PostgreSQL OID for the multirange subtype (the range type). If given,
|
|
175
|
+
# automatically sets the :converter option by looking for scalar conversion
|
|
176
|
+
# proc.
|
|
177
|
+
#
|
|
178
|
+
# If a block is given, it is treated as the :converter option.
|
|
179
|
+
def register_multirange_type(db_type, opts=OPTS, &block)
|
|
180
|
+
oid = opts[:oid]
|
|
181
|
+
soid = opts[:range_oid]
|
|
182
|
+
|
|
183
|
+
if has_converter = opts.has_key?(:converter)
|
|
184
|
+
raise Error, "can't provide both a block and :converter option to register_multirange_type" if block
|
|
185
|
+
converter = opts[:converter]
|
|
186
|
+
else
|
|
187
|
+
has_converter = true if block
|
|
188
|
+
converter = block
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
unless (soid || has_converter) && oid
|
|
192
|
+
range_oid, subtype_oid = from(:pg_range).join(:pg_type, :oid=>:rngmultitypid).where(:typname=>db_type.to_s).get([:rngmultitypid, :rngtypid])
|
|
193
|
+
soid ||= subtype_oid unless has_converter
|
|
194
|
+
oid ||= range_oid
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
db_type = db_type.to_s.dup.freeze
|
|
198
|
+
|
|
199
|
+
if soid
|
|
200
|
+
raise Error, "can't provide both a converter and :range_oid option to register" if has_converter
|
|
201
|
+
raise Error, "no conversion proc for :range_oid=>#{soid.inspect} in conversion_procs" unless converter = conversion_procs[soid]
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
raise Error, "cannot add a multirange type without a convertor (use :converter or :range_oid option or pass block)" unless converter
|
|
205
|
+
creator = Creator.new(db_type, converter)
|
|
206
|
+
add_conversion_proc(oid, creator)
|
|
207
|
+
|
|
208
|
+
@pg_multirange_schema_types[db_type] = db_type.to_sym
|
|
209
|
+
|
|
210
|
+
singleton_class.class_eval do
|
|
211
|
+
meth = :"typecast_value_#{db_type}"
|
|
212
|
+
scalar_typecast_method = :"typecast_value_#{opts.fetch(:scalar_typecast, db_type.sub('multirange', 'range'))}"
|
|
213
|
+
define_method(meth){|v| typecast_value_pg_multirange(v, creator, scalar_typecast_method)}
|
|
214
|
+
private meth
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
@schema_type_classes[db_type] = PGMultiRange
|
|
218
|
+
nil
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
private
|
|
222
|
+
|
|
223
|
+
# Handle arrays of multirange types in bound variables.
|
|
224
|
+
def bound_variable_array(a)
|
|
225
|
+
case a
|
|
226
|
+
when PGMultiRange
|
|
227
|
+
"\"#{bound_variable_arg(a, nil)}\""
|
|
228
|
+
else
|
|
229
|
+
super
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Recognize the registered database multirange types.
|
|
234
|
+
def schema_column_type(db_type)
|
|
235
|
+
@pg_multirange_schema_types[db_type] || super
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Set the :ruby_default value if the default value is recognized as a multirange.
|
|
239
|
+
def schema_post_process(_)
|
|
240
|
+
super.each do |a|
|
|
241
|
+
h = a[1]
|
|
242
|
+
db_type = h[:db_type]
|
|
243
|
+
if @pg_multirange_schema_types[db_type] && h[:default] =~ /\A#{db_type}\(.*\)\z/
|
|
244
|
+
h[:ruby_default] = get(Sequel.lit(h[:default]))
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Given a value to typecast and the type of PGMultiRange subclass:
|
|
250
|
+
# * If given a PGMultiRange with a matching type, use it directly.
|
|
251
|
+
# * If given a PGMultiRange with a different type, return a PGMultiRange
|
|
252
|
+
# with the creator's type.
|
|
253
|
+
# * If given an Array, create a new PGMultiRange instance for it, typecasting
|
|
254
|
+
# each instance using the scalar_typecast_method.
|
|
255
|
+
def typecast_value_pg_multirange(value, creator, scalar_typecast_method=nil)
|
|
256
|
+
case value
|
|
257
|
+
when PGMultiRange
|
|
258
|
+
return value if value.db_type == creator.type
|
|
259
|
+
when Array
|
|
260
|
+
# nothing
|
|
261
|
+
else
|
|
262
|
+
raise Sequel::InvalidValue, "invalid value for multirange type: #{value.inspect}"
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
if scalar_typecast_method && respond_to?(scalar_typecast_method, true)
|
|
266
|
+
value = value.map{|v| send(scalar_typecast_method, v)}
|
|
267
|
+
end
|
|
268
|
+
PGMultiRange.new(value, creator.type)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# The type of this multirange (e.g. 'int4multirange').
|
|
273
|
+
attr_accessor :db_type
|
|
274
|
+
|
|
275
|
+
# Set the array of ranges to delegate to, and the database type.
|
|
276
|
+
def initialize(ranges, db_type)
|
|
277
|
+
super(ranges)
|
|
278
|
+
@db_type = db_type.to_s
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Append the multirange SQL to the given sql string.
|
|
282
|
+
def sql_literal_append(ds, sql)
|
|
283
|
+
sql << db_type << '('
|
|
284
|
+
joiner = nil
|
|
285
|
+
conversion_meth = nil
|
|
286
|
+
each do |range|
|
|
287
|
+
if joiner
|
|
288
|
+
sql << joiner
|
|
289
|
+
else
|
|
290
|
+
joiner = ', '
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
unless range.is_a?(PGRange)
|
|
294
|
+
conversion_meth ||= :"typecast_value_#{db_type.sub('multi', '')}"
|
|
295
|
+
range = ds.db.send(conversion_meth, range)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
ds.literal_append(sql, range)
|
|
299
|
+
end
|
|
300
|
+
sql << ')'
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Return whether the value is inside any of the ranges in the multirange.
|
|
304
|
+
def cover?(value)
|
|
305
|
+
any?{|range| range.cover?(value)}
|
|
306
|
+
end
|
|
307
|
+
alias === cover?
|
|
308
|
+
|
|
309
|
+
# Don't consider multiranges with different database types equal.
|
|
310
|
+
def eql?(other)
|
|
311
|
+
if PGMultiRange === other
|
|
312
|
+
return false unless other.db_type == db_type
|
|
313
|
+
other = other.__getobj__
|
|
314
|
+
end
|
|
315
|
+
__getobj__.eql?(other)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Don't consider multiranges with different database types equal.
|
|
319
|
+
def ==(other)
|
|
320
|
+
return false if PGMultiRange === other && other.db_type != db_type
|
|
321
|
+
super
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Return a string containing the unescaped version of the multirange.
|
|
325
|
+
# Separated out for use by the bound argument code.
|
|
326
|
+
def unquoted_literal(ds)
|
|
327
|
+
val = String.new
|
|
328
|
+
val << "{"
|
|
329
|
+
|
|
330
|
+
joiner = nil
|
|
331
|
+
conversion_meth = nil
|
|
332
|
+
each do |range|
|
|
333
|
+
if joiner
|
|
334
|
+
val << joiner
|
|
335
|
+
else
|
|
336
|
+
joiner = ', '
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
unless range.is_a?(PGRange)
|
|
340
|
+
conversion_meth ||= :"typecast_value_#{db_type.sub('multi', '')}"
|
|
341
|
+
range = ds.db.send(conversion_meth, range)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
val << range.unquoted_literal(ds)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
val << "}"
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
module SQL::Builders
|
|
353
|
+
# Convert the object to a Postgres::PGMultiRange.
|
|
354
|
+
def pg_multirange(v, db_type)
|
|
355
|
+
case v
|
|
356
|
+
when Postgres::PGMultiRange
|
|
357
|
+
if v.db_type == db_type
|
|
358
|
+
v
|
|
359
|
+
else
|
|
360
|
+
Postgres::PGMultiRange.new(v, db_type)
|
|
361
|
+
end
|
|
362
|
+
when Array
|
|
363
|
+
Postgres::PGMultiRange.new(v, db_type)
|
|
364
|
+
else
|
|
365
|
+
# May not be defined unless the pg_range_ops extension is used
|
|
366
|
+
pg_range_op(v)
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
Database.register_extension(:pg_multirange, Postgres::PGMultiRange::DatabaseMethods)
|
|
372
|
+
end
|
|
@@ -4,12 +4,9 @@
|
|
|
4
4
|
# types to Sequel. PostgreSQL range types are similar to ruby's
|
|
5
5
|
# Range class, representating an array of values. However, they
|
|
6
6
|
# are more flexible than ruby's ranges, allowing exclusive beginnings
|
|
7
|
-
# and endings (ruby's range only allows exclusive endings)
|
|
8
|
-
# unbounded beginnings and endings (which ruby's range does not
|
|
9
|
-
# support).
|
|
7
|
+
# and endings (ruby's range only allows exclusive endings).
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
# that when range type values are retrieved, they are parsed and returned
|
|
9
|
+
# When PostgreSQL range values are retreived, they are parsed and returned
|
|
13
10
|
# as instances of Sequel::Postgres::PGRange. PGRange mostly acts
|
|
14
11
|
# like a Range, but it's not a Range as not all PostgreSQL range
|
|
15
12
|
# type values would be valid ruby ranges. If the range type value
|
|
@@ -19,8 +16,7 @@
|
|
|
19
16
|
# exception will be raised.
|
|
20
17
|
#
|
|
21
18
|
# In addition to the parser, this extension comes with literalizers
|
|
22
|
-
# for
|
|
23
|
-
# callbacks, so they work on all adapters.
|
|
19
|
+
# for PGRange and Range, so they can be used in queries and as bound variables.
|
|
24
20
|
#
|
|
25
21
|
# To turn an existing Range into a PGRange, use Sequel.pg_range:
|
|
26
22
|
#
|
|
@@ -158,7 +154,7 @@ module Sequel
|
|
|
158
154
|
procs = conversion_procs
|
|
159
155
|
add_conversion_proc(3908, Parser.new("tsrange", procs[1114]))
|
|
160
156
|
add_conversion_proc(3910, Parser.new("tstzrange", procs[1184]))
|
|
161
|
-
if defined?(PGArray::Creator)
|
|
157
|
+
if respond_to?(:register_array_type) && defined?(PGArray::Creator)
|
|
162
158
|
add_conversion_proc(3909, PGArray::Creator.new("tsrange", procs[3908]))
|
|
163
159
|
add_conversion_proc(3911, PGArray::Creator.new("tstzrange", procs[3910]))
|
|
164
160
|
end
|
|
@@ -215,12 +211,6 @@ module Sequel
|
|
|
215
211
|
|
|
216
212
|
db_type = db_type.to_s.dup.freeze
|
|
217
213
|
|
|
218
|
-
if converter = opts[:converter]
|
|
219
|
-
raise Error, "can't provide both a block and :converter option to register" if block
|
|
220
|
-
else
|
|
221
|
-
converter = block
|
|
222
|
-
end
|
|
223
|
-
|
|
224
214
|
if soid
|
|
225
215
|
raise Error, "can't provide both a converter and :subtype_oid option to register" if has_converter
|
|
226
216
|
raise Error, "no conversion proc for :subtype_oid=>#{soid.inspect} in conversion_procs" unless converter = conversion_procs[soid]
|
|
@@ -255,11 +245,7 @@ module Sequel
|
|
|
255
245
|
|
|
256
246
|
# Recognize the registered database range types.
|
|
257
247
|
def schema_column_type(db_type)
|
|
258
|
-
|
|
259
|
-
type
|
|
260
|
-
else
|
|
261
|
-
super
|
|
262
|
-
end
|
|
248
|
+
@pg_range_schema_types[db_type] || super
|
|
263
249
|
end
|
|
264
250
|
|
|
265
251
|
# Set the :ruby_default value if the default value is recognized as a range.
|
|
@@ -471,8 +457,10 @@ module Sequel
|
|
|
471
457
|
return @range if @range
|
|
472
458
|
raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
|
|
473
459
|
raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
|
|
460
|
+
# :nocov:
|
|
474
461
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") if STARTLESS_RANGE_NOT_SUPPORTED && !self.begin
|
|
475
462
|
raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") if ENDLESS_RANGE_NOT_SUPPORTED && !self.end
|
|
463
|
+
# :nocov:
|
|
476
464
|
@range = Range.new(self.begin, self.end, exclude_end?)
|
|
477
465
|
end
|
|
478
466
|
|