proto.io 0.0.138 → 0.0.139
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/dist/adapters/file/database.mjs +2 -2
- package/dist/adapters/storage/progres.d.ts +4 -1
- package/dist/adapters/storage/progres.js +360 -365
- package/dist/adapters/storage/progres.js.map +1 -1
- package/dist/adapters/storage/progres.mjs +363 -368
- package/dist/adapters/storage/progres.mjs.map +1 -1
- package/dist/client.mjs +3 -3
- package/dist/index.mjs +5 -5
- package/dist/internals/{index-Cn4RsP3U.mjs → index-C8hhBfuh.mjs} +2 -2
- package/dist/internals/{index-Cn4RsP3U.mjs.map → index-C8hhBfuh.mjs.map} +1 -1
- package/dist/internals/{index-DFAi7Z7H.mjs → index-DTZ5uRgF.mjs} +2 -2
- package/dist/internals/{index-DFAi7Z7H.mjs.map → index-DTZ5uRgF.mjs.map} +1 -1
- package/dist/internals/{index-DRzRIomK.mjs → index-EOtjV6U_.mjs} +2 -2
- package/dist/internals/{index-DRzRIomK.mjs.map → index-EOtjV6U_.mjs.map} +1 -1
- package/dist/internals/{random-D0f0zJgp.mjs → random-Dn9rhwfh.mjs} +3 -3
- package/dist/internals/{random-D0f0zJgp.mjs.map → random-Dn9rhwfh.mjs.map} +1 -1
- package/package.json +1 -1
|
@@ -140,267 +140,6 @@ class SQL {
|
|
|
140
140
|
}
|
|
141
141
|
const sql = (templates, ...values) => new SQL(templates, values);
|
|
142
142
|
|
|
143
|
-
//
|
|
144
|
-
// basic.ts
|
|
145
|
-
//
|
|
146
|
-
// The MIT License
|
|
147
|
-
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
148
|
-
//
|
|
149
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
150
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
151
|
-
// in the Software without restriction, including without limitation the rights
|
|
152
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
153
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
154
|
-
// furnished to do so, subject to the following conditions:
|
|
155
|
-
//
|
|
156
|
-
// The above copyright notice and this permission notice shall be included in
|
|
157
|
-
// all copies or substantial portions of the Software.
|
|
158
|
-
//
|
|
159
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
160
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
161
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
162
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
163
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
164
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
165
|
-
// THE SOFTWARE.
|
|
166
|
-
//
|
|
167
|
-
const stringArrayAttrs = ['_rperm', '_wperm'];
|
|
168
|
-
const nullSafeEqual = () => sql `IS NOT DISTINCT FROM`;
|
|
169
|
-
const nullSafeNotEqual = () => sql `IS DISTINCT FROM`;
|
|
170
|
-
const quote = (str) => utils.escapeLiteral(str);
|
|
171
|
-
const identifier = (name) => utils.escapeIdentifier(name);
|
|
172
|
-
const placeholder = (idx) => `$${idx}`;
|
|
173
|
-
const boolean = (value) => value ? 'true' : 'false';
|
|
174
|
-
const encodeSortKey = (className, key) => {
|
|
175
|
-
const [colname, ...subpath] = _.toPath(key);
|
|
176
|
-
if (_.isEmpty(subpath))
|
|
177
|
-
return sql `${{ identifier: className }}.${{ identifier: colname }}`;
|
|
178
|
-
return sql `jsonb_extract_path(
|
|
179
|
-
${{ identifier: className }}.${{ identifier: colname }},
|
|
180
|
-
${_.map(subpath, x => sql `${{ quote: x }}`)}
|
|
181
|
-
)`;
|
|
182
|
-
};
|
|
183
|
-
const random = (opts) => {
|
|
184
|
-
return opts.weight ? sql `-ln(random()) / ${{ identifier: opts.weight }}` : sql `random()`;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
//
|
|
188
|
-
// index.ts
|
|
189
|
-
//
|
|
190
|
-
// The MIT License
|
|
191
|
-
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
192
|
-
//
|
|
193
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
194
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
195
|
-
// in the Software without restriction, including without limitation the rights
|
|
196
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
197
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
198
|
-
// furnished to do so, subject to the following conditions:
|
|
199
|
-
//
|
|
200
|
-
// The above copyright notice and this permission notice shall be included in
|
|
201
|
-
// all copies or substantial portions of the Software.
|
|
202
|
-
//
|
|
203
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
204
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
205
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
206
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
207
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
208
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
209
|
-
// THE SOFTWARE.
|
|
210
|
-
//
|
|
211
|
-
const typeParser = (oid, format) => {
|
|
212
|
-
format = format ?? 'text';
|
|
213
|
-
if (format === 'text') {
|
|
214
|
-
switch (oid) {
|
|
215
|
-
case pg.types.builtins.MONEY:
|
|
216
|
-
return (value) => new Decimal(value);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return pg.types.getTypeParser(oid, format);
|
|
220
|
-
};
|
|
221
|
-
class PostgresClientDriver {
|
|
222
|
-
db;
|
|
223
|
-
constructor(db) {
|
|
224
|
-
this.db = db;
|
|
225
|
-
}
|
|
226
|
-
query(text, values = [], batchSize) {
|
|
227
|
-
const db = this.db;
|
|
228
|
-
return utilsJs.asyncStream(async function* () {
|
|
229
|
-
const client = db instanceof pg.Pool ? await db.connect() : db;
|
|
230
|
-
const stream = new QueryStream(text, values, { batchSize });
|
|
231
|
-
client.query(stream);
|
|
232
|
-
try {
|
|
233
|
-
yield* stream;
|
|
234
|
-
}
|
|
235
|
-
finally {
|
|
236
|
-
stream.destroy();
|
|
237
|
-
if (db instanceof pg.Pool)
|
|
238
|
-
client.release();
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
async version() {
|
|
243
|
-
const [{ version }] = await this.query('SELECT version()');
|
|
244
|
-
return version;
|
|
245
|
-
}
|
|
246
|
-
async databases() {
|
|
247
|
-
return _.compact(_.map(await this.query('SELECT datname FROM pg_catalog.pg_database'), x => x.datname));
|
|
248
|
-
}
|
|
249
|
-
async tables() {
|
|
250
|
-
return _.compact(_.map(await this.query(`
|
|
251
|
-
SELECT tablename FROM pg_catalog.pg_tables
|
|
252
|
-
WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
|
|
253
|
-
`), x => x.tablename));
|
|
254
|
-
}
|
|
255
|
-
async views() {
|
|
256
|
-
return _.compact(_.map(await this.query(`
|
|
257
|
-
SELECT viewname FROM pg_catalog.pg_views
|
|
258
|
-
WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
|
|
259
|
-
`), x => x.viewname));
|
|
260
|
-
}
|
|
261
|
-
async materializedViews() {
|
|
262
|
-
return _.compact(_.map(await this.query(`
|
|
263
|
-
SELECT matviewname FROM pg_catalog.pg_matviews
|
|
264
|
-
WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
|
|
265
|
-
`), x => x.matviewname));
|
|
266
|
-
}
|
|
267
|
-
async columns(table, namespace) {
|
|
268
|
-
const columns = await this.query(`
|
|
269
|
-
SELECT
|
|
270
|
-
a.attname AS column_name,
|
|
271
|
-
format_type(a.atttypid, a.atttypmod) AS data_type,
|
|
272
|
-
a.attnum ,
|
|
273
|
-
a.attnotnull
|
|
274
|
-
FROM
|
|
275
|
-
pg_namespace n,
|
|
276
|
-
pg_class t,
|
|
277
|
-
pg_attribute a
|
|
278
|
-
WHERE
|
|
279
|
-
a.attnum > 0
|
|
280
|
-
AND n.oid = t.relnamespace
|
|
281
|
-
AND a.attrelid = t.oid
|
|
282
|
-
AND NOT a.attisdropped
|
|
283
|
-
AND t.relname = '${table}'
|
|
284
|
-
${namespace ? `AND n.nspname = '${namespace}'` : ''}
|
|
285
|
-
`);
|
|
286
|
-
return _.map(columns, ({ column_name, data_type, attnotnull }) => ({
|
|
287
|
-
name: column_name,
|
|
288
|
-
type: data_type,
|
|
289
|
-
required: !!attnotnull,
|
|
290
|
-
}));
|
|
291
|
-
}
|
|
292
|
-
async indices(table, namespace) {
|
|
293
|
-
const indices = await this.query(`
|
|
294
|
-
SELECT
|
|
295
|
-
n.nspname AS schema_name,
|
|
296
|
-
t.relname AS table_name,
|
|
297
|
-
i.relname AS index_name,
|
|
298
|
-
ix.indisprimary AS is_primary,
|
|
299
|
-
ix.indisunique AS is_unique,
|
|
300
|
-
a.attname AS column_name,
|
|
301
|
-
k.indseq AS seq
|
|
302
|
-
FROM
|
|
303
|
-
pg_namespace n,
|
|
304
|
-
pg_class t,
|
|
305
|
-
pg_class i,
|
|
306
|
-
pg_index ix,
|
|
307
|
-
UNNEST(ix.indkey) WITH ORDINALITY k(attnum, indseq),
|
|
308
|
-
pg_attribute a
|
|
309
|
-
WHERE
|
|
310
|
-
t.oid = ix.indrelid
|
|
311
|
-
AND n.oid = t.relnamespace
|
|
312
|
-
AND i.oid = ix.indexrelid
|
|
313
|
-
AND a.attrelid = t.oid
|
|
314
|
-
AND a.attnum = k.attnum
|
|
315
|
-
AND t.relkind = 'r'
|
|
316
|
-
AND t.relname = '${table}'
|
|
317
|
-
${namespace ? `AND n.nspname = '${namespace}'` : ''}
|
|
318
|
-
`);
|
|
319
|
-
return _.mapValues(_.groupBy(indices, 'index_name'), v => ({
|
|
320
|
-
keys: _.map(_.sortBy(v, ({ seq }) => parseInt(seq)), 'column_name'),
|
|
321
|
-
..._.pick(_.first(v), [
|
|
322
|
-
'schema_name',
|
|
323
|
-
'table_name',
|
|
324
|
-
'index_name',
|
|
325
|
-
'is_primary',
|
|
326
|
-
'is_unique',
|
|
327
|
-
])
|
|
328
|
-
}));
|
|
329
|
-
}
|
|
330
|
-
async withClient(callback) {
|
|
331
|
-
const client = this.db instanceof pg.Pool ? await this.db.connect() : this.db;
|
|
332
|
-
try {
|
|
333
|
-
return await callback(new PostgresClientDriver(client));
|
|
334
|
-
}
|
|
335
|
-
finally {
|
|
336
|
-
if (this.db instanceof pg.Pool)
|
|
337
|
-
client.release();
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
async publish(payload) {
|
|
341
|
-
await this.withClient(async (db) => {
|
|
342
|
-
await db.query(`NOTIFY ${PROTO_POSTGRES_MSG}, ${quote(JSON.stringify(index._encodeValue(payload)))}`);
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
class PostgresDriver extends PostgresClientDriver {
|
|
347
|
-
database;
|
|
348
|
-
pubsub;
|
|
349
|
-
subscribers = [];
|
|
350
|
-
constructor(config) {
|
|
351
|
-
if (_.isEmpty(config))
|
|
352
|
-
throw Error('Invalid postgre config.');
|
|
353
|
-
const _types = { getTypeParser: typeParser };
|
|
354
|
-
const database = new pg.Pool(_.isString(config) ? { connectionString: config, types: _types } : { ...config, types: _types });
|
|
355
|
-
super(database);
|
|
356
|
-
this.database = database;
|
|
357
|
-
}
|
|
358
|
-
async shutdown() {
|
|
359
|
-
await this._release_pubsub();
|
|
360
|
-
await this.database.end();
|
|
361
|
-
}
|
|
362
|
-
async _init_pubsub() {
|
|
363
|
-
if (this.pubsub)
|
|
364
|
-
return;
|
|
365
|
-
try {
|
|
366
|
-
this.pubsub = this.database.connect();
|
|
367
|
-
const pubsub = await this.pubsub;
|
|
368
|
-
pubsub.on('notification', ({ channel, payload }) => {
|
|
369
|
-
if (_.toUpper(channel) !== PROTO_POSTGRES_MSG || !payload)
|
|
370
|
-
return;
|
|
371
|
-
try {
|
|
372
|
-
const _payload = index._decodeValue(JSON.parse(payload));
|
|
373
|
-
for (const subscriber of this.subscribers) {
|
|
374
|
-
subscriber(_payload);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
catch (e) {
|
|
378
|
-
console.error(`Unknown payload: ${e}`);
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
await pubsub.query(`LISTEN ${PROTO_POSTGRES_MSG}`);
|
|
382
|
-
}
|
|
383
|
-
catch (e) {
|
|
384
|
-
console.error(e);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
async _release_pubsub() {
|
|
388
|
-
const pubsub = this.pubsub;
|
|
389
|
-
this.pubsub = undefined;
|
|
390
|
-
await (await pubsub)?.release();
|
|
391
|
-
}
|
|
392
|
-
subscribe(callback) {
|
|
393
|
-
this._init_pubsub();
|
|
394
|
-
this.subscribers.push(callback);
|
|
395
|
-
return () => {
|
|
396
|
-
this.subscribers = this.subscribers.filter(x => x !== callback);
|
|
397
|
-
if (_.isEmpty(this.subscribers)) {
|
|
398
|
-
this._release_pubsub();
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
143
|
//
|
|
405
144
|
// compiler.ts
|
|
406
145
|
//
|
|
@@ -696,7 +435,7 @@ class QueryCompiler {
|
|
|
696
435
|
})}`;
|
|
697
436
|
}
|
|
698
437
|
return sql `${_.map(sort, (order, key) => sql `
|
|
699
|
-
${this.dialect.encodeSortKey(parent
|
|
438
|
+
${this.dialect.encodeSortKey(this, parent, key)} ${{ literal: order === 1 ? 'ASC' : 'DESC' }}
|
|
700
439
|
`)}`;
|
|
701
440
|
}
|
|
702
441
|
_selectPopulateMap(context, className, name) {
|
|
@@ -1023,7 +762,7 @@ class SqlStorage {
|
|
|
1023
762
|
}
|
|
1024
763
|
|
|
1025
764
|
//
|
|
1026
|
-
//
|
|
765
|
+
// utils.ts
|
|
1027
766
|
//
|
|
1028
767
|
// The MIT License
|
|
1029
768
|
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
@@ -1046,7 +785,363 @@ class SqlStorage {
|
|
|
1046
785
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1047
786
|
// THE SOFTWARE.
|
|
1048
787
|
//
|
|
1049
|
-
const
|
|
788
|
+
const _fetchElement = (parent, colname, subpath, dataType) => {
|
|
789
|
+
const element = sql `${{ identifier: parent.name }}.${{ identifier: parent.name.startsWith('_expr_$') ? '$' : colname }}`;
|
|
790
|
+
if (!parent.className) {
|
|
791
|
+
if (colname !== '$') {
|
|
792
|
+
return {
|
|
793
|
+
element: sql `jsonb_extract_path(${element}, ${_.map([colname, ...subpath], x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
|
|
794
|
+
json: true,
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
else if (!_.isEmpty(subpath)) {
|
|
798
|
+
return {
|
|
799
|
+
element: sql `jsonb_extract_path(${element}, ${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
|
|
800
|
+
json: true,
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
else if (!_.isEmpty(subpath)) {
|
|
805
|
+
const _subpath = sql `${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)}`;
|
|
806
|
+
if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'relation'))) {
|
|
807
|
+
return {
|
|
808
|
+
element: sql `jsonb_extract_path(to_jsonb(${element}), ${_subpath})`,
|
|
809
|
+
json: true,
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
else {
|
|
813
|
+
return {
|
|
814
|
+
element: sql `jsonb_extract_path(${element}, ${_subpath})`,
|
|
815
|
+
json: true,
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (parent.name.startsWith('_expr_$') && colname !== '$') {
|
|
820
|
+
return {
|
|
821
|
+
element: sql `jsonb_extract_path(${element}, ${{ quote: colname.startsWith('$') ? `$${colname}` : colname }})`,
|
|
822
|
+
json: true,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
return { element, json: false };
|
|
826
|
+
};
|
|
827
|
+
const resolvePaths = (compiler, className, paths) => {
|
|
828
|
+
const { paths: [colname, ...subpath], dataType } = random$1._resolveColumn(compiler.schema, className, paths.join('.'));
|
|
829
|
+
if (!_.isEmpty(subpath) && index.isVector(dataType)) {
|
|
830
|
+
if (subpath.length !== 1)
|
|
831
|
+
throw Error(`Invalid key: ${paths.join('.')}`);
|
|
832
|
+
const idx = parseInt(subpath[0]);
|
|
833
|
+
if (_.isSafeInteger(idx) && idx >= 0 && idx < dataType.dimension) {
|
|
834
|
+
return { dataType: 'number', colname: `${colname}[${idx}]`, subpath: [] };
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
throw Error(`Invalid key: ${paths.join('.')}`);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
if (!_.isEmpty(subpath) && index.isPointer(dataType)) {
|
|
841
|
+
const resolved = resolvePaths(compiler, dataType.target, subpath);
|
|
842
|
+
return { ...resolved, colname: `${colname}.${resolved.colname}` };
|
|
843
|
+
}
|
|
844
|
+
const digit = _.first(subpath);
|
|
845
|
+
if (!_.isEmpty(subpath) && index.isRelation(dataType) && digit?.match(random$1.QueryValidator.patterns.digits)) {
|
|
846
|
+
const resolved = resolvePaths(compiler, dataType.target, _.slice(subpath, 1));
|
|
847
|
+
return { dataType, colname, subpath: [digit, resolved.colname, ...resolved.subpath] };
|
|
848
|
+
}
|
|
849
|
+
return { dataType, colname, subpath };
|
|
850
|
+
};
|
|
851
|
+
const fetchElement = (compiler, parent, field) => {
|
|
852
|
+
if (parent.className) {
|
|
853
|
+
const { dataType, colname, subpath } = resolvePaths(compiler, parent.className, _.toPath(field));
|
|
854
|
+
if (index.isPointer(dataType))
|
|
855
|
+
return { element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}._id` }}`, dataType };
|
|
856
|
+
const { element, json } = _fetchElement(parent, colname, subpath, dataType);
|
|
857
|
+
return { element, json, dataType: json ? null : dataType, relation: index.isRelation(dataType) ? dataType : null };
|
|
858
|
+
}
|
|
859
|
+
const [colname, ...subpath] = _.toPath(field);
|
|
860
|
+
const { element, json } = _fetchElement(parent, colname, subpath);
|
|
861
|
+
return { element, json, dataType: null, relation: null };
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
//
|
|
865
|
+
// basic.ts
|
|
866
|
+
//
|
|
867
|
+
// The MIT License
|
|
868
|
+
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
869
|
+
//
|
|
870
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
871
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
872
|
+
// in the Software without restriction, including without limitation the rights
|
|
873
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
874
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
875
|
+
// furnished to do so, subject to the following conditions:
|
|
876
|
+
//
|
|
877
|
+
// The above copyright notice and this permission notice shall be included in
|
|
878
|
+
// all copies or substantial portions of the Software.
|
|
879
|
+
//
|
|
880
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
881
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
882
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
883
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
884
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
885
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
886
|
+
// THE SOFTWARE.
|
|
887
|
+
//
|
|
888
|
+
const stringArrayAttrs = ['_rperm', '_wperm'];
|
|
889
|
+
const nullSafeEqual = () => sql `IS NOT DISTINCT FROM`;
|
|
890
|
+
const nullSafeNotEqual = () => sql `IS DISTINCT FROM`;
|
|
891
|
+
const quote = (str) => utils.escapeLiteral(str);
|
|
892
|
+
const identifier = (name) => utils.escapeIdentifier(name);
|
|
893
|
+
const placeholder = (idx) => `$${idx}`;
|
|
894
|
+
const boolean = (value) => value ? 'true' : 'false';
|
|
895
|
+
const encodeSortKey = (compiler, parent, key) => {
|
|
896
|
+
const { element } = fetchElement(compiler, parent, key);
|
|
897
|
+
return element;
|
|
898
|
+
};
|
|
899
|
+
const random = (opts) => {
|
|
900
|
+
return opts.weight ? sql `-ln(random()) / ${{ identifier: opts.weight }}` : sql `random()`;
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
//
|
|
904
|
+
// index.ts
|
|
905
|
+
//
|
|
906
|
+
// The MIT License
|
|
907
|
+
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
908
|
+
//
|
|
909
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
910
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
911
|
+
// in the Software without restriction, including without limitation the rights
|
|
912
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
913
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
914
|
+
// furnished to do so, subject to the following conditions:
|
|
915
|
+
//
|
|
916
|
+
// The above copyright notice and this permission notice shall be included in
|
|
917
|
+
// all copies or substantial portions of the Software.
|
|
918
|
+
//
|
|
919
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
920
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
921
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
922
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
923
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
924
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
925
|
+
// THE SOFTWARE.
|
|
926
|
+
//
|
|
927
|
+
const typeParser = (oid, format) => {
|
|
928
|
+
format = format ?? 'text';
|
|
929
|
+
if (format === 'text') {
|
|
930
|
+
switch (oid) {
|
|
931
|
+
case pg.types.builtins.MONEY:
|
|
932
|
+
return (value) => new Decimal(value);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return pg.types.getTypeParser(oid, format);
|
|
936
|
+
};
|
|
937
|
+
class PostgresClientDriver {
|
|
938
|
+
db;
|
|
939
|
+
constructor(db) {
|
|
940
|
+
this.db = db;
|
|
941
|
+
}
|
|
942
|
+
query(text, values = [], batchSize) {
|
|
943
|
+
const db = this.db;
|
|
944
|
+
return utilsJs.asyncStream(async function* () {
|
|
945
|
+
const client = db instanceof pg.Pool ? await db.connect() : db;
|
|
946
|
+
const stream = new QueryStream(text, values, { batchSize });
|
|
947
|
+
client.query(stream);
|
|
948
|
+
try {
|
|
949
|
+
yield* stream;
|
|
950
|
+
}
|
|
951
|
+
finally {
|
|
952
|
+
stream.destroy();
|
|
953
|
+
if (db instanceof pg.Pool)
|
|
954
|
+
client.release();
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
async version() {
|
|
959
|
+
const [{ version }] = await this.query('SELECT version()');
|
|
960
|
+
return version;
|
|
961
|
+
}
|
|
962
|
+
async databases() {
|
|
963
|
+
return _.compact(_.map(await this.query('SELECT datname FROM pg_catalog.pg_database'), x => x.datname));
|
|
964
|
+
}
|
|
965
|
+
async tables() {
|
|
966
|
+
return _.compact(_.map(await this.query(`
|
|
967
|
+
SELECT tablename FROM pg_catalog.pg_tables
|
|
968
|
+
WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
|
|
969
|
+
`), x => x.tablename));
|
|
970
|
+
}
|
|
971
|
+
async views() {
|
|
972
|
+
return _.compact(_.map(await this.query(`
|
|
973
|
+
SELECT viewname FROM pg_catalog.pg_views
|
|
974
|
+
WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
|
|
975
|
+
`), x => x.viewname));
|
|
976
|
+
}
|
|
977
|
+
async materializedViews() {
|
|
978
|
+
return _.compact(_.map(await this.query(`
|
|
979
|
+
SELECT matviewname FROM pg_catalog.pg_matviews
|
|
980
|
+
WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'
|
|
981
|
+
`), x => x.matviewname));
|
|
982
|
+
}
|
|
983
|
+
async columns(table, namespace) {
|
|
984
|
+
const columns = await this.query(`
|
|
985
|
+
SELECT
|
|
986
|
+
a.attname AS column_name,
|
|
987
|
+
format_type(a.atttypid, a.atttypmod) AS data_type,
|
|
988
|
+
a.attnum ,
|
|
989
|
+
a.attnotnull
|
|
990
|
+
FROM
|
|
991
|
+
pg_namespace n,
|
|
992
|
+
pg_class t,
|
|
993
|
+
pg_attribute a
|
|
994
|
+
WHERE
|
|
995
|
+
a.attnum > 0
|
|
996
|
+
AND n.oid = t.relnamespace
|
|
997
|
+
AND a.attrelid = t.oid
|
|
998
|
+
AND NOT a.attisdropped
|
|
999
|
+
AND t.relname = '${table}'
|
|
1000
|
+
${namespace ? `AND n.nspname = '${namespace}'` : ''}
|
|
1001
|
+
`);
|
|
1002
|
+
return _.map(columns, ({ column_name, data_type, attnotnull }) => ({
|
|
1003
|
+
name: column_name,
|
|
1004
|
+
type: data_type,
|
|
1005
|
+
required: !!attnotnull,
|
|
1006
|
+
}));
|
|
1007
|
+
}
|
|
1008
|
+
async indices(table, namespace) {
|
|
1009
|
+
const indices = await this.query(`
|
|
1010
|
+
SELECT
|
|
1011
|
+
n.nspname AS schema_name,
|
|
1012
|
+
t.relname AS table_name,
|
|
1013
|
+
i.relname AS index_name,
|
|
1014
|
+
ix.indisprimary AS is_primary,
|
|
1015
|
+
ix.indisunique AS is_unique,
|
|
1016
|
+
a.attname AS column_name,
|
|
1017
|
+
k.indseq AS seq
|
|
1018
|
+
FROM
|
|
1019
|
+
pg_namespace n,
|
|
1020
|
+
pg_class t,
|
|
1021
|
+
pg_class i,
|
|
1022
|
+
pg_index ix,
|
|
1023
|
+
UNNEST(ix.indkey) WITH ORDINALITY k(attnum, indseq),
|
|
1024
|
+
pg_attribute a
|
|
1025
|
+
WHERE
|
|
1026
|
+
t.oid = ix.indrelid
|
|
1027
|
+
AND n.oid = t.relnamespace
|
|
1028
|
+
AND i.oid = ix.indexrelid
|
|
1029
|
+
AND a.attrelid = t.oid
|
|
1030
|
+
AND a.attnum = k.attnum
|
|
1031
|
+
AND t.relkind = 'r'
|
|
1032
|
+
AND t.relname = '${table}'
|
|
1033
|
+
${namespace ? `AND n.nspname = '${namespace}'` : ''}
|
|
1034
|
+
`);
|
|
1035
|
+
return _.mapValues(_.groupBy(indices, 'index_name'), v => ({
|
|
1036
|
+
keys: _.map(_.sortBy(v, ({ seq }) => parseInt(seq)), 'column_name'),
|
|
1037
|
+
..._.pick(_.first(v), [
|
|
1038
|
+
'schema_name',
|
|
1039
|
+
'table_name',
|
|
1040
|
+
'index_name',
|
|
1041
|
+
'is_primary',
|
|
1042
|
+
'is_unique',
|
|
1043
|
+
])
|
|
1044
|
+
}));
|
|
1045
|
+
}
|
|
1046
|
+
async withClient(callback) {
|
|
1047
|
+
const client = this.db instanceof pg.Pool ? await this.db.connect() : this.db;
|
|
1048
|
+
try {
|
|
1049
|
+
return await callback(new PostgresClientDriver(client));
|
|
1050
|
+
}
|
|
1051
|
+
finally {
|
|
1052
|
+
if (this.db instanceof pg.Pool)
|
|
1053
|
+
client.release();
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
async publish(payload) {
|
|
1057
|
+
await this.withClient(async (db) => {
|
|
1058
|
+
await db.query(`NOTIFY ${PROTO_POSTGRES_MSG}, ${quote(JSON.stringify(index._encodeValue(payload)))}`);
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
class PostgresDriver extends PostgresClientDriver {
|
|
1063
|
+
database;
|
|
1064
|
+
pubsub;
|
|
1065
|
+
subscribers = [];
|
|
1066
|
+
constructor(config) {
|
|
1067
|
+
if (_.isEmpty(config))
|
|
1068
|
+
throw Error('Invalid postgre config.');
|
|
1069
|
+
const _types = { getTypeParser: typeParser };
|
|
1070
|
+
const database = new pg.Pool(_.isString(config) ? { connectionString: config, types: _types } : { ...config, types: _types });
|
|
1071
|
+
super(database);
|
|
1072
|
+
this.database = database;
|
|
1073
|
+
}
|
|
1074
|
+
async shutdown() {
|
|
1075
|
+
await this._release_pubsub();
|
|
1076
|
+
await this.database.end();
|
|
1077
|
+
}
|
|
1078
|
+
async _init_pubsub() {
|
|
1079
|
+
if (this.pubsub)
|
|
1080
|
+
return;
|
|
1081
|
+
try {
|
|
1082
|
+
this.pubsub = this.database.connect();
|
|
1083
|
+
const pubsub = await this.pubsub;
|
|
1084
|
+
pubsub.on('notification', ({ channel, payload }) => {
|
|
1085
|
+
if (_.toUpper(channel) !== PROTO_POSTGRES_MSG || !payload)
|
|
1086
|
+
return;
|
|
1087
|
+
try {
|
|
1088
|
+
const _payload = index._decodeValue(JSON.parse(payload));
|
|
1089
|
+
for (const subscriber of this.subscribers) {
|
|
1090
|
+
subscriber(_payload);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
catch (e) {
|
|
1094
|
+
console.error(`Unknown payload: ${e}`);
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
await pubsub.query(`LISTEN ${PROTO_POSTGRES_MSG}`);
|
|
1098
|
+
}
|
|
1099
|
+
catch (e) {
|
|
1100
|
+
console.error(e);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
async _release_pubsub() {
|
|
1104
|
+
const pubsub = this.pubsub;
|
|
1105
|
+
this.pubsub = undefined;
|
|
1106
|
+
await (await pubsub)?.release();
|
|
1107
|
+
}
|
|
1108
|
+
subscribe(callback) {
|
|
1109
|
+
this._init_pubsub();
|
|
1110
|
+
this.subscribers.push(callback);
|
|
1111
|
+
return () => {
|
|
1112
|
+
this.subscribers = this.subscribers.filter(x => x !== callback);
|
|
1113
|
+
if (_.isEmpty(this.subscribers)) {
|
|
1114
|
+
this._release_pubsub();
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
//
|
|
1121
|
+
// encode.ts
|
|
1122
|
+
//
|
|
1123
|
+
// The MIT License
|
|
1124
|
+
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
1125
|
+
//
|
|
1126
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1127
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
1128
|
+
// in the Software without restriction, including without limitation the rights
|
|
1129
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1130
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
1131
|
+
// furnished to do so, subject to the following conditions:
|
|
1132
|
+
//
|
|
1133
|
+
// The above copyright notice and this permission notice shall be included in
|
|
1134
|
+
// all copies or substantial portions of the Software.
|
|
1135
|
+
//
|
|
1136
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1137
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1138
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1139
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1140
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1141
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1142
|
+
// THE SOFTWARE.
|
|
1143
|
+
//
|
|
1144
|
+
const _encodeJsonValue = (value) => {
|
|
1050
1145
|
if (_.isArray(value))
|
|
1051
1146
|
return sql `jsonb_build_array(${_.map(value, x => _encodeJsonValue(x))})`;
|
|
1052
1147
|
if (_.isPlainObject(value))
|
|
@@ -1470,106 +1565,6 @@ const updateOperation = (paths, dataType, operation) => {
|
|
|
1470
1565
|
throw Error('Invalid update operation');
|
|
1471
1566
|
};
|
|
1472
1567
|
|
|
1473
|
-
//
|
|
1474
|
-
// utils.ts
|
|
1475
|
-
//
|
|
1476
|
-
// The MIT License
|
|
1477
|
-
// Copyright (c) 2021 - 2024 O2ter Limited. All rights reserved.
|
|
1478
|
-
//
|
|
1479
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1480
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
1481
|
-
// in the Software without restriction, including without limitation the rights
|
|
1482
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1483
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
1484
|
-
// furnished to do so, subject to the following conditions:
|
|
1485
|
-
//
|
|
1486
|
-
// The above copyright notice and this permission notice shall be included in
|
|
1487
|
-
// all copies or substantial portions of the Software.
|
|
1488
|
-
//
|
|
1489
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1490
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1491
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1492
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1493
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1494
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1495
|
-
// THE SOFTWARE.
|
|
1496
|
-
//
|
|
1497
|
-
const _fetchElement = (parent, colname, subpath, dataType) => {
|
|
1498
|
-
const element = sql `${{ identifier: parent.name }}.${{ identifier: parent.name.startsWith('_expr_$') ? '$' : colname }}`;
|
|
1499
|
-
if (!parent.className) {
|
|
1500
|
-
if (colname !== '$') {
|
|
1501
|
-
return {
|
|
1502
|
-
element: sql `jsonb_extract_path(${element}, ${_.map([colname, ...subpath], x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
|
|
1503
|
-
json: true,
|
|
1504
|
-
};
|
|
1505
|
-
}
|
|
1506
|
-
else if (!_.isEmpty(subpath)) {
|
|
1507
|
-
return {
|
|
1508
|
-
element: sql `jsonb_extract_path(${element}, ${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)})`,
|
|
1509
|
-
json: true,
|
|
1510
|
-
};
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
else if (!_.isEmpty(subpath)) {
|
|
1514
|
-
const _subpath = sql `${_.map(subpath, x => sql `${{ quote: x.startsWith('$') ? `$${x}` : x }}`)}`;
|
|
1515
|
-
if (dataType === 'array' || (!_.isString(dataType) && (dataType?.type === 'array' || dataType?.type === 'relation'))) {
|
|
1516
|
-
return {
|
|
1517
|
-
element: sql `jsonb_extract_path(to_jsonb(${element}), ${_subpath})`,
|
|
1518
|
-
json: true,
|
|
1519
|
-
};
|
|
1520
|
-
}
|
|
1521
|
-
else {
|
|
1522
|
-
return {
|
|
1523
|
-
element: sql `jsonb_extract_path(${element}, ${_subpath})`,
|
|
1524
|
-
json: true,
|
|
1525
|
-
};
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
1528
|
-
if (parent.name.startsWith('_expr_$') && colname !== '$') {
|
|
1529
|
-
return {
|
|
1530
|
-
element: sql `jsonb_extract_path(${element}, ${{ quote: colname.startsWith('$') ? `$${colname}` : colname }})`,
|
|
1531
|
-
json: true,
|
|
1532
|
-
};
|
|
1533
|
-
}
|
|
1534
|
-
return { element, json: false };
|
|
1535
|
-
};
|
|
1536
|
-
const resolvePaths = (compiler, className, paths) => {
|
|
1537
|
-
const { paths: [colname, ...subpath], dataType } = random$1._resolveColumn(compiler.schema, className, paths.join('.'));
|
|
1538
|
-
if (!_.isEmpty(subpath) && index.isVector(dataType)) {
|
|
1539
|
-
if (subpath.length !== 1)
|
|
1540
|
-
throw Error(`Invalid key: ${paths.join('.')}`);
|
|
1541
|
-
const idx = parseInt(subpath[0]);
|
|
1542
|
-
if (_.isSafeInteger(idx) && idx >= 0 && idx < dataType.dimension) {
|
|
1543
|
-
return { dataType: 'number', colname: `${colname}[${idx}]`, subpath: [] };
|
|
1544
|
-
}
|
|
1545
|
-
else {
|
|
1546
|
-
throw Error(`Invalid key: ${paths.join('.')}`);
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
if (!_.isEmpty(subpath) && index.isPointer(dataType)) {
|
|
1550
|
-
const resolved = resolvePaths(compiler, dataType.target, subpath);
|
|
1551
|
-
return { ...resolved, colname: `${colname}.${resolved.colname}` };
|
|
1552
|
-
}
|
|
1553
|
-
const digit = _.first(subpath);
|
|
1554
|
-
if (!_.isEmpty(subpath) && index.isRelation(dataType) && digit?.match(random$1.QueryValidator.patterns.digits)) {
|
|
1555
|
-
const resolved = resolvePaths(compiler, dataType.target, _.slice(subpath, 1));
|
|
1556
|
-
return { dataType, colname, subpath: [digit, resolved.colname, ...resolved.subpath] };
|
|
1557
|
-
}
|
|
1558
|
-
return { dataType, colname, subpath };
|
|
1559
|
-
};
|
|
1560
|
-
const fetchElement = (compiler, parent, field) => {
|
|
1561
|
-
if (parent.className) {
|
|
1562
|
-
const { dataType, colname, subpath } = resolvePaths(compiler, parent.className, _.toPath(field));
|
|
1563
|
-
if (index.isPointer(dataType))
|
|
1564
|
-
return { element: sql `${{ identifier: parent.name }}.${{ identifier: `${colname}._id` }}`, dataType };
|
|
1565
|
-
const { element, json } = _fetchElement(parent, colname, subpath, dataType);
|
|
1566
|
-
return { element, json, dataType: json ? null : dataType, relation: index.isRelation(dataType) ? dataType : null };
|
|
1567
|
-
}
|
|
1568
|
-
const [colname, ...subpath] = _.toPath(field);
|
|
1569
|
-
const { element, json } = _fetchElement(parent, colname, subpath);
|
|
1570
|
-
return { element, json, dataType: null, relation: null };
|
|
1571
|
-
};
|
|
1572
|
-
|
|
1573
1568
|
//
|
|
1574
1569
|
// expressions.ts
|
|
1575
1570
|
//
|
|
@@ -2260,7 +2255,7 @@ const selectPopulate = (compiler, parent, populate, field) => {
|
|
|
2260
2255
|
SELECT to_jsonb(${{ identifier: populate.name }}) FROM (
|
|
2261
2256
|
SELECT ${_.map(subpaths, ({ path, type }) => _encodePopulateInclude(populate.name, path, type))}
|
|
2262
2257
|
FROM ${{ identifier: populate.name }} WHERE ${cond}
|
|
2263
|
-
${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.
|
|
2258
|
+
${!_.isEmpty(populate.sort) ? sql `ORDER BY ${compiler._encodeSort(populate.sort, { className: populate.className, name: populate.name })}` : sql ``}
|
|
2264
2259
|
${populate.limit ? sql `LIMIT ${{ literal: `${populate.limit}` }}` : sql ``}
|
|
2265
2260
|
${populate.skip ? sql `OFFSET ${{ literal: `${populate.skip}` }}` : sql ``}
|
|
2266
2261
|
${compiler.selectLock ? compiler.isUpdate ? sql `FOR UPDATE NOWAIT` : sql `FOR SHARE NOWAIT` : sql ``}
|