sequel 5.83.0 → 5.106.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/MIT-LICENSE +1 -1
- data/lib/sequel/adapters/ado/access.rb +2 -2
- data/lib/sequel/adapters/ado.rb +2 -2
- data/lib/sequel/adapters/amalgalite.rb +16 -1
- data/lib/sequel/adapters/ibmdb.rb +2 -1
- data/lib/sequel/adapters/jdbc/db2.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +4 -2
- data/lib/sequel/adapters/jdbc/h2.rb +4 -4
- 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 +3 -3
- data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
- 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 +3 -3
- data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
- data/lib/sequel/adapters/jdbc.rb +29 -15
- data/lib/sequel/adapters/mysql.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +10 -3
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +16 -0
- data/lib/sequel/adapters/postgres.rb +2 -1
- data/lib/sequel/adapters/shared/access.rb +1 -0
- data/lib/sequel/adapters/shared/db2.rb +8 -3
- data/lib/sequel/adapters/shared/mssql.rb +9 -8
- data/lib/sequel/adapters/shared/mysql.rb +16 -11
- data/lib/sequel/adapters/shared/oracle.rb +1 -0
- data/lib/sequel/adapters/shared/postgres.rb +438 -47
- data/lib/sequel/adapters/shared/sqlite.rb +9 -7
- data/lib/sequel/adapters/sqlite.rb +20 -2
- data/lib/sequel/adapters/tinytds.rb +2 -2
- data/lib/sequel/adapters/trilogy.rb +2 -3
- data/lib/sequel/ast_transformer.rb +2 -0
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
- data/lib/sequel/connection_pool/timed_queue.rb +33 -10
- data/lib/sequel/connection_pool.rb +8 -2
- data/lib/sequel/core.rb +31 -2
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +3 -3
- data/lib/sequel/database/misc.rb +29 -11
- data/lib/sequel/database/query.rb +12 -12
- data/lib/sequel/database/schema_generator.rb +117 -16
- data/lib/sequel/database/schema_methods.rb +85 -30
- data/lib/sequel/dataset/actions.rb +83 -11
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
- data/lib/sequel/dataset/prepared_statements.rb +76 -28
- data/lib/sequel/dataset/query.rb +26 -11
- data/lib/sequel/dataset/sql.rb +40 -17
- data/lib/sequel/extensions/async_thread_pool.rb +1 -1
- data/lib/sequel/extensions/caller_logging.rb +1 -3
- data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
- data/lib/sequel/extensions/connection_expiration.rb +44 -5
- data/lib/sequel/extensions/connection_validator.rb +15 -10
- data/lib/sequel/extensions/constraint_validations.rb +3 -3
- data/lib/sequel/extensions/dataset_run.rb +41 -0
- data/lib/sequel/extensions/date_arithmetic.rb +6 -6
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
- data/lib/sequel/extensions/eval_inspect.rb +3 -1
- data/lib/sequel/extensions/inflector.rb +2 -2
- data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -12
- data/lib/sequel/extensions/migration.rb +38 -21
- data/lib/sequel/extensions/null_dataset.rb +2 -2
- data/lib/sequel/extensions/pg_array_ops.rb +41 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
- data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
- data/lib/sequel/extensions/pg_enum.rb +4 -4
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
- data/lib/sequel/extensions/pg_json_ops.rb +674 -18
- data/lib/sequel/extensions/pg_multirange.rb +1 -1
- data/lib/sequel/extensions/pg_range.rb +14 -2
- data/lib/sequel/extensions/pg_row.rb +5 -3
- data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
- data/lib/sequel/extensions/provenance.rb +1 -3
- 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 +17 -5
- data/lib/sequel/extensions/set_literalizer.rb +20 -39
- data/lib/sequel/extensions/split_array_nil.rb +12 -2
- data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
- data/lib/sequel/extensions/string_agg.rb +2 -2
- data/lib/sequel/extensions/to_dot.rb +5 -0
- data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
- data/lib/sequel/model/associations.rb +215 -15
- data/lib/sequel/model/base.rb +198 -35
- data/lib/sequel/model/plugins.rb +12 -1
- data/lib/sequel/model.rb +1 -0
- data/lib/sequel/plugins/accessed_columns.rb +4 -0
- data/lib/sequel/plugins/active_model.rb +5 -6
- data/lib/sequel/plugins/association_pks.rb +2 -0
- data/lib/sequel/plugins/auto_validations.rb +2 -0
- data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
- data/lib/sequel/plugins/columns_updated.rb +4 -0
- data/lib/sequel/plugins/composition.rb +3 -1
- data/lib/sequel/plugins/constraint_validations.rb +15 -10
- data/lib/sequel/plugins/dataset_associations.rb +20 -1
- data/lib/sequel/plugins/deprecated_associations.rb +151 -0
- data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
- data/lib/sequel/plugins/dirty.rb +9 -2
- data/lib/sequel/plugins/enum.rb +1 -1
- data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/insert_returning_select.rb +10 -1
- data/lib/sequel/plugins/inspect_pk.rb +44 -0
- data/lib/sequel/plugins/instance_filters.rb +8 -1
- data/lib/sequel/plugins/instance_hooks.rb +4 -0
- data/lib/sequel/plugins/inverted_subsets.rb +1 -0
- data/lib/sequel/plugins/json_serializer.rb +6 -10
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/many_through_many.rb +21 -0
- data/lib/sequel/plugins/modification_detection.rb +2 -0
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/nested_attributes.rb +10 -5
- data/lib/sequel/plugins/paged_operations.rb +5 -2
- data/lib/sequel/plugins/pg_array_associations.rb +2 -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/pg_xmin_optimistic_locking.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +6 -6
- data/lib/sequel/plugins/serialization.rb +23 -6
- data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
- data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
- data/lib/sequel/plugins/split_values.rb +12 -0
- data/lib/sequel/plugins/sql_comments.rb +7 -2
- data/lib/sequel/plugins/static_cache.rb +13 -0
- 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 +278 -0
- data/lib/sequel/plugins/table_select.rb +7 -0
- data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
- data/lib/sequel/plugins/unused_associations.rb +11 -3
- data/lib/sequel/plugins/update_primary_key.rb +4 -0
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/validation_contexts.rb +4 -0
- data/lib/sequel/sql.rb +27 -12
- data/lib/sequel/version.rb +1 -1
- metadata +17 -240
- data/CHANGELOG +0 -1393
- 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 -104
- 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 -439
- 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.81.0.txt +0 -31
- data/doc/release_notes/5.82.0.txt +0 -61
- data/doc/release_notes/5.83.0.txt +0 -56
- 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 -204
- 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,47 @@
|
|
|
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
|
+
# # ))
|
|
168
|
+
#
|
|
169
|
+
# On PostgreSQL 18+, strip_nulls can take an argument for whether to strip in arrays
|
|
170
|
+
#
|
|
171
|
+
# j.strip_nulls(in_arrays: true) # json_strip_nulls(json_column, true)
|
|
172
|
+
# j.strip_nulls(in_arrays: false) # json_strip_nulls(json_column, false)
|
|
134
173
|
#
|
|
135
174
|
# If you are also using the pg_json extension, you should load it before
|
|
136
175
|
# loading this extension. Doing so will allow you to use the #op method on
|
|
@@ -224,7 +263,25 @@ module Sequel
|
|
|
224
263
|
function(:each_text)
|
|
225
264
|
end
|
|
226
265
|
|
|
227
|
-
#
|
|
266
|
+
# Return whether the given JSON path yields any items in the receiver.
|
|
267
|
+
# Options:
|
|
268
|
+
#
|
|
269
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression.
|
|
270
|
+
# true :: Return true
|
|
271
|
+
# false :: Return false (default behavior)
|
|
272
|
+
# :null :: Return nil
|
|
273
|
+
# :error :: raise a DatabaseError
|
|
274
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
|
275
|
+
# names, values are the values of the variable.
|
|
276
|
+
#
|
|
277
|
+
# json_op.exists("$.a") # json_exists(json, '$.a')
|
|
278
|
+
# json_op.exists("$.a", passing: {a: 1}) # json_exists(json, '$.a' PASSING 1 AS a)
|
|
279
|
+
# json_op.exists("$.a", on_error: :error) # json_exists(json, '$.a' ERROR ON ERROR)
|
|
280
|
+
def exists(path, opts=OPTS)
|
|
281
|
+
Sequel::SQL::BooleanExpression.new(:NOOP, JSONExistsOp.new(self, path, opts))
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Returns a JSON value for the object at the given path.
|
|
228
285
|
#
|
|
229
286
|
# json_op.extract('a') # json_extract_path(json, 'a')
|
|
230
287
|
# json_op.extract('a', 'b') # json_extract_path(json, 'a', 'b')
|
|
@@ -299,11 +356,117 @@ module Sequel
|
|
|
299
356
|
SQL::Function.new(function_name(:populate_recordset), arg, self)
|
|
300
357
|
end
|
|
301
358
|
|
|
302
|
-
#
|
|
359
|
+
# Return the result of applying the JSON path expression to the receiver, by default
|
|
360
|
+
# returning results as jsonb. Options:
|
|
303
361
|
#
|
|
304
|
-
#
|
|
305
|
-
|
|
306
|
-
|
|
362
|
+
# :on_empty :: How to handle case where path expression yields an empty set.
|
|
363
|
+
# Uses same values as :on_error option.
|
|
364
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression:
|
|
365
|
+
# :null :: Return nil (default)
|
|
366
|
+
# :empty_array :: Return an empty array
|
|
367
|
+
# :empty_object :: Return an empty object
|
|
368
|
+
# :error :: raise a DatabaseError
|
|
369
|
+
# any other value :: used as default value
|
|
370
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
|
371
|
+
# names, values are the values of the variable.
|
|
372
|
+
# :returning :: The data type to return (jsonb by default)
|
|
373
|
+
# :wrapper :: How to wrap returned values:
|
|
374
|
+
# true, :unconditional :: Always wrap returning values in an array
|
|
375
|
+
# :conditional :: Only wrap multiple return values in an array
|
|
376
|
+
# :omit_quotes :: Do not wrap scalar strings in quotes
|
|
377
|
+
#
|
|
378
|
+
# json_op.query("$.a") # json_query(json, '$.a')
|
|
379
|
+
# json_op.query("$.a", passing: {a: 1}) # json_query(json, '$.a' PASSING 1 AS a)
|
|
380
|
+
# json_op.query("$.a", on_error: :empty_array) # json_query(json, '$.a' EMPTY ARRAY ON ERROR)
|
|
381
|
+
# json_op.query("$.a", returning: Time) # json_query(json, '$.a' RETURNING timestamp)
|
|
382
|
+
# json_op.query("$.a", on_empty: 2) # json_query(json, '$.a' DEFAULT 2 ON EMPTY)
|
|
383
|
+
# json_op.query("$.a", wrapper: true) # json_query(json, '$.a' WITH WRAPPER)
|
|
384
|
+
def query(path, opts=OPTS)
|
|
385
|
+
self.class.new(JSONQueryOp.new(self, path, opts))
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Returns a json value stripped of all internal null values. Options:
|
|
389
|
+
#
|
|
390
|
+
# :in_arrays :: Whether to strip null values in JSON arrays
|
|
391
|
+
#
|
|
392
|
+
# json_op.strip_nulls # json_strip_nulls(json)
|
|
393
|
+
# json_op.strip_nulls(in_arrays: true) # json_strip_nulls(json, true)
|
|
394
|
+
# json_op.strip_nulls(in_arrays: false) # json_strip_nulls(json, false)
|
|
395
|
+
def strip_nulls(opts=OPTS)
|
|
396
|
+
in_arrays = opts[:in_arrays]
|
|
397
|
+
f = if in_arrays.nil?
|
|
398
|
+
function(:strip_nulls)
|
|
399
|
+
else
|
|
400
|
+
function(:strip_nulls, in_arrays)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
self.class.new(f)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Returns json_table SQL function expression, querying JSON data and returning
|
|
407
|
+
# the results as a relational view, which can be accessed similarly to a regular
|
|
408
|
+
# SQL table. This accepts a block that is handled in a similar manner to
|
|
409
|
+
# Database#create_table, though it operates differently.
|
|
410
|
+
#
|
|
411
|
+
# Table level options:
|
|
412
|
+
#
|
|
413
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression.
|
|
414
|
+
# :empty_array :: Return an empty array/result set
|
|
415
|
+
# :error :: raise a DatabaseError
|
|
416
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
|
417
|
+
# names, values are the values of the variable.
|
|
418
|
+
#
|
|
419
|
+
# Inside the block, the following methods can be used:
|
|
420
|
+
#
|
|
421
|
+
# ordinality(name) :: Include a FOR ORDINALITY column, which operates similar to an
|
|
422
|
+
# autoincrementing primary key.
|
|
423
|
+
# column(name, type, opts={}) :: Return a normal column that uses the given type.
|
|
424
|
+
# exists(name, type, opts={}) :: Return a boolean column for whether the JSON path yields any values.
|
|
425
|
+
# nested(path, &block) :: Extract nested data from the result set at the given path.
|
|
426
|
+
# This block is treated the same as a json_table block, and
|
|
427
|
+
# arbitrary levels of nesting are supported.
|
|
428
|
+
#
|
|
429
|
+
# The +column+ method supports the following options:
|
|
430
|
+
#
|
|
431
|
+
# :path :: JSON path to the object (the default is <tt>$.NAME</tt>, where +NAME+ is the
|
|
432
|
+
# name of the column).
|
|
433
|
+
# :format :: Set to +:json+ to use FORMAT JSON, when you expect the value to be a
|
|
434
|
+
# valid JSON object.
|
|
435
|
+
# :on_empty, :on_error :: How to handle case where JSON path evaluation is empty or
|
|
436
|
+
# results in an error. Values supported are:
|
|
437
|
+
# :empty_array :: Return empty array (requires <tt>format: :json</tt>)
|
|
438
|
+
# :empty_object :: Return empty object (requires <tt>format: :json</tt>)
|
|
439
|
+
# :error :: Raise a DatabaseError
|
|
440
|
+
# :null :: Return nil (NULL)
|
|
441
|
+
# :wrapper :: How to wrap returned values:
|
|
442
|
+
# true, :unconditional :: Always wrap returning values in an array
|
|
443
|
+
# :conditional :: Only wrap multiple return values in an array
|
|
444
|
+
# :keep_quotes :: Wrap scalar strings in quotes
|
|
445
|
+
# :omit_quotes :: Do not wrap scalar strings in quotes
|
|
446
|
+
#
|
|
447
|
+
# The +exists+ method supports the following options:
|
|
448
|
+
#
|
|
449
|
+
# :path :: JSON path to the object (same as +column+ option)
|
|
450
|
+
# :on_error :: How to handle case where JSON path evaluation results in an error.
|
|
451
|
+
# Values supported are:
|
|
452
|
+
# :error :: Raise a DatabaseError
|
|
453
|
+
# true :: Return true
|
|
454
|
+
# false :: Return false
|
|
455
|
+
# :null :: Return nil (NULL)
|
|
456
|
+
#
|
|
457
|
+
# Inside the block, methods for Ruby class names are also supported, allowing you
|
|
458
|
+
# to use syntax such as:
|
|
459
|
+
#
|
|
460
|
+
# json_op.table('$.a') do
|
|
461
|
+
# String :b
|
|
462
|
+
# Integer :c, path: '$.d'
|
|
463
|
+
# end
|
|
464
|
+
#
|
|
465
|
+
# One difference between this method and Database#create_table is that method_missing
|
|
466
|
+
# is not supported inside the block. Use the +column+ method for PostgreSQL types
|
|
467
|
+
# that are not mapped to Ruby classes.
|
|
468
|
+
def table(path, opts=OPTS, &block)
|
|
469
|
+
JSONTableOp.new(self, path, opts, &block)
|
|
307
470
|
end
|
|
308
471
|
|
|
309
472
|
# Builds arbitrary record from json object. You need to define the
|
|
@@ -329,6 +492,34 @@ module Sequel
|
|
|
329
492
|
function(:typeof)
|
|
330
493
|
end
|
|
331
494
|
|
|
495
|
+
# If called without arguments, operates as SQL::Wrapper#value. Otherwise,
|
|
496
|
+
# return the result of applying the JSON path expression to the receiver, by default
|
|
497
|
+
# returning results as text. Options:
|
|
498
|
+
#
|
|
499
|
+
# :on_empty :: How to handle case where path expression yields an empty set.
|
|
500
|
+
# Uses same values as :on_error option.
|
|
501
|
+
# :on_error :: How to handle errors when evaluating the JSON path expression.
|
|
502
|
+
# :null :: Return nil (default)
|
|
503
|
+
# :error :: raise a DatabaseError
|
|
504
|
+
# any other value :: used as default value
|
|
505
|
+
# :passing :: Variables to pass to the JSON path expression. Keys are variable
|
|
506
|
+
# names, values are the values of the variable.
|
|
507
|
+
# :returning :: The data type to return (text by default)
|
|
508
|
+
#
|
|
509
|
+
# json_op.value("$.a") # json_value(json, '$.a')
|
|
510
|
+
# json_op.value("$.a", passing: {a: 1}) # json_value(json, '$.a' PASSING 1 AS a)
|
|
511
|
+
# json_op.value("$.a", on_error: :error) # json_value(json, '$.a' ERROR ON ERROR)
|
|
512
|
+
# json_op.value("$.a", returning: Time) # json_value(json, '$.a' RETURNING timestamp)
|
|
513
|
+
# json_op.value("$.a", on_empty: 2) # json_value(json, '$.a' DEFAULT 2 ON EMPTY)
|
|
514
|
+
def value(path=(no_args_given = true), opts=OPTS)
|
|
515
|
+
if no_args_given
|
|
516
|
+
# Act as SQL::Wrapper#value
|
|
517
|
+
super()
|
|
518
|
+
else
|
|
519
|
+
Sequel::SQL::StringExpression.new(:NOOP, JSONValueOp.new(self, path, opts))
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
332
523
|
private
|
|
333
524
|
|
|
334
525
|
# Internals of IS [NOT] JSON support
|
|
@@ -650,13 +841,20 @@ module Sequel
|
|
|
650
841
|
Sequel::SQL::BooleanExpression.new(:NOOP, Sequel::SQL::PlaceholderLiteralString.new(str, [value, other]))
|
|
651
842
|
end
|
|
652
843
|
|
|
653
|
-
# Wrap argument in a PGArray if it is an array
|
|
844
|
+
# Wrap argument in a PGArray if it is an array or a set
|
|
654
845
|
def wrap_input_array(obj)
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
obj
|
|
846
|
+
# :nocov:
|
|
847
|
+
if Sequel.respond_to?(:pg_array)
|
|
848
|
+
# :nocov:
|
|
849
|
+
case obj
|
|
850
|
+
when Array
|
|
851
|
+
return Sequel.pg_array(obj)
|
|
852
|
+
when Set
|
|
853
|
+
return Sequel.pg_array(obj.to_a)
|
|
854
|
+
end
|
|
659
855
|
end
|
|
856
|
+
|
|
857
|
+
obj
|
|
660
858
|
end
|
|
661
859
|
|
|
662
860
|
# Wrap argument in a JSONBArray or JSONBHash if it is an array or hash.
|
|
@@ -705,6 +903,464 @@ module Sequel
|
|
|
705
903
|
end
|
|
706
904
|
end
|
|
707
905
|
|
|
906
|
+
# Object representing json_exists calls
|
|
907
|
+
class JSONExistsOp < SQL::Expression
|
|
908
|
+
ON_ERROR_SQL = {
|
|
909
|
+
true => 'TRUE',
|
|
910
|
+
false => 'FALSE',
|
|
911
|
+
:null => 'UNKNOWN',
|
|
912
|
+
:error => 'ERROR',
|
|
913
|
+
}.freeze
|
|
914
|
+
private_constant :ON_ERROR_SQL
|
|
915
|
+
|
|
916
|
+
# Expression (context_item in PostgreSQL terms), usually JSONBaseOp instance
|
|
917
|
+
attr_reader :expr
|
|
918
|
+
|
|
919
|
+
# JSON path expression to apply against the expression
|
|
920
|
+
attr_reader :path
|
|
921
|
+
|
|
922
|
+
# Variables to set in the JSON path expression
|
|
923
|
+
attr_reader :passing
|
|
924
|
+
|
|
925
|
+
# How to handle errors when evaluating the JSON path expression
|
|
926
|
+
attr_reader :on_error
|
|
927
|
+
|
|
928
|
+
# See JSONBaseOp#exists for documentation on the options.
|
|
929
|
+
def initialize(expr, path, opts=OPTS)
|
|
930
|
+
@expr = expr
|
|
931
|
+
@path = path
|
|
932
|
+
@passing = opts[:passing]
|
|
933
|
+
@on_error = opts[:on_error]
|
|
934
|
+
freeze
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
# Append the SQL function call expression to the SQL
|
|
938
|
+
def to_s_append(ds, sql)
|
|
939
|
+
to_s_append_function_name(ds, sql)
|
|
940
|
+
to_s_append_args_passing(ds, sql)
|
|
941
|
+
to_s_append_on_error(ds, sql)
|
|
942
|
+
sql << ')'
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
# Support transforming of function call expression
|
|
946
|
+
def sequel_ast_transform(transformer)
|
|
947
|
+
opts = {}
|
|
948
|
+
transform_opts(transformer, opts)
|
|
949
|
+
self.class.new(transformer.call(@expr), @path, opts)
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
private
|
|
953
|
+
|
|
954
|
+
# Set the :passing and :on_error options when doing an
|
|
955
|
+
# AST transform.
|
|
956
|
+
def transform_opts(transformer, opts)
|
|
957
|
+
if @passing
|
|
958
|
+
passing = opts[:passing] = {}
|
|
959
|
+
@passing.each do |k, v|
|
|
960
|
+
passing[k] = transformer.call(v)
|
|
961
|
+
end
|
|
962
|
+
end
|
|
963
|
+
|
|
964
|
+
opts[:on_error] = @on_error
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
def to_s_append_function_name(ds, sql)
|
|
968
|
+
sql << 'json_exists('
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
# Append the expression, path, and optional PASSING fragments
|
|
972
|
+
def to_s_append_args_passing(ds, sql)
|
|
973
|
+
ds.literal_append(sql, @expr)
|
|
974
|
+
sql << ', '
|
|
975
|
+
ds.literal_append(sql, @path)
|
|
976
|
+
|
|
977
|
+
if (passing = @passing) && !passing.empty?
|
|
978
|
+
sql << ' PASSING '
|
|
979
|
+
comma = false
|
|
980
|
+
passing.each do |k, v|
|
|
981
|
+
if comma
|
|
982
|
+
sql << ', '
|
|
983
|
+
else
|
|
984
|
+
comma = true
|
|
985
|
+
end
|
|
986
|
+
ds.literal_append(sql, v)
|
|
987
|
+
sql << " AS " << k.to_s
|
|
988
|
+
end
|
|
989
|
+
end
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
# Append the optional ON ERROR fragments
|
|
993
|
+
def to_s_append_on_error(ds, sql)
|
|
994
|
+
unless @on_error.nil?
|
|
995
|
+
sql << " "
|
|
996
|
+
to_s_append_on_value(ds, sql, @on_error)
|
|
997
|
+
sql << " ON ERROR"
|
|
998
|
+
end
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
# Append the value to use for ON ERROR
|
|
1002
|
+
def to_s_append_on_value(ds, sql, value)
|
|
1003
|
+
sql << ON_ERROR_SQL.fetch(value)
|
|
1004
|
+
end
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
# Object representing json_value calls
|
|
1008
|
+
class JSONValueOp < JSONExistsOp
|
|
1009
|
+
ON_SQL = {
|
|
1010
|
+
:null => 'NULL',
|
|
1011
|
+
:error => 'ERROR',
|
|
1012
|
+
}.freeze
|
|
1013
|
+
private_constant :ON_SQL
|
|
1014
|
+
|
|
1015
|
+
# The database type to cast returned values to
|
|
1016
|
+
attr_reader :returning
|
|
1017
|
+
|
|
1018
|
+
# How to handle cases where the JSON path expression evaluation yields
|
|
1019
|
+
# an empty set.
|
|
1020
|
+
attr_reader :on_empty
|
|
1021
|
+
|
|
1022
|
+
# See JSONBaseOp#value for documentation of the options.
|
|
1023
|
+
def initialize(expr, path, opts=OPTS)
|
|
1024
|
+
@returning = opts[:returning]
|
|
1025
|
+
@on_empty = opts[:on_empty]
|
|
1026
|
+
super
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
private
|
|
1030
|
+
|
|
1031
|
+
# Also handle transforming the returning and on_empty options.
|
|
1032
|
+
def transform_opts(transformer, opts)
|
|
1033
|
+
super
|
|
1034
|
+
opts[:returning] = @returning
|
|
1035
|
+
on_error = @on_error
|
|
1036
|
+
on_error = transformer.call(on_error) unless on_sql_value(on_error)
|
|
1037
|
+
opts[:on_error] = on_error
|
|
1038
|
+
on_empty = @on_empty
|
|
1039
|
+
on_empty = transformer.call(on_empty) unless on_sql_value(on_empty)
|
|
1040
|
+
opts[:on_empty] = on_empty
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
def to_s_append_function_name(ds, sql)
|
|
1044
|
+
sql << 'json_value('
|
|
1045
|
+
end
|
|
1046
|
+
|
|
1047
|
+
# Also append the optional RETURNING fragment
|
|
1048
|
+
def to_s_append_args_passing(ds, sql)
|
|
1049
|
+
super
|
|
1050
|
+
|
|
1051
|
+
if @returning
|
|
1052
|
+
sql << ' RETURNING ' << ds.db.cast_type_literal(@returning).to_s
|
|
1053
|
+
end
|
|
1054
|
+
end
|
|
1055
|
+
|
|
1056
|
+
# Also append the optional ON EMPTY fragment
|
|
1057
|
+
def to_s_append_on_error(ds, sql)
|
|
1058
|
+
unless @on_empty.nil?
|
|
1059
|
+
sql << " "
|
|
1060
|
+
to_s_append_on_value(ds, sql, @on_empty)
|
|
1061
|
+
sql << " ON EMPTY"
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
super
|
|
1065
|
+
end
|
|
1066
|
+
|
|
1067
|
+
# Handle DEFAULT values in ON EMPTY/ON ERROR fragments
|
|
1068
|
+
def to_s_append_on_value(ds, sql, value)
|
|
1069
|
+
if v = on_sql_value(value)
|
|
1070
|
+
sql << v
|
|
1071
|
+
else
|
|
1072
|
+
sql << 'DEFAULT '
|
|
1073
|
+
default_literal_append(ds, sql, value)
|
|
1074
|
+
end
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
# Do not auto paramterize default value, as PostgreSQL doesn't allow it.
|
|
1078
|
+
def default_literal_append(ds, sql, v)
|
|
1079
|
+
if sql.respond_to?(:skip_auto_param)
|
|
1080
|
+
sql.skip_auto_param do
|
|
1081
|
+
ds.literal_append(sql, v)
|
|
1082
|
+
end
|
|
1083
|
+
else
|
|
1084
|
+
ds.literal_append(sql, v)
|
|
1085
|
+
end
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
def on_sql_value(value)
|
|
1089
|
+
ON_SQL[value]
|
|
1090
|
+
end
|
|
1091
|
+
end
|
|
1092
|
+
|
|
1093
|
+
# Object representing json_query calls
|
|
1094
|
+
class JSONQueryOp < JSONValueOp
|
|
1095
|
+
ON_SQL = {
|
|
1096
|
+
:null => 'NULL',
|
|
1097
|
+
:error => 'ERROR',
|
|
1098
|
+
:empty_array => 'EMPTY ARRAY',
|
|
1099
|
+
:empty_object => 'EMPTY OBJECT',
|
|
1100
|
+
}.freeze
|
|
1101
|
+
private_constant :ON_SQL
|
|
1102
|
+
|
|
1103
|
+
WRAPPER = {
|
|
1104
|
+
:conditional => ' WITH CONDITIONAL WRAPPER',
|
|
1105
|
+
:unconditional => ' WITH WRAPPER',
|
|
1106
|
+
:omit_quotes => ' OMIT QUOTES'
|
|
1107
|
+
}
|
|
1108
|
+
WRAPPER[true] = WRAPPER[:unconditional]
|
|
1109
|
+
WRAPPER.freeze
|
|
1110
|
+
private_constant :WRAPPER
|
|
1111
|
+
|
|
1112
|
+
# How to handle wrapping of results
|
|
1113
|
+
attr_reader :wrapper
|
|
1114
|
+
|
|
1115
|
+
# See JSONBaseOp#query for documentation of the options.
|
|
1116
|
+
def initialize(expr, path, opts=OPTS)
|
|
1117
|
+
@wrapper = opts[:wrapper]
|
|
1118
|
+
super
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1121
|
+
private
|
|
1122
|
+
|
|
1123
|
+
# Also handle transforming the wrapper option
|
|
1124
|
+
def transform_opts(transformer, opts)
|
|
1125
|
+
super
|
|
1126
|
+
opts[:wrapper] = @wrapper
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
def to_s_append_function_name(ds, sql)
|
|
1130
|
+
sql << 'json_query('
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
# Also append the optional WRAPPER/OMIT QUOTES fragment
|
|
1134
|
+
def to_s_append_args_passing(ds, sql)
|
|
1135
|
+
super
|
|
1136
|
+
|
|
1137
|
+
if @wrapper
|
|
1138
|
+
sql << WRAPPER.fetch(@wrapper)
|
|
1139
|
+
end
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
def on_sql_value(value)
|
|
1143
|
+
ON_SQL[value]
|
|
1144
|
+
end
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
# Object representing json_table calls
|
|
1148
|
+
class JSONTableOp < SQL::Expression
|
|
1149
|
+
TABLE_ON_ERROR_SQL = {
|
|
1150
|
+
:error => ' ERROR ON ERROR',
|
|
1151
|
+
:empty_array => ' EMPTY ARRAY ON ERROR',
|
|
1152
|
+
}.freeze
|
|
1153
|
+
private_constant :TABLE_ON_ERROR_SQL
|
|
1154
|
+
|
|
1155
|
+
COLUMN_ON_SQL = {
|
|
1156
|
+
:null => ' NULL',
|
|
1157
|
+
:error => ' ERROR',
|
|
1158
|
+
:empty_array => ' EMPTY ARRAY',
|
|
1159
|
+
:empty_object => ' EMPTY OBJECT',
|
|
1160
|
+
}.freeze
|
|
1161
|
+
private_constant :COLUMN_ON_SQL
|
|
1162
|
+
|
|
1163
|
+
EXISTS_ON_ERROR_SQL = {
|
|
1164
|
+
:error => ' ERROR',
|
|
1165
|
+
true => ' TRUE',
|
|
1166
|
+
false => ' FALSE',
|
|
1167
|
+
:null => ' UNKNOWN',
|
|
1168
|
+
}.freeze
|
|
1169
|
+
private_constant :EXISTS_ON_ERROR_SQL
|
|
1170
|
+
|
|
1171
|
+
WRAPPER = {
|
|
1172
|
+
:conditional => ' WITH CONDITIONAL WRAPPER',
|
|
1173
|
+
:unconditional => ' WITH WRAPPER',
|
|
1174
|
+
:omit_quotes => ' OMIT QUOTES',
|
|
1175
|
+
:keep_quotes => ' KEEP QUOTES',
|
|
1176
|
+
}
|
|
1177
|
+
WRAPPER[true] = WRAPPER[:unconditional]
|
|
1178
|
+
WRAPPER.freeze
|
|
1179
|
+
private_constant :WRAPPER
|
|
1180
|
+
|
|
1181
|
+
# Class used to evaluate json_table blocks and nested blocks
|
|
1182
|
+
class ColumnDSL
|
|
1183
|
+
# Return array of column information recorded for the instance
|
|
1184
|
+
attr_reader :columns
|
|
1185
|
+
|
|
1186
|
+
def self.columns(&block)
|
|
1187
|
+
new(&block).columns.freeze
|
|
1188
|
+
end
|
|
1189
|
+
|
|
1190
|
+
def initialize(&block)
|
|
1191
|
+
@columns = []
|
|
1192
|
+
instance_exec(&block)
|
|
1193
|
+
end
|
|
1194
|
+
|
|
1195
|
+
# Include a FOR ORDINALITY column
|
|
1196
|
+
def ordinality(name)
|
|
1197
|
+
@columns << [:ordinality, name].freeze
|
|
1198
|
+
end
|
|
1199
|
+
|
|
1200
|
+
# Include a regular column with the given type
|
|
1201
|
+
def column(name, type, opts=OPTS)
|
|
1202
|
+
@columns << [:column, name, type, opts].freeze
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
# Include an EXISTS column with the given type
|
|
1206
|
+
def exists(name, type, opts=OPTS)
|
|
1207
|
+
@columns << [:exists, name, type, opts].freeze
|
|
1208
|
+
end
|
|
1209
|
+
|
|
1210
|
+
# Include a nested set of columns at the given path.
|
|
1211
|
+
def nested(path, &block)
|
|
1212
|
+
@columns << [:nested, path, ColumnDSL.columns(&block)].freeze
|
|
1213
|
+
end
|
|
1214
|
+
|
|
1215
|
+
# Include a bigint column
|
|
1216
|
+
def Bignum(name, opts=OPTS)
|
|
1217
|
+
@columns << [:column, name, :Bignum, opts].freeze
|
|
1218
|
+
end
|
|
1219
|
+
|
|
1220
|
+
# Define methods for handling other generic types
|
|
1221
|
+
%w'String Integer Float Numeric BigDecimal Date DateTime Time File TrueClass FalseClass'.each do |meth|
|
|
1222
|
+
klass = Object.const_get(meth)
|
|
1223
|
+
define_method(meth) do |name, opts=OPTS|
|
|
1224
|
+
@columns << [:column, name, klass, opts].freeze
|
|
1225
|
+
end
|
|
1226
|
+
end
|
|
1227
|
+
end
|
|
1228
|
+
private_constant :ColumnDSL
|
|
1229
|
+
|
|
1230
|
+
# See JSONBaseOp#table for documentation on the options.
|
|
1231
|
+
def initialize(expr, path, opts=OPTS, &block)
|
|
1232
|
+
@expr = expr
|
|
1233
|
+
@path = path
|
|
1234
|
+
@passing = opts[:passing]
|
|
1235
|
+
@on_error = opts[:on_error]
|
|
1236
|
+
@columns = opts[:_columns] || ColumnDSL.columns(&block)
|
|
1237
|
+
freeze
|
|
1238
|
+
end
|
|
1239
|
+
|
|
1240
|
+
# Append the json_table function call expression to the SQL
|
|
1241
|
+
def to_s_append(ds, sql)
|
|
1242
|
+
sql << 'json_table('
|
|
1243
|
+
ds.literal_append(sql, @expr)
|
|
1244
|
+
sql << ', '
|
|
1245
|
+
default_literal_append(ds, sql, @path)
|
|
1246
|
+
|
|
1247
|
+
if (passing = @passing) && !passing.empty?
|
|
1248
|
+
sql << ' PASSING '
|
|
1249
|
+
comma = false
|
|
1250
|
+
passing.each do |k, v|
|
|
1251
|
+
if comma
|
|
1252
|
+
sql << ', '
|
|
1253
|
+
else
|
|
1254
|
+
comma = true
|
|
1255
|
+
end
|
|
1256
|
+
ds.literal_append(sql, v)
|
|
1257
|
+
sql << " AS " << k.to_s
|
|
1258
|
+
end
|
|
1259
|
+
end
|
|
1260
|
+
|
|
1261
|
+
to_s_append_columns(ds, sql, @columns)
|
|
1262
|
+
sql << TABLE_ON_ERROR_SQL.fetch(@on_error) if @on_error
|
|
1263
|
+
sql << ')'
|
|
1264
|
+
end
|
|
1265
|
+
|
|
1266
|
+
# Support transforming of json_table expression
|
|
1267
|
+
def sequel_ast_transform(transformer)
|
|
1268
|
+
opts = {:on_error=>@on_error, :_columns=>@columns}
|
|
1269
|
+
|
|
1270
|
+
if @passing
|
|
1271
|
+
passing = opts[:passing] = {}
|
|
1272
|
+
@passing.each do |k, v|
|
|
1273
|
+
passing[k] = transformer.call(v)
|
|
1274
|
+
end
|
|
1275
|
+
end
|
|
1276
|
+
|
|
1277
|
+
self.class.new(transformer.call(@expr), @path, opts)
|
|
1278
|
+
end
|
|
1279
|
+
|
|
1280
|
+
private
|
|
1281
|
+
|
|
1282
|
+
# Append the set of column information to the SQL. Separated to handle
|
|
1283
|
+
# nested sets of columns.
|
|
1284
|
+
def to_s_append_columns(ds, sql, columns)
|
|
1285
|
+
sql << ' COLUMNS('
|
|
1286
|
+
comma = nil
|
|
1287
|
+
columns.each do |column|
|
|
1288
|
+
if comma
|
|
1289
|
+
sql << comma
|
|
1290
|
+
else
|
|
1291
|
+
comma = ', '
|
|
1292
|
+
end
|
|
1293
|
+
to_s_append_column(ds, sql, column)
|
|
1294
|
+
end
|
|
1295
|
+
sql << ')'
|
|
1296
|
+
end
|
|
1297
|
+
|
|
1298
|
+
# Append the column information to the SQL. Handles the various
|
|
1299
|
+
# types of json_table columns.
|
|
1300
|
+
def to_s_append_column(ds, sql, column)
|
|
1301
|
+
case column[0]
|
|
1302
|
+
when :column
|
|
1303
|
+
_, name, type, opts = column
|
|
1304
|
+
ds.literal_append(sql, name)
|
|
1305
|
+
sql << ' ' << ds.db.send(:type_literal, opts.merge(:type=>type)).to_s
|
|
1306
|
+
sql << ' FORMAT JSON' if opts[:format] == :json
|
|
1307
|
+
to_s_append_path(ds, sql, opts[:path])
|
|
1308
|
+
sql << WRAPPER.fetch(opts[:wrapper]) if opts[:wrapper]
|
|
1309
|
+
to_s_append_on_value(ds, sql, opts[:on_empty], " ON EMPTY")
|
|
1310
|
+
to_s_append_on_value(ds, sql, opts[:on_error], " ON ERROR")
|
|
1311
|
+
when :ordinality
|
|
1312
|
+
ds.literal_append(sql, column[1])
|
|
1313
|
+
sql << ' FOR ORDINALITY'
|
|
1314
|
+
when :exists
|
|
1315
|
+
_, name, type, opts = column
|
|
1316
|
+
ds.literal_append(sql, name)
|
|
1317
|
+
sql << ' ' << ds.db.send(:type_literal, opts.merge(:type=>type)).to_s
|
|
1318
|
+
sql << ' EXISTS'
|
|
1319
|
+
to_s_append_path(ds, sql, opts[:path])
|
|
1320
|
+
unless (on_error = opts[:on_error]).nil?
|
|
1321
|
+
sql << EXISTS_ON_ERROR_SQL.fetch(on_error) << " ON ERROR"
|
|
1322
|
+
end
|
|
1323
|
+
else # when :nested
|
|
1324
|
+
_, path, columns = column
|
|
1325
|
+
sql << 'NESTED '
|
|
1326
|
+
default_literal_append(ds, sql, path)
|
|
1327
|
+
to_s_append_columns(ds, sql, columns)
|
|
1328
|
+
end
|
|
1329
|
+
end
|
|
1330
|
+
|
|
1331
|
+
# Handle DEFAULT values in ON EMPTY/ON ERROR fragments
|
|
1332
|
+
def to_s_append_on_value(ds, sql, value, cond)
|
|
1333
|
+
if value
|
|
1334
|
+
if v = COLUMN_ON_SQL[value]
|
|
1335
|
+
sql << v
|
|
1336
|
+
else
|
|
1337
|
+
sql << ' DEFAULT '
|
|
1338
|
+
default_literal_append(ds, sql, value)
|
|
1339
|
+
end
|
|
1340
|
+
sql << cond
|
|
1341
|
+
end
|
|
1342
|
+
end
|
|
1343
|
+
|
|
1344
|
+
# Append path caluse to the SQL
|
|
1345
|
+
def to_s_append_path(ds, sql, path)
|
|
1346
|
+
if path
|
|
1347
|
+
sql << ' PATH '
|
|
1348
|
+
default_literal_append(ds, sql, path)
|
|
1349
|
+
end
|
|
1350
|
+
end
|
|
1351
|
+
|
|
1352
|
+
# Do not auto paramterize default value or path value, as PostgreSQL doesn't allow it.
|
|
1353
|
+
def default_literal_append(ds, sql, v)
|
|
1354
|
+
if sql.respond_to?(:skip_auto_param)
|
|
1355
|
+
sql.skip_auto_param do
|
|
1356
|
+
ds.literal_append(sql, v)
|
|
1357
|
+
end
|
|
1358
|
+
else
|
|
1359
|
+
ds.literal_append(sql, v)
|
|
1360
|
+
end
|
|
1361
|
+
end
|
|
1362
|
+
end
|
|
1363
|
+
|
|
708
1364
|
module JSONOpMethods
|
|
709
1365
|
# Wrap the receiver in an JSONOp so you can easily use the PostgreSQL
|
|
710
1366
|
# json functions and operators with it.
|