proto.io 0.0.137 → 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.
Files changed (39) hide show
  1. package/dist/adapters/file/database.d.ts +2 -2
  2. package/dist/adapters/file/database.mjs +2 -2
  3. package/dist/adapters/file/filesystem.d.ts +2 -2
  4. package/dist/adapters/file/google-cloud-storage.d.ts +2 -2
  5. package/dist/adapters/storage/progres.d.ts +5 -2
  6. package/dist/adapters/storage/progres.js +360 -365
  7. package/dist/adapters/storage/progres.js.map +1 -1
  8. package/dist/adapters/storage/progres.mjs +363 -368
  9. package/dist/adapters/storage/progres.mjs.map +1 -1
  10. package/dist/client.d.ts +3 -3
  11. package/dist/client.js +1 -1
  12. package/dist/client.mjs +3 -3
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.js +1 -1
  15. package/dist/index.mjs +5 -5
  16. package/dist/internals/index-C6zdNpc_.js.map +1 -1
  17. package/dist/internals/{index-BPAEvTP6.mjs → index-C8hhBfuh.mjs} +2 -11
  18. package/dist/internals/index-C8hhBfuh.mjs.map +1 -0
  19. package/dist/internals/{index-CoOTI_pk.js → index-CTyYPai3.js} +1 -10
  20. package/dist/internals/index-CTyYPai3.js.map +1 -0
  21. package/dist/internals/{index-CYO8Wsos.d.ts → index-D1h-5NmA.d.ts} +2 -2
  22. package/dist/internals/index-D1h-5NmA.d.ts.map +1 -0
  23. package/dist/internals/{index-0dgO9Wxb.d.ts → index-DRgmWum4.d.ts} +2 -2
  24. package/dist/internals/index-DRgmWum4.d.ts.map +1 -0
  25. package/dist/internals/{index-DFAi7Z7H.mjs → index-DTZ5uRgF.mjs} +2 -2
  26. package/dist/internals/{index-DFAi7Z7H.mjs.map → index-DTZ5uRgF.mjs.map} +1 -1
  27. package/dist/internals/{index-DRzRIomK.mjs → index-EOtjV6U_.mjs} +2 -2
  28. package/dist/internals/index-EOtjV6U_.mjs.map +1 -0
  29. package/dist/internals/{index-DvJvDUrO.d.ts → index-rcLPSrw6.d.ts} +1 -2
  30. package/dist/internals/index-rcLPSrw6.d.ts.map +1 -0
  31. package/dist/internals/{random-D0f0zJgp.mjs → random-Dn9rhwfh.mjs} +3 -3
  32. package/dist/internals/{random-D0f0zJgp.mjs.map → random-Dn9rhwfh.mjs.map} +1 -1
  33. package/package.json +1 -1
  34. package/dist/internals/index-0dgO9Wxb.d.ts.map +0 -1
  35. package/dist/internals/index-BPAEvTP6.mjs.map +0 -1
  36. package/dist/internals/index-CYO8Wsos.d.ts.map +0 -1
  37. package/dist/internals/index-CoOTI_pk.js.map +0 -1
  38. package/dist/internals/index-DRzRIomK.mjs.map +0 -1
  39. package/dist/internals/index-DvJvDUrO.d.ts.map +0 -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.name, key)} ${{ literal: order === 1 ? 'ASC' : 'DESC' }}
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
- // encode.ts
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 _encodeJsonValue = (value) => {
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.name, name: populate.name })}` : sql ``}
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 ``}