sequel 5.58.0 → 5.78.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 +288 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +24 -23
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +53 -17
- data/doc/cheat_sheet.rdoc +3 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +15 -0
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +20 -12
- data/doc/postgresql.rdoc +8 -8
- data/doc/querying.rdoc +1 -1
- 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/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/release_notes/5.78.0.txt +67 -0
- data/doc/schema_modification.rdoc +3 -3
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +14 -14
- data/doc/testing.rdoc +16 -12
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -1
- data/lib/sequel/adapters/jdbc/h2.rb +3 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +10 -6
- data/lib/sequel/adapters/mysql.rb +19 -7
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +1 -0
- data/lib/sequel/adapters/postgres.rb +62 -16
- data/lib/sequel/adapters/shared/access.rb +9 -1
- data/lib/sequel/adapters/shared/db2.rb +12 -0
- data/lib/sequel/adapters/shared/mssql.rb +71 -9
- data/lib/sequel/adapters/shared/mysql.rb +80 -1
- data/lib/sequel/adapters/shared/oracle.rb +17 -7
- data/lib/sequel/adapters/shared/postgres.rb +494 -164
- data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
- data/lib/sequel/adapters/shared/sqlite.rb +40 -4
- data/lib/sequel/adapters/sqlite.rb +42 -3
- data/lib/sequel/adapters/trilogy.rb +117 -0
- 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/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/database/connecting.rb +25 -1
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +65 -14
- data/lib/sequel/database/query.rb +72 -1
- data/lib/sequel/database/schema_generator.rb +2 -1
- data/lib/sequel/database/schema_methods.rb +13 -3
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +60 -13
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +15 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +62 -37
- data/lib/sequel/dataset/sql.rb +58 -36
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/exceptions.rb +5 -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 +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +21 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- 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/date_arithmetic.rb +36 -8
- data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/is_distinct_from.rb +3 -1
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +65 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pg_array.rb +33 -4
- 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 +1 -2
- 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 +5 -0
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_interval.rb +10 -11
- data/lib/sequel/extensions/pg_json.rb +10 -10
- data/lib/sequel/extensions/pg_json_ops.rb +52 -0
- data/lib/sequel/extensions/pg_multirange.rb +6 -11
- data/lib/sequel/extensions/pg_range.rb +9 -14
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/server_block.rb +2 -1
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +50 -11
- data/lib/sequel/model/base.rb +45 -21
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/plugins/auto_validations.rb +53 -15
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +27 -6
- data/lib/sequel/plugins/composition.rb +2 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +1 -1
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- 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 +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +5 -5
- data/lib/sequel/plugins/static_cache.rb +38 -0
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +29 -2
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/version.rb +1 -1
- metadata +76 -6
@@ -22,13 +22,14 @@
|
|
22
22
|
module Sequel
|
23
23
|
module Postgres
|
24
24
|
module ExtendedDateSupport
|
25
|
-
DATE_YEAR_1 = Date.new(1)
|
26
25
|
DATETIME_YEAR_1 = DateTime.new(1)
|
27
26
|
TIME_YEAR_1 = Time.at(-62135596800).utc
|
28
27
|
INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
|
29
28
|
INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
|
30
29
|
PLUS_DATE_INFINITY = Date::Infinity.new
|
31
30
|
MINUS_DATE_INFINITY = -PLUS_DATE_INFINITY
|
31
|
+
RATIONAL_60 = Rational(60)
|
32
|
+
TIME_CAN_PARSE_BC = RUBY_VERSION >= '2.5'
|
32
33
|
|
33
34
|
# Add dataset methods and update the conversion proces for dates and timestamps.
|
34
35
|
def self.extended(db)
|
@@ -36,6 +37,27 @@ module Sequel
|
|
36
37
|
procs = db.conversion_procs
|
37
38
|
procs[1082] = ::Sequel.method(:string_to_date)
|
38
39
|
procs[1184] = procs[1114] = db.method(:to_application_timestamp)
|
40
|
+
if ocps = db.instance_variable_get(:@oid_convertor_map)
|
41
|
+
# Clear the oid convertor map entries for timestamps if they
|
42
|
+
# exist, so it will regenerate new ones that use this extension.
|
43
|
+
# This is only taken when using the jdbc adapter.
|
44
|
+
Sequel.synchronize do
|
45
|
+
ocps.delete(1184)
|
46
|
+
ocps.delete(1114)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Handle BC dates and times in bound variables. This is necessary for Date values
|
52
|
+
# when using both the postgres and jdbc adapters, but also necessary for Time values
|
53
|
+
# on jdbc.
|
54
|
+
def bound_variable_arg(arg, conn)
|
55
|
+
case arg
|
56
|
+
when Time, Date
|
57
|
+
@default_dataset.literal_date_or_time(arg)
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
39
61
|
end
|
40
62
|
|
41
63
|
# Whether infinite timestamps/dates should be converted on retrieval. By default, no
|
@@ -86,27 +108,18 @@ module Sequel
|
|
86
108
|
if value.is_a?(String) && (m = /((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/.match(value)) && (m[2] || m[3])
|
87
109
|
if m[3]
|
88
110
|
value = value.sub(' BC', '').sub(' ', ' BC ')
|
89
|
-
conv = defined?(JRUBY_VERSION) && JRUBY_VERSION == '9.2.0.0'
|
90
111
|
end
|
91
|
-
if m[2]
|
92
|
-
dt = DateTime
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# :nocov:
|
101
|
-
end
|
102
|
-
unless Sequel.datetime_class == DateTime
|
103
|
-
dt = dt.to_time
|
104
|
-
if conv && (timezone == nil || timezone == :local) && !m[1]
|
105
|
-
# :nocov:
|
106
|
-
dt = Sequel.send(:convert_input_timestamp, dt.strftime("%F %T.%6N"), :local)
|
107
|
-
# :nocov:
|
108
|
-
end
|
112
|
+
if m[2]
|
113
|
+
dt = if Sequel.datetime_class == DateTime
|
114
|
+
DateTime.parse(value)
|
115
|
+
elsif TIME_CAN_PARSE_BC
|
116
|
+
Time.parse(value)
|
117
|
+
# :nocov:
|
118
|
+
else
|
119
|
+
DateTime.parse(value).to_time
|
120
|
+
# :nocov:
|
109
121
|
end
|
122
|
+
|
110
123
|
Sequel.convert_output_timestamp(dt, Sequel.application_timezone)
|
111
124
|
else
|
112
125
|
super(value)
|
@@ -176,7 +189,7 @@ module Sequel
|
|
176
189
|
|
177
190
|
# Handle BC Date objects.
|
178
191
|
def literal_date(date)
|
179
|
-
if date <
|
192
|
+
if date.year < 1
|
180
193
|
date <<= ((date.year) * 24 - 12)
|
181
194
|
date.strftime("'%Y-%m-%d BC'")
|
182
195
|
else
|
@@ -190,7 +203,7 @@ module Sequel
|
|
190
203
|
date <<= ((date.year) * 24 - 12)
|
191
204
|
date = db.from_application_timestamp(date)
|
192
205
|
minutes = (date.offset * 1440).to_i
|
193
|
-
date.strftime("'%Y-%m-%d %H:%M:%S.%
|
206
|
+
date.strftime("'%Y-%m-%d %H:%M:%S.%6N#{sprintf("%+03i%02i", *minutes.divmod(60))} BC'")
|
194
207
|
else
|
195
208
|
super
|
196
209
|
end
|
@@ -223,10 +236,7 @@ module Sequel
|
|
223
236
|
# Work around JRuby bug #4822 in Time#to_datetime for times before date of calendar reform
|
224
237
|
def literal_time(time)
|
225
238
|
if time < TIME_YEAR_1
|
226
|
-
|
227
|
-
# Work around JRuby bug #5191
|
228
|
-
dt >>= 12 if JRUBY_VERSION == '9.2.0.0'
|
229
|
-
literal_datetime(dt)
|
239
|
+
literal_datetime(DateTime.parse(super))
|
230
240
|
else
|
231
241
|
super
|
232
242
|
end
|
@@ -236,7 +246,8 @@ module Sequel
|
|
236
246
|
# Handle BC Time objects.
|
237
247
|
def literal_time(time)
|
238
248
|
if time < TIME_YEAR_1
|
239
|
-
|
249
|
+
time = db.from_application_timestamp(time)
|
250
|
+
time.strftime("'#{sprintf('%04i', time.year.abs+1)}-%m-%d %H:%M:%S.%6N#{sprintf("%+03i%02i", *(time.utc_offset/RATIONAL_60).divmod(60))} BC'")
|
240
251
|
else
|
241
252
|
super
|
242
253
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The pg_extended_integer_support extension supports literalizing
|
4
|
+
# Ruby integers outside of PostgreSQL bigint range on PostgreSQL.
|
5
|
+
# Sequel by default will raise exceptions when
|
6
|
+
# literalizing such integers, as PostgreSQL would treat them
|
7
|
+
# as numeric type values instead of integer/bigint type values
|
8
|
+
# if unquoted, which can result in unexpected negative performance
|
9
|
+
# (e.g. forcing sequential scans when index scans would be used for
|
10
|
+
# an integer/bigint type).
|
11
|
+
#
|
12
|
+
# To load the extension into a Dataset (this returns a new Dataset):
|
13
|
+
#
|
14
|
+
# dataset = dataset.extension(:pg_extended_integer_support)
|
15
|
+
#
|
16
|
+
# To load the extension into a Database, so it affects all of the
|
17
|
+
# Database's datasets:
|
18
|
+
#
|
19
|
+
# DB.extension :pg_extended_integer_support
|
20
|
+
#
|
21
|
+
# By default, the extension will quote integers outside
|
22
|
+
# bigint range:
|
23
|
+
#
|
24
|
+
# DB.literal(2**63) # => "'9223372036854775808'"
|
25
|
+
#
|
26
|
+
# Quoting the value treats the type as unknown:
|
27
|
+
#
|
28
|
+
# DB.get{pg_typeof(2**63)} # => 'unknown'
|
29
|
+
#
|
30
|
+
# PostgreSQL will implicitly cast the unknown type to the appropriate
|
31
|
+
# database type, raising an error if it cannot be casted. Be aware this
|
32
|
+
# can result in the integer value being implicitly casted to text or
|
33
|
+
# any other PostgreSQL type:
|
34
|
+
#
|
35
|
+
# # Returns a string, not an integer:
|
36
|
+
# DB.get{2**63}
|
37
|
+
# # => "9223372036854775808"
|
38
|
+
#
|
39
|
+
# You can use the Dataset#integer_outside_bigint_range_strategy method
|
40
|
+
# with the value +:raw+ to change the strategy to not quote the variable:
|
41
|
+
#
|
42
|
+
# DB.dataset.
|
43
|
+
# integer_outside_bigint_range_strategy(:raw).
|
44
|
+
# literal(2**63)
|
45
|
+
# # => "9223372036854775808"
|
46
|
+
#
|
47
|
+
# Note that not quoting the value will result in PostgreSQL treating
|
48
|
+
# the type as numeric instead of integer:
|
49
|
+
#
|
50
|
+
# DB.dataset.
|
51
|
+
# integer_outside_bigint_range_strategy(:raw).
|
52
|
+
# get{pg_typeof(2**63)}
|
53
|
+
# # => "numeric"
|
54
|
+
#
|
55
|
+
# The +:raw+ behavior was Sequel's historical behavior, but unless
|
56
|
+
# you fully understand the reprecussions of PostgreSQL using a
|
57
|
+
# numeric type for integer values, you should not use it.
|
58
|
+
#
|
59
|
+
# To get the current default behavior of raising an exception for
|
60
|
+
# integers outside of PostgreSQL bigint range, you can use a strategy
|
61
|
+
# of +:raise+.
|
62
|
+
#
|
63
|
+
# To specify a default strategy for handling integers outside
|
64
|
+
# bigint range that applies to all of a Database's datasets, you can
|
65
|
+
# use the +:integer_outside_bigint_range_strategy+ Database option with
|
66
|
+
# a value of +:raise+ or +:raw+:
|
67
|
+
#
|
68
|
+
# DB.opts[:integer_outside_bigint_range_strategy] = :raw
|
69
|
+
#
|
70
|
+
# The Database option will be used as a fallback if you did not call
|
71
|
+
# the Dataset#integer_outside_bigint_range_strategy method to specify
|
72
|
+
# a strategy for the dataset.
|
73
|
+
#
|
74
|
+
# Related module: Sequel::Postgres::ExtendedIntegerSupport
|
75
|
+
|
76
|
+
#
|
77
|
+
module Sequel
|
78
|
+
module Postgres
|
79
|
+
module ExtendedIntegerSupport
|
80
|
+
# Set the strategy for handling integers outside PostgreSQL
|
81
|
+
# bigint range. Supported values:
|
82
|
+
#
|
83
|
+
# :quote :: Quote the integer value. PostgreSQL will treat
|
84
|
+
# the integer as a unknown type, implicitly casting
|
85
|
+
# to any other type as needed. This is the default
|
86
|
+
# value when using the pg_extended_integer_support
|
87
|
+
# extension.
|
88
|
+
# :raise :: Raise error when attempting to literalize the integer
|
89
|
+
# (the default behavior of Sequel on PostgreSQL when
|
90
|
+
# not using the pg_extended_integer_support extension).
|
91
|
+
# :raw :: Use raw integer value without quoting. PostgreSQL
|
92
|
+
# will treat the integer as a numeric. This was Sequel's
|
93
|
+
# historical behavior, but it is unlikely to be desired.
|
94
|
+
def integer_outside_bigint_range_strategy(strategy)
|
95
|
+
clone(:integer_outside_bigint_range_strategy=>strategy)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Handle integers outside the bigint range by using
|
101
|
+
# the configured strategy.
|
102
|
+
def literal_integer_outside_bigint_range(v)
|
103
|
+
case @opts[:integer_outside_bigint_range_strategy] || @db.opts[:integer_outside_bigint_range_strategy]
|
104
|
+
when :raise
|
105
|
+
super
|
106
|
+
when :raw
|
107
|
+
v.to_s
|
108
|
+
else # when :quote
|
109
|
+
"'#{v}'"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
Dataset.register_extension(:pg_extended_integer_support, Postgres::ExtendedIntegerSupport)
|
116
|
+
end
|
@@ -75,16 +75,6 @@ module Sequel
|
|
75
75
|
|
76
76
|
private
|
77
77
|
|
78
|
-
# Handle inet[]/cidr[] types in bound variables.
|
79
|
-
def bound_variable_array(a)
|
80
|
-
case a
|
81
|
-
when IPAddr
|
82
|
-
"\"#{a.to_s}/#{a.instance_variable_get(:@mask_addr).to_s(2).count('1')}\""
|
83
|
-
else
|
84
|
-
super
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
78
|
# Make the column type detection recognize the inet and cidr types.
|
89
79
|
def schema_column_type(db_type)
|
90
80
|
case db_type
|
@@ -111,7 +101,7 @@ module Sequel
|
|
111
101
|
when IPAddr
|
112
102
|
value
|
113
103
|
when String
|
114
|
-
IPAddr.new(value)
|
104
|
+
IPAddr.new(typecast_check_string_length(value, 100))
|
115
105
|
else
|
116
106
|
raise Sequel::InvalidValue, "invalid value for inet/cidr: #{value.inspect}"
|
117
107
|
end
|
@@ -121,6 +111,15 @@ module Sequel
|
|
121
111
|
module InetDatasetMethods
|
122
112
|
private
|
123
113
|
|
114
|
+
# Allow auto parameterization of IPAddr instances.
|
115
|
+
def auto_param_type_fallback(v)
|
116
|
+
if defined?(super) && (type = super)
|
117
|
+
type
|
118
|
+
elsif IPAddr === v
|
119
|
+
"::inet"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
124
123
|
# Convert IPAddr value to a string and append a literal version
|
125
124
|
# of the string to the sql.
|
126
125
|
def literal_other_append(sql, value)
|
@@ -163,16 +163,6 @@ module Sequel
|
|
163
163
|
|
164
164
|
private
|
165
165
|
|
166
|
-
# Handle arrays of interval types in bound variables.
|
167
|
-
def bound_variable_array(a)
|
168
|
-
case a
|
169
|
-
when ActiveSupport::Duration
|
170
|
-
"\"#{IntervalDatabaseMethods.literal_duration(a)}\""
|
171
|
-
else
|
172
|
-
super
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
166
|
# Set the :ruby_default value if the default value is recognized as an interval.
|
177
167
|
def schema_post_process(_)
|
178
168
|
super.each do |a|
|
@@ -197,7 +187,7 @@ module Sequel
|
|
197
187
|
when Numeric
|
198
188
|
ActiveSupport::Duration.new(value, [[:seconds, value]])
|
199
189
|
when String
|
200
|
-
PARSER.call(value)
|
190
|
+
PARSER.call(typecast_check_string_length(value, 1000))
|
201
191
|
else
|
202
192
|
raise Sequel::InvalidValue, "invalid value for interval type: #{value.inspect}"
|
203
193
|
end
|
@@ -207,6 +197,15 @@ module Sequel
|
|
207
197
|
module IntervalDatasetMethods
|
208
198
|
private
|
209
199
|
|
200
|
+
# Allow auto parameterization of ActiveSupport::Duration instances.
|
201
|
+
def auto_param_type_fallback(v)
|
202
|
+
if defined?(super) && (type = super)
|
203
|
+
type
|
204
|
+
elsif ActiveSupport::Duration === v
|
205
|
+
"::interval"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
210
209
|
# Handle literalization of ActiveSupport::Duration objects, treating them as
|
211
210
|
# PostgreSQL intervals.
|
212
211
|
def literal_other_append(sql, v)
|
@@ -142,6 +142,11 @@ module Sequel
|
|
142
142
|
ds.literal_append(sql, Sequel.object_to_json(self))
|
143
143
|
sql << '::json'
|
144
144
|
end
|
145
|
+
|
146
|
+
# Allow automatic parameterization.
|
147
|
+
def sequel_auto_param_type(ds)
|
148
|
+
"::json"
|
149
|
+
end
|
145
150
|
end
|
146
151
|
|
147
152
|
jsonb_class = Class.new(base_class) do
|
@@ -151,6 +156,11 @@ module Sequel
|
|
151
156
|
ds.literal_append(sql, Sequel.object_to_json(self))
|
152
157
|
sql << '::jsonb'
|
153
158
|
end
|
159
|
+
|
160
|
+
# Allow automatic parameterization.
|
161
|
+
def sequel_auto_param_type(ds)
|
162
|
+
"::jsonb"
|
163
|
+
end
|
154
164
|
end
|
155
165
|
|
156
166
|
const_set(:"JSON#{name}Base", base_class)
|
@@ -424,16 +434,6 @@ module Sequel
|
|
424
434
|
end
|
425
435
|
end
|
426
436
|
|
427
|
-
# Handle json[] and jsonb[] types in bound variables.
|
428
|
-
def bound_variable_array(a)
|
429
|
-
case a
|
430
|
-
when JSONObject, JSONBObject
|
431
|
-
"\"#{Sequel.object_to_json(a).gsub('"', '\\"')}\""
|
432
|
-
else
|
433
|
-
super
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
437
|
# Make the column type detection recognize the json types.
|
438
438
|
def schema_column_type(db_type)
|
439
439
|
case db_type
|
@@ -123,6 +123,15 @@
|
|
123
123
|
# c = Sequel.pg_jsonb_op(:c)
|
124
124
|
# DB[:t].update(c['key1'] => 1.to_json, c['key2'] => "a".to_json)
|
125
125
|
#
|
126
|
+
# On PostgreSQL 16+, the <tt>IS [NOT] JSON</tt> operator is supported:
|
127
|
+
#
|
128
|
+
# j.is_json # j IS JSON
|
129
|
+
# j.is_json(type: :object) # j IS JSON OBJECT
|
130
|
+
# j.is_json(type: :object, unique: true) # j IS JSON OBJECT WITH UNIQUE
|
131
|
+
# j.is_not_json # j IS NOT JSON
|
132
|
+
# j.is_not_json(type: :array) # j IS NOT JSON ARRAY
|
133
|
+
# j.is_not_json(unique: true) # j IS NOT JSON WITH UNIQUE
|
134
|
+
#
|
126
135
|
# If you are also using the pg_json extension, you should load it before
|
127
136
|
# loading this extension. Doing so will allow you to use the #op method on
|
128
137
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
@@ -151,6 +160,18 @@ module Sequel
|
|
151
160
|
GET_PATH = ["(".freeze, " #> ".freeze, ")".freeze].freeze
|
152
161
|
GET_PATH_TEXT = ["(".freeze, " #>> ".freeze, ")".freeze].freeze
|
153
162
|
|
163
|
+
IS_JSON = ["(".freeze, " IS JSON".freeze, "".freeze, ")".freeze].freeze
|
164
|
+
IS_NOT_JSON = ["(".freeze, " IS NOT JSON".freeze, "".freeze, ")".freeze].freeze
|
165
|
+
EMPTY_STRING = Sequel::LiteralString.new('').freeze
|
166
|
+
WITH_UNIQUE = Sequel::LiteralString.new(' WITH UNIQUE').freeze
|
167
|
+
IS_JSON_MAP = {
|
168
|
+
nil => EMPTY_STRING,
|
169
|
+
:value => Sequel::LiteralString.new(' VALUE').freeze,
|
170
|
+
:scalar => Sequel::LiteralString.new(' SCALAR').freeze,
|
171
|
+
:object => Sequel::LiteralString.new(' OBJECT').freeze,
|
172
|
+
:array => Sequel::LiteralString.new(' ARRAY').freeze
|
173
|
+
}.freeze
|
174
|
+
|
154
175
|
# Get JSON array element or object field as json. If an array is given,
|
155
176
|
# gets the object at the specified path.
|
156
177
|
#
|
@@ -233,6 +254,30 @@ module Sequel
|
|
233
254
|
end
|
234
255
|
end
|
235
256
|
|
257
|
+
# Return whether the json object can be parsed as JSON.
|
258
|
+
#
|
259
|
+
# Options:
|
260
|
+
# :type :: Check whether the json object can be parsed as a specific type
|
261
|
+
# of JSON (:value, :scalar, :object, :array).
|
262
|
+
# :unique :: Check JSON objects for unique keys.
|
263
|
+
#
|
264
|
+
# json_op.is_json # json IS JSON
|
265
|
+
# json_op.is_json(type: :object) # json IS JSON OBJECT
|
266
|
+
# json_op.is_json(unique: true) # json IS JSON WITH UNIQUE
|
267
|
+
def is_json(opts=OPTS)
|
268
|
+
_is_json(IS_JSON, opts)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Return whether the json object cannot be parsed as JSON. The opposite
|
272
|
+
# of #is_json. See #is_json for options.
|
273
|
+
#
|
274
|
+
# json_op.is_not_json # json IS NOT JSON
|
275
|
+
# json_op.is_not_json(type: :object) # json IS NOT JSON OBJECT
|
276
|
+
# json_op.is_not_json(unique: true) # json IS NOT JSON WITH UNIQUE
|
277
|
+
def is_not_json(opts=OPTS)
|
278
|
+
_is_json(IS_NOT_JSON, opts)
|
279
|
+
end
|
280
|
+
|
236
281
|
# Returns a set of keys AS text in the json object.
|
237
282
|
#
|
238
283
|
# json_op.keys # json_object_keys(json)
|
@@ -286,6 +331,13 @@ module Sequel
|
|
286
331
|
|
287
332
|
private
|
288
333
|
|
334
|
+
# Internals of IS [NOT] JSON support
|
335
|
+
def _is_json(lit_array, opts)
|
336
|
+
raise Error, "invalid is_json :type option: #{opts[:type].inspect}" unless type = IS_JSON_MAP[opts[:type]]
|
337
|
+
unique = opts[:unique] ? WITH_UNIQUE : EMPTY_STRING
|
338
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(lit_array, [self, type, unique]))
|
339
|
+
end
|
340
|
+
|
289
341
|
# Return a placeholder literal with the given str and args, wrapped
|
290
342
|
# in an JSONOp or JSONBOp, used by operators that return json or jsonb.
|
291
343
|
def json_op(str, args)
|
@@ -220,18 +220,8 @@ module Sequel
|
|
220
220
|
|
221
221
|
private
|
222
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
223
|
# Recognize the registered database multirange types.
|
234
|
-
def
|
224
|
+
def schema_multirange_type(db_type)
|
235
225
|
@pg_multirange_schema_types[db_type] || super
|
236
226
|
end
|
237
227
|
|
@@ -346,6 +336,11 @@ module Sequel
|
|
346
336
|
|
347
337
|
val << "}"
|
348
338
|
end
|
339
|
+
|
340
|
+
# Allow automatic parameterization.
|
341
|
+
def sequel_auto_param_type(ds)
|
342
|
+
"::#{db_type}"
|
343
|
+
end
|
349
344
|
end
|
350
345
|
end
|
351
346
|
|
@@ -233,18 +233,8 @@ module Sequel
|
|
233
233
|
|
234
234
|
private
|
235
235
|
|
236
|
-
# Handle arrays of range types in bound variables.
|
237
|
-
def bound_variable_array(a)
|
238
|
-
case a
|
239
|
-
when PGRange, Range
|
240
|
-
"\"#{bound_variable_arg(a, nil)}\""
|
241
|
-
else
|
242
|
-
super
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
236
|
# Recognize the registered database range types.
|
247
|
-
def
|
237
|
+
def schema_range_type(db_type)
|
248
238
|
@pg_range_schema_types[db_type] || super
|
249
239
|
end
|
250
240
|
|
@@ -282,7 +272,7 @@ module Sequel
|
|
282
272
|
when Range
|
283
273
|
PGRange.from_range(value, parser.db_type)
|
284
274
|
when String
|
285
|
-
parser.call(value)
|
275
|
+
parser.call(typecast_check_string_length(value, 100))
|
286
276
|
else
|
287
277
|
raise Sequel::InvalidValue, "invalid value for range type: #{value.inspect}"
|
288
278
|
end
|
@@ -491,6 +481,11 @@ module Sequel
|
|
491
481
|
end
|
492
482
|
end
|
493
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
|
+
|
494
489
|
private
|
495
490
|
|
496
491
|
# Escape common range types. Instead of quoting, just backslash escape all
|
@@ -499,8 +494,8 @@ module Sequel
|
|
499
494
|
case k
|
500
495
|
when nil
|
501
496
|
''
|
502
|
-
when
|
503
|
-
ds.
|
497
|
+
when Time, Date
|
498
|
+
ds.literal_date_or_time(k, true)
|
504
499
|
when Integer, Float
|
505
500
|
k.to_s
|
506
501
|
when BigDecimal
|
@@ -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
|
@@ -519,26 +537,9 @@ module Sequel
|
|
519
537
|
|
520
538
|
private
|
521
539
|
|
522
|
-
# Format composite types used in bound variable arrays.
|
523
|
-
def bound_variable_array(arg)
|
524
|
-
case arg
|
525
|
-
when ArrayRow
|
526
|
-
"\"(#{arg.map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
|
527
|
-
when HashRow
|
528
|
-
arg.check_columns!
|
529
|
-
"\"(#{arg.values_at(*arg.columns).map{|v| bound_variable_array(v) if v}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
|
530
|
-
else
|
531
|
-
super
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
540
|
# Make the column type detection handle registered row types.
|
536
|
-
def
|
537
|
-
|
538
|
-
type
|
539
|
-
else
|
540
|
-
super
|
541
|
-
end
|
541
|
+
def schema_composite_type(db_type)
|
542
|
+
@row_schema_types[db_type] || super
|
542
543
|
end
|
543
544
|
end
|
544
545
|
end
|
@@ -1,20 +1,35 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
#
|
3
3
|
# The pg_timestamptz extension changes the default timestamp
|
4
|
-
# type for the database to be +timestamptz+ (
|
5
|
-
# instead of +timestamp+ (
|
4
|
+
# type for the database to be +timestamptz+ (<tt>timestamp with time zone</tt>)
|
5
|
+
# instead of +timestamp+ (<tt>timestamp without time zone</tt>). This is
|
6
6
|
# recommended if you are dealing with multiple timezones in your application.
|
7
|
+
#
|
8
|
+
# If you are using the auto_cast_date_and_time extension, the pg_timestamptz
|
9
|
+
# extension will automatically cast Time and DateTime values to
|
10
|
+
# <tt>TIMESTAMP WITH TIME ZONE</tt> instead of +TIMESTAMP+.
|
7
11
|
#
|
8
12
|
# To load the extension into the database:
|
9
13
|
#
|
10
14
|
# DB.extension :pg_timestamptz
|
11
15
|
#
|
12
|
-
#
|
16
|
+
# To load the extension into individual datasets:
|
17
|
+
#
|
18
|
+
# ds = ds.extension(:pg_timestamptz)
|
19
|
+
#
|
20
|
+
# Note that the loading into individual datasets only affects the integration
|
21
|
+
# with the auto_cast_date_and_time extension.
|
22
|
+
#
|
23
|
+
# Related modules: Sequel::Postgres::Timestamptz, Sequel::Postgres::TimestamptzDatasetMethods
|
13
24
|
|
14
25
|
#
|
15
26
|
module Sequel
|
16
27
|
module Postgres
|
17
28
|
module Timestamptz
|
29
|
+
def self.extended(db)
|
30
|
+
db.extend_datasets(TimestamptzDatasetMethods)
|
31
|
+
end
|
32
|
+
|
18
33
|
private
|
19
34
|
|
20
35
|
# Use timestamptz by default for generic timestamp value.
|
@@ -22,7 +37,16 @@ module Sequel
|
|
22
37
|
:timestamptz
|
23
38
|
end
|
24
39
|
end
|
40
|
+
|
41
|
+
module TimestamptzDatasetMethods
|
42
|
+
private
|
43
|
+
|
44
|
+
def literal_datetime_timestamp_cast
|
45
|
+
'TIMESTAMP WITH TIME ZONE '
|
46
|
+
end
|
47
|
+
end
|
25
48
|
end
|
26
49
|
|
50
|
+
Dataset.register_extension(:pg_timestamptz, Postgres::TimestamptzDatasetMethods)
|
27
51
|
Database.register_extension(:pg_timestamptz, Postgres::Timestamptz)
|
28
52
|
end
|