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