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.
- package/dist/Auth/AuthTypes.d.ts +4 -8
- package/dist/Auth/AuthTypes.d.ts.map +1 -1
- package/dist/Auth/setAuthProviders.d.ts.map +1 -1
- package/dist/Auth/setAuthProviders.js +4 -5
- package/dist/Auth/setAuthProviders.js.map +1 -1
- package/dist/Auth/setEmailProvider.js +1 -1
- package/dist/Auth/setEmailProvider.js.map +1 -1
- package/package.json +1 -1
- package/lib/Auth/AuthHandler.ts +0 -436
- package/lib/Auth/AuthTypes.ts +0 -285
- package/lib/Auth/getSafeReturnURL.ts +0 -35
- package/lib/Auth/sendEmail.ts +0 -83
- package/lib/Auth/setAuthProviders.ts +0 -129
- package/lib/Auth/setEmailProvider.ts +0 -85
- package/lib/Auth/setupAuthRoutes.ts +0 -161
- package/lib/DBEventsManager.ts +0 -178
- package/lib/DBSchemaBuilder.ts +0 -225
- package/lib/DboBuilder/DboBuilder.ts +0 -319
- package/lib/DboBuilder/DboBuilderTypes.ts +0 -361
- package/lib/DboBuilder/QueryBuilder/Functions.ts +0 -1153
- package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +0 -288
- package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +0 -263
- package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +0 -271
- package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +0 -136
- package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +0 -22
- package/lib/DboBuilder/QueryStreamer.ts +0 -250
- package/lib/DboBuilder/TableHandler/DataValidator.ts +0 -428
- package/lib/DboBuilder/TableHandler/TableHandler.ts +0 -205
- package/lib/DboBuilder/TableHandler/delete.ts +0 -115
- package/lib/DboBuilder/TableHandler/insert.ts +0 -183
- package/lib/DboBuilder/TableHandler/insertTest.ts +0 -78
- package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +0 -62
- package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +0 -134
- package/lib/DboBuilder/TableHandler/update.ts +0 -126
- package/lib/DboBuilder/TableHandler/updateBatch.ts +0 -49
- package/lib/DboBuilder/TableHandler/updateFile.ts +0 -48
- package/lib/DboBuilder/TableHandler/upsert.ts +0 -34
- package/lib/DboBuilder/ViewHandler/ViewHandler.ts +0 -393
- package/lib/DboBuilder/ViewHandler/count.ts +0 -38
- package/lib/DboBuilder/ViewHandler/find.ts +0 -153
- package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +0 -73
- package/lib/DboBuilder/ViewHandler/getExistsFilters.ts +0 -74
- package/lib/DboBuilder/ViewHandler/getInfo.ts +0 -32
- package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +0 -84
- package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +0 -96
- package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +0 -105
- package/lib/DboBuilder/ViewHandler/parseJoinPath.ts +0 -208
- package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +0 -163
- package/lib/DboBuilder/ViewHandler/prepareWhere.ts +0 -90
- package/lib/DboBuilder/ViewHandler/size.ts +0 -37
- package/lib/DboBuilder/ViewHandler/subscribe.ts +0 -118
- package/lib/DboBuilder/ViewHandler/validateViewRules.ts +0 -70
- package/lib/DboBuilder/dboBuilderUtils.ts +0 -222
- package/lib/DboBuilder/getColumns.ts +0 -114
- package/lib/DboBuilder/getCondition.ts +0 -201
- package/lib/DboBuilder/getSubscribeRelatedTables.ts +0 -190
- package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +0 -426
- package/lib/DboBuilder/insertNestedRecords.ts +0 -355
- package/lib/DboBuilder/parseUpdateRules.ts +0 -187
- package/lib/DboBuilder/prepareShortestJoinPaths.ts +0 -186
- package/lib/DboBuilder/runSQL.ts +0 -182
- package/lib/DboBuilder/runTransaction.ts +0 -50
- package/lib/DboBuilder/sqlErrCodeToMsg.ts +0 -254
- package/lib/DboBuilder/uploadFile.ts +0 -69
- package/lib/Event_Trigger_Tags.ts +0 -118
- package/lib/FileManager/FileManager.ts +0 -358
- package/lib/FileManager/getValidatedFileType.ts +0 -69
- package/lib/FileManager/initFileManager.ts +0 -187
- package/lib/FileManager/upload.ts +0 -62
- package/lib/FileManager/uploadStream.ts +0 -79
- package/lib/Filtering.ts +0 -463
- package/lib/JSONBValidation/validate_jsonb_schema_sql.ts +0 -502
- package/lib/JSONBValidation/validation.ts +0 -143
- package/lib/Logging.ts +0 -127
- package/lib/PostgresNotifListenManager.ts +0 -143
- package/lib/Prostgles.ts +0 -485
- package/lib/ProstglesTypes.ts +0 -196
- package/lib/PubSubManager/PubSubManager.ts +0 -609
- package/lib/PubSubManager/addSub.ts +0 -138
- package/lib/PubSubManager/addSync.ts +0 -141
- package/lib/PubSubManager/getCreatePubSubManagerError.ts +0 -72
- package/lib/PubSubManager/getPubSubManagerInitQuery.ts +0 -662
- package/lib/PubSubManager/initPubSubManager.ts +0 -79
- package/lib/PubSubManager/notifListener.ts +0 -173
- package/lib/PubSubManager/orphanTriggerCheck.ts +0 -70
- package/lib/PubSubManager/pushSubData.ts +0 -55
- package/lib/PublishParser/PublishParser.ts +0 -162
- package/lib/PublishParser/getFileTableRules.ts +0 -124
- package/lib/PublishParser/getSchemaFromPublish.ts +0 -141
- package/lib/PublishParser/getTableRulesWithoutFileTable.ts +0 -177
- package/lib/PublishParser/publishTypesAndUtils.ts +0 -399
- package/lib/RestApi.ts +0 -127
- package/lib/SchemaWatch/SchemaWatch.ts +0 -90
- package/lib/SchemaWatch/createSchemaWatchEventTrigger.ts +0 -3
- package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +0 -45
- package/lib/SchemaWatch/getWatchSchemaTagList.ts +0 -27
- package/lib/SyncReplication.ts +0 -557
- package/lib/TableConfig/TableConfig.ts +0 -468
- package/lib/TableConfig/getColumnDefinitionQuery.ts +0 -111
- package/lib/TableConfig/getConstraintDefinitionQueries.ts +0 -95
- package/lib/TableConfig/getFutureTableSchema.ts +0 -64
- package/lib/TableConfig/getPGIndexes.ts +0 -53
- package/lib/TableConfig/getTableColumnQueries.ts +0 -129
- package/lib/TableConfig/initTableConfig.ts +0 -326
- package/lib/index.ts +0 -13
- package/lib/initProstgles.ts +0 -319
- package/lib/onSocketConnected.ts +0 -102
- package/lib/runClientRequest.ts +0 -129
- package/lib/shortestPath.ts +0 -122
- package/lib/typeTests/DBoGenerated.d.ts +0 -320
- package/lib/typeTests/dboTypeCheck.ts +0 -81
- package/lib/utils.ts +0 -15
- package/tests/client/hooks.spec.ts +0 -205
- package/tests/client/index.ts +0 -139
- package/tests/client/package-lock.json +0 -637
- package/tests/client/package.json +0 -26
- package/tests/client/renderReactHook.ts +0 -177
- package/tests/client/tsconfig.json +0 -15
- package/tests/client/useProstgles.spec.ts +0 -120
- package/tests/clientFileTests.spec.ts +0 -102
- package/tests/clientOnlyQueries.spec.ts +0 -667
- package/tests/clientRestApi.spec.ts +0 -82
- package/tests/config_test/DBoGenerated.d.ts +0 -407
- package/tests/config_test/index.html +0 -109
- package/tests/config_test/index.js +0 -86
- package/tests/config_test/index.js.map +0 -1
- package/tests/config_test/index.ts +0 -91
- package/tests/config_test/init.sql +0 -48
- package/tests/config_test/package.json +0 -29
- package/tests/config_test/tsconfig.json +0 -23
- package/tests/config_testDBoGenerated.d.ts +0 -407
- package/tests/isomorphicQueries.spec.ts +0 -1493
- package/tests/server/DBoGenerated.d.ts +0 -537
- package/tests/server/index.html +0 -73
- package/tests/server/index.ts +0 -289
- package/tests/server/init.sql +0 -224
- package/tests/server/package-lock.json +0 -2164
- package/tests/server/package.json +0 -25
- package/tests/server/publishTypeCheck.ts +0 -136
- package/tests/server/server.ts +0 -35
- package/tests/server/testPublish.ts +0 -147
- package/tests/server/testTableConfig.ts +0 -156
- package/tests/server/tsconfig.json +0 -22
- package/tests/serverOnlyQueries.spec.ts +0 -32
- 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
|
-
}
|