sequel 5.39.0 → 5.63.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 +308 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +57 -25
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +13 -13
- data/doc/association_basics.rdoc +89 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/migration.rdoc +12 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +18 -11
- 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/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sql.rdoc +27 -15
- 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 +4 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +3 -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 +58 -7
- data/lib/sequel/adapters/shared/mysql.rb +40 -2
- data/lib/sequel/adapters/shared/oracle.rb +76 -0
- data/lib/sequel/adapters/shared/postgres.rb +418 -174
- data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -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/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 +5 -1
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +8 -8
- data/lib/sequel/connection_pool/timed_queue.rb +257 -0
- data/lib/sequel/connection_pool.rb +47 -30
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +26 -2
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +38 -1
- data/lib/sequel/database/schema_generator.rb +45 -52
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +107 -13
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +118 -16
- data/lib/sequel/dataset/sql.rb +177 -47
- 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 +438 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- 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/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 +7 -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 +23 -3
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
- 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 +10 -23
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +19 -13
- 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_dumper.rb +13 -2
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +325 -96
- data/lib/sequel/model/base.rb +51 -27
- 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 +2 -1
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +3 -1
- 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 +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
- data/lib/sequel/plugins/prepared_statements.rb +10 -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 +1 -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 +38 -11
- 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 +97 -43
|
@@ -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
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
#
|
|
13
13
|
# How accurate this count is depends on the number of rows
|
|
14
14
|
# added/deleted from the table since the last time it was
|
|
15
|
-
# analyzed.
|
|
15
|
+
# analyzed. If the table has not been vacuumed or analyzed
|
|
16
|
+
# yet, this can return 0 or -1 depending on the PostgreSQL
|
|
17
|
+
# version in use.
|
|
16
18
|
#
|
|
17
19
|
# To load the extension into the database:
|
|
18
20
|
#
|