sequel 5.22.0 → 5.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +66 -0
- data/README.rdoc +1 -1
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +3 -0
- data/doc/postgresql.rdoc +2 -2
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/release_notes/5.25.0.txt +32 -0
- data/doc/release_notes/5.26.0.txt +35 -0
- data/doc/testing.rdoc +1 -0
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +0 -1
- data/lib/sequel/adapters/shared/mssql.rb +9 -8
- data/lib/sequel/adapters/shared/postgres.rb +25 -7
- data/lib/sequel/adapters/shared/sqlite.rb +16 -2
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/schema_generator.rb +11 -2
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/extensions/named_timezones.rb +51 -9
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +88 -17
- data/lib/sequel/extensions/pg_json_ops.rb +124 -0
- data/lib/sequel/extensions/pg_range.rb +9 -0
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/sql_comments.rb +2 -2
- data/lib/sequel/model/base.rb +12 -5
- data/lib/sequel/plugins/association_multi_add_remove.rb +83 -0
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/caching.rb +3 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -0
- data/lib/sequel/plugins/csv_serializer.rb +26 -9
- data/lib/sequel/plugins/dirty.rb +3 -9
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +89 -30
- data/lib/sequel/plugins/sharding.rb +11 -5
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/typecast_on_load.rb +3 -2
- data/lib/sequel/sql.rb +3 -1
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +130 -0
- data/spec/bin_spec.rb +2 -2
- data/spec/core/database_spec.rb +50 -0
- data/spec/core/dataset_spec.rb +23 -1
- data/spec/core/expression_filters_spec.rb +7 -2
- data/spec/core/schema_spec.rb +18 -0
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core_extensions_spec.rb +1 -1
- data/spec/extensions/association_multi_add_remove_spec.rb +1041 -0
- data/spec/extensions/dirty_spec.rb +33 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/nested_attributes_spec.rb +48 -0
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +37 -0
- data/spec/extensions/pg_json_ops_spec.rb +67 -0
- data/spec/extensions/pg_json_spec.rb +12 -0
- data/spec/extensions/pg_range_spec.rb +19 -2
- data/spec/extensions/sharding_spec.rb +8 -0
- data/spec/extensions/spec_helper.rb +9 -2
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/guards_helper.rb +1 -1
- data/spec/integration/plugin_test.rb +27 -0
- data/spec/integration/schema_test.rb +16 -2
- data/spec/model/spec_helper.rb +1 -1
- metadata +30 -2
@@ -97,13 +97,17 @@ module Sequel
|
|
97
97
|
# Add an exclusion constraint when creating the table. Elements should be
|
98
98
|
# an array of 2 element arrays, with the first element being the column or
|
99
99
|
# expression the exclusion constraint is applied to, and the second element
|
100
|
-
# being the operator to use for the column/expression to check for exclusion
|
101
|
-
#
|
102
|
-
# Example:
|
100
|
+
# being the operator to use for the column/expression to check for exclusion:
|
103
101
|
#
|
104
102
|
# exclude([[:col1, '&&'], [:col2, '=']])
|
105
103
|
# # EXCLUDE USING gist (col1 WITH &&, col2 WITH =)
|
106
104
|
#
|
105
|
+
# To use a custom operator class, you need to use Sequel.lit with the expression
|
106
|
+
# and operator class:
|
107
|
+
#
|
108
|
+
# exclude([[Sequel.lit('col1 inet_ops'), '&&'], [:col2, '=']])
|
109
|
+
# # EXCLUDE USING gist (col1 inet_ops WITH &&, col2 WITH =)
|
110
|
+
#
|
107
111
|
# Options supported:
|
108
112
|
#
|
109
113
|
# :name :: Name the constraint with the given name (useful if you may
|
@@ -836,10 +840,14 @@ module Sequel
|
|
836
840
|
# default value is given.
|
837
841
|
def column_definition_default_sql(sql, column)
|
838
842
|
super
|
839
|
-
if !column[:serial] && !['serial', 'bigserial'].include?(column[:type].to_s) && !column[:default]
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
+
if !column[:serial] && !['serial', 'bigserial'].include?(column[:type].to_s) && !column[:default]
|
844
|
+
if (identity = column[:identity])
|
845
|
+
sql << " GENERATED "
|
846
|
+
sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
|
847
|
+
sql << " AS IDENTITY"
|
848
|
+
elsif (generated = column[:generated_always_as])
|
849
|
+
sql << " GENERATED ALWAYS AS (#{literal(generated)}) STORED"
|
850
|
+
end
|
843
851
|
end
|
844
852
|
end
|
845
853
|
|
@@ -1798,6 +1806,16 @@ module Sequel
|
|
1798
1806
|
end
|
1799
1807
|
end
|
1800
1808
|
|
1809
|
+
# Include aliases when inserting into a single table on PostgreSQL 9.5+.
|
1810
|
+
def insert_into_sql(sql)
|
1811
|
+
sql << " INTO "
|
1812
|
+
if (f = @opts[:from]) && f.length == 1
|
1813
|
+
identifier_append(sql, server_version >= 90500 ? f.first : unaliased_identifier(f.first))
|
1814
|
+
else
|
1815
|
+
source_list_append(sql, f)
|
1816
|
+
end
|
1817
|
+
end
|
1818
|
+
|
1801
1819
|
# Return the primary key to use for RETURNING in an INSERT statement
|
1802
1820
|
def insert_pk
|
1803
1821
|
if (f = opts[:from]) && !f.empty?
|
@@ -10,6 +10,10 @@ module Sequel
|
|
10
10
|
def self.mock_adapter_setup(db)
|
11
11
|
db.instance_exec do
|
12
12
|
@sqlite_version = 30903
|
13
|
+
|
14
|
+
def schema_parse_table(*)
|
15
|
+
[]
|
16
|
+
end
|
13
17
|
end
|
14
18
|
end
|
15
19
|
|
@@ -60,7 +64,7 @@ module Sequel
|
|
60
64
|
def foreign_key_list(table, opts=OPTS)
|
61
65
|
m = output_identifier_meth
|
62
66
|
h = {}
|
63
|
-
|
67
|
+
_foreign_key_list_ds(table).each do |row|
|
64
68
|
if r = h[row[:id]]
|
65
69
|
r[:columns] << m.call(row[:from])
|
66
70
|
r[:key] << m.call(row[:to]) if r[:key]
|
@@ -173,6 +177,16 @@ module Sequel
|
|
173
177
|
|
174
178
|
private
|
175
179
|
|
180
|
+
# Dataset used for parsing foreign key lists
|
181
|
+
def _foreign_key_list_ds(table)
|
182
|
+
metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table))
|
183
|
+
end
|
184
|
+
|
185
|
+
# Dataset used for parsing schema
|
186
|
+
def _parse_pragma_ds(table_name, opts)
|
187
|
+
metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
|
188
|
+
end
|
189
|
+
|
176
190
|
# Run all alter_table commands in a transaction. This is technically only
|
177
191
|
# needed for drop column.
|
178
192
|
def apply_alter_table(table, ops)
|
@@ -445,7 +459,7 @@ module Sequel
|
|
445
459
|
# Parse the output of the table_info pragma
|
446
460
|
def parse_pragma(table_name, opts)
|
447
461
|
pks = 0
|
448
|
-
sch =
|
462
|
+
sch = _parse_pragma_ds(table_name, opts).map do |row|
|
449
463
|
row.delete(:cid)
|
450
464
|
row[:allow_null] = row.delete(:notnull).to_i == 0
|
451
465
|
row[:default] = row.delete(:dflt_value)
|
@@ -16,6 +16,18 @@ module Sequel
|
|
16
16
|
c = TinyTds::Client.new(opts)
|
17
17
|
c.query_options.merge!(:cache_rows=>false)
|
18
18
|
|
19
|
+
if opts[:ansi]
|
20
|
+
sql = %w(
|
21
|
+
ANSI_NULLS
|
22
|
+
ANSI_PADDING
|
23
|
+
ANSI_WARNINGS
|
24
|
+
ANSI_NULL_DFLT_ON
|
25
|
+
QUOTED_IDENTIFIER
|
26
|
+
CONCAT_NULL_YIELDS_NULL
|
27
|
+
).map{|v| "SET #{v} ON"}.join(";")
|
28
|
+
log_connection_yield(sql, c){c.execute(sql)}
|
29
|
+
end
|
30
|
+
|
19
31
|
if (ts = opts[:textsize])
|
20
32
|
sql = "SET TEXTSIZE #{typecast_value_integer(ts)}"
|
21
33
|
log_connection_yield(sql, c){c.execute(sql)}
|
@@ -35,7 +35,7 @@ module Sequel
|
|
35
35
|
# Yield to the block, logging any errors at error level to all loggers,
|
36
36
|
# and all other queries with the duration at warn or info level.
|
37
37
|
def log_connection_yield(sql, conn, args=nil)
|
38
|
-
return yield if
|
38
|
+
return yield if skip_logging?
|
39
39
|
sql = "#{connection_info(conn) if conn && log_connection_info}#{sql}#{"; #{args.inspect}" if args}"
|
40
40
|
timer = Sequel.start_timer
|
41
41
|
|
@@ -58,6 +58,12 @@ module Sequel
|
|
58
58
|
|
59
59
|
private
|
60
60
|
|
61
|
+
# Determine if logging should be skipped. Defaults to true if no loggers
|
62
|
+
# have been specified.
|
63
|
+
def skip_logging?
|
64
|
+
@loggers.empty?
|
65
|
+
end
|
66
|
+
|
61
67
|
# String including information about the connection, for use when logging
|
62
68
|
# connection info.
|
63
69
|
def connection_info(conn)
|
@@ -110,6 +110,9 @@ module Sequel
|
|
110
110
|
# yet exist on referenced table (but will exist before the transaction commits).
|
111
111
|
# Basically it adds DEFERRABLE INITIALLY DEFERRED on key creation.
|
112
112
|
# If you use :immediate as the value, uses DEFERRABLE INITIALLY IMMEDIATE.
|
113
|
+
# :generated_always_as :: Specify a GENERATED ALWAYS AS column expression,
|
114
|
+
# if generated columns are supported (PostgreSQL 12+, MariaDB 5.2.0+,
|
115
|
+
# and MySQL 5.7.6+).
|
113
116
|
# :index :: Create an index on this column. If given a hash, use the hash as the
|
114
117
|
# options for the index.
|
115
118
|
# :key :: For foreign key columns, the column in the associated table
|
@@ -126,15 +129,21 @@ module Sequel
|
|
126
129
|
# be used if you have a single, nonautoincrementing primary key column
|
127
130
|
# (use the primary_key method in that case).
|
128
131
|
# :primary_key_constraint_name :: The name to give the primary key constraint
|
132
|
+
# :primary_key_deferrable :: Similar to :deferrable, but for the primary key constraint
|
133
|
+
# if :primary_key is used.
|
129
134
|
# :type :: Overrides the type given as the argument. Generally not used by column
|
130
135
|
# itself, but can be passed as an option to other methods that call column.
|
131
136
|
# :unique :: Mark the column as unique, generally has the same effect as
|
132
137
|
# creating a unique index on the column.
|
133
138
|
# :unique_constraint_name :: The name to give the unique key constraint
|
139
|
+
# :unique_deferrable :: Similar to :deferrable, but for the unique constraint if :unique
|
140
|
+
# is used.
|
141
|
+
#
|
142
|
+
# PostgreSQL specific options:
|
143
|
+
#
|
144
|
+
# :identity :: Create an identity column.
|
134
145
|
#
|
135
146
|
# MySQL specific options:
|
136
|
-
# :generated_always_as :: Specify a GENERATED ALWAYS AS column expression,
|
137
|
-
# if generated columns are supported.
|
138
147
|
# :generated_type :: Set the type of column when using :generated_always_as,
|
139
148
|
# should be :virtual or :stored to force a type.
|
140
149
|
def column(name, type, opts = OPTS)
|
@@ -586,6 +586,7 @@ module Sequel
|
|
586
586
|
sql << " CONSTRAINT #{quote_identifier(name)}"
|
587
587
|
end
|
588
588
|
sql << ' PRIMARY KEY'
|
589
|
+
constraint_deferrable_sql_append(sql, column[:primary_key_deferrable])
|
589
590
|
end
|
590
591
|
end
|
591
592
|
|
@@ -606,6 +607,7 @@ module Sequel
|
|
606
607
|
sql << " CONSTRAINT #{quote_identifier(name)}"
|
607
608
|
end
|
608
609
|
sql << ' UNIQUE'
|
610
|
+
constraint_deferrable_sql_append(sql, column[:unique_deferrable])
|
609
611
|
end
|
610
612
|
end
|
611
613
|
|
@@ -333,6 +333,7 @@ module Sequel
|
|
333
333
|
# after every 50 records.
|
334
334
|
# :return :: When this is set to :primary_key, returns an array of
|
335
335
|
# autoincremented primary key values for the rows inserted.
|
336
|
+
# This does not have an effect if +values+ is a Dataset.
|
336
337
|
# :server :: Set the server/shard to use for the transaction and insert
|
337
338
|
# queries.
|
338
339
|
# :slice :: Same as :commit_every, :commit_every takes precedence.
|
@@ -1069,7 +1070,7 @@ module Sequel
|
|
1069
1070
|
|
1070
1071
|
# Set the server to use to :default unless it is already set in the passed opts
|
1071
1072
|
def default_server_opts(opts)
|
1072
|
-
if @db.sharded?
|
1073
|
+
if @db.sharded? && !opts.has_key?(:server)
|
1073
1074
|
opts = Hash[opts]
|
1074
1075
|
opts[:server] = @opts[:server] || :default
|
1075
1076
|
end
|
@@ -1080,7 +1081,7 @@ module Sequel
|
|
1080
1081
|
# :read_only server unless a specific server is set.
|
1081
1082
|
def execute(sql, opts=OPTS, &block)
|
1082
1083
|
db = @db
|
1083
|
-
if db.sharded?
|
1084
|
+
if db.sharded? && !opts.has_key?(:server)
|
1084
1085
|
opts = Hash[opts]
|
1085
1086
|
opts[:server] = @opts[:server] || (@opts[:lock] ? :default : :read_only)
|
1086
1087
|
opts
|
@@ -2,18 +2,21 @@
|
|
2
2
|
#
|
3
3
|
# Allows the use of named timezones via TZInfo (requires tzinfo).
|
4
4
|
# Forces the use of DateTime as Sequel's datetime_class, since
|
5
|
-
#
|
6
|
-
# and UTC.
|
5
|
+
# historically, Ruby's Time class doesn't support timezones other
|
6
|
+
# than local and UTC. To continue using Ruby's Time class when using
|
7
|
+
# the named_timezones extension:
|
8
|
+
#
|
9
|
+
# # Load the extension
|
10
|
+
# Sequel.extension :named_timezones
|
11
|
+
#
|
12
|
+
# # Set Sequel.datetime_class back to Time
|
13
|
+
# Sequel.datetime_class = Time
|
7
14
|
#
|
8
15
|
# This allows you to either pass strings or TZInfo::Timezone
|
9
16
|
# instance to Sequel.database_timezone=, application_timezone=, and
|
10
17
|
# typecast_timezone=. If a string is passed, it is converted to a
|
11
18
|
# TZInfo::Timezone using TZInfo::Timezone.get.
|
12
19
|
#
|
13
|
-
# To load the extension:
|
14
|
-
#
|
15
|
-
# Sequel.extension :named_timezones
|
16
|
-
#
|
17
20
|
# Let's say you have the database server in New York and the
|
18
21
|
# application server in Los Angeles. For historical reasons, data
|
19
22
|
# is stored in local New York time, but the application server only
|
@@ -37,7 +40,8 @@
|
|
37
40
|
# Note that typecasting from the database timezone to the application
|
38
41
|
# timezone when fetching rows is dependent on the database adapter,
|
39
42
|
# and only works on adapters where Sequel itself does the conversion.
|
40
|
-
# It should work
|
43
|
+
# It should work with the mysql, postgres, sqlite, ibmdb, and jdbc
|
44
|
+
# adapters.
|
41
45
|
#
|
42
46
|
# Related module: Sequel::NamedTimezones
|
43
47
|
|
@@ -63,10 +67,48 @@ module Sequel
|
|
63
67
|
|
64
68
|
private
|
65
69
|
|
66
|
-
|
67
|
-
|
70
|
+
if RUBY_VERSION >= '2.6'
|
71
|
+
# Convert the given input Time (which must be in UTC) to the given input timezone,
|
72
|
+
# which should be a TZInfo::Timezone instance.
|
73
|
+
def convert_input_time_other(v, input_timezone)
|
74
|
+
Time.new(v.year, v.mon, v.day, v.hour, v.min, (v.sec + Rational(v.nsec, 1000000000)), input_timezone)
|
75
|
+
rescue TZInfo::AmbiguousTime
|
76
|
+
raise unless disamb = tzinfo_disambiguator_for(v)
|
77
|
+
period = input_timezone.period_for_local(v, &disamb)
|
78
|
+
offset = period.utc_total_offset
|
79
|
+
Time.at(v.to_i - offset, :in => input_timezone)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Convert the given input Time to the given output timezone,
|
83
|
+
# which should be a TZInfo::Timezone instance.
|
84
|
+
def convert_output_time_other(v, output_timezone)
|
85
|
+
Time.at(v.to_i, :in => output_timezone)
|
86
|
+
end
|
87
|
+
else
|
68
88
|
# :nodoc:
|
69
89
|
# :nocov:
|
90
|
+
def convert_input_time_other(v, input_timezone)
|
91
|
+
local_offset = input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
92
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
93
|
+
end
|
94
|
+
|
95
|
+
if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
|
96
|
+
def convert_output_time_other(v, output_timezone)
|
97
|
+
v = output_timezone.utc_to_local(v.getutc)
|
98
|
+
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
99
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i + local_offset
|
100
|
+
end
|
101
|
+
else
|
102
|
+
def convert_output_time_other(v, output_timezone)
|
103
|
+
v = output_timezone.utc_to_local(v.getutc)
|
104
|
+
local_offset = output_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset
|
105
|
+
Time.new(1970, 1, 1, 0, 0, 0, local_offset) + v.to_i
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Handle both TZInfo 1 and TZInfo 2
|
111
|
+
if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
|
70
112
|
def convert_input_datetime_other(v, input_timezone)
|
71
113
|
local_offset = Rational(input_timezone.period_for_local(v, &tzinfo_disambiguator_for(v)).utc_total_offset, 86400)
|
72
114
|
(v - local_offset).new_offset(local_offset)
|
@@ -340,14 +340,18 @@ module Sequel
|
|
340
340
|
raise Sequel::Error, "invalid array, empty string" if eos?
|
341
341
|
raise Sequel::Error, "invalid array, doesn't start with {" unless scan(/((\[\d+:\d+\])+=)?\{/)
|
342
342
|
|
343
|
+
# :nocov:
|
343
344
|
while !eos?
|
345
|
+
# :nocov:
|
344
346
|
char = scan(/[{}",]|[^{}",]+/)
|
345
347
|
if char == ','
|
346
348
|
# Comma outside quoted string indicates end of current entry
|
347
349
|
new_entry
|
348
350
|
elsif char == '"'
|
349
351
|
raise Sequel::Error, "invalid array, opening quote with existing recorded data" unless @recorded.empty?
|
352
|
+
# :nocov:
|
350
353
|
while true
|
354
|
+
# :nocov:
|
351
355
|
char = scan(/["\\]|[^"\\]+/)
|
352
356
|
if char == '\\'
|
353
357
|
@recorded << getch
|
@@ -12,7 +12,7 @@
|
|
12
12
|
#
|
13
13
|
# Note that wrapping JSON primitives changes the behavior for
|
14
14
|
# JSON false and null values. Because only +false+ and +nil+
|
15
|
-
# in Ruby are considered
|
15
|
+
# in Ruby are considered falsey, wrapping these objects results
|
16
16
|
# in unexpected behavior if you use the values directly in
|
17
17
|
# conditionals:
|
18
18
|
#
|
@@ -208,11 +208,19 @@ module Sequel
|
|
208
208
|
JSONB_PRIMITIVE_WRAPPER_MAPPING.freeze
|
209
209
|
|
210
210
|
JSON_COMBINED_WRAPPER_MAPPING =JSON_WRAPPER_MAPPING.merge(JSON_PRIMITIVE_WRAPPER_MAPPING).freeze
|
211
|
-
JSON_WRAP_CLASSES = JSON_COMBINED_WRAPPER_MAPPING.keys.freeze
|
212
|
-
|
213
211
|
JSONB_COMBINED_WRAPPER_MAPPING =JSONB_WRAPPER_MAPPING.merge(JSONB_PRIMITIVE_WRAPPER_MAPPING).freeze
|
214
212
|
JSONB_WRAP_CLASSES = JSONB_COMBINED_WRAPPER_MAPPING.keys.freeze
|
215
213
|
|
214
|
+
Sequel::Deprecation.deprecate_constant(self, :JSON_WRAPPER_MAPPING)
|
215
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_WRAPPER_MAPPING)
|
216
|
+
Sequel::Deprecation.deprecate_constant(self, :JSON_PRIMITIVE_WRAPPER_MAPPING)
|
217
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_PRIMITIVE_WRAPPER_MAPPING)
|
218
|
+
Sequel::Deprecation.deprecate_constant(self, :JSON_COMBINED_WRAPPER_MAPPING)
|
219
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_COMBINED_WRAPPER_MAPPING)
|
220
|
+
Sequel::Deprecation.deprecate_constant(self, :JSONB_WRAP_CLASSES)
|
221
|
+
|
222
|
+
JSON_WRAP_CLASSES = [Hash, Array, String, Integer, Float, NilClass, TrueClass, FalseClass].freeze
|
223
|
+
|
216
224
|
# Methods enabling Database object integration with the json type.
|
217
225
|
module JSONDatabaseMethods
|
218
226
|
def self.extended(db)
|
@@ -228,6 +236,69 @@ module Sequel
|
|
228
236
|
end
|
229
237
|
end
|
230
238
|
|
239
|
+
# Return the wrapper class for the json type if value is Hash or Array.
|
240
|
+
def self.json_wrapper(value)
|
241
|
+
case value
|
242
|
+
when ::Hash
|
243
|
+
JSONHash
|
244
|
+
when ::Array
|
245
|
+
JSONArray
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Return the wrapper class for the jsonb type if value is Hash or Array.
|
250
|
+
def self.jsonb_wrapper(value)
|
251
|
+
case value
|
252
|
+
when ::Hash
|
253
|
+
JSONBHash
|
254
|
+
when ::Array
|
255
|
+
JSONBArray
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# Return the wrapper class for the json type if value is a supported type.
|
260
|
+
def self.json_primitive_wrapper(value)
|
261
|
+
case value
|
262
|
+
when ::Hash
|
263
|
+
JSONHash
|
264
|
+
when ::Array
|
265
|
+
JSONArray
|
266
|
+
when ::String
|
267
|
+
JSONString
|
268
|
+
when ::Integer
|
269
|
+
JSONInteger
|
270
|
+
when ::Float
|
271
|
+
JSONFloat
|
272
|
+
when ::NilClass
|
273
|
+
JSONNull
|
274
|
+
when ::TrueClass
|
275
|
+
JSONTrue
|
276
|
+
when ::FalseClass
|
277
|
+
JSONFalse
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Return the wrapper class for the jsonb type if value is a supported type.
|
282
|
+
def self.jsonb_primitive_wrapper(value)
|
283
|
+
case value
|
284
|
+
when ::Hash
|
285
|
+
JSONBHash
|
286
|
+
when ::Array
|
287
|
+
JSONBArray
|
288
|
+
when ::String
|
289
|
+
JSONBString
|
290
|
+
when ::Integer
|
291
|
+
JSONBInteger
|
292
|
+
when ::Float
|
293
|
+
JSONBFloat
|
294
|
+
when ::NilClass
|
295
|
+
JSONBNull
|
296
|
+
when ::TrueClass
|
297
|
+
JSONBTrue
|
298
|
+
when ::FalseClass
|
299
|
+
JSONBFalse
|
300
|
+
end
|
301
|
+
end
|
231
302
|
|
232
303
|
# Deprecated
|
233
304
|
def self.db_parse_json(s)
|
@@ -326,9 +397,9 @@ module Sequel
|
|
326
397
|
# Wrap the parsed JSON value in the appropriate JSON wrapper class.
|
327
398
|
# Only wrap primitive values if wrap_json_primitives is set.
|
328
399
|
def _wrap_json(value)
|
329
|
-
if klass =
|
400
|
+
if klass = JSONDatabaseMethods.json_wrapper(value)
|
330
401
|
klass.new(value)
|
331
|
-
elsif klass =
|
402
|
+
elsif klass = JSONDatabaseMethods.json_primitive_wrapper(value)
|
332
403
|
if wrap_json_primitives
|
333
404
|
klass.new(value)
|
334
405
|
else
|
@@ -342,9 +413,9 @@ module Sequel
|
|
342
413
|
# Wrap the parsed JSON value in the appropriate JSONB wrapper class.
|
343
414
|
# Only wrap primitive values if wrap_json_primitives is set.
|
344
415
|
def _wrap_jsonb(value)
|
345
|
-
if klass =
|
416
|
+
if klass = JSONDatabaseMethods.jsonb_wrapper(value)
|
346
417
|
klass.new(value)
|
347
|
-
elsif klass =
|
418
|
+
elsif klass = JSONDatabaseMethods.jsonb_primitive_wrapper(value)
|
348
419
|
if wrap_json_primitives
|
349
420
|
klass.new(value)
|
350
421
|
else
|
@@ -413,10 +484,10 @@ module Sequel
|
|
413
484
|
_wrap_json(_parse_json(value))
|
414
485
|
end
|
415
486
|
when *JSON_WRAP_CLASSES
|
416
|
-
|
487
|
+
JSONDatabaseMethods.json_primitive_wrapper(value).new(value)
|
417
488
|
when JSONBObject
|
418
489
|
value = value.__getobj__
|
419
|
-
|
490
|
+
JSONDatabaseMethods.json_primitive_wrapper(value).new(value)
|
420
491
|
else
|
421
492
|
raise Sequel::InvalidValue, "invalid value for json: #{value.inspect}"
|
422
493
|
end
|
@@ -433,11 +504,11 @@ module Sequel
|
|
433
504
|
else
|
434
505
|
_wrap_jsonb(_parse_json(value))
|
435
506
|
end
|
436
|
-
when *
|
437
|
-
|
507
|
+
when *JSON_WRAP_CLASSES
|
508
|
+
JSONDatabaseMethods.jsonb_primitive_wrapper(value).new(value)
|
438
509
|
when JSONObject
|
439
510
|
value = value.__getobj__
|
440
|
-
|
511
|
+
JSONDatabaseMethods.jsonb_primitive_wrapper(value).new(value)
|
441
512
|
else
|
442
513
|
raise Sequel::InvalidValue, "invalid value for jsonb: #{value.inspect}"
|
443
514
|
end
|
@@ -460,7 +531,7 @@ module Sequel
|
|
460
531
|
Postgres::JSONHash.new(v)
|
461
532
|
when Postgres::JSONBObject
|
462
533
|
v = v.__getobj__
|
463
|
-
Postgres::
|
534
|
+
Postgres::JSONDatabaseMethods.json_primitive_wrapper(v).new(v)
|
464
535
|
else
|
465
536
|
Sequel.pg_json_op(v)
|
466
537
|
end
|
@@ -472,7 +543,7 @@ module Sequel
|
|
472
543
|
def pg_json_wrap(v)
|
473
544
|
case v
|
474
545
|
when *Postgres::JSON_WRAP_CLASSES
|
475
|
-
Postgres::
|
546
|
+
Postgres::JSONDatabaseMethods.json_primitive_wrapper(v).new(v)
|
476
547
|
else
|
477
548
|
raise Error, "invalid value passed to Sequel.pg_json_wrap: #{v.inspect}"
|
478
549
|
end
|
@@ -492,7 +563,7 @@ module Sequel
|
|
492
563
|
Postgres::JSONBHash.new(v)
|
493
564
|
when Postgres::JSONObject
|
494
565
|
v = v.__getobj__
|
495
|
-
Postgres::
|
566
|
+
Postgres::JSONDatabaseMethods.jsonb_primitive_wrapper(v).new(v)
|
496
567
|
else
|
497
568
|
Sequel.pg_jsonb_op(v)
|
498
569
|
end
|
@@ -503,8 +574,8 @@ module Sequel
|
|
503
574
|
# other types.
|
504
575
|
def pg_jsonb_wrap(v)
|
505
576
|
case v
|
506
|
-
when *Postgres::
|
507
|
-
Postgres::
|
577
|
+
when *Postgres::JSON_WRAP_CLASSES
|
578
|
+
Postgres::JSONDatabaseMethods.jsonb_primitive_wrapper(v).new(v)
|
508
579
|
else
|
509
580
|
raise Error, "invalid value passed to Sequel.pg_jsonb_wrap: #{v.inspect}"
|
510
581
|
end
|