sequel 5.80.0 → 5.92.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/bin/sequel +9 -4
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/ibmdb.rb +1 -0
- data/lib/sequel/adapters/jdbc/db2.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +2 -2
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
- data/lib/sequel/adapters/jdbc/oracle.rb +5 -5
- data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
- data/lib/sequel/adapters/jdbc/sqlite.rb +2 -2
- data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
- data/lib/sequel/adapters/jdbc.rb +8 -8
- data/lib/sequel/adapters/mysql2.rb +8 -1
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +18 -5
- data/lib/sequel/adapters/shared/mysql.rb +8 -4
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +106 -13
- data/lib/sequel/adapters/shared/sqlite.rb +4 -2
- data/lib/sequel/adapters/sqlite.rb +4 -0
- data/lib/sequel/adapters/trilogy.rb +1 -2
- data/lib/sequel/connection_pool/sharded_threaded.rb +26 -10
- data/lib/sequel/connection_pool/threaded.rb +26 -10
- data/lib/sequel/connection_pool.rb +2 -2
- data/lib/sequel/core.rb +15 -0
- data/lib/sequel/database/connecting.rb +20 -26
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +46 -10
- data/lib/sequel/database/query.rb +11 -11
- data/lib/sequel/database/schema_generator.rb +8 -0
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +9 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -1
- data/lib/sequel/dataset/query.rb +9 -5
- data/lib/sequel/dataset/sql.rb +25 -5
- data/lib/sequel/extensions/caller_logging.rb +2 -0
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/dataset_run.rb +41 -0
- data/lib/sequel/extensions/migration.rb +23 -3
- data/lib/sequel/extensions/null_dataset.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +1 -1
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +93 -10
- data/lib/sequel/extensions/pg_enum.rb +3 -3
- data/lib/sequel/extensions/pg_json_ops.rb +642 -9
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
- data/lib/sequel/extensions/provenance.rb +2 -0
- data/lib/sequel/extensions/query_blocker.rb +172 -0
- data/lib/sequel/extensions/schema_caching.rb +24 -9
- data/lib/sequel/extensions/schema_dumper.rb +16 -4
- data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
- data/lib/sequel/extensions/stdio_logger.rb +48 -0
- data/lib/sequel/extensions/string_agg.rb +17 -4
- data/lib/sequel/extensions/temporarily_release_connection.rb +178 -0
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +28 -3
- data/lib/sequel/model/base.rb +67 -18
- data/lib/sequel/plugins/association_pks.rb +1 -1
- data/lib/sequel/plugins/column_encryption.rb +1 -1
- data/lib/sequel/plugins/composition.rb +1 -1
- data/lib/sequel/plugins/defaults_setter.rb +16 -4
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/forbid_lazy_load.rb +14 -1
- data/lib/sequel/plugins/input_transformer.rb +1 -1
- data/lib/sequel/plugins/inspect_pk.rb +44 -0
- data/lib/sequel/plugins/instance_filters.rb +4 -1
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +10 -5
- data/lib/sequel/plugins/optimistic_locking.rb +2 -0
- data/lib/sequel/plugins/paged_operations.rb +5 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
- data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
- data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
- data/lib/sequel/plugins/rcte_tree.rb +1 -1
- data/lib/sequel/plugins/serialization.rb +11 -5
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/static_cache_cache.rb +50 -13
- data/lib/sequel/plugins/subset_conditions.rb +85 -5
- data/lib/sequel/plugins/subset_static_cache.rb +263 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +6 -2
- data/lib/sequel/plugins/validate_associated.rb +1 -1
- data/lib/sequel/sql.rb +16 -6
- data/lib/sequel/version.rb +1 -1
- metadata +12 -234
- data/CHANGELOG +0 -1355
- data/README.rdoc +0 -936
- data/doc/advanced_associations.rdoc +0 -884
- data/doc/association_basics.rdoc +0 -1859
- data/doc/bin_sequel.rdoc +0 -146
- data/doc/cheat_sheet.rdoc +0 -255
- data/doc/code_order.rdoc +0 -102
- data/doc/core_extensions.rdoc +0 -405
- data/doc/dataset_basics.rdoc +0 -96
- data/doc/dataset_filtering.rdoc +0 -222
- data/doc/extensions.rdoc +0 -77
- data/doc/fork_safety.rdoc +0 -84
- data/doc/mass_assignment.rdoc +0 -98
- data/doc/migration.rdoc +0 -660
- data/doc/model_dataset_method_design.rdoc +0 -129
- data/doc/model_hooks.rdoc +0 -254
- data/doc/model_plugins.rdoc +0 -270
- data/doc/mssql_stored_procedures.rdoc +0 -43
- data/doc/object_model.rdoc +0 -563
- data/doc/opening_databases.rdoc +0 -436
- data/doc/postgresql.rdoc +0 -611
- data/doc/prepared_statements.rdoc +0 -144
- data/doc/querying.rdoc +0 -1070
- data/doc/reflection.rdoc +0 -120
- data/doc/release_notes/5.0.0.txt +0 -159
- data/doc/release_notes/5.1.0.txt +0 -31
- data/doc/release_notes/5.10.0.txt +0 -84
- data/doc/release_notes/5.11.0.txt +0 -83
- data/doc/release_notes/5.12.0.txt +0 -141
- data/doc/release_notes/5.13.0.txt +0 -27
- data/doc/release_notes/5.14.0.txt +0 -63
- data/doc/release_notes/5.15.0.txt +0 -39
- data/doc/release_notes/5.16.0.txt +0 -110
- data/doc/release_notes/5.17.0.txt +0 -31
- data/doc/release_notes/5.18.0.txt +0 -69
- data/doc/release_notes/5.19.0.txt +0 -28
- data/doc/release_notes/5.2.0.txt +0 -33
- data/doc/release_notes/5.20.0.txt +0 -89
- data/doc/release_notes/5.21.0.txt +0 -87
- data/doc/release_notes/5.22.0.txt +0 -48
- data/doc/release_notes/5.23.0.txt +0 -56
- data/doc/release_notes/5.24.0.txt +0 -56
- data/doc/release_notes/5.25.0.txt +0 -32
- data/doc/release_notes/5.26.0.txt +0 -35
- data/doc/release_notes/5.27.0.txt +0 -21
- data/doc/release_notes/5.28.0.txt +0 -16
- data/doc/release_notes/5.29.0.txt +0 -22
- data/doc/release_notes/5.3.0.txt +0 -121
- data/doc/release_notes/5.30.0.txt +0 -20
- data/doc/release_notes/5.31.0.txt +0 -148
- data/doc/release_notes/5.32.0.txt +0 -46
- data/doc/release_notes/5.33.0.txt +0 -24
- data/doc/release_notes/5.34.0.txt +0 -40
- data/doc/release_notes/5.35.0.txt +0 -56
- data/doc/release_notes/5.36.0.txt +0 -60
- data/doc/release_notes/5.37.0.txt +0 -30
- data/doc/release_notes/5.38.0.txt +0 -28
- data/doc/release_notes/5.39.0.txt +0 -19
- data/doc/release_notes/5.4.0.txt +0 -80
- data/doc/release_notes/5.40.0.txt +0 -40
- data/doc/release_notes/5.41.0.txt +0 -25
- data/doc/release_notes/5.42.0.txt +0 -136
- data/doc/release_notes/5.43.0.txt +0 -98
- data/doc/release_notes/5.44.0.txt +0 -32
- data/doc/release_notes/5.45.0.txt +0 -34
- data/doc/release_notes/5.46.0.txt +0 -87
- data/doc/release_notes/5.47.0.txt +0 -59
- data/doc/release_notes/5.48.0.txt +0 -14
- data/doc/release_notes/5.49.0.txt +0 -59
- data/doc/release_notes/5.5.0.txt +0 -61
- data/doc/release_notes/5.50.0.txt +0 -78
- data/doc/release_notes/5.51.0.txt +0 -47
- data/doc/release_notes/5.52.0.txt +0 -87
- data/doc/release_notes/5.53.0.txt +0 -23
- data/doc/release_notes/5.54.0.txt +0 -27
- data/doc/release_notes/5.55.0.txt +0 -21
- data/doc/release_notes/5.56.0.txt +0 -51
- data/doc/release_notes/5.57.0.txt +0 -23
- data/doc/release_notes/5.58.0.txt +0 -31
- data/doc/release_notes/5.59.0.txt +0 -73
- data/doc/release_notes/5.6.0.txt +0 -31
- data/doc/release_notes/5.60.0.txt +0 -22
- data/doc/release_notes/5.61.0.txt +0 -43
- data/doc/release_notes/5.62.0.txt +0 -132
- data/doc/release_notes/5.63.0.txt +0 -33
- data/doc/release_notes/5.64.0.txt +0 -50
- data/doc/release_notes/5.65.0.txt +0 -21
- data/doc/release_notes/5.66.0.txt +0 -24
- data/doc/release_notes/5.67.0.txt +0 -32
- data/doc/release_notes/5.68.0.txt +0 -61
- data/doc/release_notes/5.69.0.txt +0 -26
- data/doc/release_notes/5.7.0.txt +0 -108
- data/doc/release_notes/5.70.0.txt +0 -35
- data/doc/release_notes/5.71.0.txt +0 -21
- data/doc/release_notes/5.72.0.txt +0 -33
- data/doc/release_notes/5.73.0.txt +0 -66
- data/doc/release_notes/5.74.0.txt +0 -45
- data/doc/release_notes/5.75.0.txt +0 -35
- data/doc/release_notes/5.76.0.txt +0 -86
- data/doc/release_notes/5.77.0.txt +0 -63
- data/doc/release_notes/5.78.0.txt +0 -67
- data/doc/release_notes/5.79.0.txt +0 -28
- data/doc/release_notes/5.8.0.txt +0 -170
- data/doc/release_notes/5.80.0.txt +0 -40
- data/doc/release_notes/5.9.0.txt +0 -99
- data/doc/schema_modification.rdoc +0 -679
- data/doc/security.rdoc +0 -443
- data/doc/sharding.rdoc +0 -286
- data/doc/sql.rdoc +0 -648
- data/doc/testing.rdoc +0 -190
- data/doc/thread_safety.rdoc +0 -15
- data/doc/transactions.rdoc +0 -250
- data/doc/validations.rdoc +0 -558
- data/doc/virtual_rows.rdoc +0 -265
@@ -88,6 +88,12 @@
|
|
88
88
|
# j.path_query_array('$.foo') # jsonb_path_query_array(jsonb_column, '$.foo')
|
89
89
|
# j.path_query_first('$.foo') # jsonb_path_query_first(jsonb_column, '$.foo')
|
90
90
|
#
|
91
|
+
# For the PostgreSQL 12+ SQL/JSON path functions, one argument is required (+path+) and
|
92
|
+
# two more arguments are optional (+vars+ and +silent+). +path+ specifies the JSON path.
|
93
|
+
# +vars+ specifies a hash or a string in JSON format of named variables to be
|
94
|
+
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
95
|
+
# errors are not suppressed.
|
96
|
+
#
|
91
97
|
# On PostgreSQL 13+ timezone-aware SQL/JSON path functions and operators are supported:
|
92
98
|
#
|
93
99
|
# j.path_exists_tz!('$.foo') # jsonb_path_exists_tz(jsonb_column, '$.foo')
|
@@ -96,12 +102,6 @@
|
|
96
102
|
# j.path_query_array_tz('$.foo') # jsonb_path_query_array_tz(jsonb_column, '$.foo')
|
97
103
|
# j.path_query_first_tz('$.foo') # jsonb_path_query_first_tz(jsonb_column, '$.foo')
|
98
104
|
#
|
99
|
-
# For the PostgreSQL 12+ SQL/JSON path functions, one argument is required (+path+) and
|
100
|
-
# two more arguments are optional (+vars+ and +silent+). +path+ specifies the JSON path.
|
101
|
-
# +vars+ specifies a hash or a string in JSON format of named variables to be
|
102
|
-
# substituted in +path+. +silent+ specifies whether errors are suppressed. By default,
|
103
|
-
# errors are not suppressed.
|
104
|
-
#
|
105
105
|
# On PostgreSQL 14+, The JSONB <tt>[]</tt> method will use subscripts instead of being
|
106
106
|
# the same as +get+, if the value being wrapped is an identifer:
|
107
107
|
#
|
@@ -129,8 +129,42 @@
|
|
129
129
|
# j.is_json(type: :object) # j IS JSON OBJECT
|
130
130
|
# j.is_json(type: :object, unique: true) # j IS JSON OBJECT WITH UNIQUE
|
131
131
|
# j.is_not_json # j IS NOT JSON
|
132
|
-
# j.is_not_json(type: :array)
|
133
|
-
# j.is_not_json(unique: true)
|
132
|
+
# j.is_not_json(type: :array) # j IS NOT JSON ARRAY
|
133
|
+
# j.is_not_json(unique: true) # j IS NOT JSON WITH UNIQUE
|
134
|
+
#
|
135
|
+
# On PostgreSQL 17+, the additional JSON functions are supported (see method documentation
|
136
|
+
# for additional options):
|
137
|
+
#
|
138
|
+
# j.exists('$.foo') # json_exists(jsonb_column, '$.foo')
|
139
|
+
# j.value('$.foo') # json_value(jsonb_column, '$.foo')
|
140
|
+
# j.query('$.foo') # json_query(jsonb_column, '$.foo')
|
141
|
+
#
|
142
|
+
# j.exists('$.foo', passing: {a: 1}) # json_exists(jsonb_column, '$.foo' PASSING 1 AS a)
|
143
|
+
# j.value('$.foo', returning: Time) # json_value(jsonb_column, '$.foo' RETURNING timestamp)
|
144
|
+
# j.query('$.foo', wrapper: true) # json_query(jsonb_column, '$.foo' WITH WRAPPER)
|
145
|
+
#
|
146
|
+
# j.table('$.foo') do
|
147
|
+
# String :bar
|
148
|
+
# Integer :baz
|
149
|
+
# end
|
150
|
+
# # json_table("jsonb_column", '$.foo' COLUMNS("bar" text, "baz" integer))
|
151
|
+
#
|
152
|
+
# j.table('$.foo', passing: {a: 1}) do
|
153
|
+
# ordinality :id
|
154
|
+
# String :bar, format: :json, on_error: :empty_object
|
155
|
+
# nested '$.baz' do
|
156
|
+
# Integer :q, path: '$.quux', on_empty: :error
|
157
|
+
# end
|
158
|
+
# exists :x, Date, on_error: false
|
159
|
+
# end
|
160
|
+
# # json_table(jsonb_column, '$.foo' PASSING 1 AS a COLUMNS(
|
161
|
+
# # "id" FOR ORDINALITY,
|
162
|
+
# # "bar" text FORMAT JSON EMPTY OBJECT ON ERROR,
|
163
|
+
# # NESTED '$.baz' COLUMNS(
|
164
|
+
# # "q" integer PATH '$.quux' ERROR ON EMPTY
|
165
|
+
# # ),
|
166
|
+
# # "d" date EXISTS FALSE ON ERROR
|
167
|
+
# # ))
|
134
168
|
#
|
135
169
|
# If you are also using the pg_json extension, you should load it before
|
136
170
|
# loading this extension. Doing so will allow you to use the #op method on
|
@@ -224,7 +258,25 @@ module Sequel
|
|
224
258
|
function(:each_text)
|
225
259
|
end
|
226
260
|
|
227
|
-
#
|
261
|
+
# Return whether the given JSON path yields any items in the receiver.
|
262
|
+
# Options:
|
263
|
+
#
|
264
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression.
|
265
|
+
# true :: Return true
|
266
|
+
# false :: Return false (default behavior)
|
267
|
+
# :null :: Return nil
|
268
|
+
# :error :: raise a DatabaseError
|
269
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
270
|
+
# names, values are the values of the variable.
|
271
|
+
#
|
272
|
+
# json_op.exists("$.a") # json_exists(json, '$.a')
|
273
|
+
# json_op.exists("$.a", passing: {a: 1}) # json_exists(json, '$.a' PASSING 1 AS a)
|
274
|
+
# json_op.exists("$.a", on_error: :error) # json_exists(json, '$.a' ERROR ON ERROR)
|
275
|
+
def exists(path, opts=OPTS)
|
276
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, JSONExistsOp.new(self, path, opts))
|
277
|
+
end
|
278
|
+
|
279
|
+
# Returns a JSON value for the object at the given path.
|
228
280
|
#
|
229
281
|
# json_op.extract('a') # json_extract_path(json, 'a')
|
230
282
|
# json_op.extract('a', 'b') # json_extract_path(json, 'a', 'b')
|
@@ -299,6 +351,35 @@ module Sequel
|
|
299
351
|
SQL::Function.new(function_name(:populate_recordset), arg, self)
|
300
352
|
end
|
301
353
|
|
354
|
+
# Return the result of applying the JSON path expression to the receiver, by default
|
355
|
+
# returning results as jsonb. Options:
|
356
|
+
#
|
357
|
+
# :on_empty :: How to handle case where path expression yields an empty set.
|
358
|
+
# Uses same values as :on_error option.
|
359
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression:
|
360
|
+
# :null :: Return nil (default)
|
361
|
+
# :empty_array :: Return an empty array
|
362
|
+
# :empty_object :: Return an empty object
|
363
|
+
# :error :: raise a DatabaseError
|
364
|
+
# any other value :: used as default value
|
365
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
366
|
+
# names, values are the values of the variable.
|
367
|
+
# :returning :: The data type to return (jsonb by default)
|
368
|
+
# :wrapper :: How to wrap returned values:
|
369
|
+
# true, :unconditional :: Always wrap returning values in an array
|
370
|
+
# :conditional :: Only wrap multiple return values in an array
|
371
|
+
# :omit_quotes :: Do not wrap scalar strings in quotes
|
372
|
+
#
|
373
|
+
# json_op.query("$.a") # json_query(json, '$.a')
|
374
|
+
# json_op.query("$.a", passing: {a: 1}) # json_query(json, '$.a' PASSING 1 AS a)
|
375
|
+
# json_op.query("$.a", on_error: :empty_array) # json_query(json, '$.a' EMPTY ARRAY ON ERROR)
|
376
|
+
# json_op.query("$.a", returning: Time) # json_query(json, '$.a' RETURNING timestamp)
|
377
|
+
# json_op.query("$.a", on_empty: 2) # json_query(json, '$.a' DEFAULT 2 ON EMPTY)
|
378
|
+
# json_op.query("$.a", wrapper: true) # json_query(json, '$.a' WITH WRAPPER)
|
379
|
+
def query(path, opts=OPTS)
|
380
|
+
self.class.new(JSONQueryOp.new(self, path, opts))
|
381
|
+
end
|
382
|
+
|
302
383
|
# Returns a json value stripped of all internal null values.
|
303
384
|
#
|
304
385
|
# json_op.strip_nulls # json_strip_nulls(json)
|
@@ -306,6 +387,72 @@ module Sequel
|
|
306
387
|
self.class.new(function(:strip_nulls))
|
307
388
|
end
|
308
389
|
|
390
|
+
# Returns json_table SQL function expression, querying JSON data and returning
|
391
|
+
# the results as a relational view, which can be accessed similarly to a regular
|
392
|
+
# SQL table. This accepts a block that is handled in a similar manner to
|
393
|
+
# Database#create_table, though it operates differently.
|
394
|
+
#
|
395
|
+
# Table level options:
|
396
|
+
#
|
397
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression.
|
398
|
+
# :empty_array :: Return an empty array/result set
|
399
|
+
# :error :: raise a DatabaseError
|
400
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
401
|
+
# names, values are the values of the variable.
|
402
|
+
#
|
403
|
+
# Inside the block, the following methods can be used:
|
404
|
+
#
|
405
|
+
# ordinality(name) :: Include a FOR ORDINALITY column, which operates similar to an
|
406
|
+
# autoincrementing primary key.
|
407
|
+
# column(name, type, opts={}) :: Return a normal column that uses the given type.
|
408
|
+
# exists(name, type, opts={}) :: Return a boolean column for whether the JSON path yields any values.
|
409
|
+
# nested(path, &block) :: Extract nested data from the result set at the given path.
|
410
|
+
# This block is treated the same as a json_table block, and
|
411
|
+
# arbitrary levels of nesting are supported.
|
412
|
+
#
|
413
|
+
# The +column+ method supports the following options:
|
414
|
+
#
|
415
|
+
# :path :: JSON path to the object (the default is <tt>$.NAME</tt>, where +NAME+ is the
|
416
|
+
# name of the column).
|
417
|
+
# :format :: Set to +:json+ to use FORMAT JSON, when you expect the value to be a
|
418
|
+
# valid JSON object.
|
419
|
+
# :on_empty, :on_error :: How to handle case where JSON path evaluation is empty or
|
420
|
+
# results in an error. Values supported are:
|
421
|
+
# :empty_array :: Return empty array (requires <tt>format: :json</tt>)
|
422
|
+
# :empty_object :: Return empty object (requires <tt>format: :json</tt>)
|
423
|
+
# :error :: Raise a DatabaseError
|
424
|
+
# :null :: Return nil (NULL)
|
425
|
+
# :wrapper :: How to wrap returned values:
|
426
|
+
# true, :unconditional :: Always wrap returning values in an array
|
427
|
+
# :conditional :: Only wrap multiple return values in an array
|
428
|
+
# :keep_quotes :: Wrap scalar strings in quotes
|
429
|
+
# :omit_quotes :: Do not wrap scalar strings in quotes
|
430
|
+
#
|
431
|
+
# The +exists+ method supports the following options:
|
432
|
+
#
|
433
|
+
# :path :: JSON path to the object (same as +column+ option)
|
434
|
+
# :on_error :: How to handle case where JSON path evaluation results in an error.
|
435
|
+
# Values supported are:
|
436
|
+
# :error :: Raise a DatabaseError
|
437
|
+
# true :: Return true
|
438
|
+
# false :: Return false
|
439
|
+
# :null :: Return nil (NULL)
|
440
|
+
#
|
441
|
+
# Inside the block, methods for Ruby class names are also supported, allowing you
|
442
|
+
# to use syntax such as:
|
443
|
+
#
|
444
|
+
# json_op.table('$.a') do
|
445
|
+
# String :b
|
446
|
+
# Integer :c, path: '$.d'
|
447
|
+
# end
|
448
|
+
#
|
449
|
+
# One difference between this method and Database#create_table is that method_missing
|
450
|
+
# is not supported inside the block. Use the +column+ method for PostgreSQL types
|
451
|
+
# that are not mapped to Ruby classes.
|
452
|
+
def table(path, opts=OPTS, &block)
|
453
|
+
JSONTableOp.new(self, path, opts, &block)
|
454
|
+
end
|
455
|
+
|
309
456
|
# Builds arbitrary record from json object. You need to define the
|
310
457
|
# structure of the record using #as on the resulting object:
|
311
458
|
#
|
@@ -329,6 +476,34 @@ module Sequel
|
|
329
476
|
function(:typeof)
|
330
477
|
end
|
331
478
|
|
479
|
+
# If called without arguments, operates as SQL::Wrapper#value. Otherwise,
|
480
|
+
# return the result of applying the JSON path expression to the receiver, by default
|
481
|
+
# returning results as text. Options:
|
482
|
+
#
|
483
|
+
# :on_empty :: How to handle case where path expression yields an empty set.
|
484
|
+
# Uses same values as :on_error option.
|
485
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression.
|
486
|
+
# :null :: Return nil (default)
|
487
|
+
# :error :: raise a DatabaseError
|
488
|
+
# any other value :: used as default value
|
489
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
490
|
+
# names, values are the values of the variable.
|
491
|
+
# :returning :: The data type to return (text by default)
|
492
|
+
#
|
493
|
+
# json_op.value("$.a") # json_value(json, '$.a')
|
494
|
+
# json_op.value("$.a", passing: {a: 1}) # json_value(json, '$.a' PASSING 1 AS a)
|
495
|
+
# json_op.value("$.a", on_error: :error) # json_value(json, '$.a' ERROR ON ERROR)
|
496
|
+
# json_op.value("$.a", returning: Time) # json_value(json, '$.a' RETURNING timestamp)
|
497
|
+
# json_op.value("$.a", on_empty: 2) # json_value(json, '$.a' DEFAULT 2 ON EMPTY)
|
498
|
+
def value(path=(no_args_given = true), opts=OPTS)
|
499
|
+
if no_args_given
|
500
|
+
# Act as SQL::Wrapper#value
|
501
|
+
super()
|
502
|
+
else
|
503
|
+
Sequel::SQL::StringExpression.new(:NOOP, JSONValueOp.new(self, path, opts))
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
332
507
|
private
|
333
508
|
|
334
509
|
# Internals of IS [NOT] JSON support
|
@@ -705,6 +880,464 @@ module Sequel
|
|
705
880
|
end
|
706
881
|
end
|
707
882
|
|
883
|
+
# Object representing json_exists calls
|
884
|
+
class JSONExistsOp < SQL::Expression
|
885
|
+
ON_ERROR_SQL = {
|
886
|
+
true => 'TRUE',
|
887
|
+
false => 'FALSE',
|
888
|
+
:null => 'UNKNOWN',
|
889
|
+
:error => 'ERROR',
|
890
|
+
}.freeze
|
891
|
+
private_constant :ON_ERROR_SQL
|
892
|
+
|
893
|
+
# Expression (context_item in PostgreSQL terms), usually JSONBaseOp instance
|
894
|
+
attr_reader :expr
|
895
|
+
|
896
|
+
# JSON path expression to apply against the expression
|
897
|
+
attr_reader :path
|
898
|
+
|
899
|
+
# Variables to set in the JSON path expression
|
900
|
+
attr_reader :passing
|
901
|
+
|
902
|
+
# How to handle errors when evaluating the JSON path expression
|
903
|
+
attr_reader :on_error
|
904
|
+
|
905
|
+
# See JSONBaseOp#exists for documentation on the options.
|
906
|
+
def initialize(expr, path, opts=OPTS)
|
907
|
+
@expr = expr
|
908
|
+
@path = path
|
909
|
+
@passing = opts[:passing]
|
910
|
+
@on_error = opts[:on_error]
|
911
|
+
freeze
|
912
|
+
end
|
913
|
+
|
914
|
+
# Append the SQL function call expression to the SQL
|
915
|
+
def to_s_append(ds, sql)
|
916
|
+
to_s_append_function_name(ds, sql)
|
917
|
+
to_s_append_args_passing(ds, sql)
|
918
|
+
to_s_append_on_error(ds, sql)
|
919
|
+
sql << ')'
|
920
|
+
end
|
921
|
+
|
922
|
+
# Support transforming of function call expression
|
923
|
+
def sequel_ast_transform(transformer)
|
924
|
+
opts = {}
|
925
|
+
transform_opts(transformer, opts)
|
926
|
+
self.class.new(transformer.call(@expr), @path, opts)
|
927
|
+
end
|
928
|
+
|
929
|
+
private
|
930
|
+
|
931
|
+
# Set the :passing and :on_error options when doing an
|
932
|
+
# AST transform.
|
933
|
+
def transform_opts(transformer, opts)
|
934
|
+
if @passing
|
935
|
+
passing = opts[:passing] = {}
|
936
|
+
@passing.each do |k, v|
|
937
|
+
passing[k] = transformer.call(v)
|
938
|
+
end
|
939
|
+
end
|
940
|
+
|
941
|
+
opts[:on_error] = @on_error
|
942
|
+
end
|
943
|
+
|
944
|
+
def to_s_append_function_name(ds, sql)
|
945
|
+
sql << 'json_exists('
|
946
|
+
end
|
947
|
+
|
948
|
+
# Append the expression, path, and optional PASSING fragments
|
949
|
+
def to_s_append_args_passing(ds, sql)
|
950
|
+
ds.literal_append(sql, @expr)
|
951
|
+
sql << ', '
|
952
|
+
ds.literal_append(sql, @path)
|
953
|
+
|
954
|
+
if (passing = @passing) && !passing.empty?
|
955
|
+
sql << ' PASSING '
|
956
|
+
comma = false
|
957
|
+
passing.each do |k, v|
|
958
|
+
if comma
|
959
|
+
sql << ', '
|
960
|
+
else
|
961
|
+
comma = true
|
962
|
+
end
|
963
|
+
ds.literal_append(sql, v)
|
964
|
+
sql << " AS " << k.to_s
|
965
|
+
end
|
966
|
+
end
|
967
|
+
end
|
968
|
+
|
969
|
+
# Append the optional ON ERROR fragments
|
970
|
+
def to_s_append_on_error(ds, sql)
|
971
|
+
unless @on_error.nil?
|
972
|
+
sql << " "
|
973
|
+
to_s_append_on_value(ds, sql, @on_error)
|
974
|
+
sql << " ON ERROR"
|
975
|
+
end
|
976
|
+
end
|
977
|
+
|
978
|
+
# Append the value to use for ON ERROR
|
979
|
+
def to_s_append_on_value(ds, sql, value)
|
980
|
+
sql << ON_ERROR_SQL.fetch(value)
|
981
|
+
end
|
982
|
+
end
|
983
|
+
|
984
|
+
# Object representing json_value calls
|
985
|
+
class JSONValueOp < JSONExistsOp
|
986
|
+
ON_SQL = {
|
987
|
+
:null => 'NULL',
|
988
|
+
:error => 'ERROR',
|
989
|
+
}.freeze
|
990
|
+
private_constant :ON_SQL
|
991
|
+
|
992
|
+
# The database type to cast returned values to
|
993
|
+
attr_reader :returning
|
994
|
+
|
995
|
+
# How to handle cases where the JSON path expression evaluation yields
|
996
|
+
# an empty set.
|
997
|
+
attr_reader :on_empty
|
998
|
+
|
999
|
+
# See JSONBaseOp#value for documentation of the options.
|
1000
|
+
def initialize(expr, path, opts=OPTS)
|
1001
|
+
@returning = opts[:returning]
|
1002
|
+
@on_empty = opts[:on_empty]
|
1003
|
+
super
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
private
|
1007
|
+
|
1008
|
+
# Also handle transforming the returning and on_empty options.
|
1009
|
+
def transform_opts(transformer, opts)
|
1010
|
+
super
|
1011
|
+
opts[:returning] = @returning
|
1012
|
+
on_error = @on_error
|
1013
|
+
on_error = transformer.call(on_error) unless on_sql_value(on_error)
|
1014
|
+
opts[:on_error] = on_error
|
1015
|
+
on_empty = @on_empty
|
1016
|
+
on_empty = transformer.call(on_empty) unless on_sql_value(on_empty)
|
1017
|
+
opts[:on_empty] = on_empty
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def to_s_append_function_name(ds, sql)
|
1021
|
+
sql << 'json_value('
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
# Also append the optional RETURNING fragment
|
1025
|
+
def to_s_append_args_passing(ds, sql)
|
1026
|
+
super
|
1027
|
+
|
1028
|
+
if @returning
|
1029
|
+
sql << ' RETURNING ' << ds.db.cast_type_literal(@returning).to_s
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
# Also append the optional ON EMPTY fragment
|
1034
|
+
def to_s_append_on_error(ds, sql)
|
1035
|
+
unless @on_empty.nil?
|
1036
|
+
sql << " "
|
1037
|
+
to_s_append_on_value(ds, sql, @on_empty)
|
1038
|
+
sql << " ON EMPTY"
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
super
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
# Handle DEFAULT values in ON EMPTY/ON ERROR fragments
|
1045
|
+
def to_s_append_on_value(ds, sql, value)
|
1046
|
+
if v = on_sql_value(value)
|
1047
|
+
sql << v
|
1048
|
+
else
|
1049
|
+
sql << 'DEFAULT '
|
1050
|
+
default_literal_append(ds, sql, value)
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
# Do not auto paramterize default value, as PostgreSQL doesn't allow it.
|
1055
|
+
def default_literal_append(ds, sql, v)
|
1056
|
+
if sql.respond_to?(:skip_auto_param)
|
1057
|
+
sql.skip_auto_param do
|
1058
|
+
ds.literal_append(sql, v)
|
1059
|
+
end
|
1060
|
+
else
|
1061
|
+
ds.literal_append(sql, v)
|
1062
|
+
end
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
def on_sql_value(value)
|
1066
|
+
ON_SQL[value]
|
1067
|
+
end
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
# Object representing json_query calls
|
1071
|
+
class JSONQueryOp < JSONValueOp
|
1072
|
+
ON_SQL = {
|
1073
|
+
:null => 'NULL',
|
1074
|
+
:error => 'ERROR',
|
1075
|
+
:empty_array => 'EMPTY ARRAY',
|
1076
|
+
:empty_object => 'EMPTY OBJECT',
|
1077
|
+
}.freeze
|
1078
|
+
private_constant :ON_SQL
|
1079
|
+
|
1080
|
+
WRAPPER = {
|
1081
|
+
:conditional => ' WITH CONDITIONAL WRAPPER',
|
1082
|
+
:unconditional => ' WITH WRAPPER',
|
1083
|
+
:omit_quotes => ' OMIT QUOTES'
|
1084
|
+
}
|
1085
|
+
WRAPPER[true] = WRAPPER[:unconditional]
|
1086
|
+
WRAPPER.freeze
|
1087
|
+
private_constant :WRAPPER
|
1088
|
+
|
1089
|
+
# How to handle wrapping of results
|
1090
|
+
attr_reader :wrapper
|
1091
|
+
|
1092
|
+
# See JSONBaseOp#query for documentation of the options.
|
1093
|
+
def initialize(expr, path, opts=OPTS)
|
1094
|
+
@wrapper = opts[:wrapper]
|
1095
|
+
super
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
private
|
1099
|
+
|
1100
|
+
# Also handle transforming the wrapper option
|
1101
|
+
def transform_opts(transformer, opts)
|
1102
|
+
super
|
1103
|
+
opts[:wrapper] = @wrapper
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
def to_s_append_function_name(ds, sql)
|
1107
|
+
sql << 'json_query('
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
# Also append the optional WRAPPER/OMIT QUOTES fragment
|
1111
|
+
def to_s_append_args_passing(ds, sql)
|
1112
|
+
super
|
1113
|
+
|
1114
|
+
if @wrapper
|
1115
|
+
sql << WRAPPER.fetch(@wrapper)
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
def on_sql_value(value)
|
1120
|
+
ON_SQL[value]
|
1121
|
+
end
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
# Object representing json_table calls
|
1125
|
+
class JSONTableOp < SQL::Expression
|
1126
|
+
TABLE_ON_ERROR_SQL = {
|
1127
|
+
:error => ' ERROR ON ERROR',
|
1128
|
+
:empty_array => ' EMPTY ARRAY ON ERROR',
|
1129
|
+
}.freeze
|
1130
|
+
private_constant :TABLE_ON_ERROR_SQL
|
1131
|
+
|
1132
|
+
COLUMN_ON_SQL = {
|
1133
|
+
:null => ' NULL',
|
1134
|
+
:error => ' ERROR',
|
1135
|
+
:empty_array => ' EMPTY ARRAY',
|
1136
|
+
:empty_object => ' EMPTY OBJECT',
|
1137
|
+
}.freeze
|
1138
|
+
private_constant :COLUMN_ON_SQL
|
1139
|
+
|
1140
|
+
EXISTS_ON_ERROR_SQL = {
|
1141
|
+
:error => ' ERROR',
|
1142
|
+
true => ' TRUE',
|
1143
|
+
false => ' FALSE',
|
1144
|
+
:null => ' UNKNOWN',
|
1145
|
+
}.freeze
|
1146
|
+
private_constant :EXISTS_ON_ERROR_SQL
|
1147
|
+
|
1148
|
+
WRAPPER = {
|
1149
|
+
:conditional => ' WITH CONDITIONAL WRAPPER',
|
1150
|
+
:unconditional => ' WITH WRAPPER',
|
1151
|
+
:omit_quotes => ' OMIT QUOTES',
|
1152
|
+
:keep_quotes => ' KEEP QUOTES',
|
1153
|
+
}
|
1154
|
+
WRAPPER[true] = WRAPPER[:unconditional]
|
1155
|
+
WRAPPER.freeze
|
1156
|
+
private_constant :WRAPPER
|
1157
|
+
|
1158
|
+
# Class used to evaluate json_table blocks and nested blocks
|
1159
|
+
class ColumnDSL
|
1160
|
+
# Return array of column information recorded for the instance
|
1161
|
+
attr_reader :columns
|
1162
|
+
|
1163
|
+
def self.columns(&block)
|
1164
|
+
new(&block).columns.freeze
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
def initialize(&block)
|
1168
|
+
@columns = []
|
1169
|
+
instance_exec(&block)
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
# Include a FOR ORDINALITY column
|
1173
|
+
def ordinality(name)
|
1174
|
+
@columns << [:ordinality, name].freeze
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
# Include a regular column with the given type
|
1178
|
+
def column(name, type, opts=OPTS)
|
1179
|
+
@columns << [:column, name, type, opts].freeze
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
# Include an EXISTS column with the given type
|
1183
|
+
def exists(name, type, opts=OPTS)
|
1184
|
+
@columns << [:exists, name, type, opts].freeze
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
# Include a nested set of columns at the given path.
|
1188
|
+
def nested(path, &block)
|
1189
|
+
@columns << [:nested, path, ColumnDSL.columns(&block)].freeze
|
1190
|
+
end
|
1191
|
+
|
1192
|
+
# Include a bigint column
|
1193
|
+
def Bignum(name, opts=OPTS)
|
1194
|
+
@columns << [:column, name, :Bignum, opts].freeze
|
1195
|
+
end
|
1196
|
+
|
1197
|
+
# Define methods for handling other generic types
|
1198
|
+
%w'String Integer Float Numeric BigDecimal Date DateTime Time File TrueClass FalseClass'.each do |meth|
|
1199
|
+
klass = Object.const_get(meth)
|
1200
|
+
define_method(meth) do |name, opts=OPTS|
|
1201
|
+
@columns << [:column, name, klass, opts].freeze
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
end
|
1205
|
+
private_constant :ColumnDSL
|
1206
|
+
|
1207
|
+
# See JSONBaseOp#table for documentation on the options.
|
1208
|
+
def initialize(expr, path, opts=OPTS, &block)
|
1209
|
+
@expr = expr
|
1210
|
+
@path = path
|
1211
|
+
@passing = opts[:passing]
|
1212
|
+
@on_error = opts[:on_error]
|
1213
|
+
@columns = opts[:_columns] || ColumnDSL.columns(&block)
|
1214
|
+
freeze
|
1215
|
+
end
|
1216
|
+
|
1217
|
+
# Append the json_table function call expression to the SQL
|
1218
|
+
def to_s_append(ds, sql)
|
1219
|
+
sql << 'json_table('
|
1220
|
+
ds.literal_append(sql, @expr)
|
1221
|
+
sql << ', '
|
1222
|
+
default_literal_append(ds, sql, @path)
|
1223
|
+
|
1224
|
+
if (passing = @passing) && !passing.empty?
|
1225
|
+
sql << ' PASSING '
|
1226
|
+
comma = false
|
1227
|
+
passing.each do |k, v|
|
1228
|
+
if comma
|
1229
|
+
sql << ', '
|
1230
|
+
else
|
1231
|
+
comma = true
|
1232
|
+
end
|
1233
|
+
ds.literal_append(sql, v)
|
1234
|
+
sql << " AS " << k.to_s
|
1235
|
+
end
|
1236
|
+
end
|
1237
|
+
|
1238
|
+
to_s_append_columns(ds, sql, @columns)
|
1239
|
+
sql << TABLE_ON_ERROR_SQL.fetch(@on_error) if @on_error
|
1240
|
+
sql << ')'
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
# Support transforming of json_table expression
|
1244
|
+
def sequel_ast_transform(transformer)
|
1245
|
+
opts = {:on_error=>@on_error, :_columns=>@columns}
|
1246
|
+
|
1247
|
+
if @passing
|
1248
|
+
passing = opts[:passing] = {}
|
1249
|
+
@passing.each do |k, v|
|
1250
|
+
passing[k] = transformer.call(v)
|
1251
|
+
end
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
self.class.new(transformer.call(@expr), @path, opts)
|
1255
|
+
end
|
1256
|
+
|
1257
|
+
private
|
1258
|
+
|
1259
|
+
# Append the set of column information to the SQL. Separated to handle
|
1260
|
+
# nested sets of columns.
|
1261
|
+
def to_s_append_columns(ds, sql, columns)
|
1262
|
+
sql << ' COLUMNS('
|
1263
|
+
comma = nil
|
1264
|
+
columns.each do |column|
|
1265
|
+
if comma
|
1266
|
+
sql << comma
|
1267
|
+
else
|
1268
|
+
comma = ', '
|
1269
|
+
end
|
1270
|
+
to_s_append_column(ds, sql, column)
|
1271
|
+
end
|
1272
|
+
sql << ')'
|
1273
|
+
end
|
1274
|
+
|
1275
|
+
# Append the column information to the SQL. Handles the various
|
1276
|
+
# types of json_table columns.
|
1277
|
+
def to_s_append_column(ds, sql, column)
|
1278
|
+
case column[0]
|
1279
|
+
when :column
|
1280
|
+
_, name, type, opts = column
|
1281
|
+
ds.literal_append(sql, name)
|
1282
|
+
sql << ' ' << ds.db.send(:type_literal, opts.merge(:type=>type)).to_s
|
1283
|
+
sql << ' FORMAT JSON' if opts[:format] == :json
|
1284
|
+
to_s_append_path(ds, sql, opts[:path])
|
1285
|
+
sql << WRAPPER.fetch(opts[:wrapper]) if opts[:wrapper]
|
1286
|
+
to_s_append_on_value(ds, sql, opts[:on_empty], " ON EMPTY")
|
1287
|
+
to_s_append_on_value(ds, sql, opts[:on_error], " ON ERROR")
|
1288
|
+
when :ordinality
|
1289
|
+
ds.literal_append(sql, column[1])
|
1290
|
+
sql << ' FOR ORDINALITY'
|
1291
|
+
when :exists
|
1292
|
+
_, name, type, opts = column
|
1293
|
+
ds.literal_append(sql, name)
|
1294
|
+
sql << ' ' << ds.db.send(:type_literal, opts.merge(:type=>type)).to_s
|
1295
|
+
sql << ' EXISTS'
|
1296
|
+
to_s_append_path(ds, sql, opts[:path])
|
1297
|
+
unless (on_error = opts[:on_error]).nil?
|
1298
|
+
sql << EXISTS_ON_ERROR_SQL.fetch(on_error) << " ON ERROR"
|
1299
|
+
end
|
1300
|
+
else # when :nested
|
1301
|
+
_, path, columns = column
|
1302
|
+
sql << 'NESTED '
|
1303
|
+
default_literal_append(ds, sql, path)
|
1304
|
+
to_s_append_columns(ds, sql, columns)
|
1305
|
+
end
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
# Handle DEFAULT values in ON EMPTY/ON ERROR fragments
|
1309
|
+
def to_s_append_on_value(ds, sql, value, cond)
|
1310
|
+
if value
|
1311
|
+
if v = COLUMN_ON_SQL[value]
|
1312
|
+
sql << v
|
1313
|
+
else
|
1314
|
+
sql << ' DEFAULT '
|
1315
|
+
default_literal_append(ds, sql, value)
|
1316
|
+
end
|
1317
|
+
sql << cond
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
# Append path caluse to the SQL
|
1322
|
+
def to_s_append_path(ds, sql, path)
|
1323
|
+
if path
|
1324
|
+
sql << ' PATH '
|
1325
|
+
default_literal_append(ds, sql, path)
|
1326
|
+
end
|
1327
|
+
end
|
1328
|
+
|
1329
|
+
# Do not auto paramterize default value or path value, as PostgreSQL doesn't allow it.
|
1330
|
+
def default_literal_append(ds, sql, v)
|
1331
|
+
if sql.respond_to?(:skip_auto_param)
|
1332
|
+
sql.skip_auto_param do
|
1333
|
+
ds.literal_append(sql, v)
|
1334
|
+
end
|
1335
|
+
else
|
1336
|
+
ds.literal_append(sql, v)
|
1337
|
+
end
|
1338
|
+
end
|
1339
|
+
end
|
1340
|
+
|
708
1341
|
module JSONOpMethods
|
709
1342
|
# Wrap the receiver in an JSONOp so you can easily use the PostgreSQL
|
710
1343
|
# json functions and operators with it.
|