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
@@ -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 Date, Time
|
57
|
+
literal(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
|
@@ -83,30 +105,21 @@ module Sequel
|
|
83
105
|
# If convert_infinite_timestamps is true and the value is infinite, return an appropriate
|
84
106
|
# value based on the convert_infinite_timestamps setting.
|
85
107
|
def to_application_timestamp(value)
|
86
|
-
if value.is_a?(String) && (m =
|
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
|
@@ -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.%N#{format_timestamp_offset(*(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
|
@@ -76,7 +76,7 @@
|
|
76
76
|
#
|
77
77
|
# This extension integrates with the pg_array extension. If you plan
|
78
78
|
# to use arrays of hstore types, load the pg_array extension before the
|
79
|
-
#
|
79
|
+
# pg_hstore extension:
|
80
80
|
#
|
81
81
|
# DB.extension :pg_array, :pg_hstore
|
82
82
|
#
|
@@ -280,6 +280,11 @@ module Sequel
|
|
280
280
|
str
|
281
281
|
end
|
282
282
|
|
283
|
+
# Allow automatic parameterization.
|
284
|
+
def sequel_auto_param_type(ds)
|
285
|
+
"::hstore"
|
286
|
+
end
|
287
|
+
|
283
288
|
private
|
284
289
|
|
285
290
|
# Return a new hash based on the input hash with string
|
@@ -62,6 +62,19 @@
|
|
62
62
|
# # Delete a key
|
63
63
|
# DB[:tab].update(h: Sequel.hstore_op(:h).delete('k1'))
|
64
64
|
#
|
65
|
+
# On PostgreSQL 14+, The hstore <tt>[]</tt> method will use subscripts instead of being
|
66
|
+
# the same as +get+, if the value being wrapped is an identifer:
|
67
|
+
#
|
68
|
+
# Sequel.hstore_op(:hstore_column)['a'] # hstore_column['a']
|
69
|
+
# Sequel.hstore_op(Sequel[:h][:s])['a'] # h.s['a']
|
70
|
+
#
|
71
|
+
# This support allows you to use hstore subscripts in UPDATE statements to update only
|
72
|
+
# part of a column:
|
73
|
+
#
|
74
|
+
# h = Sequel.hstore_op(:h)
|
75
|
+
# DB[:t].update(h['key1'] => 'val1', h['key2'] => 'val2')
|
76
|
+
# # UPDATE "t" SET "h"['key1'] = 'val1', "h"['key2'] = 'val2'
|
77
|
+
#
|
65
78
|
# See the PostgreSQL hstore function and operator documentation for more
|
66
79
|
# details on what these functions and operators do.
|
67
80
|
#
|
@@ -114,10 +127,15 @@ module Sequel
|
|
114
127
|
#
|
115
128
|
# hstore_op['a'] # (hstore -> 'a')
|
116
129
|
def [](key)
|
117
|
-
v = Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)])
|
118
130
|
if key.is_a?(Array) || (defined?(Sequel::Postgres::PGArray) && key.is_a?(Sequel::Postgres::PGArray)) || (defined?(Sequel::Postgres::ArrayOp) && key.is_a?(Sequel::Postgres::ArrayOp))
|
119
|
-
wrap_output_array(
|
131
|
+
wrap_output_array(Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)]))
|
120
132
|
else
|
133
|
+
v = case @value
|
134
|
+
when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
|
135
|
+
HStoreSubscriptOp.new(self, key)
|
136
|
+
else
|
137
|
+
Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, key])
|
138
|
+
end
|
121
139
|
Sequel::SQL::StringExpression.new(:NOOP, v)
|
122
140
|
end
|
123
141
|
end
|
@@ -304,6 +322,38 @@ module Sequel
|
|
304
322
|
end
|
305
323
|
end
|
306
324
|
|
325
|
+
# Represents hstore subscripts. This is abstracted because the
|
326
|
+
# subscript support depends on the database version.
|
327
|
+
class HStoreSubscriptOp < SQL::Expression
|
328
|
+
SUBSCRIPT = ["".freeze, "[".freeze, "]".freeze].freeze
|
329
|
+
|
330
|
+
# The expression being subscripted
|
331
|
+
attr_reader :expression
|
332
|
+
|
333
|
+
# The subscript to use
|
334
|
+
attr_reader :sub
|
335
|
+
|
336
|
+
# Set the expression and subscript to the given arguments
|
337
|
+
def initialize(expression, sub)
|
338
|
+
@expression = expression
|
339
|
+
@sub = sub
|
340
|
+
freeze
|
341
|
+
end
|
342
|
+
|
343
|
+
# Use subscripts instead of -> operator on PostgreSQL 14+
|
344
|
+
def to_s_append(ds, sql)
|
345
|
+
server_version = ds.db.server_version
|
346
|
+
frag = server_version && server_version >= 140000 ? SUBSCRIPT : HStoreOp::LOOKUP
|
347
|
+
ds.literal_append(sql, Sequel::SQL::PlaceholderLiteralString.new(frag, [@expression, @sub]))
|
348
|
+
end
|
349
|
+
|
350
|
+
# Support transforming of hstore subscripts
|
351
|
+
def sequel_ast_transform(transformer)
|
352
|
+
self.class.new(transformer.call(@expression), transformer.call(@sub))
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
|
307
357
|
module HStoreOpMethods
|
308
358
|
# Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
|
309
359
|
# hstore functions and operators with it.
|
@@ -356,7 +406,7 @@ end
|
|
356
406
|
if defined?(Sequel::CoreRefinements)
|
357
407
|
module Sequel::CoreRefinements
|
358
408
|
refine Symbol do
|
359
|
-
|
409
|
+
send INCLUDE_METH, Sequel::Postgres::HStoreOpMethods
|
360
410
|
end
|
361
411
|
end
|
362
412
|
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)
|
@@ -32,8 +32,16 @@
|
|
32
32
|
#
|
33
33
|
# Related module: Sequel::Postgres::IntervalDatabaseMethods
|
34
34
|
|
35
|
+
require 'active_support'
|
35
36
|
require 'active_support/duration'
|
36
37
|
|
38
|
+
# :nocov:
|
39
|
+
begin
|
40
|
+
require 'active_support/version'
|
41
|
+
rescue LoadError
|
42
|
+
end
|
43
|
+
# :nocov:
|
44
|
+
|
37
45
|
module Sequel
|
38
46
|
module Postgres
|
39
47
|
module IntervalDatabaseMethods
|
@@ -61,34 +69,47 @@ module Sequel
|
|
61
69
|
|
62
70
|
# Creates callable objects that convert strings into ActiveSupport::Duration instances.
|
63
71
|
class Parser
|
72
|
+
# Whether ActiveSupport::Duration.new takes parts as array instead of hash
|
73
|
+
USE_PARTS_ARRAY = !defined?(ActiveSupport::VERSION::STRING) || ActiveSupport::VERSION::STRING < '5.1'
|
74
|
+
|
75
|
+
if defined?(ActiveSupport::Duration::SECONDS_PER_MONTH)
|
76
|
+
SECONDS_PER_MONTH = ActiveSupport::Duration::SECONDS_PER_MONTH
|
77
|
+
SECONDS_PER_YEAR = ActiveSupport::Duration::SECONDS_PER_YEAR
|
78
|
+
# :nocov:
|
79
|
+
else
|
80
|
+
SECONDS_PER_MONTH = 2592000
|
81
|
+
SECONDS_PER_YEAR = 31557600
|
82
|
+
# :nocov:
|
83
|
+
end
|
84
|
+
|
64
85
|
# Parse the interval input string into an ActiveSupport::Duration instance.
|
65
86
|
def call(string)
|
66
87
|
raise(InvalidValue, "invalid or unhandled interval format: #{string.inspect}") unless matches = /\A([+-]?\d+ years?\s?)?([+-]?\d+ mons?\s?)?([+-]?\d+ days?\s?)?(?:(?:([+-])?(\d{2,10}):(\d\d):(\d\d(\.\d+)?))|([+-]?\d+ hours?\s?)?([+-]?\d+ mins?\s?)?([+-]?\d+(\.\d+)? secs?\s?)?)?\z/.match(string)
|
67
88
|
|
68
89
|
value = 0
|
69
|
-
parts =
|
90
|
+
parts = {}
|
70
91
|
|
71
92
|
if v = matches[1]
|
72
93
|
v = v.to_i
|
73
|
-
value +=
|
74
|
-
parts
|
94
|
+
value += SECONDS_PER_YEAR * v
|
95
|
+
parts[:years] = v
|
75
96
|
end
|
76
97
|
if v = matches[2]
|
77
98
|
v = v.to_i
|
78
|
-
value +=
|
79
|
-
parts
|
99
|
+
value += SECONDS_PER_MONTH * v
|
100
|
+
parts[:months] = v
|
80
101
|
end
|
81
102
|
if v = matches[3]
|
82
103
|
v = v.to_i
|
83
104
|
value += 86400 * v
|
84
|
-
parts
|
105
|
+
parts[:days] = v
|
85
106
|
end
|
86
107
|
if matches[5]
|
87
108
|
seconds = matches[5].to_i * 3600 + matches[6].to_i * 60
|
88
109
|
seconds += matches[8] ? matches[7].to_f : matches[7].to_i
|
89
110
|
seconds *= -1 if matches[4] == '-'
|
90
111
|
value += seconds
|
91
|
-
parts
|
112
|
+
parts[:seconds] = seconds
|
92
113
|
elsif matches[9] || matches[10] || matches[11]
|
93
114
|
seconds = 0
|
94
115
|
if v = matches[9]
|
@@ -101,8 +122,14 @@ module Sequel
|
|
101
122
|
seconds += matches[12] ? v.to_f : v.to_i
|
102
123
|
end
|
103
124
|
value += seconds
|
104
|
-
parts
|
125
|
+
parts[:seconds] = seconds
|
126
|
+
end
|
127
|
+
|
128
|
+
# :nocov:
|
129
|
+
if USE_PARTS_ARRAY
|
130
|
+
parts = parts.to_a
|
105
131
|
end
|
132
|
+
# :nocov:
|
106
133
|
|
107
134
|
ActiveSupport::Duration.new(value, parts)
|
108
135
|
end
|
@@ -136,16 +163,6 @@ module Sequel
|
|
136
163
|
|
137
164
|
private
|
138
165
|
|
139
|
-
# Handle arrays of interval types in bound variables.
|
140
|
-
def bound_variable_array(a)
|
141
|
-
case a
|
142
|
-
when ActiveSupport::Duration
|
143
|
-
"\"#{IntervalDatabaseMethods.literal_duration(a)}\""
|
144
|
-
else
|
145
|
-
super
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
166
|
# Set the :ruby_default value if the default value is recognized as an interval.
|
150
167
|
def schema_post_process(_)
|
151
168
|
super.each do |a|
|
@@ -170,7 +187,7 @@ module Sequel
|
|
170
187
|
when Numeric
|
171
188
|
ActiveSupport::Duration.new(value, [[:seconds, value]])
|
172
189
|
when String
|
173
|
-
PARSER.call(value)
|
190
|
+
PARSER.call(typecast_check_string_length(value, 1000))
|
174
191
|
else
|
175
192
|
raise Sequel::InvalidValue, "invalid value for interval type: #{value.inspect}"
|
176
193
|
end
|
@@ -180,6 +197,15 @@ module Sequel
|
|
180
197
|
module IntervalDatasetMethods
|
181
198
|
private
|
182
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
|
+
|
183
209
|
# Handle literalization of ActiveSupport::Duration objects, treating them as
|
184
210
|
# PostgreSQL intervals.
|
185
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)
|
@@ -387,11 +397,9 @@ module Sequel
|
|
387
397
|
# argument is true), or a String, Numeric, true, false, or nil
|
388
398
|
# if the json library used supports that.
|
389
399
|
def _parse_json(s)
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
|
394
|
-
end
|
400
|
+
Sequel.parse_json(s)
|
401
|
+
rescue Sequel.json_parser_error_class => e
|
402
|
+
raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
|
395
403
|
end
|
396
404
|
|
397
405
|
# Wrap the parsed JSON value in the appropriate JSON wrapper class.
|
@@ -426,16 +434,6 @@ module Sequel
|
|
426
434
|
end
|
427
435
|
end
|
428
436
|
|
429
|
-
# Handle json[] and jsonb[] types in bound variables.
|
430
|
-
def bound_variable_array(a)
|
431
|
-
case a
|
432
|
-
when JSONObject, JSONBObject
|
433
|
-
"\"#{Sequel.object_to_json(a).gsub('"', '\\"')}\""
|
434
|
-
else
|
435
|
-
super
|
436
|
-
end
|
437
|
-
end
|
438
|
-
|
439
437
|
# Make the column type detection recognize the json types.
|
440
438
|
def schema_column_type(db_type)
|
441
439
|
case db_type
|
@@ -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
|
#
|
@@ -101,6 +102,27 @@
|
|
101
102
|
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
102
103
|
# errors are not suppressed.
|
103
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
|
+
#
|
104
126
|
# If you are also using the pg_json extension, you should load it before
|
105
127
|
# loading this extension. Doing so will allow you to use the #op method on
|
106
128
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|
@@ -323,6 +345,24 @@ module Sequel
|
|
323
345
|
PATH_EXISTS = ["(".freeze, " @? ".freeze, ")".freeze].freeze
|
324
346
|
PATH_MATCH = ["(".freeze, " @@ ".freeze, ")".freeze].freeze
|
325
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
|
+
|
326
366
|
# jsonb expression for deletion of the given argument from the
|
327
367
|
# current jsonb.
|
328
368
|
#
|
@@ -582,6 +622,37 @@ module Sequel
|
|
582
622
|
end
|
583
623
|
end
|
584
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
|
+
|
585
656
|
module JSONOpMethods
|
586
657
|
# Wrap the receiver in an JSONOp so you can easily use the PostgreSQL
|
587
658
|
# json functions and operators with it.
|
@@ -674,7 +745,7 @@ end
|
|
674
745
|
if defined?(Sequel::CoreRefinements)
|
675
746
|
module Sequel::CoreRefinements
|
676
747
|
refine Symbol do
|
677
|
-
|
748
|
+
send INCLUDE_METH, Sequel::Postgres::JSONOpMethods
|
678
749
|
end
|
679
750
|
end
|
680
751
|
end
|