prostgles-server 4.2.158 → 4.2.159

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 (145) hide show
  1. package/dist/Auth/AuthTypes.d.ts +4 -8
  2. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  3. package/dist/Auth/setAuthProviders.d.ts.map +1 -1
  4. package/dist/Auth/setAuthProviders.js +4 -5
  5. package/dist/Auth/setAuthProviders.js.map +1 -1
  6. package/dist/Auth/setEmailProvider.js +1 -1
  7. package/dist/Auth/setEmailProvider.js.map +1 -1
  8. package/package.json +1 -1
  9. package/lib/Auth/AuthHandler.ts +0 -436
  10. package/lib/Auth/AuthTypes.ts +0 -285
  11. package/lib/Auth/getSafeReturnURL.ts +0 -35
  12. package/lib/Auth/sendEmail.ts +0 -83
  13. package/lib/Auth/setAuthProviders.ts +0 -129
  14. package/lib/Auth/setEmailProvider.ts +0 -85
  15. package/lib/Auth/setupAuthRoutes.ts +0 -161
  16. package/lib/DBEventsManager.ts +0 -178
  17. package/lib/DBSchemaBuilder.ts +0 -225
  18. package/lib/DboBuilder/DboBuilder.ts +0 -319
  19. package/lib/DboBuilder/DboBuilderTypes.ts +0 -361
  20. package/lib/DboBuilder/QueryBuilder/Functions.ts +0 -1153
  21. package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +0 -288
  22. package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +0 -263
  23. package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +0 -271
  24. package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +0 -136
  25. package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +0 -22
  26. package/lib/DboBuilder/QueryStreamer.ts +0 -250
  27. package/lib/DboBuilder/TableHandler/DataValidator.ts +0 -428
  28. package/lib/DboBuilder/TableHandler/TableHandler.ts +0 -205
  29. package/lib/DboBuilder/TableHandler/delete.ts +0 -115
  30. package/lib/DboBuilder/TableHandler/insert.ts +0 -183
  31. package/lib/DboBuilder/TableHandler/insertTest.ts +0 -78
  32. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +0 -62
  33. package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +0 -134
  34. package/lib/DboBuilder/TableHandler/update.ts +0 -126
  35. package/lib/DboBuilder/TableHandler/updateBatch.ts +0 -49
  36. package/lib/DboBuilder/TableHandler/updateFile.ts +0 -48
  37. package/lib/DboBuilder/TableHandler/upsert.ts +0 -34
  38. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +0 -393
  39. package/lib/DboBuilder/ViewHandler/count.ts +0 -38
  40. package/lib/DboBuilder/ViewHandler/find.ts +0 -153
  41. package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +0 -73
  42. package/lib/DboBuilder/ViewHandler/getExistsFilters.ts +0 -74
  43. package/lib/DboBuilder/ViewHandler/getInfo.ts +0 -32
  44. package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +0 -84
  45. package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +0 -96
  46. package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +0 -105
  47. package/lib/DboBuilder/ViewHandler/parseJoinPath.ts +0 -208
  48. package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +0 -163
  49. package/lib/DboBuilder/ViewHandler/prepareWhere.ts +0 -90
  50. package/lib/DboBuilder/ViewHandler/size.ts +0 -37
  51. package/lib/DboBuilder/ViewHandler/subscribe.ts +0 -118
  52. package/lib/DboBuilder/ViewHandler/validateViewRules.ts +0 -70
  53. package/lib/DboBuilder/dboBuilderUtils.ts +0 -222
  54. package/lib/DboBuilder/getColumns.ts +0 -114
  55. package/lib/DboBuilder/getCondition.ts +0 -201
  56. package/lib/DboBuilder/getSubscribeRelatedTables.ts +0 -190
  57. package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +0 -426
  58. package/lib/DboBuilder/insertNestedRecords.ts +0 -355
  59. package/lib/DboBuilder/parseUpdateRules.ts +0 -187
  60. package/lib/DboBuilder/prepareShortestJoinPaths.ts +0 -186
  61. package/lib/DboBuilder/runSQL.ts +0 -182
  62. package/lib/DboBuilder/runTransaction.ts +0 -50
  63. package/lib/DboBuilder/sqlErrCodeToMsg.ts +0 -254
  64. package/lib/DboBuilder/uploadFile.ts +0 -69
  65. package/lib/Event_Trigger_Tags.ts +0 -118
  66. package/lib/FileManager/FileManager.ts +0 -358
  67. package/lib/FileManager/getValidatedFileType.ts +0 -69
  68. package/lib/FileManager/initFileManager.ts +0 -187
  69. package/lib/FileManager/upload.ts +0 -62
  70. package/lib/FileManager/uploadStream.ts +0 -79
  71. package/lib/Filtering.ts +0 -463
  72. package/lib/JSONBValidation/validate_jsonb_schema_sql.ts +0 -502
  73. package/lib/JSONBValidation/validation.ts +0 -143
  74. package/lib/Logging.ts +0 -127
  75. package/lib/PostgresNotifListenManager.ts +0 -143
  76. package/lib/Prostgles.ts +0 -485
  77. package/lib/ProstglesTypes.ts +0 -196
  78. package/lib/PubSubManager/PubSubManager.ts +0 -609
  79. package/lib/PubSubManager/addSub.ts +0 -138
  80. package/lib/PubSubManager/addSync.ts +0 -141
  81. package/lib/PubSubManager/getCreatePubSubManagerError.ts +0 -72
  82. package/lib/PubSubManager/getPubSubManagerInitQuery.ts +0 -662
  83. package/lib/PubSubManager/initPubSubManager.ts +0 -79
  84. package/lib/PubSubManager/notifListener.ts +0 -173
  85. package/lib/PubSubManager/orphanTriggerCheck.ts +0 -70
  86. package/lib/PubSubManager/pushSubData.ts +0 -55
  87. package/lib/PublishParser/PublishParser.ts +0 -162
  88. package/lib/PublishParser/getFileTableRules.ts +0 -124
  89. package/lib/PublishParser/getSchemaFromPublish.ts +0 -141
  90. package/lib/PublishParser/getTableRulesWithoutFileTable.ts +0 -177
  91. package/lib/PublishParser/publishTypesAndUtils.ts +0 -399
  92. package/lib/RestApi.ts +0 -127
  93. package/lib/SchemaWatch/SchemaWatch.ts +0 -90
  94. package/lib/SchemaWatch/createSchemaWatchEventTrigger.ts +0 -3
  95. package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +0 -45
  96. package/lib/SchemaWatch/getWatchSchemaTagList.ts +0 -27
  97. package/lib/SyncReplication.ts +0 -557
  98. package/lib/TableConfig/TableConfig.ts +0 -468
  99. package/lib/TableConfig/getColumnDefinitionQuery.ts +0 -111
  100. package/lib/TableConfig/getConstraintDefinitionQueries.ts +0 -95
  101. package/lib/TableConfig/getFutureTableSchema.ts +0 -64
  102. package/lib/TableConfig/getPGIndexes.ts +0 -53
  103. package/lib/TableConfig/getTableColumnQueries.ts +0 -129
  104. package/lib/TableConfig/initTableConfig.ts +0 -326
  105. package/lib/index.ts +0 -13
  106. package/lib/initProstgles.ts +0 -319
  107. package/lib/onSocketConnected.ts +0 -102
  108. package/lib/runClientRequest.ts +0 -129
  109. package/lib/shortestPath.ts +0 -122
  110. package/lib/typeTests/DBoGenerated.d.ts +0 -320
  111. package/lib/typeTests/dboTypeCheck.ts +0 -81
  112. package/lib/utils.ts +0 -15
  113. package/tests/client/hooks.spec.ts +0 -205
  114. package/tests/client/index.ts +0 -139
  115. package/tests/client/package-lock.json +0 -637
  116. package/tests/client/package.json +0 -26
  117. package/tests/client/renderReactHook.ts +0 -177
  118. package/tests/client/tsconfig.json +0 -15
  119. package/tests/client/useProstgles.spec.ts +0 -120
  120. package/tests/clientFileTests.spec.ts +0 -102
  121. package/tests/clientOnlyQueries.spec.ts +0 -667
  122. package/tests/clientRestApi.spec.ts +0 -82
  123. package/tests/config_test/DBoGenerated.d.ts +0 -407
  124. package/tests/config_test/index.html +0 -109
  125. package/tests/config_test/index.js +0 -86
  126. package/tests/config_test/index.js.map +0 -1
  127. package/tests/config_test/index.ts +0 -91
  128. package/tests/config_test/init.sql +0 -48
  129. package/tests/config_test/package.json +0 -29
  130. package/tests/config_test/tsconfig.json +0 -23
  131. package/tests/config_testDBoGenerated.d.ts +0 -407
  132. package/tests/isomorphicQueries.spec.ts +0 -1493
  133. package/tests/server/DBoGenerated.d.ts +0 -537
  134. package/tests/server/index.html +0 -73
  135. package/tests/server/index.ts +0 -289
  136. package/tests/server/init.sql +0 -224
  137. package/tests/server/package-lock.json +0 -2164
  138. package/tests/server/package.json +0 -25
  139. package/tests/server/publishTypeCheck.ts +0 -136
  140. package/tests/server/server.ts +0 -35
  141. package/tests/server/testPublish.ts +0 -147
  142. package/tests/server/testTableConfig.ts +0 -156
  143. package/tests/server/tsconfig.json +0 -22
  144. package/tests/serverOnlyQueries.spec.ts +0 -32
  145. package/tests/test.sh +0 -20
