sequel 5.45.0 → 5.77.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 +434 -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 +27 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +28 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- 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/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/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +23 -13
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +3 -3
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +63 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +24 -22
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +56 -51
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +89 -45
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +42 -0
- data/lib/sequel/adapters/shared/mssql.rb +91 -10
- data/lib/sequel/adapters/shared/mysql.rb +78 -3
- data/lib/sequel/adapters/shared/oracle.rb +86 -7
- data/lib/sequel/adapters/shared/postgres.rb +576 -171
- data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
- data/lib/sequel/adapters/shared/sqlite.rb +92 -8
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +99 -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 +57 -31
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +70 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +11 -6
- data/lib/sequel/database/schema_methods.rb +23 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +111 -15
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +170 -41
- data/lib/sequel/dataset/sql.rb +190 -71
- 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 +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +14 -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/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- 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 +11 -10
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +1 -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 +57 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +33 -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 +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
- 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 +11 -11
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +125 -2
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +13 -26
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- 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/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +286 -92
- data/lib/sequel/model/base.rb +53 -33
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +74 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +29 -8
- data/lib/sequel/plugins/composition.rb +3 -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/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 +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +8 -3
- 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 +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_array_associations.rb +46 -34
- 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 +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 +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- 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 +41 -11
- 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 +109 -19
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The pg_auto_parameterize_in_array extension builds on the pg_auto_parameterize
|
4
|
+
# extension, adding support for handling additional types when converting from
|
5
|
+
# IN to = ANY and NOT IN to != ALL:
|
6
|
+
#
|
7
|
+
# DB[:table].where(column: [1.0, 2.0, ...])
|
8
|
+
# # Without extension: column IN ($1::numeric, $2:numeric, ...) # bound variables: 1.0, 2.0, ...
|
9
|
+
# # With extension: column = ANY($1::numeric[]) # bound variables: [1.0, 2.0, ...]
|
10
|
+
#
|
11
|
+
# This prevents the use of an unbounded number of bound variables based on the
|
12
|
+
# size of the array, as well as using different SQL for different array sizes.
|
13
|
+
#
|
14
|
+
# The following types are supported when doing the conversions, with the database
|
15
|
+
# type used:
|
16
|
+
#
|
17
|
+
# Float :: if any are infinite or NaN, double precision, otherwise numeric
|
18
|
+
# BigDecimal :: numeric
|
19
|
+
# Date :: date
|
20
|
+
# Time :: timestamp (or timestamptz if pg_timestamptz extension is used)
|
21
|
+
# DateTime :: timestamp (or timestamptz if pg_timestamptz extension is used)
|
22
|
+
# Sequel::SQLTime :: time
|
23
|
+
# Sequel::SQL::Blob :: bytea
|
24
|
+
#
|
25
|
+
# String values are also supported using the +text+ type, but only if the
|
26
|
+
# +:treat_string_list_as_text_array+ Database option is used. This is because
|
27
|
+
# treating strings as text can break programs, since the type for
|
28
|
+
# literal strings in PostgreSQL is +unknown+, not +text+.
|
29
|
+
#
|
30
|
+
# The conversion is only done for single dimensional arrays that have more
|
31
|
+
# than two elements, where all elements are of the same class (other than
|
32
|
+
# nil values).
|
33
|
+
#
|
34
|
+
# Related module: Sequel::Postgres::AutoParameterizeInArray
|
35
|
+
|
36
|
+
module Sequel
|
37
|
+
module Postgres
|
38
|
+
# Enable automatically parameterizing queries.
|
39
|
+
module AutoParameterizeInArray
|
40
|
+
# Transform column IN (...) expressions into column = ANY($)
|
41
|
+
# and column NOT IN (...) expressions into column != ALL($)
|
42
|
+
# using an array bound variable for the ANY/ALL argument,
|
43
|
+
# if all values inside the predicate are of the same type and
|
44
|
+
# the type is handled by the extension.
|
45
|
+
# This is the same optimization PostgreSQL performs internally,
|
46
|
+
# but this reduces the number of bound variables.
|
47
|
+
def complex_expression_sql_append(sql, op, args)
|
48
|
+
case op
|
49
|
+
when :IN, :"NOT IN"
|
50
|
+
l, r = args
|
51
|
+
if auto_param?(sql) && (type = _bound_variable_type_for_array(r))
|
52
|
+
if op == :IN
|
53
|
+
op = :"="
|
54
|
+
func = :ANY
|
55
|
+
else
|
56
|
+
op = :!=
|
57
|
+
func = :ALL
|
58
|
+
end
|
59
|
+
args = [l, Sequel.function(func, Sequel.pg_array(r, type))]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# The bound variable type string to use for the bound variable array.
|
69
|
+
# Returns nil if a bound variable should not be used for the array.
|
70
|
+
def _bound_variable_type_for_array(r)
|
71
|
+
return unless Array === r && r.size > 1
|
72
|
+
classes = r.map(&:class)
|
73
|
+
classes.uniq!
|
74
|
+
classes.delete(NilClass)
|
75
|
+
return unless classes.size == 1
|
76
|
+
|
77
|
+
klass = classes[0]
|
78
|
+
if klass == Integer
|
79
|
+
# This branch is not taken on Ruby <2.4, because of the Fixnum/Bignum split.
|
80
|
+
# However, that causes no problems as pg_auto_parameterize handles integer
|
81
|
+
# arrays natively (though the SQL used is different)
|
82
|
+
"int8"
|
83
|
+
elsif klass == String
|
84
|
+
"text" if db.typecast_value(:boolean, db.opts[:treat_string_list_as_text_array])
|
85
|
+
elsif klass == BigDecimal
|
86
|
+
"numeric"
|
87
|
+
elsif klass == Date
|
88
|
+
"date"
|
89
|
+
elsif klass == Time
|
90
|
+
@db.cast_type_literal(Time)
|
91
|
+
elsif klass == Float
|
92
|
+
# PostgreSQL treats literal floats as numeric, not double precision
|
93
|
+
# But older versions of PostgreSQL don't handle Infinity/NaN in numeric
|
94
|
+
r.all?{|v| v.nil? || v.finite?} ? "numeric" : "double precision"
|
95
|
+
elsif klass == Sequel::SQLTime
|
96
|
+
"time"
|
97
|
+
elsif klass == DateTime
|
98
|
+
@db.cast_type_literal(DateTime)
|
99
|
+
elsif klass == Sequel::SQL::Blob
|
100
|
+
"bytea"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
Database.register_extension(:pg_auto_parameterize_in_array) do |db|
|
107
|
+
db.extension(:pg_array, :pg_auto_parameterize)
|
108
|
+
db.extend_datasets(Postgres::AutoParameterizeInArray)
|
109
|
+
end
|
110
|
+
end
|
@@ -166,8 +166,7 @@ module Sequel
|
|
166
166
|
def schema_post_process(_)
|
167
167
|
super.each do |_, s|
|
168
168
|
oid = s[:oid]
|
169
|
-
if values = Sequel.synchronize{@enum_labels[oid]}
|
170
|
-
s[:type] = :enum
|
169
|
+
if s[:type] == :enum && (values = Sequel.synchronize{@enum_labels[oid]})
|
171
170
|
s[:enum_values] = values
|
172
171
|
end
|
173
172
|
end
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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,6 +32,7 @@
|
|
32
32
|
#
|
33
33
|
# Related module: Sequel::Postgres::IntervalDatabaseMethods
|
34
34
|
|
35
|
+
require 'active_support'
|
35
36
|
require 'active_support/duration'
|
36
37
|
|
37
38
|
# :nocov:
|
@@ -162,16 +163,6 @@ module Sequel
|
|
162
163
|
|
163
164
|
private
|
164
165
|
|
165
|
-
# Handle arrays of interval types in bound variables.
|
166
|
-
def bound_variable_array(a)
|
167
|
-
case a
|
168
|
-
when ActiveSupport::Duration
|
169
|
-
"\"#{IntervalDatabaseMethods.literal_duration(a)}\""
|
170
|
-
else
|
171
|
-
super
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
166
|
# Set the :ruby_default value if the default value is recognized as an interval.
|
176
167
|
def schema_post_process(_)
|
177
168
|
super.each do |a|
|
@@ -196,7 +187,7 @@ module Sequel
|
|
196
187
|
when Numeric
|
197
188
|
ActiveSupport::Duration.new(value, [[:seconds, value]])
|
198
189
|
when String
|
199
|
-
PARSER.call(value)
|
190
|
+
PARSER.call(typecast_check_string_length(value, 1000))
|
200
191
|
else
|
201
192
|
raise Sequel::InvalidValue, "invalid value for interval type: #{value.inspect}"
|
202
193
|
end
|
@@ -206,6 +197,15 @@ module Sequel
|
|
206
197
|
module IntervalDatasetMethods
|
207
198
|
private
|
208
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
|
+
|
209
209
|
# Handle literalization of ActiveSupport::Duration objects, treating them as
|
210
210
|
# PostgreSQL intervals.
|
211
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
|