sequel 5.22.0 → 5.26.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.
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