@@ -1,1493 +0,0 @@
1
- import { strict as assert } from 'assert';
2
- import * as fs from "fs";
3
- import { DBOFullyTyped } from "../dist/DBSchemaBuilder";
4
- import type { DBHandlerClient } from "./client";
5
- import {
6
- test,
7
- //@ts-ignore
8
- describe
9
- } from "node:test";
10
- import { SubscriptionHandler, pickKeys } from "prostgles-types";
11
-
12
-
13
- export const isomorphicQueries = async (db: DBOFullyTyped | DBHandlerClient, log: (msg: string, extra?: any) => void) => {
14
- log("Starting isomorphic queries");
15
- const isServer = !!(db.items as any).dboBuilder;
16
- await describe("Isomorphic queries", async () => {
17
- await test("Deleting stale data", async () => {
18
- const itemsCount = await db.items.count?.()
19
- if(itemsCount){
20
- log("DELETING items");
21
-
22
- /* Access controlled */
23
- await db.items4.delete!({ });
24
-
25
- await db.items4_pub.delete!({ });
26
- await db.items3.delete!({ });
27
- await db.items2.delete!({ });
28
- await db.items.delete!({ });
29
- }
30
- await db.sql!(`TRUNCATE items RESTART IDENTITY CASCADE;`);
31
- });
32
-
33
- await test("Error structure", async () => {
34
- const errFind = await db.items.find?.({ h: "a" }).catch(err => err);
35
- const errCount = await db.items.count?.({ h: "a" }).catch(err => err);
36
- const errSize = await db.items.size?.({ h: "a" }).catch(err => err);
37
- const errFindOne = await db.items.findOne?.({ h: "a" }).catch(err => err);
38
- const errDelete = await db.items.delete?.({ h: "a" }).catch(err => err);
39
- const errUpdate = await db.items.update?.({}, { h: "a" }).catch(err => err);
40
- const errUpdateBatch = await db.items.updateBatch?.([[{}, { h: "a" }]]).catch(err => err);
41
- const errUpsert = await db.items.upsert?.({}, { h: "a" }).catch(err => err);
42
- const errInsert = await db.items.insert?.({ h: "a" }).catch(err => err);
43
- const errSubscribe = await db.items.subscribe?.({ h: "a" }, {}, console.warn).catch(err => err);
44
- const errSubscribeOne = await db.items.subscribeOne?.({ h: "a" }, {}, console.warn).catch(err => err);
45
-
46
- for(const err of [errFind, errCount, errSize, errFindOne, errDelete, errInsert, errUpdate, errUpdateBatch, errUpsert, errSubscribe, errSubscribeOne]){
47
- const clientOnlyError = {
48
- message: 'malformed array literal: "a"',
49
- code: '22P02',
50
- code_info: 'invalid_text_representation',
51
- name: 'error',
52
- severity: 'ERROR',
53
- }
54
- if (isServer) {
55
- const allKeys = ["message", "code", "name", "severity", "detail", "query", "length", "position", "file", "line", "routine", "code_info"];
56
- assert.deepStrictEqual(Object.keys(err ?? {}).sort(), allKeys.sort());
57
- assert.deepStrictEqual(pickKeys(err, Object.keys(clientOnlyError)), clientOnlyError);
58
- assert.equal(typeof err.detail, "string");
59
- assert.equal(typeof err.query, "string");
60
- assert.equal(typeof err.length, "number");
61
- assert.equal(typeof err.position, "string");
62
- assert.equal(typeof err.file, "string");
63
- assert.equal(typeof err.line, "string");
64
- assert.equal(typeof err.routine, "string");
65
- assert.equal(typeof err.code_info, "string");
66
- } else {
67
- assert.deepStrictEqual(err, clientOnlyError);
68
- }
69
- }
70
- });
71
-
72
- await test("Prepare data", async () => {
73
- if(!db.sql) throw "db.sql missing";
74
- const res = await db.items.insert!([{ name: "a" }, { name: "a" }, { name: "b" }], { returning: "*" });
75
- assert.equal(res.length, 3);
76
- const added1 = '04 Dec 1995 00:12:00';
77
- const added2 = '04 Dec 1996 00:12:00';
78
- const added3 = '04 Dec 1997 00:12:00';
79
-
80
- await db.items2.insert!([{ name: "a", items_id: res[0]!.id }]);
81
- await db.items3.insert!([{ name: "a" }, { name: "za123" }]);
82
- await db.items4.insert!([
83
- { name: "abc1", public: "public data", added: added1 },
84
- { name: "abc2", public: "public data", added: added1 },
85
- { name: "abcd", public: "public data d", added: added2 }
86
- ]);
87
- await db[`prostgles_test.basic1`].insert!({
88
- id_basic: { txt: "basic" },
89
- txt: "basic1"
90
- });
91
- await db.sql(`REFRESH MATERIALIZED VIEW prostgles_test.mv_basic1;`);
92
- assert.deepStrictEqual(await db["prostgles_test.mv_basic1"].find!(), await db["prostgles_test.basic1"].find!())
93
-
94
- /* Ensure */
95
- await db[`"*"`].insert!([{ "*": "a" }, { "*": "a" }, { "*": "b" }]);
96
- await db[`"""*"""`].insert!([{ [`"*"`]: "a" }, { [`"*"`]: "a" }, { [`"*"`]: "b" }]);
97
-
98
- await db.various.insert!([
99
- { name: "abc9", added: added1, jsn: { "a": { "b": 2 } } },
100
- { name: "abc1", added: added2, jsn: { "a": { "b": 3 } } },
101
- { name: "abc81 here", added: added3, jsn: { "a": { "b": 2 } } }
102
- ]);
103
-
104
- await db.sql("TRUNCATE files CASCADE");
105
- });
106
-
107
-
108
- const json = { a: true, arr: "2", arr1: 3, arr2: [1], arrStr: ["1123.string"] }
109
- await test("merge json", async () => {
110
- const inserted = await db.tjson.insert!({ colOneOf: "a", json }, { returning: "*" });
111
- const res = await db.tjson.update!({ colOneOf: "a" },{ json: { $merge: [{ a: false }] } }, { returning: "*" });
112
- assert.deepStrictEqual(res?.[0].json, { ...json, a: false });
113
- });
114
-
115
- await test("json array converted to pg array filter bug", async () => {
116
- const result = await db.tjson.find!({ json: [2] });
117
- assert.deepStrictEqual(result, []);
118
- });
119
-
120
- await test("onConflict do update", async () => {
121
- const initial = await db.items4.insert!({ id: -99, name: "onConflict", public: "onConflict" }, { returning: "*" });
122
- const updated = await db.items4.insert!({ id: -99, name: "onConflict", public: "onConflict2" }, { onConflict: "DoUpdate", returning: "*" });
123
- assert.equal(initial.id, -99);
124
- assert.equal(initial.public, "onConflict");
125
- assert.equal(updated.id, -99);
126
- assert.equal(updated.public, "onConflict2");
127
- await db.items4.delete!({ id: -99 });
128
- });
129
-
130
- const fileFolder = `${__dirname}/../../server/dist/server/media/`;
131
- const fileName = "sample_file.txt";
132
- await test("Local file upload", async () => {
133
- let str = "This is a string",
134
- data = Buffer.from(str, "utf-8"),
135
- mediaFile = { data, name: fileName }
136
-
137
- const file = await db.files.insert!(mediaFile, { returning: "*" });
138
- const _data = fs.readFileSync(fileFolder + file.name);
139
- assert.equal(str, _data.toString('utf8'));
140
-
141
- await tryRun("Nested insert", async () => {
142
-
143
- const nestedInsert = await db.users_public_info.insert!({ name: "somename.txt", avatar: mediaFile }, { returning: "*" });
144
- const { name, avatar } = nestedInsert;
145
- const { extension, content_type, original_name } = avatar;
146
- assert.deepStrictEqual(
147
- { extension, content_type, original_name },
148
- {
149
- extension: 'txt',
150
- content_type: 'text/plain',
151
- original_name: 'sample_file.txt',
152
- }
153
- );
154
-
155
- assert.equal(name, "somename.txt");
156
- });
157
-
158
- });
159
-
160
- await test("Local file delete", async () => {
161
- const file = {
162
- data: Buffer.from("str", "utf-8"),
163
- name: "will delete.txt"
164
- }
165
- await db.files.insert!(file);
166
-
167
- const files = await db.files.find!({ original_name: file.name });
168
- assert.equal(files.length, 1);
169
- const exists0 = fs.existsSync(fileFolder+files[0].name);
170
- assert.equal(exists0, true);
171
- await db.files.delete!({ original_name: file.name }, { returning: "*" });
172
- const exists = fs.existsSync(fileFolder+files[0].name);
173
- assert.equal(exists, false);
174
- })
175
-
176
- await test("Local file update", async () => {
177
- const initialStr = "str";
178
- const newStr = "str new";
179
- const file = {
180
- data: Buffer.from(initialStr, "utf-8"),
181
- name: "will update.txt"
182
- }
183
- const newFile = {
184
- data: Buffer.from(newStr, "utf-8"),
185
- name: "will update new.txt"
186
- }
187
- await db.files.insert!(file);
188
- const originals = await db.files.find!({ original_name: file.name });
189
- assert.equal(originals.length, 1);
190
- const [original] = originals;
191
- const initialFileStr = fs.readFileSync(fileFolder + original.name).toString('utf8');
192
- assert.equal(initialStr, initialFileStr);
193
-
194
- await db.files.update!({ id: original.id }, newFile);
195
-
196
- const newFileStr = fs.readFileSync(fileFolder + original.name).toString('utf8');
197
- assert.equal(newStr, newFileStr);
198
-
199
- const newF = await db.files.findOne!({ id: original.id });
200
-
201
- assert.equal(newF?.original_name, newFile.name)
202
- });
203
-
204
- await test("getColumns definition", async () => {
205
- const res = await db.tr2.getColumns!("fr");
206
- const expected = [
207
- {
208
- "label": "Id",
209
- "name": "id",
210
- "data_type": "integer",
211
- "udt_name": "int4",
212
- "element_type": null,
213
- is_updatable: true,
214
- "element_udt_name": null,
215
- "is_pkey": true,
216
- "column_default": null,
217
- "comment": null,
218
- "ordinal_position": 1,
219
- "is_generated": false,
220
- "is_nullable": false,
221
- "references": null,
222
- "has_default": true,
223
- "tsDataType": "number",
224
- "insert": true,
225
- "select": true,
226
- "orderBy": true,
227
- "filter": true,
228
- "update": true,
229
- "delete": true
230
- },
231
- {
232
- "label": "Tr1 id",
233
- "name": "tr1_id",
234
- "data_type": "integer",
235
- "udt_name": "int4",
236
- "element_type": null,
237
- is_updatable: true,
238
- "element_udt_name": null,
239
- "is_pkey": false,
240
- "column_default": null,
241
- "comment": null,
242
- "ordinal_position": 2,
243
- "is_generated": false,
244
- "is_nullable": true,
245
- "references": [{
246
- "ftable": "tr1",
247
- "fcols": [
248
- "id"
249
- ],
250
- "cols": [
251
- "tr1_id"
252
- ]
253
- }],
254
- "has_default": false,
255
- "tsDataType": "number",
256
- "insert": true,
257
- "select": true,
258
- "orderBy": true,
259
- "filter": true,
260
- "update": true,
261
- "delete": true
262
- },
263
- {
264
- "label": "fr_t1",
265
- hint: "hint...",
266
- min: "a",
267
- max: "b",
268
- "name": "t1",
269
- "data_type": "text",
270
- "udt_name": "text",
271
- "element_type": null,
272
- is_updatable: true,
273
- "element_udt_name": null,
274
- "is_pkey": false,
275
- "column_default": null,
276
- "comment": null,
277
- "ordinal_position": 3,
278
- "is_generated": false,
279
- "is_nullable": true,
280
- "references": null,
281
- "has_default": false,
282
- "tsDataType": "string",
283
- "insert": true,
284
- "select": true,
285
- "orderBy": true,
286
- "filter": true,
287
- "update": true,
288
- "delete": true
289
- },
290
- {
291
- "label": "en_t2",
292
- "name": "t2",
293
- "data_type": "text",
294
- "udt_name": "text",
295
- "element_type": null,
296
- is_updatable: true,
297
- "element_udt_name": null,
298
- "is_pkey": false,
299
- "column_default": null,
300
- "comment": null,
301
- "ordinal_position": 4,
302
- "is_generated": false,
303
- "is_nullable": true,
304
- "references": null,
305
- "has_default": false,
306
- "tsDataType": "string",
307
- "insert": true,
308
- "select": true,
309
- "orderBy": true,
310
- "filter": true,
311
- "update": true,
312
- "delete": true
313
- }
314
- ];
315
-
316
- assert.deepStrictEqual(
317
- res,
318
- expected
319
- );
320
- const resDynamic = await db.tr2.getColumns!("fr", { rule: "update", filter: {}, data: { t2: "a" } });
321
- assert.deepStrictEqual(
322
- resDynamic,
323
- expected
324
- );
325
- });
326
-
327
- await test("returnType", async () => {
328
-
329
- const whereStatement = await db.tr1.find!({ t1: "a" }, { returnType: "statement-where" });
330
-
331
- assert.equal(whereStatement, `"t1" = 'a'`);
332
- });
333
-
334
- await test("Table config triggers", async () => {
335
- const tr1 = await db.tr1.insert!({ })
336
- const tr2 = await db.tr2.insert!({
337
- tr1_id: 1,
338
- t1: "a",
339
- t2: "b"
340
- });
341
- try {
342
- await db.tr2.delete!();
343
- } catch(e){
344
-
345
- }
346
- const one = await db.tr2.findOne!({
347
- t1: "a",
348
- t2: "b"
349
- });
350
- if(!one) {
351
- throw "Row missing";
352
- }
353
- });
354
-
355
- await test("$unnest_words", async () => {
356
- const res = await db.various.find!({}, { returnType: "values", select: { name: "$unnest_words" } });
357
-
358
- assert.deepStrictEqual( res, [
359
- 'abc9',
360
- 'abc1',
361
- 'abc81',
362
- 'here'
363
- ]);
364
- })
365
-
366
- /**
367
- * Group by/Distinct
368
- */
369
- await test("Group by/Distinct", async () => {
370
- const res = await db.items.find!({}, { select: { name: 1 }, groupBy: true });
371
- const resV = await db.items.find!({}, { select: { name: 1 }, groupBy: true, returnType: "values" });
372
-
373
- assert.deepStrictEqual(
374
- res,
375
- [
376
- { name: 'a' },
377
- { name: 'b' },
378
- ]
379
- );
380
- assert.deepStrictEqual(
381
- resV,
382
- ["a", "b"]
383
- );
384
- });
385
-
386
- await test("sql returntype default-with-rollback", async () => {
387
- const item = { name: "a" };
388
- const res = await db.sql!("delete from items2 returning name; ", {}, { returnType: "default-with-rollback" });
389
- assert.deepEqual(res.rows, [item]);
390
- const count = await db.items2.count!();
391
- assert.equal(count, 1);
392
- });
393
-
394
- /**
395
- * returnType "value"
396
- */
397
- await test("returnType: value", async () => {
398
- const resVl = await db.items.find!({}, { select: { name: { $array_agg: ["name"] } }, returnType: "value" });
399
-
400
- assert.deepStrictEqual(
401
- resVl,
402
- ["a", "a", "b"]
403
- );
404
- });
405
-
406
- /**
407
- * TODO -> ADD ALL FILTER TYPES
408
- */
409
- await test("FTS filtering", async () => {
410
- const res = await db.various.count!({ "tsv.@@.to_tsquery": ["a"] });
411
- assert.equal(res, 0);
412
-
413
-
414
- const d = await db.various.findOne!(
415
- { "name.@@.to_tsquery": ["abc81"] },
416
- { select: {
417
- h: { "$ts_headline_simple": ["name", { plainto_tsquery: "abc81" }] },
418
- hh: { "$ts_headline": ["name", "abc81"] } ,
419
- added: "$year",
420
- addedY: { "$date": ["added"] }
421
- }});
422
- // console.log(d);
423
- await db.various.findOne!(
424
- { },
425
- { select: {
426
- h: { "$ts_headline_simple": ["name", { plainto_tsquery: "abc81" }] },
427
- hh: { "$ts_headline": ["name", "abc81"] } ,
428
- added: "$year",
429
- addedY: { "$date": ["added"] }
430
- }});
431
-
432
- /*
433
- * Dates become strings after reaching client.
434
- * Serialize col dataTypes and then recast ??
435
- */
436
- assert.deepStrictEqual(JSON.parse(JSON.stringify(d)), {
437
- h: '<b>abc81</b> here',
438
- hh: '<b>abc81</b> here',
439
- added: '1997',
440
- addedY: '1997-12-04',
441
- });
442
- });
443
-
444
- await test("$term_highlight", async () => {
445
- const term = "abc81";
446
- const res = await db.various.find!(
447
- { "hIdx.>": -2 },
448
- { select: {
449
- h: { $term_highlight: [["name"], term, { }] },
450
- hFull: { $term_highlight: ["*", "81", { }] },
451
- hOrdered: { $term_highlight: [["name", "id"], "81", { }] },
452
- hIdx: { $term_highlight: [["name"], term, { returnType: "index" }] },
453
- hBool: { $term_highlight: [["name"], term, { returnType: "boolean" }] },
454
- hObj: { $term_highlight: [["name"], term, { returnType: "object" }] },
455
- hObjAll: { $term_highlight: ["*", term, { returnType: "object" }] },
456
- },
457
- orderBy: { hIdx: -1 }
458
- }
459
- );
460
-
461
- assert.deepStrictEqual(
462
- res[0],
463
- {
464
- h:["name: ",["abc81"]," here"],
465
-
466
- /* Search all allowed fields using "*" */
467
- hFull: [
468
- 'id: 3, h: , name: abc',
469
- [ '81' ],
470
- ' here, tsv: , jsn: {"a":{"b":2}}, added: 1997-12-04 00:12:00'
471
- ],
472
-
473
- /* Search specific fields in specific order */
474
- hOrdered:["name: abc",["81"]," here, id: 3"],
475
- hIdx: 6,
476
- hBool: true,
477
- hObj: {
478
- name: [
479
- '', ['abc81'],' here'
480
- ]
481
- },
482
- hObjAll: {
483
- name: [
484
- '', ['abc81'],' here'
485
- ]
486
- },
487
- }
488
- )
489
- });
490
-
491
- await test("funcFilters: $term_highlight", async () => {
492
- const term = "abc81";
493
- const res = await db.various.count!(
494
- { $term_highlight: [["*"], term, { returnType: "boolean" }] }
495
- );
496
- assert.equal(+res, 1)
497
- });
498
-
499
-
500
- const testToEnsureTriggersAreDisabled = async (sub: SubscriptionHandler, table_name: string) => {
501
- const getTableTriggers = async (table_name: string) => {
502
- return await db.sql?.(`
503
- SELECT tgname, tgenabled = 'O' as enabled
504
- FROM pg_catalog.pg_trigger
505
- WHERE tgname like format('prostgles_triggers_%s_', \${table_name}) || '%'
506
- `,
507
- { table_name },
508
- { returnType: "rows" }
509
- ) as { tgname: string; enabled: boolean; }[];
510
- }
511
- let validTriggers = await getTableTriggers(table_name);
512
- console.log("unsubscribe start - " + table_name)
513
- await sub.unsubscribe();
514
- console.log("unsubscribe end - " + table_name);
515
- assert.equal(
516
- validTriggers.filter(t => t.enabled).length,
517
- 3,
518
- `3 Triggers should be enabled: \n${
519
- JSON.stringify({
520
- table_name,
521
- filter: sub.filter,
522
- validTriggers
523
- }, null, 2)
524
- }`);
525
- await db.sql?.(`DELETE FROM prostgles.app_triggers`, []);
526
- validTriggers = await getTableTriggers(table_name);
527
- assert.equal(validTriggers.length, 3, '3 Triggers should exist but be disabled');
528
- assert.equal(validTriggers.filter(t => t.enabled).length, 0);
529
- }
530
-
531
- await test("subscribe", async () => {
532
- await tryRunP("subscribe", async (resolve, reject) => {
533
- await db.various.insert!({ id: 99 });
534
- const sub = await db.various.subscribe!({ id: 99 }, { }, async items => {
535
- const item = items[0];
536
-
537
- if(item && item.name === "zz3zz3"){
538
- await db.various.delete!({ name: "zz3zz3" });
539
- await testToEnsureTriggersAreDisabled(sub, "various");
540
- resolve(true)
541
- }
542
- });
543
- await db.various.update!({ id: 99 }, { name: "zz3zz1" });
544
- await db.various.update!({ id: 99 }, { name: "zz3zz2" });
545
- await db.various.update!({ id: 99 }, { name: "zz3zz3" });
546
- }, { timeout: 4000 });
547
- });
548
-
549
- await test("subscribe to schema.table", async () => {
550
- await tryRunP("subscribe to schema.table", async (resolve, reject) => {
551
- let runs = 0;
552
- const sub = await db[`prostgles_test.basic1`].subscribe!({}, {}, async items => {
553
- runs++;
554
- if(runs === 1){
555
- if(items.length !== 1) {
556
- reject("Should have 1 item");
557
- } else {
558
- await db[`prostgles_test.basic1`].insert!({
559
- txt: "basic12"
560
- });
561
- }
562
- } else if(runs === 2){
563
- if(items.length !== 2) {
564
- reject("Should have 2 items");
565
- } else {
566
- await sub.unsubscribe();
567
- resolve(true);
568
- }
569
- } else {
570
- reject("Expecting only 2 runs")
571
- }
572
- });
573
- });
574
- })
575
-
576
- await test("parallel subscriptions", async () => {
577
- await tryRunP("parallel subscriptions", async (resolve, reject) => {
578
-
579
- let callbacksFired = {};
580
- const subscriptions = await Promise.all(
581
- [1,2,3,4,5].map(async subId => {
582
- const handler = await db.various.subscribe!({ id: 99 }, { }, async items => {
583
- callbacksFired[subId] = true;
584
- });
585
-
586
- return {
587
- handler,
588
- }
589
- })
590
- ).catch(reject);
591
-
592
- await tout(2000);
593
- await Promise.all(Object.values(subscriptions).map(async ({ handler }) => handler.unsubscribe()));
594
- assert.equal(Object.keys(callbacksFired).length, 5);
595
- resolve(true);
596
-
597
- });
598
- });
599
-
600
- await test("subscribeOne with throttle", async () => {
601
- await tryRunP("subscribeOne with throttle", async (resolve, reject) => {
602
- await db.various.insert!({ id: 99 });
603
- const start = Date.now(); // name: "zz3zz"
604
- const sub = await db.various.subscribeOne!({ id: 99 }, { throttle: 1700 }, async item => {
605
- const now = Date.now();
606
- if(item && item.name === "zz3zz2" && now - start > 1600 && now - start < 1800){
607
- await db.various.delete!({ name: "zz3zz2" });
608
- sub.unsubscribe()
609
- resolve(true)
610
- }
611
- });
612
- await db.various.update!({ id: 99 }, { name: "zz3zz1" });
613
- await db.various.update!({ id: 99 }, { name: "zz3zz2" });
614
- }, { timeout: 4000 });
615
- });
616
-
617
- await test("JSON filtering", async () => {
618
- const res = await db.various.count!({ "jsn->a->>b": '3' });
619
- assert.equal(res, 1)
620
- });
621
-
622
- await test("Complex filtering", async () => {
623
- const res = await db.various.count!({
624
- $and: [
625
- {
626
- $filter: [
627
- { $year: ["added"] },
628
- "=",
629
- '1996'
630
- ]
631
- },
632
- {
633
- $filter: [
634
- { $Mon: ["added"] },
635
- "=",
636
- 'Dec'
637
- ]
638
- }
639
-
640
- ]
641
- });
642
- assert.equal(res, 1)
643
- });
644
-
645
- await test("template_string function", async () => {
646
- const res = await db.various.findOne!({ name: 'abc9' }, { select: { tstr: { $template_string: ["{name} is hehe"] } } });
647
- const res2 = await db.various.findOne!({ name: 'abc9' }, { select: { tstr: { $template_string: ["is hehe"] } } });
648
- assert.equal(res?.tstr, "abc9 is hehe")
649
- assert.equal(res2?.tstr, "is hehe")
650
- });
651
-
652
- await test("Between filtering", async () => {
653
- const res = await db.various.count!({
654
- added: { $between: [
655
- '06 Dec 1995 00:12:00',
656
- '03 Dec 1997 00:12:00'
657
- ] } });
658
- assert.equal(res, 1)
659
- });
660
- await test("In filtering", async () => {
661
- const res = await db.various.count!({ added: { $in: ['04 Dec 1996 00:12:00'] } });
662
- assert.equal(res, 1)
663
- });
664
-
665
- await test("Order by", async () => {
666
- const res = await db.items.find!({}, { select: { name: 1 }, orderBy: [{ key: "name", asc: false, nulls: "first", nullEmpty: true }] });
667
- assert.deepStrictEqual(res, [{ name: 'b'}, { name: 'a'}, { name: 'a'}]);
668
- });
669
- await test("Order by aliased func", async () => {
670
- const res = await db.items.find!({ }, { select: { uname: { $upper: ["name"] }, count: { $countAll: [] } }, orderBy: { uname: -1 }});
671
- assert.deepStrictEqual(res, [{ uname: 'B', count: '1'}, { uname: 'A', count: '2'} ])
672
- });
673
- await test("Filter by aliased func", async () => {
674
- const res = await db.items.find!({ uname: 'B' }, { select: { uname: { $upper: ["name"] }, count: { $countAll: [] } } });
675
- assert.deepStrictEqual(res, [{ uname: 'B', count: '1'}])
676
- });
677
- await test("Count with Filter by aliased func ", async () => {
678
- const res = await db.items.count!({ uname: 'A' }, { select: { uname: { $upper: ["name"] } } });
679
- assert.deepStrictEqual(res, 2)
680
- });
681
- await test("Count with Aggregate and Filter by aliased func ", async () => {
682
- const res = await db.items.count!({ uname: 'A' }, { select: { uname: { $upper: ["name"] }, count: { $countAll: [] } } });
683
- assert.deepStrictEqual(res, 1)
684
- });
685
- await test("Count with complex filter ", async () => {
686
- const res = await db.items.count!({
687
- $filter: [
688
- { $upper: ["name"] },
689
- "$in",
690
- ["A"]
691
- ]
692
- });
693
- assert.deepStrictEqual(res, 2)
694
- });
695
- await test("Order by aggregation", async () => {
696
- const res = await db.items.find!({ }, { select: { name: 1, count: { $countAll: [] } }, orderBy: { count: -1 }});
697
- assert.deepStrictEqual(res, [ { name: 'a', count: '2'} , { name: 'b', count: '1'} ])
698
- });
699
- await test("Order by colliding alias name", async () => {
700
- const res = await db.items.find!({ }, { select: { name: { $countAll: [] }, n: { $left: ["name", 1]} }, orderBy: { name: -1 }});
701
- assert.deepStrictEqual(res, [ { name: '2', n: 'a' } , { name: '1', n: 'b'} ])
702
- });
703
-
704
- await test("Update batch example", async () => {
705
-
706
- await db.items4.updateBatch!([
707
- [{ name: "abc1" }, { name: "abc" }],
708
- [{ name: "abc2" }, { name: "abc" }]
709
- ]);
710
- assert.equal(await db.items4.count!({ name: "abc" }), 2);
711
- })
712
-
713
- await test("Function example", async () => {
714
-
715
- const f = await db.items4.findOne!({}, { select: { public: 1, p_5: { $left: ["public", 3] } } });
716
- assert.equal(f?.p_5.length, 3);
717
- assert.equal(f?.p_5, f.public.substr(0, 3));
718
-
719
- // Nested function
720
- const fg = await db.items2.findOne!({}, { select: { id: 1, name: 1, items3: { name: "$upper" } } });// { $upper: ["public"] } } });
721
- assert.deepStrictEqual(fg, { id: 1, name: 'a', items3: [ { name: 'A' } ] });
722
-
723
- // Date utils
724
- const Mon = await db.items4.findOne!({ name: "abc" }, { select: { added: "$Mon" } });
725
- assert.deepStrictEqual(Mon, { added: "Dec" });
726
-
727
- // Date + agg
728
- const MonAgg = await db.items4.find!({ name: "abc" }, { select: { added: "$Mon", public: "$count" } });
729
- assert.deepStrictEqual(MonAgg, [{ added: "Dec", public: '2' }]);
730
-
731
- // Returning
732
- const returningParam = { returning: { id: 1, name: 1, public: 1 , $rowhash: 1, added_day: { "$day": ["added"] } }} as const ; // ctid: 1,
733
- let i = await db.items4_pub.insert!( { name: "abc123", public: "public data", added: '04 Dec 1995 00:12:00' }, returningParam);
734
- assert.deepStrictEqual(i, { id: 1, name: 'abc123', public: 'public data', $rowhash: '347c26babad535aa697a794af89195fe', added_day: 'monday' }); // , ctid: '(0,1)'
735
-
736
- let u = await db.items4_pub.update! ({ name: "abc123" }, { public: "public data2" }, returningParam);
737
- assert.deepStrictEqual(u, [{ id: 1, name: 'abc123', public: 'public data2', $rowhash: '9d18ddfbff9e13411d13f82d414644de', added_day: 'monday' }]);
738
-
739
- let d = await db.items4_pub.delete!( { name: "abc123" }, returningParam);
740
- assert.deepStrictEqual(d, [{ id: 1, name: 'abc123', public: 'public data2', $rowhash: '9d18ddfbff9e13411d13f82d414644de', added_day: 'monday' }]);
741
-
742
- console.log("TODO: socket.io stringifies dates")
743
- });
744
-
745
- await test("JSONB filtering", async () => {
746
- const obj = { propName: 3232 };
747
- const row = await db.obj_table.insert!({ obj }, { returning: "*" });
748
- const sameRow = await db.obj_table.findOne!({obj });
749
- const sameRow1 = await db.obj_table.findOne!({ obj: { "=": obj } });
750
- const sameRow2 = await db.obj_table.findOne!({ "obj.=": obj });
751
- const count = await db.obj_table.count!({ obj });
752
- assert.deepStrictEqual(row, sameRow);
753
- assert.deepStrictEqual(row, sameRow1);
754
- assert.deepStrictEqual(row, sameRow2);
755
- assert.deepStrictEqual(+count, 1);
756
- })
757
-
758
- await test("Postgis examples", async () => {
759
- await db.shapes.delete!();
760
- const p1 = { $ST_GeomFromText: ["POINT(-1 1)", 4326] },
761
- p2 = { $ST_GeomFromText: ["POINT(-2 2)", 4326] };
762
- await db.shapes.insert!([
763
- { geom: p1, geog: p1 },
764
- { geom: p2, geog: p2 },
765
- ])
766
-
767
- /** Basic functions and extent filters */
768
- const f = await db.shapes.findOne!({ $and: [
769
- {"geom.&&.st_makeenvelope": [
770
- -3, 2,
771
- -2, 2
772
- ]},
773
- {"geog.&&.st_makeenvelope": [
774
- -3, 2,
775
- -2, 2
776
- ] }]
777
- }, {
778
- select: {
779
- geomTxt: {"$ST_AsText": ["geom"]},
780
- geomGeo: {"$ST_AsGeoJSON": ["geom"]},
781
- },
782
- orderBy: "geom"
783
- });
784
- assert.deepStrictEqual(f, {
785
- geomGeo: {
786
- coordinates: [-2,2],
787
- type: 'Point'
788
- },
789
- geomTxt: 'POINT(-2 2)'
790
- });
791
-
792
- /**Aggregate functions */
793
- const aggs = await db.shapes.findOne!({ }, {
794
- select: {
795
- xMin: { "$ST_XMin_Agg": ["geom"] },
796
- xMax: { "$ST_XMax_Agg": ["geom"] },
797
- yMin: { "$ST_YMin_Agg": ["geom"] },
798
- yMax: { "$ST_YMax_Agg": ["geom"] },
799
- zMin: { "$ST_ZMin_Agg": ["geom"] },
800
- zMax: { "$ST_ZMax_Agg": ["geom"] },
801
- extent: { "$ST_Extent": ["geom"] },
802
- // extent3D: { "$ST_3DExtent": ["geom"] },
803
- },
804
- });
805
- assert.deepStrictEqual(aggs, {
806
- xMax: -1,
807
- xMin: -2,
808
- yMax: 2,
809
- yMin: 1,
810
- zMax: 0,
811
- zMin: 0,
812
- extent: 'BOX(-2 1,-1 2)',
813
- // extent3D: 'BOX3D(-2 1 0,-1 2 6.952908662134e-310)' <-- looks like a value that will fail tests at some point
814
- });
815
-
816
- });
817
-
818
-
819
- await test("jsonbSchema validation", async () => {
820
-
821
- /**
822
- *
823
- tjson: {
824
- json: { jsonbSchema: {
825
- a: { type: "boolean" },
826
- arr: { enum: ["1", "2", "3"] },
827
- arr1: { enum: [1, 2, 3] },
828
- arr2: { type: "integer[]" },
829
- o: { oneOf: [{ o1: { type: "integer" } }, { o2: { type: "boolean" } }], optional: true },
830
- }
831
- }
832
- },
833
- */
834
-
835
- const fo = await db.tjson.insert!({ colOneOf: "a", json }, { returning: "*"});
836
- // assert.deepStrictEqual(fo.json, json);
837
- await db.tjson.insert!({ colOneOf: "a", json: {...json, o: { o1: 2 } } })
838
- try {
839
- await db.tjson.insert!({ colOneOf: "a", json: { a: true, arr: "22"} });
840
- throw "Should have failed"
841
- } catch(e){
842
- // Expected
843
- }
844
- });
845
-
846
-
847
- await test("find and findOne", async () => {
848
-
849
- const fo = await db.items.findOne!();
850
- const f = await db.items.find!();
851
- assert.deepStrictEqual(fo, { h: null, id: 1, name: 'a' });
852
- assert.deepStrictEqual(f[0], { h: null, id: 1, name: 'a' });
853
- });
854
-
855
- await test("Result size", async () => {
856
- const is75bits = await db.items.size!({
857
- }, { select: { name: 1 } });
858
- assert.equal(is75bits, '75', "Result size query failed")
859
- });
860
-
861
- await test("Basic exists", async () => {
862
- const expect0 = await db.items.count!({
863
- $and: [
864
- { $exists: { items2: { name: "a" } } },
865
- { $exists: { items3: { name: "b" } } },
866
- ]
867
- });
868
- assert.equal(expect0, 0, "$exists query failed")
869
- });
870
-
871
- await test("Basic fts with shorthand notation", async () => {
872
- const res = await db.items.count!({
873
- $and: [
874
- { $exists: { items2: { "name.@@.to_tsquery": ["a"] } } },
875
- { $exists: { items3: { "name.@@.to_tsquery": ["b"] } } },
876
- ]
877
- });
878
- // assert.deepStrictEqual(res, { name: 'a'})
879
- assert.equal(res, 0, "FTS query failed")
880
- });
881
-
882
- await test("Exists with shortest path wildcard filter example", async () => {
883
- const expect2 = await db.items.find!({
884
- $and: [
885
- { $existsJoined: { "**.items3": { name: "a" } } },
886
- { $existsJoined: { items2: { name: "a" } } }
887
- ]
888
- });
889
- assert.equal(expect2.length, 2, "$existsJoined query failed");
890
- const expectNothing = await db.items.find!({
891
- $and: [
892
- { $existsJoined: { "**.items3": { name: "nothing" } } },
893
- { $existsJoined: { items2: { name: "a" } } }
894
- ]
895
- });
896
- assert.equal(expectNothing.length, 0, "$existsJoined query failed");
897
- });
898
-
899
-
900
- await test("Exists with exact path filter example", async () => {
901
- const _expect2 = await db.items.find!({
902
- $and: [
903
- // { "items2": { name: "a" } },
904
- // { "items2.items3": { name: "a" } },
905
- { $existsJoined: { items2: { name: "a" } } }
906
- ]
907
- });
908
- assert.equal(_expect2.length, 2, "$existsJoined query failed");
909
- });
910
-
911
- await test("Not Exists with exact path filter example", async () => {
912
- const _expect1 = await db.items.find!({
913
- $and: [
914
- { $notExistsJoined: { items2: { name: "a" } } }
915
- ]
916
- });
917
- assert.equal(_expect1.length, 1, "$notExistsJoined query failed");
918
- });
919
-
920
- /* Upsert */
921
- await test("Upsert example", async () => {
922
- await db.items.upsert!({ name: "tx" }, { name: "tx" });
923
- await db.items.upsert!({ name: "tx" }, { name: "tx" });
924
- assert.equal(await db.items.count!({ name: "tx" }), 1, "upsert command failed");
925
- });
926
-
927
- /* Joins example */
928
- await test("Joins example", async () => {
929
- const items = await db.items.find!({}, {
930
- select: {
931
- "*": 1,
932
- items3: "*",
933
- items22: db.leftJoin?.items2({}, "*")
934
- }
935
- });
936
-
937
- if(!items.length || !items.every(it => Array.isArray(it.items3) && Array.isArray(it.items22))){
938
- console.log(items[0].items3)
939
- throw "Joined select query failed";
940
- }
941
- });
942
-
943
- /* Joins duplicate table example */
944
- await test("Joins repeating table example", async () => {
945
- const items2 = await db.items.find!({}, {
946
- select: {
947
- "*": 1,
948
- items2: "*"
949
- }
950
- });
951
- const items2j = await db.items.find!({}, {
952
- select: {
953
- "*": 1,
954
- items2: "*",
955
- items2j: db.leftJoin?.items2({}, "*")
956
- }
957
- });
958
-
959
- items2.forEach((d, i)=> {
960
- assert.deepStrictEqual(d.items2, items2j[i].items2, "Joins duplicate aliased table query failed");
961
- assert.deepStrictEqual(d.items2, items2j[i].items2j, "Joins duplicate aliased table query failed");
962
- });
963
- });
964
-
965
- await test("Join aggregate functions example", async () => {
966
- const singleShortHandAgg = await db.items.findOne!(
967
- {},
968
- { select: { id: "$max" }}
969
- );
970
- const singleAgg = await db.items.findOne!(
971
- {},
972
- { select: { id: { "$max": ["id"] } }}
973
- );
974
- assert.deepStrictEqual(singleShortHandAgg, { id: 4 });
975
- assert.deepStrictEqual(singleAgg, { id: 4 });
976
-
977
- const shortHandAggJoined = await db.items.findOne!(
978
- { id: 4 },
979
- { select: { id: 1, items2: { name: "$max" } }}
980
- );
981
- assert.deepStrictEqual(shortHandAggJoined, { id: 4, items2: [] });
982
- });
983
-
984
- /* $rowhash -> Custom column that returms md5(ctid + allowed select columns). Used in joins & CRUD to bypass PKey details */
985
- await test("$rowhash example", async () => {
986
- const rowhash = await db.items.findOne!({}, { select: { $rowhash: 1, "*": 1 }});
987
- const f = { $rowhash: rowhash?.$rowhash };
988
- const rowhashView = await db.v_items.findOne!({}, { select: { $rowhash: 1 }});
989
- const rh1 = await db.items.findOne!({ $rowhash: rowhash?.$rowhash }, { select: { $rowhash: 1 }});
990
- const rhView = await db.v_items.findOne!({ $rowhash: rowhashView?.$rowhash }, { select: { $rowhash: 1 }});
991
- // console.log({ rowhash, f });
992
-
993
- await db.items.update!(f, { name: 'a' });
994
-
995
- // console.log(rowhash, rh1)
996
- // console.log(rowhashView, rhView)
997
- if(
998
- typeof rowhash?.$rowhash !== "string" ||
999
- typeof rowhashView?.$rowhash !== "string" ||
1000
- rowhash.$rowhash !== rh1?.$rowhash ||
1001
- rowhashView.$rowhash !== rhView?.$rowhash
1002
- ){
1003
- throw "$rowhash query failed";
1004
- }
1005
- });
1006
-
1007
- await test("Reference column nested insert", async () => {
1008
- const nestedRow = { name: "nested_insert" };
1009
- const parentRow = { name: "parent insert" }
1010
- const pr = await db.items2.insert!({ items_id: nestedRow, ...parentRow }, { returning: "*" });
1011
-
1012
- const childRows = await db.items.find!(nestedRow);
1013
- assert.equal(childRows.length, 1);
1014
- assert.deepStrictEqual(await db.items2.findOne!(parentRow), { hh: null, id: pr.id, ...parentRow, items_id: childRows[0].id });
1015
-
1016
- });
1017
-
1018
- await test("Join escaped table names with quotes", async () => {
1019
- await db[`"""quoted0"""`].insert!({
1020
- [`"text_col0"`]: "0",
1021
- [`"quoted1_id"`]: {
1022
- [`"text_col1"`]: "1",
1023
- [`"quoted2_id"`]: {
1024
- [`"text_col2"`]: "2",
1025
- }
1026
- }
1027
- });
1028
-
1029
- const res = await db[`"""quoted0"""`].find!({
1030
- [`"text_col0"`]: "0",
1031
- }, {
1032
- select: {
1033
- "*": 1,
1034
- [`"""quoted2"""`]: {
1035
- [`"text_col2"`]: 1,
1036
- [`"id2"`]: "$min",
1037
- [`id2 max`]: { "$max": [`"id2"`] },
1038
- }
1039
- }
1040
- });
1041
-
1042
- assert.deepStrictEqual(res[0], {
1043
- '"""quoted2"""': [
1044
- {
1045
- '"text_col2"': '2',
1046
- 'id2 max': 1,
1047
- '"id2"': 1,
1048
- }
1049
- ],
1050
- '"id0"': 1,
1051
- '"quoted1_id"': 1,
1052
- '"text_col0"': '0'
1053
- });
1054
-
1055
-
1056
- const aliasedQuotedJoin = await db[`"""quoted0"""`].find!({
1057
- [`"text_col0"`]: "0",
1058
- }, {
1059
- select: {
1060
- "*": 1,
1061
- '"ali as"': {
1062
- $leftJoin: '"""quoted2"""',
1063
- select: {
1064
- '"text_col2"': { $left: ['"text_col2"', 2] },
1065
- '"id2"': "$min",
1066
- 'id2 max': { "$max": [`"id2"`] },
1067
- }
1068
- }
1069
- }
1070
- });
1071
-
1072
- assert.deepStrictEqual(aliasedQuotedJoin, [{
1073
- '"id0"': 1,
1074
- '"quoted1_id"': 1,
1075
- '"text_col0"': '0',
1076
- '"ali as"': [{
1077
- '"text_col2"': '2',
1078
- '"id2"': 1,
1079
- 'id2 max': 1,
1080
- }]
1081
- }]);
1082
-
1083
- const exists1 = await db[`"""quoted0"""`].find!({
1084
- $existsJoined: {
1085
- path: ['"""quoted1"""', '"""quoted2"""'],
1086
- filter: {
1087
- '"id2"': 1,
1088
- }
1089
- }
1090
- }, { select: "*" });
1091
- /** Duplicated tables */
1092
- const exists2 = await db[`"""quoted0"""`].find!({
1093
- $existsJoined: {
1094
- path: ['"""quoted1"""', '"""quoted2"""','"""quoted1"""', '"""quoted2"""'],
1095
- filter: {
1096
- '"id2"': 1,
1097
- }
1098
- }
1099
- }, { select: "*" });
1100
- assert.deepStrictEqual(exists1, exists2)
1101
- });
1102
-
1103
- await test("subscribe to escaped table name", async () => {
1104
- await tryRunP("subscribe to escaped table name", async (resolve, reject) => {
1105
- const filter = { [`"text_col0"`]: "0" }
1106
- let runs = 0;
1107
- setTimeout(async () => {
1108
- /** Used for debugging */
1109
- if(runs < 2){
1110
- const appName = await db.sql?.(`
1111
- SELECT application_name
1112
- FROM pg_catalog.pg_stat_activity
1113
- WHERE pid = pg_backend_pid()
1114
- `, [], { returnType: "rows" });
1115
- const apps = await db.sql?.(`SELECT * FROM prostgles.apps`, [], { returnType: "value" });
1116
- const app_triggers = await db.sql?.(`SELECT * FROM prostgles.app_triggers`, [], { returnType: "rows" });
1117
- log("show-logs");
1118
- log(JSON.stringify({ appName, apps, app_triggers }, null, 2));
1119
- }
1120
- }, 2000);
1121
- const sub = await db[`"""quoted0"""`].subscribe!(filter, { }, async items => {
1122
- const item = items[0];
1123
- log(JSON.stringify({
1124
- item,
1125
- runs,
1126
- }, null, 2));
1127
- if(item && item[`"text_col0"`] === "0"){
1128
- runs++;
1129
- if(runs === 1){
1130
- await db[`"""quoted0"""`].update!(filter, filter);
1131
- }
1132
- if(runs < 2){
1133
- return;
1134
- }
1135
- try {
1136
- await testToEnsureTriggersAreDisabled(sub, `"""quoted0"""`)
1137
- resolve(true);
1138
- } catch(e){
1139
- reject(e);
1140
- }
1141
- }
1142
- }).catch(reject);
1143
- });
1144
- });
1145
- const testName = "subscribe using a filter bigger than block_size"
1146
- await test(testName, async () => {
1147
- await tryRunP(testName, async (resolve, reject) => {
1148
- const sub = await db[`"""quoted0"""`].subscribe!({ [`"text_col0"`]: "0".repeat(2e3) }, { }, async items => {
1149
-
1150
- setTimeout(async () => {
1151
- if(!sub) return
1152
- await sub.unsubscribe();
1153
- resolve(true);
1154
- }, 10);
1155
- });
1156
-
1157
- });
1158
- });
1159
-
1160
- await test("Reverse join with agg", async () => {
1161
- const inserted = await db.tr1.insert!({ tr2: { t1: "a", t2: "b" } }, { returning: "*" });
1162
-
1163
- const idAggSelect = {
1164
- ids: {
1165
- $array_agg: ["id"],
1166
- }
1167
- }
1168
- const normalJoin = await db.tr1.find!({}, { orderBy: { id: true }, select: { "*": 1, tr2: { $innerJoin: "tr2", filter: { t1: "a" }, select: idAggSelect } } })
1169
- const reverseJoin = await db.tr2.find!({ t1: "a" }, { orderBy: { id: true }, select: { "*": 1, tr1: { $innerJoin: "tr1", select: idAggSelect } } });
1170
- assert.deepStrictEqual(normalJoin[0], {"id": 1,"t1": null,"tr2": [{ "ids": [1] }]});
1171
- assert.deepStrictEqual(normalJoin[1], {"id": 2,"t1": null,"tr2": [{ "ids": [2] }]});
1172
- assert.deepStrictEqual(reverseJoin[0], { "id": 1, "tr1_id": 1, "t1": "a", "t2": "b", "tr1": [{ "ids": [1] }] });
1173
- assert.deepStrictEqual(reverseJoin[1], { "id": 2, "tr1_id": 2, "t1": "a", "t2": "b", "tr1": [{ "ids": [2] }] });
1174
- });
1175
-
1176
- await test("Related table subscribe", async () => {
1177
- await tryRunP("Related table subscribe", async (resolve, reject) => {
1178
- let runs = 0;
1179
- const sub = await db.tr1.subscribe!(
1180
- {},
1181
- {
1182
- select: {
1183
- "*": 1,
1184
- tr2: "*",
1185
- tr3: "*",
1186
- }
1187
- },
1188
- async _rows => {
1189
- runs++;
1190
- console.log({ _rows, runs });
1191
- if(runs === 1){
1192
- await db.tr2.insert!({ tr1_id: _rows[0]!.id, t1: "a", t2: "b" });
1193
- } else if(runs === 2){
1194
- await sub.unsubscribe();
1195
- resolve(true);
1196
- }
1197
-
1198
- }
1199
- );
1200
-
1201
- });
1202
- });
1203
-
1204
- await test("Nested sort by computed col", async () => {
1205
- const getSorted = (asc = false) => db.tr1.find!({}, {
1206
- select: {
1207
- "*": 1,
1208
- tr2: {
1209
- maxId: { $max: ["id"] }
1210
- }
1211
- },
1212
- orderBy: {
1213
- "tr2.maxId": asc
1214
- }
1215
- })
1216
- const sortedAsc = await getSorted(true);
1217
- const sortedDesc = await getSorted(false);
1218
- assert.deepStrictEqual(
1219
- sortedAsc.map(d => d.tr2[0].maxId).slice(0).reverse(),
1220
- sortedDesc.map(d => d.tr2[0].maxId)
1221
- );
1222
- });
1223
-
1224
- await test("Nested function on different than source column getNewQuery name bug fix", async () => {
1225
- const res = await db.tr1.find!({}, {
1226
- select: {
1227
- "*": 1,
1228
- tr2: {
1229
- sign: { $sign: ["tr1_id"] }
1230
- }
1231
- },
1232
- orderBy: {
1233
- id: true
1234
- }
1235
- });
1236
- assert.deepStrictEqual(res.map(row => [row.id, row.tr2[0]!.sign]), [[1,1], [2, 1]]);
1237
- })
1238
-
1239
- await test("Reference column deep nested insert", async () => {
1240
-
1241
- const pr = await db.items4a.insert!({
1242
- items_id: { name: "it" },
1243
- items2_id: { name: "it2", items_id: { name: "it" } },
1244
- name: "it4a"
1245
- }, { returning: "*" });
1246
- const itemsCount = await db.items.count!({ name: "it" })
1247
- const items2Count = await db.items2.count!({ name: "it2" })
1248
- const items4aCount = await db.items4a.count!({ name: "it4a" })
1249
-
1250
- assert.equal(+itemsCount, 2);
1251
- assert.equal(+items2Count, 1);
1252
- assert.equal(+items4aCount, 1);
1253
-
1254
- });
1255
-
1256
- await test("Multi reference column nested insert", async () => {
1257
- await db.items_multi.insert!({
1258
- items0_id: { name: "multi" },
1259
- items1_id: { name: "multi" },
1260
- items2_id: { name: "multi" },
1261
- items3_id: { name: "multi" },
1262
- name: "root_multi"
1263
- }, { returning: "*" });
1264
- const itemsCount = await db.items.count!({ name: "multi" })
1265
- assert.equal(+itemsCount, 4);
1266
-
1267
- const multiItem = await db.items_multi.findOne!({ name: "root_multi" }, { select: { "*": 1, items: "*" } });
1268
- assert.equal(multiItem?.name, "root_multi");
1269
- assert.equal(multiItem?.items.filter(d => d.name === "multi").length, 4);
1270
-
1271
- });
1272
-
1273
- await test("Join path with order by nested", async () => {
1274
-
1275
- await db.items_multi.insert!({
1276
- items0_id: { name: "multi0" },
1277
- items1_id: { name: "multi1" },
1278
- name: "root_multi"
1279
- }, { returning: "*" });
1280
-
1281
- const res = await db.items_multi.find!(
1282
- {},
1283
- {
1284
- select: {
1285
- "*": 1,
1286
- i0: db.innerJoin?.items_multi(
1287
- { name: "multi0" },
1288
- "*",
1289
- { path: [{ table: "items", on: [{ items0_id: "id" }] }] }
1290
- )
1291
- },
1292
- orderBy: {
1293
- "i0.name": -1
1294
- }
1295
- }
1296
- );
1297
- assert.equal(res.length, 1);
1298
- assert.equal(res[0].i0[0].name, "multi0");
1299
- assert.equal(res[0].items2_id, null);
1300
- assert.equal(res[0].items2_id, null);
1301
- });
1302
-
1303
- await test("Self join", async () => {
1304
- await db.self_join.delete!();
1305
- const a = await db.self_join.insert!({ name: "a" });
1306
- const a1 = await db.self_join.insert!({ name: "a", my_id: { name: "b" } });
1307
- const a2 = await db.self_join.insert!({ name: "a", my_id1: { name: "b1" } });
1308
-
1309
- const one = await db.self_join.find!({}, {
1310
- select: {
1311
- name: 1,
1312
- my: {
1313
- $innerJoin: [{ table: "self_join", on: [{ my_id: "id" }] }],
1314
- filter: { name: "b" },
1315
- select: "*",
1316
- orderBy: "name"
1317
- }
1318
- }
1319
- });
1320
- assert.equal(one.length, 1);
1321
- assert.equal(one[0].my.length, 1);
1322
- assert.equal(one[0].my[0].name, "b");
1323
- });
1324
-
1325
-
1326
- await test("One to many multi join duplicate row bug fix", async () => {
1327
- await db.symbols.insert!([
1328
- {
1329
- id: "btc",
1330
- trades: [
1331
- { price: 1 },
1332
- { price: 3 },
1333
- { price: 2 },
1334
- ]
1335
- },{
1336
- id: "eth",
1337
- trades: [
1338
- { price: .1 },
1339
- { price: .3 },
1340
- { price: .2 },
1341
- ]
1342
- },{
1343
- id: "abc"
1344
- }
1345
- ]);
1346
-
1347
- const res = await db.symbols.find!({}, {
1348
- select: {
1349
- id: 1,
1350
- trades: "*",
1351
- tradesAlso: {
1352
- $leftJoin: "trades",
1353
- select: "*",
1354
- },
1355
- }
1356
- });
1357
- assert.equal(res.length, 3);
1358
- res.forEach(row => {
1359
- assert(typeof row.id, "number");
1360
- assert(typeof (row as any).price, "number");
1361
- assert(typeof (row as any).symbol, "string");
1362
- assert.deepStrictEqual(row.trades, row.tradesAlso);
1363
- if(row.id !== "abc"){
1364
- assert.equal(row.trades.length, 3)
1365
- }
1366
- });
1367
-
1368
- const resSortedInnerJoin = await db.symbols.find!({}, {
1369
- select: {
1370
- id: 1,
1371
- trades: {
1372
- $innerJoin: "trades",
1373
- select: "*",
1374
- orderBy: { price: -1 },
1375
- },
1376
- tradesAlso: {
1377
- $innerJoin: "trades",
1378
- select: "*",
1379
- orderBy: { price: 1 },
1380
- },
1381
- }
1382
- });
1383
- assert.equal(resSortedInnerJoin.length, 2);
1384
- resSortedInnerJoin.forEach(row => {
1385
- assert.deepStrictEqual(row.trades.slice(0).reverse(), row.tradesAlso);
1386
- assert.notEqual(row.id, "abc");
1387
- });
1388
- });
1389
- });
1390
-
1391
- await test("Having clause", async () => {
1392
- const res = await db.items.find!(
1393
- {},
1394
- {
1395
- select: { name: 1, c: { $countAll: [] }
1396
- },
1397
- having: {
1398
- c: 4,
1399
- }
1400
- });
1401
- assert.deepStrictEqual(res, [{
1402
- c: '4',
1403
- name: 'multi'
1404
- }]);
1405
- });
1406
-
1407
- await test("Having clause with complex filter", async () => {
1408
- const res = await db.items.find!(
1409
- {},
1410
- {
1411
- select: { name: 1, c: { $countAll: [] }
1412
- },
1413
- having: {
1414
- $filter: [
1415
- { $countAll: [] },
1416
- "=",
1417
- 4
1418
- ],
1419
- }
1420
- });
1421
- assert.deepStrictEqual(res, [{
1422
- c: '4',
1423
- name: 'multi'
1424
- }]);
1425
- });
1426
- await test("Nested join having clause", async () => {
1427
- const res = await db.items.find!(
1428
- {},
1429
- {
1430
- select: {
1431
- name: 1,
1432
- itms2: {
1433
- $innerJoin: "items2",
1434
- select: {
1435
- name: 1,
1436
- c: { $countAll: [] }
1437
- },
1438
- having: { c: 1 }
1439
- },
1440
- },
1441
- }
1442
- );
1443
- assert.deepStrictEqual(res, [
1444
- {
1445
- name: 'a',
1446
- itms2: [{ c: 1, name: 'a' }],
1447
- },
1448
- {
1449
- name: 'a',
1450
- itms2: [{ c: 1, name: 'a' }],
1451
- }
1452
- ]);
1453
- });
1454
-
1455
- }
1456
-
1457
-
1458
- export async function tryRun(desc: string, func: () => any, log?: Function){
1459
- try {
1460
- await func();
1461
- } catch(err) {
1462
- console.error(desc + " FAILED:", err);
1463
- log?.("FAIL: ", err);
1464
- console.trace(err)
1465
- await tout(50);
1466
- throw err;
1467
- }
1468
- }
1469
- export function tryRunP(desc: string, func: (resolve: any, reject: any) => any, opts?: { log?: Function; timeout?: number; }){
1470
- return new Promise(async (rv, rj) => {
1471
- const testTimeout = Number.isFinite(opts?.timeout)? setTimeout(() => {
1472
- const errMsg = `${desc} failed. Reason: Timout reached: ${opts!.timeout}ms`;
1473
- opts?.log?.(errMsg);
1474
- rj(errMsg);
1475
- }, opts!.timeout) : undefined
1476
- try {
1477
- await func(rv, rj);
1478
- clearTimeout(testTimeout);
1479
- } catch(err: any){
1480
- opts?.log?.(`${desc} failed: ` + JSON.stringify(err));
1481
- rj(err);
1482
- await tout(50);
1483
- throw err;
1484
- }
1485
- });
1486
- }
1487
- const tout = (t = 3000) => {
1488
- return new Promise(async (resolve, reject) => {
1489
- setTimeout(() => {
1490
- resolve(true)
1491
- },t)
1492
- });
1493
- }