jsonbadger 0.5.0 → 0.6.1
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.
- package/README.md +36 -18
- package/docs/api/connection.md +144 -0
- package/docs/api/delta-tracker.md +106 -0
- package/docs/api/document.md +77 -0
- package/docs/api/field-types.md +329 -0
- package/docs/api/index.md +35 -0
- package/docs/api/model.md +392 -0
- package/docs/api/query-builder.md +81 -0
- package/docs/api/schema.md +204 -0
- package/docs/architecture-flow.md +397 -0
- package/docs/examples.md +495 -218
- package/docs/jsonb-ops.md +171 -0
- package/docs/lifecycle/model-compilation.md +111 -0
- package/docs/lifecycle.md +146 -0
- package/docs/query-translation.md +11 -10
- package/package.json +10 -3
- package/src/connection/connect.js +12 -17
- package/src/connection/connection.js +128 -0
- package/src/connection/server-capabilities.js +60 -59
- package/src/constants/defaults.js +32 -19
- package/src/constants/{id-strategies.js → id-strategy.js} +28 -29
- package/src/constants/intake-mode.js +8 -0
- package/src/debug/debug-logger.js +17 -15
- package/src/errors/model-overwrite-error.js +25 -0
- package/src/errors/query-error.js +25 -23
- package/src/errors/validation-error.js +25 -23
- package/src/field-types/base-field-type.js +137 -140
- package/src/field-types/builtins/advanced.js +365 -365
- package/src/field-types/builtins/index.js +579 -585
- package/src/field-types/field-type-namespace.js +9 -0
- package/src/field-types/registry.js +149 -122
- package/src/index.js +26 -36
- package/src/migration/ensure-index.js +157 -154
- package/src/migration/ensure-schema.js +27 -15
- package/src/migration/ensure-table.js +44 -31
- package/src/migration/schema-indexes-resolver.js +8 -6
- package/src/model/document-instance.js +29 -540
- package/src/model/document.js +60 -0
- package/src/model/factory/constants.js +36 -0
- package/src/model/factory/index.js +58 -0
- package/src/model/model.js +875 -0
- package/src/model/operations/delete-one.js +39 -0
- package/src/model/operations/insert-one.js +35 -0
- package/src/model/operations/query-builder.js +132 -0
- package/src/model/operations/update-one.js +333 -0
- package/src/model/state.js +34 -0
- package/src/schema/field-definition-parser.js +213 -218
- package/src/schema/path-introspection.js +87 -82
- package/src/schema/schema-compiler.js +126 -212
- package/src/schema/schema.js +621 -138
- package/src/sql/index.js +17 -0
- package/src/sql/jsonb/ops.js +153 -0
- package/src/{query → sql/jsonb}/path-parser.js +54 -43
- package/src/sql/jsonb/read/elem-match.js +133 -0
- package/src/{query → sql/jsonb/read}/operators/contains.js +13 -7
- package/src/sql/jsonb/read/operators/elem-match.js +9 -0
- package/src/{query → sql/jsonb/read}/operators/has-all-keys.js +17 -11
- package/src/{query → sql/jsonb/read}/operators/has-any-keys.js +18 -11
- package/src/sql/jsonb/read/operators/has-key.js +12 -0
- package/src/{query → sql/jsonb/read}/operators/jsonpath-exists.js +22 -15
- package/src/{query → sql/jsonb/read}/operators/jsonpath-match.js +22 -15
- package/src/{query → sql/jsonb/read}/operators/size.js +23 -16
- package/src/sql/parameter-binder.js +18 -13
- package/src/sql/read/build-count-query.js +12 -0
- package/src/sql/read/build-find-query.js +25 -0
- package/src/sql/read/limit-skip.js +21 -0
- package/src/sql/read/sort.js +85 -0
- package/src/sql/read/where/base-fields.js +310 -0
- package/src/sql/read/where/casting.js +90 -0
- package/src/sql/read/where/context.js +79 -0
- package/src/sql/read/where/field-clause.js +58 -0
- package/src/sql/read/where/index.js +38 -0
- package/src/sql/read/where/operator-entries.js +29 -0
- package/src/{query → sql/read/where}/operators/all.js +16 -10
- package/src/sql/read/where/operators/eq.js +12 -0
- package/src/{query → sql/read/where}/operators/gt.js +23 -16
- package/src/{query → sql/read/where}/operators/gte.js +23 -16
- package/src/{query → sql/read/where}/operators/in.js +18 -12
- package/src/sql/read/where/operators/index.js +40 -0
- package/src/{query → sql/read/where}/operators/lt.js +23 -16
- package/src/{query → sql/read/where}/operators/lte.js +23 -16
- package/src/sql/read/where/operators/ne.js +12 -0
- package/src/{query → sql/read/where}/operators/nin.js +18 -12
- package/src/{query → sql/read/where}/operators/regex.js +14 -8
- package/src/sql/read/where/operators.js +126 -0
- package/src/sql/read/where/text-operators.js +83 -0
- package/src/sql/run.js +46 -0
- package/src/sql/write/build-delete-query.js +33 -0
- package/src/sql/write/build-insert-query.js +42 -0
- package/src/sql/write/build-update-query.js +65 -0
- package/src/utils/assert.js +34 -27
- package/src/utils/delta-tracker/.archive/1 tracker-redesign-codex-v2.md +250 -0
- package/src/utils/delta-tracker/.archive/1 tracker-redesign-gemini.md +101 -0
- package/src/utils/delta-tracker/.archive/2 evaluation by gemini.txt +65 -0
- package/src/utils/delta-tracker/.archive/2 evaluation by grok.txt +39 -0
- package/src/utils/delta-tracker/.archive/3 gemini evaluate grok.txt +37 -0
- package/src/utils/delta-tracker/.archive/3 grok evaluate gemini.txt +63 -0
- package/src/utils/delta-tracker/.archive/4 gemini veredict.txt +16 -0
- package/src/utils/delta-tracker/.archive/index.1.js +587 -0
- package/src/utils/delta-tracker/.archive/index.2.js +612 -0
- package/src/utils/delta-tracker/index.js +592 -0
- package/src/utils/dirty-tracker/inline.js +335 -0
- package/src/utils/dirty-tracker/instance.js +414 -0
- package/src/utils/dirty-tracker/static.js +343 -0
- package/src/utils/json-safe.js +13 -9
- package/src/utils/object-path.js +227 -33
- package/src/utils/object.js +408 -168
- package/src/utils/string.js +55 -0
- package/src/utils/value.js +169 -30
- package/docs/api.md +0 -152
- package/src/connection/disconnect.js +0 -16
- package/src/connection/pool-store.js +0 -46
- package/src/model/model-factory.js +0 -555
- package/src/query/limit-skip-compiler.js +0 -31
- package/src/query/operators/elem-match.js +0 -3
- package/src/query/operators/eq.js +0 -6
- package/src/query/operators/has-key.js +0 -6
- package/src/query/operators/index.js +0 -60
- package/src/query/operators/ne.js +0 -6
- package/src/query/query-builder.js +0 -93
- package/src/query/sort-compiler.js +0 -30
- package/src/query/where-compiler.js +0 -477
- package/src/sql/sql-runner.js +0 -31
|
@@ -1,555 +0,0 @@
|
|
|
1
|
-
import defaults from '#src/constants/defaults.js';
|
|
2
|
-
import QueryError from '#src/errors/query-error.js';
|
|
3
|
-
|
|
4
|
-
import {get_connection_options, get_server_capabilities, has_pool} from '#src/connection/pool-store.js';
|
|
5
|
-
import {assert_id_strategy_capability} from '#src/connection/server-capabilities.js';
|
|
6
|
-
import ensure_index from '#src/migration/ensure-index.js';
|
|
7
|
-
import ensure_schema from '#src/migration/ensure-schema.js';
|
|
8
|
-
import resolve_schema_indexes from '#src/migration/schema-indexes-resolver.js';
|
|
9
|
-
import ensure_table from '#src/migration/ensure-table.js';
|
|
10
|
-
|
|
11
|
-
import document_instance from '#src/model/document-instance.js';
|
|
12
|
-
import IdStrategies, {assert_valid_id_strategy} from '#src/constants/id-strategies.js';
|
|
13
|
-
import QueryBuilder from '#src/query/query-builder.js';
|
|
14
|
-
import where_compiler from '#src/query/where-compiler.js';
|
|
15
|
-
|
|
16
|
-
import {bind_parameter, create_parameter_state} from '#src/sql/parameter-binder.js';
|
|
17
|
-
import sql_runner from '#src/sql/sql-runner.js';
|
|
18
|
-
|
|
19
|
-
import {assert_condition, assert_identifier, quote_identifier} from '#src/utils/assert.js';
|
|
20
|
-
import {has_own} from '#src/utils/object.js';
|
|
21
|
-
import {build_path_literal} from '#src/utils/object-path.js';
|
|
22
|
-
import {jsonb_stringify} from '#src/utils/json.js';
|
|
23
|
-
import {is_not_object} from '#src/utils/value.js';
|
|
24
|
-
|
|
25
|
-
const update_path_root_segment_pattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
26
|
-
const update_path_nested_segment_pattern = /^(?:[a-zA-Z_][a-zA-Z0-9_]*|[0-9]+)$/;
|
|
27
|
-
|
|
28
|
-
export default function model(schema_instance, model_configuration) {
|
|
29
|
-
assert_condition(schema_instance && typeof schema_instance.validate === 'function', 'schema_instance is required');
|
|
30
|
-
assert_condition(is_not_object(model_configuration) === false, 'model options are required');
|
|
31
|
-
|
|
32
|
-
const model_options = Object.assign({}, defaults.model_options, model_configuration);
|
|
33
|
-
assert_identifier(model_options.table_name, 'table_name');
|
|
34
|
-
assert_identifier(model_options.data_column, 'data_column');
|
|
35
|
-
|
|
36
|
-
let indexes_ensured = false;
|
|
37
|
-
|
|
38
|
-
function Model(data) {
|
|
39
|
-
this.data = data || {};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
Model.model_name = model_options.table_name;
|
|
43
|
-
Model.schema_instance = schema_instance;
|
|
44
|
-
Model.model_options = model_options;
|
|
45
|
-
Model.resolve_id_strategy = function () {
|
|
46
|
-
const connection_options = get_connection_options();
|
|
47
|
-
const model_id_strategy = model_options.id_strategy;
|
|
48
|
-
const server_id_strategy = connection_options.id_strategy;
|
|
49
|
-
const final_id_strategy = model_id_strategy ?? server_id_strategy;
|
|
50
|
-
|
|
51
|
-
assert_valid_id_strategy(final_id_strategy);
|
|
52
|
-
|
|
53
|
-
return final_id_strategy;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
Model.resolve_auto_index = function () {
|
|
57
|
-
const connection_options = get_connection_options();
|
|
58
|
-
const model_auto_index = model_options.auto_index;
|
|
59
|
-
const server_auto_index = connection_options.auto_index;
|
|
60
|
-
const final_auto_index = model_auto_index ?? server_auto_index;
|
|
61
|
-
|
|
62
|
-
assert_condition(typeof final_auto_index === 'boolean', 'auto_index must be a boolean');
|
|
63
|
-
|
|
64
|
-
return final_auto_index;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
Model.assert_id_strategy_supported = function () {
|
|
68
|
-
const final_id_strategy = Model.resolve_id_strategy();
|
|
69
|
-
assert_model_id_strategy_supported(final_id_strategy);
|
|
70
|
-
return final_id_strategy;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
document_instance(Model);
|
|
74
|
-
|
|
75
|
-
Model.ensure_table = async function () {
|
|
76
|
-
const final_table_name = model_options.table_name;
|
|
77
|
-
const final_id_strategy = Model.assert_id_strategy_supported();
|
|
78
|
-
|
|
79
|
-
await ensure_table(final_table_name, model_options.data_column, final_id_strategy);
|
|
80
|
-
|
|
81
|
-
if(indexes_ensured || !Model.resolve_auto_index()) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
await ensure_schema_indexes(final_table_name);
|
|
86
|
-
indexes_ensured = true;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
Model.ensure_index = async function () {
|
|
90
|
-
const final_table_name = model_options.table_name;
|
|
91
|
-
const final_id_strategy = Model.assert_id_strategy_supported();
|
|
92
|
-
|
|
93
|
-
await ensure_table(final_table_name, model_options.data_column, final_id_strategy);
|
|
94
|
-
await ensure_schema_indexes(final_table_name);
|
|
95
|
-
|
|
96
|
-
indexes_ensured = true;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
Model.ensure_schema = async function () {
|
|
100
|
-
const final_id_strategy = Model.assert_id_strategy_supported();
|
|
101
|
-
await ensure_schema(model_options.table_name, model_options.data_column, schema_instance, final_id_strategy);
|
|
102
|
-
indexes_ensured = true;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
Model.find = function (query_filter, projection_value) {
|
|
106
|
-
return new QueryBuilder(Model, 'find', query_filter || {}, projection_value || null);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
Model.find_one = function (query_filter) {
|
|
110
|
-
return new QueryBuilder(Model, 'find_one', query_filter || {}, null);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
Model.count_documents = function (query_filter) {
|
|
114
|
-
return new QueryBuilder(Model, 'count_documents', query_filter || {}, null);
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
Model.update_one = async function (query_filter, update_definition) {
|
|
118
|
-
const update_object = update_definition || {};
|
|
119
|
-
const set_definition = update_object.$set;
|
|
120
|
-
const insert_definition = update_object.$insert;
|
|
121
|
-
const set_lax_definition = update_object.$set_lax;
|
|
122
|
-
|
|
123
|
-
assert_supported_update_definition(set_definition, insert_definition, set_lax_definition);
|
|
124
|
-
|
|
125
|
-
await Model.ensure_table();
|
|
126
|
-
|
|
127
|
-
const table_identifier = quote_identifier(model_options.table_name);
|
|
128
|
-
const data_identifier = quote_identifier(model_options.data_column);
|
|
129
|
-
const where_result = where_compiler(query_filter || {}, {
|
|
130
|
-
data_column: model_options.data_column,
|
|
131
|
-
schema: schema_instance
|
|
132
|
-
});
|
|
133
|
-
const parameter_state = create_parameter_state(where_result.next_index);
|
|
134
|
-
let data_expression = data_identifier;
|
|
135
|
-
|
|
136
|
-
data_expression = apply_set_updates(data_expression, set_definition, parameter_state);
|
|
137
|
-
data_expression = apply_insert_updates(data_expression, insert_definition, parameter_state);
|
|
138
|
-
data_expression = apply_set_lax_updates(data_expression, set_lax_definition, parameter_state);
|
|
139
|
-
|
|
140
|
-
const sql_text =
|
|
141
|
-
'WITH target_row AS (' + 'SELECT id FROM ' + table_identifier + ' WHERE ' + where_result.sql + ' LIMIT 1' + ') ' +
|
|
142
|
-
'UPDATE ' + table_identifier + ' AS target_table ' +
|
|
143
|
-
'SET ' + data_identifier + ' = ' + data_expression + ', updated_at = NOW() ' +
|
|
144
|
-
'FROM target_row ' +
|
|
145
|
-
'WHERE target_table.id = target_row.id ' +
|
|
146
|
-
'RETURNING target_table.' + data_identifier + ' AS data';
|
|
147
|
-
const sql_params = where_result.params.concat(parameter_state.params);
|
|
148
|
-
const query_result = await sql_runner(sql_text, sql_params);
|
|
149
|
-
|
|
150
|
-
if(query_result.rows.length === 0) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return query_result.rows[0].data;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
Model.delete_one = async function (query_filter) {
|
|
158
|
-
const table_identifier = quote_identifier(model_options.table_name);
|
|
159
|
-
const data_identifier = quote_identifier(model_options.data_column);
|
|
160
|
-
const where_result = where_compiler(query_filter || {}, {
|
|
161
|
-
data_column: model_options.data_column,
|
|
162
|
-
schema: schema_instance
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
const sql_text =
|
|
166
|
-
'WITH target_row AS (' + 'SELECT id FROM ' + table_identifier + ' WHERE ' + where_result.sql + ' LIMIT 1' + ') ' +
|
|
167
|
-
'DELETE FROM ' + table_identifier + ' AS target_table ' +
|
|
168
|
-
'USING target_row ' +
|
|
169
|
-
'WHERE target_table.id = target_row.id ' +
|
|
170
|
-
'RETURNING target_table.' + data_identifier + ' AS data';
|
|
171
|
-
let query_result;
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
query_result = await sql_runner(sql_text, where_result.params);
|
|
175
|
-
} catch(error) {
|
|
176
|
-
if(is_missing_relation_query_error(error)) {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
throw error;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if(query_result.rows.length === 0) {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return query_result.rows[0].data;
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
function cast_update_value(path_value, next_value) {
|
|
191
|
-
const field_type = resolve_update_field_type(path_value);
|
|
192
|
-
|
|
193
|
-
if(!field_type || typeof field_type.cast !== 'function') {
|
|
194
|
-
return next_value;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
let casted_value = next_value;
|
|
198
|
-
|
|
199
|
-
if(typeof field_type.apply_set === 'function') {
|
|
200
|
-
casted_value = field_type.apply_set(casted_value, {path: path_value, mode: 'update'});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return field_type.cast(casted_value, {path: path_value, mode: 'update'});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function resolve_update_field_type(path_value) {
|
|
207
|
-
if(!schema_instance || typeof schema_instance.path !== 'function') {
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return schema_instance.path(path_value);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function assert_supported_update_definition(set_definition, insert_definition, set_lax_definition) {
|
|
215
|
-
const has_set_updates = set_definition !== undefined;
|
|
216
|
-
const has_insert_updates = insert_definition !== undefined;
|
|
217
|
-
const has_set_lax_updates = set_lax_definition !== undefined;
|
|
218
|
-
|
|
219
|
-
if(!has_set_updates && !has_insert_updates && !has_set_lax_updates) {
|
|
220
|
-
throw new QueryError('update_one requires at least one supported update operator', {
|
|
221
|
-
allowed: ['$set', '$insert', '$set_lax']
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if(has_set_updates && is_not_object(set_definition)) {
|
|
226
|
-
throw new QueryError('update_definition.$set must be an object', {
|
|
227
|
-
allowed: ['$set', '$insert', '$set_lax']
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if(has_insert_updates && is_not_object(insert_definition)) {
|
|
232
|
-
throw new QueryError('update_definition.$insert must be an object', {
|
|
233
|
-
allowed: ['$set', '$insert', '$set_lax']
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if(has_set_lax_updates && is_not_object(set_lax_definition)) {
|
|
238
|
-
throw new QueryError('update_definition.$set_lax must be an object', {
|
|
239
|
-
allowed: ['$set', '$insert', '$set_lax']
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const update_path_entries = collect_update_path_entries([
|
|
244
|
-
{operator_name: '$set', definition: set_definition},
|
|
245
|
-
{operator_name: '$insert', definition: insert_definition},
|
|
246
|
-
{operator_name: '$set_lax', definition: set_lax_definition}
|
|
247
|
-
]);
|
|
248
|
-
|
|
249
|
-
assert_no_conflicting_update_paths(update_path_entries);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function apply_set_updates(data_expression, set_definition, parameter_state) {
|
|
253
|
-
if(set_definition === undefined) {
|
|
254
|
-
return data_expression;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const set_entries = Object.entries(set_definition);
|
|
258
|
-
let set_index = 0;
|
|
259
|
-
let next_expression = data_expression;
|
|
260
|
-
|
|
261
|
-
while(set_index < set_entries.length) {
|
|
262
|
-
const set_entry = set_entries[set_index];
|
|
263
|
-
const path_value = set_entry[0];
|
|
264
|
-
const next_value = set_entry[1];
|
|
265
|
-
const casted_value = cast_update_value(path_value, next_value);
|
|
266
|
-
const path_literal = build_update_path_literal(path_value);
|
|
267
|
-
const placeholder = bind_parameter(parameter_state, jsonb_stringify(casted_value));
|
|
268
|
-
|
|
269
|
-
next_expression = 'jsonb_set(' + next_expression + ", '" + path_literal + "', " + placeholder + '::jsonb, true)';
|
|
270
|
-
set_index += 1;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return next_expression;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function apply_insert_updates(data_expression, insert_definition, parameter_state) {
|
|
277
|
-
if(insert_definition === undefined) {
|
|
278
|
-
return data_expression;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const insert_entries = Object.entries(insert_definition);
|
|
282
|
-
let insert_index = 0;
|
|
283
|
-
let next_expression = data_expression;
|
|
284
|
-
|
|
285
|
-
while(insert_index < insert_entries.length) {
|
|
286
|
-
const insert_entry = insert_entries[insert_index];
|
|
287
|
-
const path_value = insert_entry[0];
|
|
288
|
-
const insert_config = normalize_insert_update(path_value, insert_entry[1]);
|
|
289
|
-
const casted_value = cast_update_value(path_value, insert_config.value);
|
|
290
|
-
const path_literal = build_update_path_literal(path_value);
|
|
291
|
-
const value_placeholder = bind_parameter(parameter_state, jsonb_stringify(casted_value));
|
|
292
|
-
const insert_after_sql = insert_config.insert_after ? 'true' : 'false';
|
|
293
|
-
|
|
294
|
-
next_expression =
|
|
295
|
-
'jsonb_insert(' + next_expression + ", '" + path_literal + "', " + value_placeholder + '::jsonb, ' + insert_after_sql + ')';
|
|
296
|
-
insert_index += 1;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return next_expression;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function apply_set_lax_updates(data_expression, set_lax_definition, parameter_state) {
|
|
303
|
-
if(set_lax_definition === undefined) {
|
|
304
|
-
return data_expression;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const set_lax_entries = Object.entries(set_lax_definition);
|
|
308
|
-
let set_lax_index = 0;
|
|
309
|
-
let next_expression = data_expression;
|
|
310
|
-
|
|
311
|
-
while(set_lax_index < set_lax_entries.length) {
|
|
312
|
-
const set_lax_entry = set_lax_entries[set_lax_index];
|
|
313
|
-
const path_value = set_lax_entry[0];
|
|
314
|
-
const set_lax_config = normalize_set_lax_update(path_value, set_lax_entry[1]);
|
|
315
|
-
const casted_value = cast_update_value(path_value, set_lax_config.value);
|
|
316
|
-
const path_literal = build_update_path_literal(path_value);
|
|
317
|
-
const value_placeholder = bind_parameter(parameter_state, cast_update_parameter_value(casted_value));
|
|
318
|
-
const create_if_missing_sql = set_lax_config.create_if_missing ? 'true' : 'false';
|
|
319
|
-
|
|
320
|
-
next_expression =
|
|
321
|
-
'jsonb_set_lax(' + next_expression + ", '" + path_literal + "', " + value_placeholder + '::jsonb, ' +
|
|
322
|
-
create_if_missing_sql + ", '" + set_lax_config.null_value_treatment + "')";
|
|
323
|
-
set_lax_index += 1;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
return next_expression;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function build_update_path_literal(path_value) {
|
|
330
|
-
return build_path_literal(split_update_path(path_value));
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function collect_update_path_entries(update_operator_entries) {
|
|
334
|
-
const update_path_entries = [];
|
|
335
|
-
let operator_index = 0;
|
|
336
|
-
|
|
337
|
-
while(operator_index < update_operator_entries.length) {
|
|
338
|
-
const operator_entry = update_operator_entries[operator_index];
|
|
339
|
-
const definition = operator_entry.definition;
|
|
340
|
-
|
|
341
|
-
if(definition === undefined) {
|
|
342
|
-
operator_index += 1;
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const path_entries = Object.entries(definition);
|
|
347
|
-
let path_index = 0;
|
|
348
|
-
|
|
349
|
-
while(path_index < path_entries.length) {
|
|
350
|
-
const path_entry = path_entries[path_index];
|
|
351
|
-
const path_value = path_entry[0];
|
|
352
|
-
|
|
353
|
-
split_update_path(path_value, operator_entry.operator_name);
|
|
354
|
-
update_path_entries.push({
|
|
355
|
-
operator_name: operator_entry.operator_name,
|
|
356
|
-
path: path_value
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
path_index += 1;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
operator_index += 1;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return update_path_entries;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function assert_no_conflicting_update_paths(update_path_entries) {
|
|
369
|
-
let left_index = 0;
|
|
370
|
-
|
|
371
|
-
while(left_index < update_path_entries.length) {
|
|
372
|
-
let right_index = left_index + 1;
|
|
373
|
-
|
|
374
|
-
while(right_index < update_path_entries.length) {
|
|
375
|
-
const left_entry = update_path_entries[left_index];
|
|
376
|
-
const right_entry = update_path_entries[right_index];
|
|
377
|
-
|
|
378
|
-
if(do_update_paths_conflict(left_entry.path, right_entry.path)) {
|
|
379
|
-
throw new QueryError('Conflicting update paths are not allowed in a single update_one call', {
|
|
380
|
-
left: left_entry,
|
|
381
|
-
right: right_entry
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
right_index += 1;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
left_index += 1;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function do_update_paths_conflict(left_path, right_path) {
|
|
393
|
-
if(left_path === right_path) {
|
|
394
|
-
return true;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return left_path.indexOf(right_path + '.') === 0 || right_path.indexOf(left_path + '.') === 0;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
function split_update_path(path_value, operator_name) {
|
|
401
|
-
const path_segments = path_value.split('.');
|
|
402
|
-
let segment_index = 0;
|
|
403
|
-
|
|
404
|
-
while(segment_index < path_segments.length) {
|
|
405
|
-
const segment_value = path_segments[segment_index];
|
|
406
|
-
const is_root = segment_index === 0;
|
|
407
|
-
|
|
408
|
-
if(segment_value.length === 0) {
|
|
409
|
-
throw new QueryError('Update path contains an empty segment', {
|
|
410
|
-
operator: operator_name,
|
|
411
|
-
path: path_value
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if(is_root && !update_path_root_segment_pattern.test(segment_value)) {
|
|
416
|
-
throw new QueryError('Update path root segment has invalid characters', {
|
|
417
|
-
operator: operator_name,
|
|
418
|
-
path: path_value
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if(!is_root && !update_path_nested_segment_pattern.test(segment_value)) {
|
|
423
|
-
throw new QueryError('Update path contains an invalid nested segment', {
|
|
424
|
-
operator: operator_name,
|
|
425
|
-
path: path_value
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
segment_index += 1;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return path_segments;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function cast_update_parameter_value(casted_value) {
|
|
436
|
-
if(casted_value === null) {
|
|
437
|
-
return null;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return jsonb_stringify(casted_value);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function normalize_insert_update(path_value, insert_definition_value) {
|
|
444
|
-
if(is_not_object(insert_definition_value)) {
|
|
445
|
-
return {
|
|
446
|
-
value: insert_definition_value,
|
|
447
|
-
insert_after: false
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
assert_condition(has_own(insert_definition_value, 'value'), '$insert entry for path "' + path_value + '" must include value');
|
|
452
|
-
|
|
453
|
-
if(insert_definition_value.insert_after !== undefined) {
|
|
454
|
-
assert_condition(typeof insert_definition_value.insert_after === 'boolean', '$insert.insert_after for path "' + path_value + '" must be a boolean');
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
value: insert_definition_value.value,
|
|
459
|
-
insert_after: insert_definition_value.insert_after === true
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
function normalize_set_lax_update(path_value, set_lax_definition_value) {
|
|
464
|
-
if(is_not_object(set_lax_definition_value)) {
|
|
465
|
-
return {
|
|
466
|
-
value: set_lax_definition_value,
|
|
467
|
-
create_if_missing: true,
|
|
468
|
-
null_value_treatment: 'use_json_null'
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
assert_condition(has_own(set_lax_definition_value, 'value'), '$set_lax entry for path "' + path_value + '" must include value');
|
|
473
|
-
|
|
474
|
-
if(set_lax_definition_value.create_if_missing !== undefined) {
|
|
475
|
-
assert_condition(typeof set_lax_definition_value.create_if_missing === 'boolean', '$set_lax.create_if_missing for path "' + path_value + '" must be a boolean');
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const null_value_treatment = set_lax_definition_value.null_value_treatment ?? 'use_json_null';
|
|
479
|
-
assert_valid_set_lax_null_treatment(path_value, null_value_treatment);
|
|
480
|
-
|
|
481
|
-
return {
|
|
482
|
-
value: set_lax_definition_value.value,
|
|
483
|
-
create_if_missing: set_lax_definition_value.create_if_missing !== false,
|
|
484
|
-
null_value_treatment: null_value_treatment
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function assert_valid_set_lax_null_treatment(path_value, null_value_treatment) {
|
|
489
|
-
const allowed_null_treatments = ['raise_exception', 'use_json_null', 'delete_key', 'return_target'];
|
|
490
|
-
assert_condition(
|
|
491
|
-
allowed_null_treatments.includes(null_value_treatment),
|
|
492
|
-
'$set_lax.null_value_treatment for path "' + path_value + '" must be one of: ' + allowed_null_treatments.join(', ')
|
|
493
|
-
);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
function is_missing_relation_query_error(error) {
|
|
497
|
-
if(!(error instanceof QueryError)) {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
const cause_message = error.details && error.details.cause;
|
|
502
|
-
|
|
503
|
-
if(typeof cause_message !== 'string') {
|
|
504
|
-
return false;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
return cause_message.indexOf('relation ') !== -1 && cause_message.indexOf('does not exist') !== -1;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
function assert_model_id_strategy_supported(final_id_strategy) {
|
|
511
|
-
if(final_id_strategy !== IdStrategies.uuidv7) {
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if(!has_pool()) {
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
const server_capabilities = get_server_capabilities();
|
|
520
|
-
assert_id_strategy_capability(final_id_strategy, server_capabilities);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
async function ensure_schema_indexes(final_table_name) {
|
|
524
|
-
const schema_indexes = resolve_schema_indexes(schema_instance);
|
|
525
|
-
let index_position = 0;
|
|
526
|
-
|
|
527
|
-
while(index_position < schema_indexes.length) {
|
|
528
|
-
const index_definition = schema_indexes[index_position];
|
|
529
|
-
|
|
530
|
-
await ensure_index(
|
|
531
|
-
final_table_name,
|
|
532
|
-
index_definition.index_spec,
|
|
533
|
-
model_options.data_column,
|
|
534
|
-
index_definition.index_options
|
|
535
|
-
);
|
|
536
|
-
|
|
537
|
-
index_position += 1;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
if(has_pool()) {
|
|
542
|
-
const connection_options = get_connection_options();
|
|
543
|
-
const eager_id_strategy = model_options.id_strategy ?? connection_options.id_strategy;
|
|
544
|
-
|
|
545
|
-
if(eager_id_strategy === IdStrategies.uuidv7) {
|
|
546
|
-
Model.assert_id_strategy_supported();
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return Model;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {is_nan} from '#src/utils/value.js';
|
|
2
|
-
|
|
3
|
-
export default function limit_skip_compiler(limit_value, skip_value) {
|
|
4
|
-
let sql_fragment = '';
|
|
5
|
-
|
|
6
|
-
if(is_valid_integer(limit_value)) {
|
|
7
|
-
sql_fragment += ' LIMIT ' + normalize_integer(limit_value);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if(is_valid_integer(skip_value)) {
|
|
11
|
-
sql_fragment += ' OFFSET ' + normalize_integer(skip_value);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return sql_fragment;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function is_valid_integer(value) {
|
|
18
|
-
if(value === null || value === undefined) {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if(is_nan(value)) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return Number(value) >= 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function normalize_integer(value) {
|
|
30
|
-
return Math.floor(Number(value));
|
|
31
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import {bind_parameter} from '#src/sql/parameter-binder.js';
|
|
2
|
-
|
|
3
|
-
export default function eq_operator(sql_expression, comparison_value, parameter_state) {
|
|
4
|
-
const placeholder = bind_parameter(parameter_state, String(comparison_value));
|
|
5
|
-
return sql_expression + ' = ' + placeholder;
|
|
6
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import {bind_parameter} from '#src/sql/parameter-binder.js';
|
|
2
|
-
|
|
3
|
-
export default function has_key_operator(jsonb_expression, comparison_value, parameter_state) {
|
|
4
|
-
const placeholder = bind_parameter(parameter_state, String(comparison_value));
|
|
5
|
-
return jsonb_expression + ' ? ' + placeholder;
|
|
6
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import all_operator from './all.js';
|
|
2
|
-
import contains_operator from './contains.js';
|
|
3
|
-
import elem_match_operator from './elem-match.js';
|
|
4
|
-
import eq_operator from './eq.js';
|
|
5
|
-
import gt_operator from './gt.js';
|
|
6
|
-
import gte_operator from './gte.js';
|
|
7
|
-
import has_all_keys_operator from './has-all-keys.js';
|
|
8
|
-
import has_any_keys_operator from './has-any-keys.js';
|
|
9
|
-
import has_key_operator from './has-key.js';
|
|
10
|
-
import in_operator from './in.js';
|
|
11
|
-
import jsonpath_exists_operator from './jsonpath-exists.js';
|
|
12
|
-
import jsonpath_match_operator from './jsonpath-match.js';
|
|
13
|
-
import lt_operator from './lt.js';
|
|
14
|
-
import lte_operator from './lte.js';
|
|
15
|
-
import ne_operator from './ne.js';
|
|
16
|
-
import nin_operator from './nin.js';
|
|
17
|
-
import regex_operator from './regex.js';
|
|
18
|
-
import size_operator from './size.js';
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
all_operator,
|
|
22
|
-
contains_operator,
|
|
23
|
-
elem_match_operator,
|
|
24
|
-
eq_operator,
|
|
25
|
-
gt_operator,
|
|
26
|
-
gte_operator,
|
|
27
|
-
has_all_keys_operator,
|
|
28
|
-
has_any_keys_operator,
|
|
29
|
-
has_key_operator,
|
|
30
|
-
in_operator,
|
|
31
|
-
jsonpath_exists_operator,
|
|
32
|
-
jsonpath_match_operator,
|
|
33
|
-
lt_operator,
|
|
34
|
-
lte_operator,
|
|
35
|
-
ne_operator,
|
|
36
|
-
nin_operator,
|
|
37
|
-
regex_operator,
|
|
38
|
-
size_operator
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export default {
|
|
42
|
-
all_operator,
|
|
43
|
-
contains_operator,
|
|
44
|
-
elem_match_operator,
|
|
45
|
-
eq_operator,
|
|
46
|
-
gt_operator,
|
|
47
|
-
gte_operator,
|
|
48
|
-
has_all_keys_operator,
|
|
49
|
-
has_any_keys_operator,
|
|
50
|
-
has_key_operator,
|
|
51
|
-
in_operator,
|
|
52
|
-
jsonpath_exists_operator,
|
|
53
|
-
jsonpath_match_operator,
|
|
54
|
-
lt_operator,
|
|
55
|
-
lte_operator,
|
|
56
|
-
ne_operator,
|
|
57
|
-
nin_operator,
|
|
58
|
-
regex_operator,
|
|
59
|
-
size_operator
|
|
60
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import {bind_parameter} from '#src/sql/parameter-binder.js';
|
|
2
|
-
|
|
3
|
-
export default function ne_operator(sql_expression, comparison_value, parameter_state) {
|
|
4
|
-
const placeholder = bind_parameter(parameter_state, String(comparison_value));
|
|
5
|
-
return sql_expression + ' != ' + placeholder;
|
|
6
|
-
}
|