prostgles-server 4.2.157 → 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 (159) hide show
  1. package/dist/Auth/AuthHandler.js +2 -2
  2. package/dist/Auth/AuthHandler.js.map +1 -1
  3. package/dist/Auth/AuthTypes.d.ts +4 -8
  4. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  5. package/dist/Auth/setAuthProviders.d.ts +1 -1
  6. package/dist/Auth/setAuthProviders.d.ts.map +1 -1
  7. package/dist/Auth/setAuthProviders.js +6 -7
  8. package/dist/Auth/setAuthProviders.js.map +1 -1
  9. package/dist/Auth/setEmailProvider.d.ts +1 -1
  10. package/dist/Auth/setEmailProvider.d.ts.map +1 -1
  11. package/dist/Auth/setEmailProvider.js +22 -2
  12. package/dist/Auth/setEmailProvider.js.map +1 -1
  13. package/dist/Auth/setupAuthRoutes.js +1 -1
  14. package/dist/Auth/setupAuthRoutes.js.map +1 -1
  15. package/dist/Prostgles.d.ts +1 -0
  16. package/dist/Prostgles.d.ts.map +1 -1
  17. package/dist/Prostgles.js +6 -0
  18. package/dist/Prostgles.js.map +1 -1
  19. package/dist/initProstgles.d.ts.map +1 -1
  20. package/dist/initProstgles.js +2 -6
  21. package/dist/initProstgles.js.map +1 -1
  22. package/package.json +1 -1
  23. package/lib/Auth/AuthHandler.ts +0 -436
  24. package/lib/Auth/AuthTypes.ts +0 -285
  25. package/lib/Auth/getSafeReturnURL.ts +0 -35
  26. package/lib/Auth/sendEmail.ts +0 -83
  27. package/lib/Auth/setAuthProviders.ts +0 -129
  28. package/lib/Auth/setEmailProvider.ts +0 -63
  29. package/lib/Auth/setupAuthRoutes.ts +0 -161
  30. package/lib/DBEventsManager.ts +0 -178
  31. package/lib/DBSchemaBuilder.ts +0 -225
  32. package/lib/DboBuilder/DboBuilder.ts +0 -319
  33. package/lib/DboBuilder/DboBuilderTypes.ts +0 -361
  34. package/lib/DboBuilder/QueryBuilder/Functions.ts +0 -1153
  35. package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +0 -288
  36. package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +0 -263
  37. package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +0 -271
  38. package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +0 -136
  39. package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +0 -22
  40. package/lib/DboBuilder/QueryStreamer.ts +0 -250
  41. package/lib/DboBuilder/TableHandler/DataValidator.ts +0 -428
  42. package/lib/DboBuilder/TableHandler/TableHandler.ts +0 -205
  43. package/lib/DboBuilder/TableHandler/delete.ts +0 -115
  44. package/lib/DboBuilder/TableHandler/insert.ts +0 -183
  45. package/lib/DboBuilder/TableHandler/insertTest.ts +0 -78
  46. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +0 -62
  47. package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +0 -134
  48. package/lib/DboBuilder/TableHandler/update.ts +0 -126
  49. package/lib/DboBuilder/TableHandler/updateBatch.ts +0 -49
  50. package/lib/DboBuilder/TableHandler/updateFile.ts +0 -48
  51. package/lib/DboBuilder/TableHandler/upsert.ts +0 -34
  52. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +0 -393
  53. package/lib/DboBuilder/ViewHandler/count.ts +0 -38
  54. package/lib/DboBuilder/ViewHandler/find.ts +0 -153
  55. package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +0 -73
  56. package/lib/DboBuilder/ViewHandler/getExistsFilters.ts +0 -74
  57. package/lib/DboBuilder/ViewHandler/getInfo.ts +0 -32
  58. package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +0 -84
  59. package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +0 -96
  60. package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +0 -105
  61. package/lib/DboBuilder/ViewHandler/parseJoinPath.ts +0 -208
  62. package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +0 -163
  63. package/lib/DboBuilder/ViewHandler/prepareWhere.ts +0 -90
  64. package/lib/DboBuilder/ViewHandler/size.ts +0 -37
  65. package/lib/DboBuilder/ViewHandler/subscribe.ts +0 -118
  66. package/lib/DboBuilder/ViewHandler/validateViewRules.ts +0 -70
  67. package/lib/DboBuilder/dboBuilderUtils.ts +0 -222
  68. package/lib/DboBuilder/getColumns.ts +0 -114
  69. package/lib/DboBuilder/getCondition.ts +0 -201
  70. package/lib/DboBuilder/getSubscribeRelatedTables.ts +0 -190
  71. package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +0 -426
  72. package/lib/DboBuilder/insertNestedRecords.ts +0 -355
  73. package/lib/DboBuilder/parseUpdateRules.ts +0 -187
  74. package/lib/DboBuilder/prepareShortestJoinPaths.ts +0 -186
  75. package/lib/DboBuilder/runSQL.ts +0 -182
  76. package/lib/DboBuilder/runTransaction.ts +0 -50
  77. package/lib/DboBuilder/sqlErrCodeToMsg.ts +0 -254
  78. package/lib/DboBuilder/uploadFile.ts +0 -69
  79. package/lib/Event_Trigger_Tags.ts +0 -118
  80. package/lib/FileManager/FileManager.ts +0 -358
  81. package/lib/FileManager/getValidatedFileType.ts +0 -69
  82. package/lib/FileManager/initFileManager.ts +0 -187
  83. package/lib/FileManager/upload.ts +0 -62
  84. package/lib/FileManager/uploadStream.ts +0 -79
  85. package/lib/Filtering.ts +0 -463
  86. package/lib/JSONBValidation/validate_jsonb_schema_sql.ts +0 -502
  87. package/lib/JSONBValidation/validation.ts +0 -143
  88. package/lib/Logging.ts +0 -127
  89. package/lib/PostgresNotifListenManager.ts +0 -143
  90. package/lib/Prostgles.ts +0 -479
  91. package/lib/ProstglesTypes.ts +0 -196
  92. package/lib/PubSubManager/PubSubManager.ts +0 -609
  93. package/lib/PubSubManager/addSub.ts +0 -138
  94. package/lib/PubSubManager/addSync.ts +0 -141
  95. package/lib/PubSubManager/getCreatePubSubManagerError.ts +0 -72
  96. package/lib/PubSubManager/getPubSubManagerInitQuery.ts +0 -662
  97. package/lib/PubSubManager/initPubSubManager.ts +0 -79
  98. package/lib/PubSubManager/notifListener.ts +0 -173
  99. package/lib/PubSubManager/orphanTriggerCheck.ts +0 -70
  100. package/lib/PubSubManager/pushSubData.ts +0 -55
  101. package/lib/PublishParser/PublishParser.ts +0 -162
  102. package/lib/PublishParser/getFileTableRules.ts +0 -124
  103. package/lib/PublishParser/getSchemaFromPublish.ts +0 -141
  104. package/lib/PublishParser/getTableRulesWithoutFileTable.ts +0 -177
  105. package/lib/PublishParser/publishTypesAndUtils.ts +0 -399
  106. package/lib/RestApi.ts +0 -127
  107. package/lib/SchemaWatch/SchemaWatch.ts +0 -90
  108. package/lib/SchemaWatch/createSchemaWatchEventTrigger.ts +0 -3
  109. package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +0 -45
  110. package/lib/SchemaWatch/getWatchSchemaTagList.ts +0 -27
  111. package/lib/SyncReplication.ts +0 -557
  112. package/lib/TableConfig/TableConfig.ts +0 -468
  113. package/lib/TableConfig/getColumnDefinitionQuery.ts +0 -111
  114. package/lib/TableConfig/getConstraintDefinitionQueries.ts +0 -95
  115. package/lib/TableConfig/getFutureTableSchema.ts +0 -64
  116. package/lib/TableConfig/getPGIndexes.ts +0 -53
  117. package/lib/TableConfig/getTableColumnQueries.ts +0 -129
  118. package/lib/TableConfig/initTableConfig.ts +0 -326
  119. package/lib/index.ts +0 -13
  120. package/lib/initProstgles.ts +0 -322
  121. package/lib/onSocketConnected.ts +0 -102
  122. package/lib/runClientRequest.ts +0 -129
  123. package/lib/shortestPath.ts +0 -122
  124. package/lib/typeTests/DBoGenerated.d.ts +0 -320
  125. package/lib/typeTests/dboTypeCheck.ts +0 -81
  126. package/lib/utils.ts +0 -15
  127. package/tests/client/hooks.spec.ts +0 -205
  128. package/tests/client/index.ts +0 -139
  129. package/tests/client/package-lock.json +0 -637
  130. package/tests/client/package.json +0 -26
  131. package/tests/client/renderReactHook.ts +0 -177
  132. package/tests/client/tsconfig.json +0 -15
  133. package/tests/client/useProstgles.spec.ts +0 -120
  134. package/tests/clientFileTests.spec.ts +0 -102
  135. package/tests/clientOnlyQueries.spec.ts +0 -667
  136. package/tests/clientRestApi.spec.ts +0 -82
  137. package/tests/config_test/DBoGenerated.d.ts +0 -407
  138. package/tests/config_test/index.html +0 -109
  139. package/tests/config_test/index.js +0 -86
  140. package/tests/config_test/index.js.map +0 -1
  141. package/tests/config_test/index.ts +0 -91
  142. package/tests/config_test/init.sql +0 -48
  143. package/tests/config_test/package.json +0 -29
  144. package/tests/config_test/tsconfig.json +0 -23
  145. package/tests/config_testDBoGenerated.d.ts +0 -407
  146. package/tests/isomorphicQueries.spec.ts +0 -1493
  147. package/tests/server/DBoGenerated.d.ts +0 -537
  148. package/tests/server/index.html +0 -73
  149. package/tests/server/index.ts +0 -289
  150. package/tests/server/init.sql +0 -224
  151. package/tests/server/package-lock.json +0 -2164
  152. package/tests/server/package.json +0 -25
  153. package/tests/server/publishTypeCheck.ts +0 -136
  154. package/tests/server/server.ts +0 -35
  155. package/tests/server/testPublish.ts +0 -147
  156. package/tests/server/testTableConfig.ts +0 -156
  157. package/tests/server/tsconfig.json +0 -22
  158. package/tests/serverOnlyQueries.spec.ts +0 -32
  159. 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
- }