sequel 5.22.0 → 5.26.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +66 -0
  3. data/README.rdoc +1 -1
  4. data/doc/dataset_filtering.rdoc +15 -0
  5. data/doc/opening_databases.rdoc +3 -0
  6. data/doc/postgresql.rdoc +2 -2
  7. data/doc/release_notes/5.23.0.txt +56 -0
  8. data/doc/release_notes/5.24.0.txt +56 -0
  9. data/doc/release_notes/5.25.0.txt +32 -0
  10. data/doc/release_notes/5.26.0.txt +35 -0
  11. data/doc/testing.rdoc +1 -0
  12. data/lib/sequel/adapters/jdbc.rb +7 -1
  13. data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
  14. data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
  15. data/lib/sequel/adapters/mysql2.rb +0 -1
  16. data/lib/sequel/adapters/shared/mssql.rb +9 -8
  17. data/lib/sequel/adapters/shared/postgres.rb +25 -7
  18. data/lib/sequel/adapters/shared/sqlite.rb +16 -2
  19. data/lib/sequel/adapters/tinytds.rb +12 -0
  20. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  21. data/lib/sequel/database/logging.rb +7 -1
  22. data/lib/sequel/database/schema_generator.rb +11 -2
  23. data/lib/sequel/database/schema_methods.rb +2 -0
  24. data/lib/sequel/dataset/actions.rb +3 -2
  25. data/lib/sequel/extensions/named_timezones.rb +51 -9
  26. data/lib/sequel/extensions/pg_array.rb +4 -0
  27. data/lib/sequel/extensions/pg_json.rb +88 -17
  28. data/lib/sequel/extensions/pg_json_ops.rb +124 -0
  29. data/lib/sequel/extensions/pg_range.rb +9 -0
  30. data/lib/sequel/extensions/pg_row.rb +3 -1
  31. data/lib/sequel/extensions/sql_comments.rb +2 -2
  32. data/lib/sequel/model/base.rb +12 -5
  33. data/lib/sequel/plugins/association_multi_add_remove.rb +83 -0
  34. data/lib/sequel/plugins/association_proxies.rb +3 -2
  35. data/lib/sequel/plugins/caching.rb +3 -0
  36. data/lib/sequel/plugins/class_table_inheritance.rb +10 -0
  37. data/lib/sequel/plugins/csv_serializer.rb +26 -9
  38. data/lib/sequel/plugins/dirty.rb +3 -9
  39. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  40. data/lib/sequel/plugins/nested_attributes.rb +7 -0
  41. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +89 -30
  42. data/lib/sequel/plugins/sharding.rb +11 -5
  43. data/lib/sequel/plugins/static_cache.rb +8 -3
  44. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  45. data/lib/sequel/plugins/typecast_on_load.rb +3 -2
  46. data/lib/sequel/sql.rb +3 -1
  47. data/lib/sequel/timezones.rb +50 -11
  48. data/lib/sequel/version.rb +1 -1
  49. data/spec/adapters/postgres_spec.rb +130 -0
  50. data/spec/bin_spec.rb +2 -2
  51. data/spec/core/database_spec.rb +50 -0
  52. data/spec/core/dataset_spec.rb +23 -1
  53. data/spec/core/expression_filters_spec.rb +7 -2
  54. data/spec/core/schema_spec.rb +18 -0
  55. data/spec/core/spec_helper.rb +1 -1
  56. data/spec/core_extensions_spec.rb +1 -1
  57. data/spec/extensions/association_multi_add_remove_spec.rb +1041 -0
  58. data/spec/extensions/dirty_spec.rb +33 -0
  59. data/spec/extensions/insert_conflict_spec.rb +103 -0
  60. data/spec/extensions/named_timezones_spec.rb +109 -2
  61. data/spec/extensions/nested_attributes_spec.rb +48 -0
  62. data/spec/extensions/pg_auto_constraint_validations_spec.rb +37 -0
  63. data/spec/extensions/pg_json_ops_spec.rb +67 -0
  64. data/spec/extensions/pg_json_spec.rb +12 -0
  65. data/spec/extensions/pg_range_spec.rb +19 -2
  66. data/spec/extensions/sharding_spec.rb +8 -0
  67. data/spec/extensions/spec_helper.rb +9 -2
  68. data/spec/extensions/static_cache_cache_spec.rb +35 -0
  69. data/spec/guards_helper.rb +1 -1
  70. data/spec/integration/plugin_test.rb +27 -0
  71. data/spec/integration/schema_test.rb +16 -2
  72. data/spec/model/spec_helper.rb +1 -1
  73. 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] && (identity = column[:identity])
840
- sql << " GENERATED "
841
- sql << (identity == :always ? "ALWAYS" : "BY DEFAULT")
842
- sql << " AS IDENTITY"
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
- metadata_dataset.with_sql("PRAGMA foreign_key_list(?)", input_identifier_meth.call(table)).each do |row|
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 = metadata_dataset.with_sql("PRAGMA table_info(?)", input_identifier_meth(opts[:dataset]).call(table_name)).map do |row|
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)}
@@ -55,7 +55,7 @@ module Sequel
55
55
  NotNullConstraintViolation
56
56
  when 1062
57
57
  UniqueConstraintViolation
58
- when 1451, 1452
58
+ when 1451, 1452, 1216, 1217
59
59
  ForeignKeyConstraintViolation
60
60
  when 4025
61
61
  CheckConstraintViolation
@@ -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 @loggers.empty?
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
- # ruby's Time class doesn't support timezones other than local
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 on mysql, postgres, sqlite, ibmdb, and jdbc.
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
- # Handle both TZInfo 1 and TZInfo 2
67
- if defined?(TZInfo::VERSION) && TZInfo::VERSION > '2'
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 falesy, wrapping these objects results
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 = JSON_WRAPPER_MAPPING[value.class]
400
+ if klass = JSONDatabaseMethods.json_wrapper(value)
330
401
  klass.new(value)
331
- elsif klass = JSON_PRIMITIVE_WRAPPER_MAPPING[value.class]
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 = JSONB_WRAPPER_MAPPING[value.class]
416
+ if klass = JSONDatabaseMethods.jsonb_wrapper(value)
346
417
  klass.new(value)
347
- elsif klass = JSONB_PRIMITIVE_WRAPPER_MAPPING[value.class]
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
- JSON_COMBINED_WRAPPER_MAPPING[value.class].new(value)
487
+ JSONDatabaseMethods.json_primitive_wrapper(value).new(value)
417
488
  when JSONBObject
418
489
  value = value.__getobj__
419
- JSON_COMBINED_WRAPPER_MAPPING[value.class].new(value)
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 *JSONB_WRAP_CLASSES
437
- JSONB_COMBINED_WRAPPER_MAPPING[value.class].new(value)
507
+ when *JSON_WRAP_CLASSES
508
+ JSONDatabaseMethods.jsonb_primitive_wrapper(value).new(value)
438
509
  when JSONObject
439
510
  value = value.__getobj__
440
- JSONB_COMBINED_WRAPPER_MAPPING[value.class].new(value)
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::JSON_COMBINED_WRAPPER_MAPPING[v.class].new(v)
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::JSON_COMBINED_WRAPPER_MAPPING[v.class].new(v)
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::JSONB_COMBINED_WRAPPER_MAPPING[v.class].new(v)
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::JSONB_WRAP_CLASSES
507
- Postgres::JSONB_COMBINED_WRAPPER_MAPPING[v.class].new(v)
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