sequel 5.39.0 → 5.72.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 +408 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +13 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +26 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- 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/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +28 -16
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- 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/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +90 -9
- data/lib/sequel/adapters/shared/mysql.rb +47 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -1
- data/lib/sequel/adapters/shared/postgres.rb +496 -178
- data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
- data/lib/sequel/adapters/shared/sqlite.rb +116 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +55 -31
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +46 -53
- data/lib/sequel/database/schema_methods.rb +18 -2
- data/lib/sequel/dataset/actions.rb +108 -14
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +171 -44
- data/lib/sequel/dataset/sql.rb +182 -47
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +439 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- 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 +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +11 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +32 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -3
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +11 -24
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +21 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- 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.rb +2 -0
- data/lib/sequel/model/associations.rb +345 -101
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -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 +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +10 -6
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- 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 +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +46 -12
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +132 -38
@@ -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,367 @@
|
|
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
|
+
# Recognize the registered database multirange types.
|
224
|
+
def schema_multirange_type(db_type)
|
225
|
+
@pg_multirange_schema_types[db_type] || super
|
226
|
+
end
|
227
|
+
|
228
|
+
# Set the :ruby_default value if the default value is recognized as a multirange.
|
229
|
+
def schema_post_process(_)
|
230
|
+
super.each do |a|
|
231
|
+
h = a[1]
|
232
|
+
db_type = h[:db_type]
|
233
|
+
if @pg_multirange_schema_types[db_type] && h[:default] =~ /\A#{db_type}\(.*\)\z/
|
234
|
+
h[:ruby_default] = get(Sequel.lit(h[:default]))
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Given a value to typecast and the type of PGMultiRange subclass:
|
240
|
+
# * If given a PGMultiRange with a matching type, use it directly.
|
241
|
+
# * If given a PGMultiRange with a different type, return a PGMultiRange
|
242
|
+
# with the creator's type.
|
243
|
+
# * If given an Array, create a new PGMultiRange instance for it, typecasting
|
244
|
+
# each instance using the scalar_typecast_method.
|
245
|
+
def typecast_value_pg_multirange(value, creator, scalar_typecast_method=nil)
|
246
|
+
case value
|
247
|
+
when PGMultiRange
|
248
|
+
return value if value.db_type == creator.type
|
249
|
+
when Array
|
250
|
+
# nothing
|
251
|
+
else
|
252
|
+
raise Sequel::InvalidValue, "invalid value for multirange type: #{value.inspect}"
|
253
|
+
end
|
254
|
+
|
255
|
+
if scalar_typecast_method && respond_to?(scalar_typecast_method, true)
|
256
|
+
value = value.map{|v| send(scalar_typecast_method, v)}
|
257
|
+
end
|
258
|
+
PGMultiRange.new(value, creator.type)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# The type of this multirange (e.g. 'int4multirange').
|
263
|
+
attr_accessor :db_type
|
264
|
+
|
265
|
+
# Set the array of ranges to delegate to, and the database type.
|
266
|
+
def initialize(ranges, db_type)
|
267
|
+
super(ranges)
|
268
|
+
@db_type = db_type.to_s
|
269
|
+
end
|
270
|
+
|
271
|
+
# Append the multirange SQL to the given sql string.
|
272
|
+
def sql_literal_append(ds, sql)
|
273
|
+
sql << db_type << '('
|
274
|
+
joiner = nil
|
275
|
+
conversion_meth = nil
|
276
|
+
each do |range|
|
277
|
+
if joiner
|
278
|
+
sql << joiner
|
279
|
+
else
|
280
|
+
joiner = ', '
|
281
|
+
end
|
282
|
+
|
283
|
+
unless range.is_a?(PGRange)
|
284
|
+
conversion_meth ||= :"typecast_value_#{db_type.sub('multi', '')}"
|
285
|
+
range = ds.db.send(conversion_meth, range)
|
286
|
+
end
|
287
|
+
|
288
|
+
ds.literal_append(sql, range)
|
289
|
+
end
|
290
|
+
sql << ')'
|
291
|
+
end
|
292
|
+
|
293
|
+
# Return whether the value is inside any of the ranges in the multirange.
|
294
|
+
def cover?(value)
|
295
|
+
any?{|range| range.cover?(value)}
|
296
|
+
end
|
297
|
+
alias === cover?
|
298
|
+
|
299
|
+
# Don't consider multiranges with different database types equal.
|
300
|
+
def eql?(other)
|
301
|
+
if PGMultiRange === other
|
302
|
+
return false unless other.db_type == db_type
|
303
|
+
other = other.__getobj__
|
304
|
+
end
|
305
|
+
__getobj__.eql?(other)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Don't consider multiranges with different database types equal.
|
309
|
+
def ==(other)
|
310
|
+
return false if PGMultiRange === other && other.db_type != db_type
|
311
|
+
super
|
312
|
+
end
|
313
|
+
|
314
|
+
# Return a string containing the unescaped version of the multirange.
|
315
|
+
# Separated out for use by the bound argument code.
|
316
|
+
def unquoted_literal(ds)
|
317
|
+
val = String.new
|
318
|
+
val << "{"
|
319
|
+
|
320
|
+
joiner = nil
|
321
|
+
conversion_meth = nil
|
322
|
+
each do |range|
|
323
|
+
if joiner
|
324
|
+
val << joiner
|
325
|
+
else
|
326
|
+
joiner = ', '
|
327
|
+
end
|
328
|
+
|
329
|
+
unless range.is_a?(PGRange)
|
330
|
+
conversion_meth ||= :"typecast_value_#{db_type.sub('multi', '')}"
|
331
|
+
range = ds.db.send(conversion_meth, range)
|
332
|
+
end
|
333
|
+
|
334
|
+
val << range.unquoted_literal(ds)
|
335
|
+
end
|
336
|
+
|
337
|
+
val << "}"
|
338
|
+
end
|
339
|
+
|
340
|
+
# Allow automatic parameterization.
|
341
|
+
def sequel_auto_param_type(ds)
|
342
|
+
"::#{db_type}"
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
module SQL::Builders
|
348
|
+
# Convert the object to a Postgres::PGMultiRange.
|
349
|
+
def pg_multirange(v, db_type)
|
350
|
+
case v
|
351
|
+
when Postgres::PGMultiRange
|
352
|
+
if v.db_type == db_type
|
353
|
+
v
|
354
|
+
else
|
355
|
+
Postgres::PGMultiRange.new(v, db_type)
|
356
|
+
end
|
357
|
+
when Array
|
358
|
+
Postgres::PGMultiRange.new(v, db_type)
|
359
|
+
else
|
360
|
+
# May not be defined unless the pg_range_ops extension is used
|
361
|
+
pg_range_op(v)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
Database.register_extension(:pg_multirange, Postgres::PGMultiRange::DatabaseMethods)
|
367
|
+
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
|
#
|
@@ -237,23 +233,9 @@ module Sequel
|
|
237
233
|
|
238
234
|
private
|
239
235
|
|
240
|
-
# Handle arrays of range types in bound variables.
|
241
|
-
def bound_variable_array(a)
|
242
|
-
case a
|
243
|
-
when PGRange, Range
|
244
|
-
"\"#{bound_variable_arg(a, nil)}\""
|
245
|
-
else
|
246
|
-
super
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
236
|
# Recognize the registered database range types.
|
251
|
-
def
|
252
|
-
|
253
|
-
type
|
254
|
-
else
|
255
|
-
super
|
256
|
-
end
|
237
|
+
def schema_range_type(db_type)
|
238
|
+
@pg_range_schema_types[db_type] || super
|
257
239
|
end
|
258
240
|
|
259
241
|
# Set the :ruby_default value if the default value is recognized as a range.
|
@@ -290,7 +272,7 @@ module Sequel
|
|
290
272
|
when Range
|
291
273
|
PGRange.from_range(value, parser.db_type)
|
292
274
|
when String
|
293
|
-
parser.call(value)
|
275
|
+
parser.call(typecast_check_string_length(value, 100))
|
294
276
|
else
|
295
277
|
raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}"
|
296
278
|
end
|
@@ -499,6 +481,11 @@ module Sequel
|
|
499
481
|
end
|
500
482
|
end
|
501
483
|
|
484
|
+
# Allow automatic parameterization for ranges with types.
|
485
|
+
def sequel_auto_param_type(ds)
|
486
|
+
"::#{db_type}" if db_type
|
487
|
+
end
|
488
|
+
|
502
489
|
private
|
503
490
|
|
504
491
|
# Escape common range types. Instead of quoting, just backslash escape all
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
#
|
3
3
|
# The pg_range_ops extension adds support to Sequel's DSL to make
|
4
|
-
# it easier to call PostgreSQL range functions and operators.
|
4
|
+
# it easier to call PostgreSQL range and multirange functions and operators.
|
5
5
|
#
|
6
6
|
# To load the extension:
|
7
7
|
#
|
@@ -11,10 +11,11 @@
|
|
11
11
|
#
|
12
12
|
# r = Sequel.pg_range_op(:range)
|
13
13
|
#
|
14
|
-
# If you have also loaded the pg_range
|
15
|
-
# Sequel.pg_range as well:
|
14
|
+
# If you have also loaded the pg_range or pg_multirange extensions, you can use
|
15
|
+
# Sequel.pg_range or Sequel.pg_multirange as well:
|
16
16
|
#
|
17
17
|
# r = Sequel.pg_range(:range)
|
18
|
+
# r = Sequel.pg_multirange(:range)
|
18
19
|
#
|
19
20
|
# Also, on most Sequel expression objects, you can call the pg_range
|
20
21
|
# method:
|
@@ -46,13 +47,25 @@
|
|
46
47
|
# r.upper_inc # upper_inc(range)
|
47
48
|
# r.lower_inf # lower_inf(range)
|
48
49
|
# r.upper_inf # upper_inf(range)
|
50
|
+
#
|
51
|
+
# All of the above methods work for both ranges and multiranges, as long
|
52
|
+
# as PostgreSQL supports the operation. The following methods are also
|
53
|
+
# supported:
|
54
|
+
#
|
55
|
+
# r.range_merge # range_merge(range)
|
56
|
+
# r.unnest # unnest(range)
|
57
|
+
# r.multirange # multirange(range)
|
58
|
+
#
|
59
|
+
# +range_merge+ and +unnest+ expect the receiver to represent a multirange
|
60
|
+
# value, while +multi_range+ expects the receiver to represent a range value.
|
49
61
|
#
|
50
|
-
# See the PostgreSQL range function and operator documentation for more
|
62
|
+
# See the PostgreSQL range and multirange function and operator documentation for more
|
51
63
|
# details on what these functions and operators do.
|
52
64
|
#
|
53
|
-
# If you are also using the pg_range extension, you should
|
54
|
-
# loading this extension. Doing so will allow you to use
|
55
|
-
#
|
65
|
+
# If you are also using the pg_range or pg_multirange extension, you should
|
66
|
+
# load them before loading this extension. Doing so will allow you to use
|
67
|
+
# PGRange#op and PGMultiRange#op to get a RangeOp, allowing you to perform
|
68
|
+
# range operations on range literals.
|
56
69
|
#
|
57
70
|
# Related module: Sequel::Postgres::RangeOp
|
58
71
|
|
@@ -77,9 +90,12 @@ module Sequel
|
|
77
90
|
:overlaps => ["(".freeze, " && ".freeze, ")".freeze].freeze,
|
78
91
|
}.freeze
|
79
92
|
|
80
|
-
%w'lower upper isempty lower_inc upper_inc lower_inf upper_inf'.each do |f|
|
93
|
+
%w'lower upper isempty lower_inc upper_inc lower_inf upper_inf unnest'.each do |f|
|
81
94
|
class_eval("def #{f}; function(:#{f}) end", __FILE__, __LINE__)
|
82
95
|
end
|
96
|
+
%w'range_merge multirange'.each do |f|
|
97
|
+
class_eval("def #{f}; RangeOp.new(function(:#{f})) end", __FILE__, __LINE__)
|
98
|
+
end
|
83
99
|
OPERATORS.each_key do |f|
|
84
100
|
class_eval("def #{f}(v); operator(:#{f}, v) end", __FILE__, __LINE__)
|
85
101
|
end
|
@@ -127,6 +143,18 @@ module Sequel
|
|
127
143
|
end
|
128
144
|
end
|
129
145
|
end
|
146
|
+
|
147
|
+
# :nocov:
|
148
|
+
if defined?(PGMultiRange)
|
149
|
+
# :nocov:
|
150
|
+
class PGMultiRange
|
151
|
+
# Wrap the PGRange instance in an RangeOp, allowing you to easily use
|
152
|
+
# the PostgreSQL range functions and operators with literal ranges.
|
153
|
+
def op
|
154
|
+
RangeOp.new(self)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
130
158
|
end
|
131
159
|
|
132
160
|
module SQL::Builders
|
@@ -160,7 +188,7 @@ end
|
|
160
188
|
if defined?(Sequel::CoreRefinements)
|
161
189
|
module Sequel::CoreRefinements
|
162
190
|
refine Symbol do
|
163
|
-
|
191
|
+
send INCLUDE_METH, Sequel::Postgres::RangeOpMethods
|
164
192
|
end
|
165
193
|
end
|
166
194
|
end
|
@@ -136,6 +136,15 @@ module Sequel
|
|
136
136
|
ds.quote_schema_table_append(sql, db_type)
|
137
137
|
end
|
138
138
|
end
|
139
|
+
|
140
|
+
# Allow automatic parameterization if all values support it.
|
141
|
+
def sequel_auto_param_type(ds)
|
142
|
+
if db_type && all?{|v| nil == v || ds.send(:auto_param_type, v)}
|
143
|
+
s = String.new << "::"
|
144
|
+
ds.quote_schema_table_append(s, db_type)
|
145
|
+
s
|
146
|
+
end
|
147
|
+
end
|
139
148
|
end
|
140
149
|
|
141
150
|
# Class for row-valued/composite types that are treated as hashes.
|
@@ -208,6 +217,15 @@ module Sequel
|
|
208
217
|
ds.quote_schema_table_append(sql, db_type)
|
209
218
|
end
|
210
219
|
end
|
220
|
+
|
221
|
+
# Allow automatic parameterization if all values support it.
|
222
|
+
def sequel_auto_param_type(ds)
|
223
|
+
if db_type && all?{|_,v| nil == v || ds.send(:auto_param_type, v)}
|
224
|
+
s = String.new << "::"
|
225
|
+
ds.quote_schema_table_append(s, db_type)
|
226
|
+
s
|
227
|
+
end
|
228
|
+
end
|
211
229
|
end
|
212
230
|
|
213
231
|
ROW_TYPE_CLASSES = [HashRow, ArrayRow].freeze
|
@@ -482,6 +500,7 @@ module Sequel
|
|
482
500
|
row_type(db_type, v)
|
483
501
|
end
|
484
502
|
private meth
|
503
|
+
alias_method(meth, meth)
|
485
504
|
end
|
486
505
|
|
487
506
|
nil
|
@@ -518,26 +537,9 @@ module Sequel
|
|
518
537
|
|
519
538
|
private
|
520
539
|
|
521
|
-
# Format composite types used in bound variable arrays.
|
522
|
-
def bound_variable_array(arg)
|
523
|
-
case arg
|
524
|
-
when ArrayRow
|
525
|
-
"\"(#{arg.map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
|
526
|
-
when HashRow
|
527
|
-
arg.check_columns!
|
528
|
-
"\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
|
529
|
-
else
|
530
|
-
super
|
531
|
-
end
|
532
|
-
end
|
533
|
-
|
534
540
|
# Make the column type detection handle registered row types.
|
535
|
-
def
|
536
|
-
|
537
|
-
type
|
538
|
-
else
|
539
|
-
super
|
540
|
-
end
|
541
|
+
def schema_composite_type(db_type)
|
542
|
+
@row_schema_types[db_type] || super
|
541
543
|
end
|
542
544
|
end
|
543
545
|
end
|
data/lib/sequel/extensions/s.rb
CHANGED