pogi 2.11.0

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 (82) hide show
  1. package/.vscode/launch.json +35 -0
  2. package/CHANGELOG.md +277 -0
  3. package/LICENSE +21 -0
  4. package/README.md +85 -0
  5. package/docs/API/PgDb.md +218 -0
  6. package/docs/API/PgSchema.md +91 -0
  7. package/docs/API/PgTable.md +365 -0
  8. package/docs/API/QueryOptions.md +77 -0
  9. package/docs/API/condition.md +133 -0
  10. package/docs/connection.md +91 -0
  11. package/docs/css/docs.css +164 -0
  12. package/docs/executingSqlFile.md +44 -0
  13. package/docs/faq.md +15 -0
  14. package/docs/functions.md +19 -0
  15. package/docs/generatingInterfaceForTables.md +35 -0
  16. package/docs/index.md +48 -0
  17. package/docs/logger.md +40 -0
  18. package/docs/mappingDatabaseTypes.md +89 -0
  19. package/docs/notification.md +19 -0
  20. package/docs/pitfalls.md +73 -0
  21. package/docs/streams.md +68 -0
  22. package/docs/transaction.md +65 -0
  23. package/lib/bin/generateInterface.d.ts +1 -0
  24. package/lib/bin/generateInterface.js +53 -0
  25. package/lib/bin/generateInterface.js.map +1 -0
  26. package/lib/connectionOptions.d.ts +25 -0
  27. package/lib/connectionOptions.js +3 -0
  28. package/lib/connectionOptions.js.map +1 -0
  29. package/lib/index.d.ts +6 -0
  30. package/lib/index.js +10 -0
  31. package/lib/index.js.map +1 -0
  32. package/lib/pgConverters.d.ts +10 -0
  33. package/lib/pgConverters.js +66 -0
  34. package/lib/pgConverters.js.map +1 -0
  35. package/lib/pgDb.d.ts +86 -0
  36. package/lib/pgDb.js +745 -0
  37. package/lib/pgDb.js.map +1 -0
  38. package/lib/pgDbLogger.d.ts +5 -0
  39. package/lib/pgDbLogger.js +3 -0
  40. package/lib/pgDbLogger.js.map +1 -0
  41. package/lib/pgDbOperators.d.ts +113 -0
  42. package/lib/pgDbOperators.js +44 -0
  43. package/lib/pgDbOperators.js.map +1 -0
  44. package/lib/pgSchema.d.ts +16 -0
  45. package/lib/pgSchema.js +16 -0
  46. package/lib/pgSchema.js.map +1 -0
  47. package/lib/pgTable.d.ts +131 -0
  48. package/lib/pgTable.js +322 -0
  49. package/lib/pgTable.js.map +1 -0
  50. package/lib/pgUtils.d.ts +31 -0
  51. package/lib/pgUtils.js +157 -0
  52. package/lib/pgUtils.js.map +1 -0
  53. package/lib/queryAble.d.ts +76 -0
  54. package/lib/queryAble.js +330 -0
  55. package/lib/queryAble.js.map +1 -0
  56. package/lib/queryWhere.d.ts +8 -0
  57. package/lib/queryWhere.js +249 -0
  58. package/lib/queryWhere.js.map +1 -0
  59. package/mkdocs.yml +25 -0
  60. package/package.json +65 -0
  61. package/spec/resources/init.sql +122 -0
  62. package/spec/resources/throw_exception.sql +5 -0
  63. package/spec/resources/tricky.sql +13 -0
  64. package/spec/run.js +5 -0
  65. package/spec/support/jasmine.json +9 -0
  66. package/src/bin/generateInterface.ts +54 -0
  67. package/src/connectionOptions.ts +42 -0
  68. package/src/index.ts +6 -0
  69. package/src/pgConverters.ts +55 -0
  70. package/src/pgDb.ts +820 -0
  71. package/src/pgDbLogger.ts +13 -0
  72. package/src/pgDbOperators.ts +62 -0
  73. package/src/pgSchema.ts +15 -0
  74. package/src/pgTable.ts +401 -0
  75. package/src/pgUtils.ts +176 -0
  76. package/src/queryAble.ts +393 -0
  77. package/src/queryWhere.ts +326 -0
  78. package/src/test/pgDbOperatorSpec.ts +492 -0
  79. package/src/test/pgDbSpec.ts +1339 -0
  80. package/src/test/pgServiceRestartTest.ts +1500 -0
  81. package/src/tsconfig.json +33 -0
  82. package/utils_sql/lower.sql +4 -0
@@ -0,0 +1,1339 @@
1
+ /// <reference types="jasmine"/>
2
+ import { PgDb } from "../pgDb";
3
+ import { PgTable } from "../pgTable";
4
+ import * as _ from 'lodash';
5
+
6
+ const util = require('util');
7
+
8
+ function w(func) {
9
+ return function (done) {
10
+ return (async () => {
11
+ try {
12
+ await func();
13
+ } catch (e) {
14
+ console.log('------------------------------');
15
+ console.error(e.message, e.stack);
16
+ console.log('------------------------------');
17
+ expect('Exception: ' + e.message).toBeFalsy();
18
+ }
19
+ return done();
20
+ })();
21
+ }
22
+ }
23
+
24
+
25
+ /**
26
+ * {,ads,"asdf""fd,",",3," "}
27
+ *
28
+ s = ',ads,"asdf""fd,",",3," "';
29
+ e =
30
+ e.exec(s)
31
+ */
32
+ function parseComplexTypeArray(str) {
33
+ let list = JSON.parse('[' + str.substring(1, str.length - 1) + ']');
34
+
35
+ let result = [];
36
+ for (let elementStr of list) {
37
+ result.push(parseComplexType(elementStr));
38
+ }
39
+ return result;
40
+ }
41
+
42
+ function parseComplexType(str) {
43
+ //cut of '(', ')'
44
+ str = str.substring(1, str.length - 1);
45
+ let e = /"((?:[^"]|"")*)"(?:,|$)|([^,]*)(?:,|$)/g;
46
+ let valList = [];
47
+ let parsingResult;
48
+ let valStr;
49
+ let hasNextValue;
50
+ /**
51
+ * parsingResult.index<str.length check for finish is not reliable
52
+ * as if the last value is null it goes undetected, e.g. (,,)
53
+ */
54
+ do {
55
+ parsingResult = e.exec(str);
56
+ valStr = (parsingResult[0] == '' || parsingResult[0] == ',' || parsingResult[2] == 'null') ? null : parsingResult[1] || parsingResult[2];
57
+ if (parsingResult[0] == '"",' || parsingResult[0] == '""') {
58
+ valStr = '';
59
+ }
60
+ valList.push(valStr ? valStr.replace(/""/g, '"') : valStr);
61
+ hasNextValue = parsingResult[0].substring(parsingResult[0].length - 1, parsingResult[0].length) == ',';
62
+ } while (hasNextValue);
63
+ return valList;
64
+ }
65
+
66
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = 800000;
67
+
68
+ describe("pgdb", () => {
69
+ let pgdb: PgDb;
70
+ let schema = 'pgdb_test';
71
+ let hiddenSchema = 'pgdb_test_hidden';
72
+ let table: PgTable<any>;
73
+ let tableGroups: PgTable<any>;
74
+ let tableTypes: PgTable<any>;
75
+
76
+ beforeAll(w(async () => {
77
+ /**
78
+ * Using environment variables, e.g.
79
+ * PGUSER (defaults USER env var, so optional)
80
+ * PGDATABASE (defaults USER env var, so optional)
81
+ * PGPASSWORD
82
+ * PGPORT
83
+ * etc...
84
+ */
85
+ try {
86
+ pgdb = await PgDb.connect({ connectionString: "postgres://" });
87
+ } catch (e) {
88
+ console.error("connection failed! Are you specified PGUSER/PGDATABASE/PGPASSWORD correctly?");
89
+ process.exit(1);
90
+ }
91
+
92
+ //create schema without access rights
93
+ let current_role = await pgdb.queryOneField('SELECT current_role');
94
+
95
+ await pgdb.run('CREATE SCHEMA IF NOT EXISTS "' + hiddenSchema + '"');
96
+ await pgdb.run('GRANT ALL ON SCHEMA "' + hiddenSchema + '" TO "' + current_role + '"');
97
+ await pgdb.execute('spec/resources/init.sql', (cmd) => cmd.replace(/__SCHEMA__/g, '"' + hiddenSchema + '"'));
98
+ await pgdb.run('REVOKE ALL ON SCHEMA "' + hiddenSchema + '" FROM "' + current_role + '" CASCADE');
99
+
100
+ //await pgdb.run('DROP SCHEMA IF EXISTS "' + schema + '" CASCADE ');
101
+ await pgdb.run('CREATE SCHEMA IF NOT EXISTS "' + schema + '"');
102
+ await pgdb.execute('spec/resources/init.sql', (cmd) => cmd.replace(/__SCHEMA__/g, '"' + schema + '"'));
103
+ await pgdb.reload();
104
+
105
+ pgdb.setLogger(console);
106
+ table = pgdb.schemas[schema]['users'];
107
+ tableGroups = pgdb.schemas[schema]['groups'];
108
+ tableTypes = pgdb.schemas[schema]['types'];
109
+ }));
110
+
111
+ beforeEach(w(async () => {
112
+ await table.delete({});
113
+ await tableGroups.delete({});
114
+ await tableTypes.delete({});
115
+ }));
116
+
117
+ afterEach(w(async () => {
118
+ if (pgdb.pool.waitingCount != 0) {
119
+ expect('Not all connection is released').toBeFalsy();
120
+ await pgdb.pool.end();
121
+ }
122
+ }));
123
+
124
+ it("Exception on name collision", w(async () => {
125
+ await table.insert({ name: 'A' });
126
+ try {
127
+ await pgdb.query(`select u1.id, u2.id from ${table} u1 left join ${table} u2 ON true `);
128
+ expect(false).toBeTruthy();
129
+ } catch (e) {
130
+ expect(/Name collision for the query, two or more fields have the same name./.test(e.message)).toBeTruthy();
131
+ }
132
+ }));
133
+
134
+
135
+ it("After adding parser should be able to parse complex type", w(async () => {
136
+ await pgdb.setTypeParser('permissionForResourceType', (val) => parseComplexType(val));
137
+ await pgdb.setTypeParser('_permissionForResourceType', (val) => val == "{}" ? [] : parseComplexTypeArray(val));
138
+ await table.insert({
139
+ name: 'Piszkos Fred',
140
+ permission: "(read,book)",
141
+ permissionList: [
142
+ '(admin,"chaos j ()"",""""j ,")',
143
+ '(write,book)',
144
+ '(write,)',
145
+ '(,"")',
146
+ '(,)',
147
+ '(write,null)'],
148
+ });
149
+ let pf = await table.findOne({ name: 'Piszkos Fred' });
150
+ expect(pf.permission).toEqual(['read', 'book']);
151
+ expect(pf.permissionList[0]).toEqual(['admin', 'chaos j ()",""j ,']);
152
+ expect(pf.permissionList[1]).toEqual(['write', 'book']);
153
+ expect(pf.permissionList[2]).toEqual(['write', null]);
154
+ expect(pf.permissionList[3]).toEqual([null, ""]);
155
+ expect(pf.permissionList[4]).toEqual([null, null]);
156
+ expect(pf.permissionList[5]).toEqual(['write', null]);
157
+ expect(pf.permissionList.length).toEqual(6);
158
+ }));
159
+
160
+ it("Complex type could be easily read and converted to json", w(async () => {
161
+ await table.insert({
162
+ name: 'Elveszett cirkalo',
163
+ permission: "(read,book)",
164
+ permissionList: [
165
+ '(admin,"chaos j ()"",""""j ,")',
166
+ '(write,book)',
167
+ '(write,)',
168
+ '(,"")',
169
+ '(,)',
170
+ '(write,null)'],
171
+ });
172
+
173
+ let res = await table.query(
174
+ `SELECT to_json(permission) perjson, to_json("permissionList") perlistjson
175
+ FROM ${table}
176
+ WHERE name='Elveszett cirkalo' `);
177
+ expect(res[0].perjson).toEqual({ permission: 'read', resource: 'book' });
178
+ expect(res[0].perlistjson).toEqual([
179
+ { permission: 'admin', resource: 'chaos j ()",""j ,' },
180
+ { permission: 'write', resource: 'book' },
181
+ { permission: 'write', resource: null },
182
+ { permission: null, resource: '' },
183
+ { permission: null, resource: null },
184
+ { permission: 'write', resource: 'null' }
185
+ ]);
186
+ }));
187
+
188
+ it("List for a non array column in the condition should be converted to 'IN Array'", w(async () => {
189
+ await table.insert({ name: 'Fulig Jimmy', favourites: ['sport'] });
190
+ await table.insert({ name: 'Vanek ur', favourites: ['sport', 'food'] });
191
+ await table.insert({ name: 'Gorcsev Ivan', favourites: ['tech', 'food'] });
192
+
193
+ let res1 = await table.findAll();
194
+ let res2 = await table.find({ id: res1.map(e => e.id) });
195
+ expect(res1.map(c => c.id)).toEqual(res2.map(c => c.id));
196
+ }));
197
+
198
+ it("Testing where function", w(async () => {
199
+ await table.insert({ name: 'Fulig Jimmy', favourites: ['sport'] });
200
+ await table.insert({ name: 'Vanek ur', favourites: ['sport', 'food'] });
201
+ await table.insert({ name: 'Gorcsev Ivan', favourites: ['tech', 'food', 'sport'] });
202
+
203
+ let res = await table.findWhere(':fav = ANY("favourites")', { fav: 'sport' });
204
+ expect(res.length).toEqual(3);
205
+ res = await table.findWhere(':fav = ANY("favourites")', { fav: 'food' });
206
+ expect(res.length).toEqual(2);
207
+ res = await table.findWhere(':fav = ANY("favourites")', { fav: 'tech' });
208
+ expect(res.length).toEqual(1);
209
+ }));
210
+
211
+ it("Upsert - with column names", w(async () => {
212
+ await table.upsert({ name: 'Fulig Jimmy', textList: ['hiking'] }, { columns: ['name'] });
213
+ let res = await table.findWhere(':fav = ANY("textList")', { fav: 'hiking' });
214
+ expect(res.length).toEqual(1);
215
+
216
+ await table.upsert({ name: 'Fulig Jimmy', textList: ['biking'] }, { columns: ['name'] });
217
+ res = await table.findWhere(':fav = ANY("textList")', { fav: 'hiking' });
218
+ expect(res.length).toEqual(0);
219
+
220
+ res = await table.findWhere(':fav = ANY("textList")', { fav: 'biking' });
221
+ expect(res.length).toEqual(1);
222
+ }));
223
+
224
+ it("Upsert - with primary key", w(async () => {
225
+ await table.upsert({ id: 1, name: 'Fulig Jimmy', textList: ['hiking'] });
226
+ let res = await table.findWhere(':fav = ANY("textList")', { fav: 'hiking' });
227
+ expect(res.length).toEqual(1);
228
+
229
+ await table.upsert({ id: 1, name: 'Fulig Jimmy', textList: ['biking'] });
230
+ res = await table.findWhere(':fav = ANY("textList")', { fav: 'hiking' });
231
+ expect(res.length).toEqual(0);
232
+
233
+ res = await table.findWhere(':fav = ANY("textList")', { fav: 'biking' });
234
+ expect(res.length).toEqual(1);
235
+ }));
236
+
237
+ it("Upsert - with constraint name", w(async () => {
238
+ await table.upsert({ name: 'Fulig Jimmy', textList: ['hiking'] }, { constraint: "users_name_key" });
239
+ let res = await table.findWhere(':fav = ANY("textList")', { fav: 'hiking' });
240
+ expect(res.length).toEqual(1);
241
+
242
+ await table.upsert({ name: 'Fulig Jimmy', textList: ['biking'] }, { constraint: "users_name_key" });
243
+ res = await table.findWhere(':fav = ANY("textList")', { fav: 'hiking' });
244
+ expect(res.length).toEqual(0);
245
+
246
+ res = await table.findWhere(':fav = ANY("textList")', { fav: 'biking' });
247
+ expect(res.length).toEqual(1);
248
+ }));
249
+
250
+ it("UpsertAndGet - with constraint name", w(async () => {
251
+ let res = await table.upsertAndGet({ name: 'Fulig Jimmy', textList: ['hiking'] }, { constraint: "users_name_key" });
252
+ expect(res.textList).toEqual(['hiking']);
253
+
254
+ res = await table.upsertAndGet({ name: 'Fulig Jimmy', textList: ['biking'] }, { constraint: "users_name_key" });
255
+ expect(res.textList).toEqual(['biking']);
256
+ }));
257
+
258
+ it("Ignore field with undefined value if requested, but keep with null value", w(async () => {
259
+ await table.insert({ name: 'A', numberList: [1, 2] });
260
+
261
+ let res = await table.find({ name: undefined }, { skipUndefined: true });
262
+ expect(res.length).toEqual(1);
263
+
264
+ res = await table.find({ name: null }, { skipUndefined: true });
265
+ expect(res.length).toEqual(0);
266
+
267
+ let res2 = await table.updateAndGetOne({ name: 'A' }, {
268
+ numberList: undefined,
269
+ favourites: ['sport']
270
+ }, { skipUndefined: true });
271
+ expect(res2.numberList).toEqual([1, 2]);
272
+
273
+ res2 = await table.updateAndGetOne({
274
+ name: 'A',
275
+ numberList: undefined
276
+ }, { numberList: null }, { skipUndefined: true });
277
+ expect(res2.numberList).toEqual(null);
278
+ }));
279
+
280
+ it("Test return only column values ", w(async () => {
281
+ await table.insert({ name: 'A', membership: 'gold' });
282
+ await table.insert({ name: 'B', membership: 'gold' });
283
+ await table.insert({ name: 'C', membership: 'bronze' });
284
+
285
+ let res1 = await table.queryOneColumn("SELECT name || '_' || membership FROM " + table + " WHERE LENGTH(name)=1");
286
+ expect(res1).toEqual(['A_gold', 'B_gold', 'C_bronze']);
287
+ }));
288
+
289
+ it("Test count ", w(async () => {
290
+ await table.insert({ name: 'A', membership: 'gold' });
291
+ await table.insert({ name: 'B', membership: 'gold' });
292
+ await table.insert({ name: 'C', membership: 'bronze' });
293
+
294
+ let res1 = await table.count({ membership: 'gold' });
295
+ expect(res1).toEqual(2);
296
+ }));
297
+
298
+ it("Test AND - OR ", w(async () => {
299
+ await table.insert({ name: 'A', membership: 'gold', favourites: ['sport'] });
300
+ await table.insert([
301
+ { name: 'BC', membership: 'gold', favourites: ['sport'] },
302
+ { name: 'D', membership: 'bronze', favourites: ['tech'] },
303
+ { name: 'E', membership: 'bronze', favourites: ['tech', 'food'] }
304
+ ]);
305
+
306
+ let res;
307
+ res = await table.find({ 'membership': "bronze", or: [{ 'name': 'BC' }, { favourites: 'food', name: 'E' }] });
308
+ expect(res.length).toEqual(1);
309
+
310
+ res = await table.find({ name: 'A' });
311
+ res = await table.count({
312
+ and: [
313
+ { or: [{ name: ['A', 'BC'] }, { 'updated >': res[0].updated }] },
314
+ { or: [{ membership: 'bronze' }, { 'favourites @>': ['food'] }] }
315
+ ]
316
+ });
317
+ expect(res).toEqual(2);
318
+ }));
319
+
320
+ it("Test insert with switched fields", w(async () => {
321
+ await table.insert([{ name: 'A', membership: 'gold' }, { membership: 'gold', name: 'B' }]);
322
+
323
+ let res = await pgdb.query("SELECT count(*) FROM :!schema.:!table WHERE membership = :membership", {
324
+ schema: schema,
325
+ table: 'users',
326
+ membership: 'gold'
327
+ });
328
+ expect(res[0].count).toEqual(2);
329
+ }));
330
+
331
+ it("Test named parameters ", w(async () => {
332
+ await table.insert({ name: 'A', membership: 'gold' });
333
+ await table.insert({ name: 'B', membership: 'gold' });
334
+ await table.insert({ name: 'C', membership: 'bronze' });
335
+
336
+ let res = await pgdb.query("SELECT count(*) FROM :!schema.:!table WHERE membership = :membership", {
337
+ schema: schema,
338
+ table: 'users',
339
+ membership: 'gold'
340
+ });
341
+ expect(res[0].count).toEqual(2);
342
+ }));
343
+
344
+ it("text[]", w(async () => {
345
+ await table.insert({ name: 'A', textList: ['good', 'better', 'best'] });
346
+ let res = await table.findOne({ name: 'A' });
347
+ expect(res.textList).toEqual(['good', 'better', 'best']);
348
+ }));
349
+
350
+ it("integer[]", w(async () => {
351
+ await table.insert({ name: 'A', numberList: [1, 2, 3] });
352
+ let res = await table.findOne({ name: 'A' });
353
+ expect(res.numberList).toEqual([1, 2, 3]);
354
+ }));
355
+
356
+ it("integer[]", w(async () => {
357
+ await table.insert({ name: 'A', numberList: [null, 1, 2, 3] });
358
+ let res = await table.findOne({ name: 'A' });
359
+ expect(res.numberList).toEqual([null, 1, 2, 3]);
360
+ }));
361
+
362
+ it("bigInt[]", w(async () => {
363
+ await table.insert({ name: 'A', bigNumberList: [1, 2, 3] });
364
+ let res = await table.findOne({ name: 'A' });
365
+ expect(res.bigNumberList).toEqual([1, 2, 3]);
366
+
367
+ await table.insert({ name: 'B', bigNumberList: [1, Number.MAX_SAFE_INTEGER + 10] });
368
+ try {
369
+ await table.findOne({ name: 'B' });
370
+ expect(false).toBeTruthy();
371
+ } catch (e) {
372
+ expect(/Number can't be represented in javascript/.test(e.message)).toBeTruthy();
373
+ }
374
+ }));
375
+
376
+ it("bigInt[] cursor callback", w(async () => {
377
+ await table.insert({ name: 'A', bigNumberList: [1, 2, 3] });
378
+ let res;
379
+ await table.queryWithOnCursorCallback(`SELECT * FROM ${table}`, null, null, (rec) => {
380
+ res = rec.bigNumberList;
381
+ });
382
+ expect(res).toEqual([1, 2, 3]);
383
+
384
+ await table.insert({ name: 'B', bigNumberList: [1, Number.MAX_SAFE_INTEGER + 10] });
385
+ try {
386
+ await table.queryWithOnCursorCallback(`SELECT * FROM ${table}`, null, null, () => {
387
+ });
388
+ expect(false).toBeTruthy();
389
+ } catch (e) {
390
+ expect(/Number can't be represented in javascript/.test(e.message)).toBeTruthy();
391
+ }
392
+ }));
393
+
394
+ it("timestamptz[]", w(async () => {
395
+ await table.insert({
396
+ name: 'A',
397
+ timestamptzList: [new Date('2000-01-01 00:00:00').toISOString(), new Date('2001-01-01 00:00:00').toISOString()]
398
+ });
399
+ let res = await table.findOne({ name: 'A' });
400
+ expect(res.timestamptzList[0]).toEqual(new Date('2000-01-01 00:00:00'));
401
+ expect(res.timestamptzList[1]).toEqual(new Date('2001-01-01 00:00:00'));
402
+ expect(res.timestamptzList.length).toEqual(2);
403
+ }));
404
+
405
+ it("timestamp and timestamptz", w(async () => {
406
+ await table.insert({
407
+ name: 'A',
408
+ created: new Date('2000-01-01 00:00:00'),
409
+ createdtz: new Date('2000-01-01 00:00:00')
410
+ });
411
+ let res = await table.findOne({ name: 'A' });
412
+
413
+ expect(res.created).toEqual(new Date('2000-01-01 00:00:00'));
414
+ expect(res.createdtz).toEqual(new Date('2000-01-01 00:00:00'));
415
+
416
+ res = await table.count({ 'created': new Date('2000-01-01 00:00:00') });
417
+ expect(res).toEqual(1);
418
+
419
+ res = await table.count({ 'createdtz': new Date('2000-01-01 00:00:00') });
420
+ expect(res).toEqual(1);
421
+
422
+ let d = new Date('2000-01-01 00:00:00').toISOString();
423
+ await table.query(`INSERT INTO ${table} (name, created, createdtz) values ('A2', '${d}'::timestamptz, '${d}'::timestamptz)`);
424
+ res = await table.findOne({ name: 'A2' });
425
+
426
+ // this expectation is depend on machine timezone
427
+ // expect(res.created).toEqual(new Date('2000-01-01 00:00:00'));
428
+ expect(res.createdtz).toEqual(new Date('2000-01-01 00:00:00'));
429
+
430
+ res = await table.query(`SELECT * FROM ${table} WHERE name='A2' AND created='${d}'::timestamptz`);
431
+ expect(res.length).toEqual(1);
432
+
433
+ res = await table.query(`SELECT * FROM ${table} WHERE name='A2' AND createdtz='${d}'::timestamptz`);
434
+ expect(res.length).toEqual(1);
435
+ }));
436
+
437
+ it("transaction - rollback", w(async () => {
438
+ await table.insert({ name: 'A' });
439
+ let res;
440
+
441
+ let pgdbwt = await pgdb.transactionBegin();
442
+ let tablewt = pgdbwt.schemas[schema]['users'];
443
+ await tablewt.insert({ name: 'B' });
444
+
445
+ res = await table.count();
446
+ expect(res).toEqual(1);
447
+
448
+ res = await tablewt.count();
449
+ expect(res).toEqual(2);
450
+
451
+ await pgdbwt.transactionRollback();
452
+
453
+ res = await table.findAll();
454
+ expect(res.length).toEqual(1);
455
+ expect(res[0].name).toEqual('A');
456
+ }));
457
+
458
+ it("transaction - savepoint - rollback", w(async () => {
459
+ let tr = await pgdb.transactionBegin();
460
+ let table = tr.schemas[schema]['users'];
461
+ await table.insert({ name: 'a' });
462
+
463
+ let res = await table.count();
464
+ expect(res).toEqual(1);
465
+
466
+ tr.savePoint('name');
467
+ await table.insert({ name: 'b' });
468
+ res = await table.count();
469
+ expect(res).toEqual(2);
470
+
471
+ await tr.transactionRollback({ savePoint: 'name' });
472
+
473
+ res = await table.count();
474
+ expect(res).toEqual(1);
475
+ await tr.transactionRollback();
476
+ res = await table.count();
477
+ expect(res).toEqual(0);
478
+
479
+ }));
480
+
481
+ it("transaction should keep the table definitions", w(async () => {
482
+ const pgDB = pgdb; //= await PgDb.connect({connectionString: "postgres://"});
483
+ const pgDBTrans = await pgDB.transactionBegin();
484
+
485
+ let list1 = Object.keys(pgDB.tables);
486
+ let list2 = Object.keys(pgDBTrans.tables);
487
+
488
+ await pgDBTrans.transactionCommit();
489
+
490
+ expect(list1.length).toEqual(list2.length);
491
+ expect(list1.length > 0).toBeTruthy();
492
+
493
+ }));
494
+
495
+
496
+ it("transaction - commit", w(async () => {
497
+ await table.insert({ name: 'A' });
498
+
499
+ let pgdbwt = await pgdb.transactionBegin();
500
+ let tablewt = <PgTable<any>>pgdbwt.schemas[schema]['users'];
501
+ await tablewt.insert({ name: 'B' });
502
+
503
+ let res;
504
+ res = await table.findAll();
505
+ expect(res.length).toEqual(1);
506
+ expect(res[0].name).toEqual('A');
507
+
508
+ res = await tablewt.count();
509
+ expect(res).toEqual(2);
510
+
511
+ await pgdbwt.transactionCommit();
512
+
513
+ res = await table.findAll();
514
+ expect(res.length).toEqual(2);
515
+ expect(res[0].name).toEqual('A');
516
+ expect(res[1].name).toEqual('B');
517
+ }));
518
+
519
+ it("transaction - error + rollback", w(async () => {
520
+ let pgdbwt = await pgdb.transactionBegin();
521
+ let tablewt = pgdbwt[schema]['users'];
522
+ await tablewt.insert({ name: 'A' });
523
+
524
+ try {
525
+ await tablewt.insertAndGet({ name: 'C', bigNumberList: [1, 2, Number.MAX_SAFE_INTEGER + 100] });
526
+ expect(false).toBeTruthy();
527
+ } catch (e) {
528
+ expect(/Number can't be represented in javascript/.test(e.message)).toBeTruthy();
529
+ await pgdbwt.transactionRollback();
530
+ }
531
+ let res = await table.count();
532
+ expect(res).toEqual(0);
533
+
534
+ }));
535
+
536
+ it("cursor with callback", w(async () => {
537
+ await table.insert({ name: 'A', numberList: [1, 2, 3] });
538
+ await table.insert({ name: 'B' });
539
+ let size = await table.count();
540
+ let streamSize = 0;
541
+ await table.queryWithOnCursorCallback(`SELECT * FROM ${table}`, null, null, (r) => {
542
+ streamSize++;
543
+ return true;
544
+ });
545
+ expect(streamSize).toBe(1);
546
+ }));
547
+
548
+ it("stream - auto connection handling - normal", w(async () => {
549
+ let counter = 0;
550
+ let stream = await table.queryAsStream(`SELECT * FROM generate_series(0, $1) num`, [1001]);
551
+ stream.on('data', (c: any) => {
552
+ if (c.num != counter) {
553
+ expect(false).toBeTruthy();
554
+ }
555
+ counter++;
556
+ });
557
+ await new Promise(resolve => {
558
+ stream.on('end', resolve);
559
+ stream.on('error', resolve);
560
+ });
561
+ expect(counter).toEqual(1001 + 1);
562
+ }));
563
+
564
+ it("stream - auto connection handling - early close", w(async () => {
565
+ let counter = 0;
566
+ let stream = await table.queryAsStream(`SELECT * FROM generate_series(0,1002) num`);
567
+ await new Promise((resolve, reject) => {
568
+ stream.on('end', resolve);
569
+ stream.on('error', reject);
570
+ stream.on('data', (c: any) => {
571
+ if (counter == 10) {
572
+ stream.emit('close', 'e');
573
+ return resolve(undefined);
574
+ }
575
+ counter++;
576
+ });
577
+
578
+ });
579
+ expect(counter).toEqual(10);
580
+ }));
581
+
582
+ it("stream - auto connection handling - error", w(async () => {
583
+ let counter = 0;
584
+ let stillSafe = Number.MAX_SAFE_INTEGER - 5;
585
+ let wrongNum = Number.MAX_SAFE_INTEGER + 100;
586
+ let stream = await table.queryAsStream(`SELECT * FROM generate_series(${stillSafe}, ${wrongNum}) num`);
587
+ stream.on('data', (c: any) => {
588
+ counter++;
589
+ });
590
+ await new Promise(resolve => {
591
+ stream.on('end', resolve);
592
+ stream.on('error', resolve);
593
+ });
594
+ expect(counter).toEqual(6);
595
+ }));
596
+
597
+ it("stream - with transactions handling - normal", w(async () => {
598
+ let pgdbwt = await pgdb.transactionBegin();
599
+ let tablewt = pgdbwt[schema]['users'];
600
+ await tablewt.insert({ name: 'A', numberList: [1, 2, 3] });
601
+ await tablewt.insert({ name: 'B' });
602
+
603
+ let counter = 0;
604
+ let stream = await tablewt.queryAsStream(`SELECT * FROM ${tablewt}`);
605
+ stream.on('data', (c: any) => counter++);
606
+ await new Promise(resolve => {
607
+ stream.on('end', resolve);
608
+ stream.on('error', resolve);
609
+ });
610
+ expect(counter).toEqual(2);
611
+
612
+ counter = await tablewt.count();
613
+ expect(counter).toEqual(2);
614
+ await pgdbwt.transactionRollback();
615
+
616
+ counter = await table.count();
617
+ expect(counter).toEqual(0);
618
+ }));
619
+
620
+ it("stream - with transactions handling - early close", w(async () => {
621
+ let pgdbwt = await pgdb.transactionBegin();
622
+ let tablewt = <PgTable<any>>pgdbwt[schema]['users'];
623
+ await tablewt.insert({ name: 'A', numberList: [1, 2, 3] });
624
+ await tablewt.insert({ name: 'B' });
625
+ await tablewt.insert({ name: 'C' });
626
+ await tablewt.insert({ name: 'D' });
627
+
628
+ let counter = 0;
629
+ let stream = await tablewt.queryAsStream(`SELECT * FROM ${tablewt}`);
630
+ await new Promise((resolve, reject) => {
631
+ stream.on('end', resolve);
632
+ stream.on('error', reject);
633
+ stream.on('data', (c: any) => {
634
+ if (counter == 2) {
635
+ stream.emit('close', 'e');
636
+ return resolve(undefined);
637
+ }
638
+ counter++;
639
+ });
640
+ });
641
+ expect(counter).toEqual(2);
642
+
643
+ counter = await tablewt.count();
644
+ expect(counter).toEqual(4);
645
+ await pgdbwt.transactionRollback();
646
+
647
+ counter = await table.count();
648
+ expect(counter).toEqual(0);
649
+ }));
650
+
651
+ it("stream - with transactions handling - error", w(async () => {
652
+ let pgdbwt = await pgdb.transactionBegin();
653
+ let tablewt = <PgTable<any>>pgdbwt[schema]['users'];
654
+ await tablewt.insert({ name: 'A', bigNumberList: [1, 2, 3] });
655
+ await tablewt.insert({ name: 'B' });
656
+ await tablewt.insert({ name: 'C', bigNumberList: [1, 2, Number.MAX_SAFE_INTEGER + 100] });
657
+ await tablewt.insert({ name: 'D' });
658
+
659
+ let counter = 0;
660
+ let stream = await tablewt.queryAsStream(`SELECT * FROM ${tablewt}`);
661
+ try {
662
+ stream.on('data', (c: any) => {
663
+ counter++;
664
+ });
665
+ await new Promise((resolve, reject) => {
666
+ stream.on('end', resolve);
667
+ stream.on('error', reject);
668
+ });
669
+ expect(false).toBeTruthy();
670
+ } catch (e) {
671
+ expect(/Number can't be represented in javascript/.test(e.message)).toBeTruthy();
672
+ }
673
+ expect(counter).toEqual(2);
674
+
675
+ counter = await tablewt.count();
676
+ expect(counter).toEqual(4);
677
+ await pgdbwt.transactionRollback();
678
+
679
+ counter = await table.count();
680
+ expect(counter).toEqual(0);
681
+ }));
682
+
683
+ it("truncate", w(async () => {
684
+ await table.insert({ name: 'A' });
685
+ await table.insert({ name: 'B' });
686
+
687
+ await table.truncate();
688
+ let size = await table.count();
689
+ expect(size).toEqual(0);
690
+ }));
691
+
692
+ it("truncate + special types", w(async () => {
693
+ await pgdb.setTypeParser('permissionForResourceType', (val) => parseComplexType(val));
694
+ await pgdb.setTypeParser('_permissionForResourceType', (val) => val == "{}" ? [] : parseComplexTypeArray(val));
695
+ await table.insert({
696
+ name: 'Piszkos Fred',
697
+ permission: "(read,book)",
698
+ permissionList: [
699
+ '(admin,"chaos j ()"",""""j ,")',
700
+ '(write,book)',
701
+ '(write,)',
702
+ '(,"")',
703
+ '(,)',
704
+ '(write,null)'],
705
+ });
706
+
707
+ await table.truncate();
708
+
709
+ await table.insert({
710
+ name: 'Piszkos Fred',
711
+ permission: "(read,book)",
712
+ permissionList: [
713
+ '(admin,"chaos j ()"",""""j ,")',
714
+ '(write,book)',
715
+ '(write,)',
716
+ '(,"")',
717
+ '(,)',
718
+ '(write,null)'],
719
+ });
720
+ let pf = await table.findOne({ name: 'Piszkos Fred' });
721
+ expect(pf.permission).toEqual(['read', 'book']);
722
+ expect(pf.permissionList[0]).toEqual(['admin', 'chaos j ()",""j ,']);
723
+ }));
724
+
725
+ it("truncate - cascade", w(async () => {
726
+ let g = await tableGroups.insertAndGet({ name: 'G' });
727
+ await table.insert({ name: 'A', mainGroup: g.id });
728
+ await table.insert({ name: 'B', mainGroup: g.id });
729
+ await tableGroups.truncate({ cascade: true, restartIdentity: true });
730
+
731
+ let size = await table.count();
732
+ expect(size).toEqual(0);
733
+
734
+ let g2 = await tableGroups.insertAndGet({ name: 'G' }, { return: ['id'] });
735
+ expect(g.id >= g2.id).toBeTruthy()
736
+ }));
737
+
738
+ it("orderBy", w(async () => {
739
+ await table.insert({ name: 'A', aCategory: 'A' });
740
+ await table.insert({ name: 'B', aCategory: 'B' });
741
+ await table.insert({ name: 'C', aCategory: 'C' });
742
+ await table.insert({ name: 'A2', aCategory: 'A' });
743
+ await table.insert({ name: 'B2', aCategory: 'B' });
744
+ await table.insert({ name: 'C2', aCategory: 'C' });
745
+
746
+ let res;
747
+ res = await table.find({}, { orderBy: ['aCategory', 'name'], fields: ['name'] });
748
+ expect(res.map(v => v.name)).toEqual(['A', 'A2', 'B', 'B2', 'C', 'C2']);
749
+
750
+ res = await table.find({}, { orderBy: ['aCategory asc', 'name desc'], fields: ['name'] });
751
+ expect(res.map(v => v.name)).toEqual(['A2', 'A', 'B2', 'B', 'C2', 'C']);
752
+
753
+ res = await table.find({}, { orderBy: ['+aCategory', '-name'], fields: ['name'] });
754
+ expect(res.map(v => v.name)).toEqual(['A2', 'A', 'B2', 'B', 'C2', 'C']);
755
+
756
+ res = await table.find({}, { orderBy: '"aCategory" asc, name desc', fields: ['name'] });
757
+ expect(res.map(v => v.name)).toEqual(['A2', 'A', 'B2', 'B', 'C2', 'C']);
758
+ }));
759
+
760
+ it("stored proc", w(async () => {
761
+ await table.insert({ name: 'A', membership: 'gold' });
762
+ await table.insert({ name: 'B', membership: 'gold' });
763
+ expect(pgdb[schema].fn['list_gold_users']).toBeDefined();
764
+ expect(pgdb.fn['increment']).toBeDefined();
765
+ let s = await pgdb.run('select current_schema');
766
+ console.log(s);
767
+ let res = await pgdb[schema].fn['list_gold_users']();
768
+ console.log(res);
769
+ expect(res).toEqual(['A', 'B']);
770
+
771
+ res = await pgdb.fn['increment'](3);
772
+ console.log(res);
773
+ expect(res).toEqual(4);
774
+ }));
775
+
776
+ it("executing sql file - if there is an exception, should be thrown", w(async () => {
777
+ try {
778
+ await pgdb.execute('spec/resources/throw_exception.sql', (cmd) => cmd.replace(/__SCHEMA__/g, '"' + schema + '"'));
779
+ expect(false).toBeTruthy();
780
+ } catch (e) {
781
+ expect('' + e).toEqual("error: division_by_zero");
782
+ }
783
+ }));
784
+
785
+ it("select/update/delete should throw exception if the condition contains undefined value", w(async () => {
786
+ await table.insert({ name: 'A', membership: 'gold' });
787
+ let conditions = { name: 'A', membership: undefined };
788
+
789
+ try {
790
+ await table.find(conditions);
791
+ expect(false).toBeTruthy();
792
+ } catch (e) {
793
+ expect('' + e).toEqual('Error: Invalid conditions! Field value undefined: "membership". Either delete the field, set it to null or use the options.skipUndefined parameter.');
794
+ }
795
+ let res = await table.find(conditions, { skipUndefined: true });
796
+ expect(res.length == 1).toBeTruthy();
797
+
798
+ try {
799
+ await table.update(conditions, { name: 'B' });
800
+ expect(false).toBeTruthy();
801
+ } catch (e) {
802
+ expect('' + e).toEqual('Error: Invalid conditions! Field value undefined: "membership". Either delete the field, set it to null or use the options.skipUndefined parameter.');
803
+ }
804
+ await table.update(conditions, { name: 'B' }, { skipUndefined: true });
805
+ res = await table.find(conditions, { skipUndefined: true });
806
+ expect(res.length == 0).toBeTruthy();
807
+
808
+ try {
809
+ conditions.name = 'B';
810
+ await table.delete(conditions);
811
+ expect(false).toBeTruthy();
812
+ } catch (e) {
813
+ expect('' + e).toEqual('Error: Invalid conditions! Field value undefined: "membership". Either delete the field, set it to null or use the options.skipUndefined parameter.');
814
+ }
815
+ await table.delete(conditions, { skipUndefined: true });
816
+ res = await table.findAll();
817
+ expect(res.length == 0).toBeTruthy();
818
+
819
+ }));
820
+
821
+ it("Testing deleteAndGet ", w(async () => {
822
+ await table.insert({ name: 'A' });
823
+ let res = await table.deleteAndGetOne({ name: 'A' });
824
+ expect(res != null).toBeTruthy();
825
+ expect(res.name == 'A').toBeTruthy();
826
+
827
+ let res2 = await table.deleteAndGetOne({ name: 'A' });
828
+ expect(res2 == null).toBeTruthy();
829
+ }));
830
+
831
+ it("Testing postprocess function", w(async () => {
832
+ await table.insert({ name: 'A' });
833
+
834
+ pgdb.setPostProcessResult((res, fields, logger) => {
835
+ res[0].name = 'B';
836
+ });
837
+ let res = await pgdb.query(`select * from ${table}`);
838
+ expect(res[0].name == 'B').toBeTruthy();
839
+ res = await table.findAll();
840
+ expect(res[0].name == 'B').toBeTruthy();
841
+ pgdb.setPostProcessResult(null);
842
+ res = await table.findAll();
843
+ expect(res[0].name == 'A').toBeTruthy();
844
+ }));
845
+
846
+ it("Testing deleteAndGet", w(async () => {
847
+ await table.insert([{ name: 'A' }, { name: 'B' }]);
848
+ let res = await table.deleteAndGet({ name: ['A', 'B'] });
849
+ expect(res.length == 2).toBeTruthy();
850
+ }));
851
+
852
+ it("Testing sql execution", w(async () => {
853
+ await pgdb.execute('spec/resources/tricky.sql', (cmd) => cmd.replace(/__SCHEMA__/g, '"' + schema + '"'));
854
+ }));
855
+
856
+ it("Testing text array parsing", w(async () => {
857
+ let list = ["'A'", '"A"', 'normal', '//', '\\', '\\\\\\"', '\\\\"', '\\"', '""', "''", '--', '/*', '', '<!--', JSON.stringify({
858
+ a: 1,
859
+ b: "aprocska\"kalapocska'bennecsacskamacskamocska"
860
+ })];
861
+ await table.insert({ name: 'A', textList: list });
862
+ let rec: any = await table.findOne({ name: 'A' });
863
+ console.log(list + '\n' + rec.textList);
864
+ let isDifferent = list.some((v, i) => rec.textList[i] !== v);
865
+ expect(isDifferent).toBeFalsy();
866
+ }));
867
+
868
+ it("Testing jsonb array parsing", w(async () => {
869
+ let list = [{
870
+ "id": "fl1ace84f744",
871
+ "name": "Wire 2017-09-17 at 11.57.55.png",
872
+ "type": "image/png",
873
+ "path": "/data/files/fl1ace84f744/Wire 2017-09-17 at 11.57.55.png",
874
+ "title": "Wire 2017-09-17 at 11.57.55"
875
+ }];
876
+ await table.insert({ name: 'A', jsonbList: list });
877
+ let rec: any = await table.findOne({ name: 'A' });
878
+ console.log('xxx', rec.jsonbList, typeof rec.jsonbList[0]);
879
+ console.log(JSON.stringify(list) + '\n' + JSON.stringify(rec.jsonbList));
880
+ let isDifferent = list.some((v, i) => !_.isEqual(rec.jsonbList[i], v));
881
+ expect(isDifferent).toBeFalsy();
882
+ }));
883
+
884
+ it("Testing distinct", w(async () => {
885
+ await table.insert({ name: 'A', aCategory: 'A' });
886
+ await table.insert({ name: 'B', aCategory: 'A' });
887
+ let recs = await table.find({ aCategory: 'A' }, { fields: ['aCategory'], distinct: true });
888
+ expect(recs.length).toEqual(1);
889
+ }));
890
+
891
+ it("Testing queryOne", w(async () => {
892
+ await table.insert({ name: 'A', aCategory: 'A' });
893
+ await table.insert({ name: 'B', aCategory: 'A' });
894
+ try {
895
+ let recs = await table.queryOne(`SELECT * FROM ${table} WHERE "aCategory" = 'A'`);
896
+ expect(false).toBeTruthy();
897
+ } catch (e) {
898
+ expect('' + e).toEqual("Error: More then one rows exists");
899
+ }
900
+ }));
901
+ it("Testing queryFirst", w(async () => {
902
+ await table.insert({ name: 'A', aCategory: 'A' });
903
+ await table.insert({ name: 'B', aCategory: 'A' });
904
+ let rec = await table.queryFirst(`SELECT * FROM ${table} WHERE "aCategory" = 'A'`);
905
+ expect(rec.aCategory).toEqual('A');
906
+ }));
907
+
908
+ it("Testing forUpdate", w(async () => {
909
+ await table.insert({ name: 'A', aCategory: 'A' });
910
+ let pgdb1 = await table.db.transactionBegin();
911
+ await pgdb1.tables['users'].find({ aCategory: 'A' }, { forUpdate: true });
912
+ let p = table.update({ aCategory: 'A' }, { name: 'C' });
913
+ await new Promise(resolve => setTimeout(resolve, 500));
914
+ await pgdb1.tables['users'].update({ aCategory: 'A' }, { name: 'B' });
915
+ await pgdb1.transactionCommit();
916
+ await p;
917
+ let rec = await table.findFirst({ aCategory: 'A' });
918
+ expect(rec.name).toEqual('C');
919
+ }));
920
+
921
+ it("Testing update where something is null", w(async () => {
922
+ await table.insert({ name: 'A', aCategory: 'A' });
923
+ await table.update({ textList: null }, { name: 'B' });
924
+ let rec = await table.findFirst({ aCategory: 'A' });
925
+ expect(rec.name).toEqual('B');
926
+ }));
927
+
928
+ it("Testing query with array equals", w(async () => {
929
+ await table.insert({ name: 'A', aCategory: 'A' });
930
+ await table.insert({ name: 'B', aCategory: 'A' });
931
+ await table.insert({ name: 'C', aCategory: 'A' });
932
+ let recs = await table.find({ name: ['A', 'B'] });
933
+ expect(recs.length).toEqual(2);
934
+ }));
935
+
936
+ it("Testing query with array not equals", w(async () => {
937
+ await table.insert({ name: 'A', aCategory: 'A' });
938
+ await table.insert({ name: 'B', aCategory: 'A' });
939
+ await table.insert({ name: 'C', aCategory: 'A' });
940
+ let recs = await table.find({ '"name" !=': ['A', 'B'] });
941
+ expect(recs.length).toEqual(1);
942
+ }));
943
+
944
+ it("Testing empty result with one field", w(async () => {
945
+ let recs = await table.queryOneColumn(`select name from ${table}`);
946
+ expect(recs.length).toEqual(0);
947
+ }));
948
+
949
+ it("Testing empty result with one column", w(async () => {
950
+ let rec = await table.queryOneField(`select name from ${table}`);
951
+ expect(rec).toEqual(null);
952
+ }));
953
+
954
+ it("Testing NOTIFY and LISTEN (listen)", w(async () => {
955
+ let called = null;
956
+ let payload = 'hello';
957
+
958
+ await table.db.listen('test_channel', (notification) => called = notification.payload);
959
+ await table.db.notify('test_channel', payload);
960
+
961
+ await new Promise((resolve) => setTimeout(resolve, 1000));
962
+ expect(called).toEqual(payload);
963
+ }));
964
+
965
+ it("Testing NOTIFY and LISTEN (unlisten)", w(async () => {
966
+ let called = null;
967
+ let payload = 'hello';
968
+
969
+ await table.db.listen('test_channel', (notification) => called = notification.payload);
970
+ await table.db.unlisten('test_channel');
971
+ await table.db.notify('test_channel', payload);
972
+ await new Promise((resolve) => setTimeout(resolve, 1000));
973
+ expect(called).toEqual(null);
974
+ }));
975
+
976
+ it("Testing Empty 'or' condition", w(async () => {
977
+ await table.insert({ name: 'A', aCategory: 'A' });
978
+ let recs = await table.find({ or: [] });
979
+ expect(recs.length).toEqual(1);
980
+ }));
981
+
982
+ it("Testing Empty 'and' condition", w(async () => {
983
+ await table.insert({ name: 'A', aCategory: 'A' });
984
+ let recs = await table.find({ and: [] });
985
+ expect(recs.length).toEqual(1);
986
+ }));
987
+
988
+ it("Testing row mode", w(async () => {
989
+ await table.insert({ name: 'A', aCategory: 'A' });
990
+ let result = await table.queryAsRows(`select * from ${table}`);
991
+ expect(result.rows.length).toEqual(1);
992
+ expect(result.columns.includes('id'));
993
+ expect(result.columns.includes('name'));
994
+ expect(result.columns.includes('aCategory'));
995
+ }));
996
+
997
+ it("Testing output formats of default types", w(async () => {
998
+ await tableTypes.insert({
999
+ text: 'apple',
1000
+ int: 23,
1001
+ bigInt: 233,
1002
+ real: 0.5,
1003
+ double: 0.25,
1004
+ bool: false,
1005
+ json: { bool: true, number: 12, date: new Date(5), text: 'A' },
1006
+ jsonB: { bool: true, number: 12, date: new Date(5), text: 'A' },
1007
+ timestamptz: new Date(7),
1008
+
1009
+ arrayText: ['apple', 'pear'],
1010
+ arrayInt: [1, 2],
1011
+ arrayBigInt: [100, 101, 102],
1012
+ arrayReal: [0.5, 1.5, 2.5],
1013
+ arrayDouble: [0.25, 1.25, 2.25],
1014
+ arrayBool: [true, false, true],
1015
+ arrayJson: [
1016
+ { bool: true, number: 12, text: 'A' },
1017
+ { bool: true, number: 12, text: 'A' }
1018
+ ],
1019
+ arrayJsonB: [
1020
+ { bool: true, number: 12, text: 'A' },
1021
+ { bool: true, number: 12, text: 'A' }
1022
+ ],
1023
+ arrayTimestamptz: [new Date(1), new Date(2)]
1024
+ });
1025
+
1026
+ let result = await tableTypes.findFirst({});
1027
+ expect(result.text).toEqual('apple');
1028
+ expect(result.int).toEqual(23);
1029
+ expect(result.bigInt).toEqual(233);
1030
+ expect(result.real).toEqual(0.5);
1031
+ expect(result.double).toEqual(0.25);
1032
+ expect(result.bool).toEqual(false);
1033
+
1034
+ expect(result.json.bool).toEqual(true);
1035
+ expect(result.json.number).toEqual(12);
1036
+ expect(typeof result.json.date).toEqual('string'); //This is not a feature, only actual situation, that dates are not stored in Date format in json objects
1037
+ expect(result.json.text).toEqual('A');
1038
+
1039
+ expect(result.jsonB.bool).toEqual(true);
1040
+ expect(result.jsonB.number).toEqual(12);
1041
+ expect(typeof result.jsonB.date).toEqual('string');
1042
+ expect(result.jsonB.text).toEqual('A');
1043
+
1044
+ expect(result.timestamptz).toEqual(new Date(7));
1045
+
1046
+ expect(result.arrayText).toEqual(['apple', 'pear']);
1047
+ expect(result.arrayInt).toEqual([1, 2]);
1048
+ expect(result.arrayBigInt).toEqual([100, 101, 102]);
1049
+ expect(result.arrayReal).toEqual([0.5, 1.5, 2.5]);
1050
+ expect(result.arrayDouble).toEqual([0.25, 1.25, 2.25]);
1051
+ expect(result.arrayBool).toEqual([true, false, true]);
1052
+ expect(result.arrayJson).toEqual([
1053
+ { bool: true, number: 12, text: 'A' },
1054
+ { bool: true, number: 12, text: 'A' }
1055
+ ]);
1056
+ expect(result.arrayJsonB).toEqual([
1057
+ { bool: true, number: 12, text: 'A' },
1058
+ { bool: true, number: 12, text: 'A' }
1059
+ ]);
1060
+ expect(result.arrayTimestamptz).toEqual([new Date(1), new Date(2)]);
1061
+ }));
1062
+
1063
+ it("Testing output formats of enum types - query", w(async () => {
1064
+ let pgdbwt = await pgdb.transactionBegin();
1065
+
1066
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1067
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumTable"`);
1068
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1069
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumTable" (m ${schema}.mood NULL)`);
1070
+ await pgdbwt.reload();
1071
+ let table = pgdbwt.schemas[schema].tables["enumTable"];
1072
+ await table.insert({ m: 'sad' });
1073
+ let result = await table.findFirst({});
1074
+ expect(result.m).toEqual('sad');
1075
+
1076
+ await pgdbwt.run(`
1077
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1078
+ ALTER TABLE ${schema}."enumTable" ALTER COLUMN m TYPE ${schema}.mood_new USING m::text::${schema}.mood_new;
1079
+ `);
1080
+ result = await table.findFirst({});
1081
+ expect(result.m).toEqual('sad');
1082
+
1083
+ await pgdbwt.transactionRollback();
1084
+ }));
1085
+
1086
+ it("Testing output formats of enum types - queryWithOnCursorCallback without stop", w(async () => {
1087
+ let pgdbwt = await pgdb.transactionBegin();
1088
+
1089
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1090
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumTable"`);
1091
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1092
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumTable" (m ${schema}.mood NULL)`);
1093
+ await pgdbwt.reload();
1094
+ let table = pgdbwt.schemas[schema].tables["enumTable"];
1095
+ await table.insert([{ m: 'sad' }, { m: 'sad' }]);
1096
+ let counter = 0;
1097
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumTable"`, [], {}, (data) => {
1098
+ expect(data.m).toEqual('sad');
1099
+ ++counter;
1100
+ });
1101
+ expect(counter).toEqual(2);
1102
+
1103
+ await pgdbwt.run(`
1104
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1105
+ ALTER TABLE ${schema}."enumTable" ALTER COLUMN m TYPE ${schema}.mood_new USING m::text::${schema}.mood_new;
1106
+ `);
1107
+ counter = 0;
1108
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumTable"`, [], {}, (data) => {
1109
+ expect(data.m).toEqual('sad');
1110
+ ++counter;
1111
+ });
1112
+ expect(counter).toEqual(2);
1113
+
1114
+ await pgdbwt.transactionRollback();
1115
+ }));
1116
+
1117
+ it("Testing output formats of enum types - queryWithOnCursorCallback with stop", w(async () => {
1118
+ let pgdbwt = await pgdb.transactionBegin();
1119
+
1120
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1121
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumTable"`);
1122
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1123
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumTable" (m ${schema}.mood NULL)`);
1124
+ await pgdbwt.reload();
1125
+ let table = pgdbwt.schemas[schema].tables["enumTable"];
1126
+ await table.insert([{ m: 'sad' }, { m: 'sad' }]);
1127
+ let counter = 0;
1128
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumTable"`, [], {}, (data) => {
1129
+ expect(data.m).toEqual('sad');
1130
+ ++counter;
1131
+ return true;
1132
+ });
1133
+ expect(counter).toEqual(1);
1134
+
1135
+ await pgdbwt.run(`
1136
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1137
+ ALTER TABLE ${schema}."enumTable" ALTER COLUMN m TYPE ${schema}.mood_new USING m::text::${schema}.mood_new;
1138
+ `);
1139
+ counter = 0;
1140
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumTable"`, [], {}, (data) => {
1141
+ expect(data.m).toEqual('sad');
1142
+ ++counter;
1143
+ return true;
1144
+ });
1145
+ expect(counter).toEqual(1);
1146
+
1147
+ await pgdbwt.transactionRollback();
1148
+ }));
1149
+
1150
+ it("Testing output formats of enum types - queryAsStream", w(async () => {
1151
+ let pgdbwt = await pgdb.transactionBegin();
1152
+
1153
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1154
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumTable"`);
1155
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1156
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumTable" (m ${schema}.mood NULL)`);
1157
+ await pgdbwt.reload();
1158
+ let table = pgdbwt.schemas[schema].tables["enumTable"];
1159
+ await table.insert({ m: 'sad' });
1160
+ let stream = await table.find({}, { stream: true });
1161
+ stream.on("data", (data) => {
1162
+ expect(data.m).toEqual('sad');
1163
+ });
1164
+ await new Promise<any>((resolve, reject) => {
1165
+ stream.on('error', reject);
1166
+ stream.on('close', resolve);
1167
+ });
1168
+ await pgdbwt.run(`
1169
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1170
+ ALTER TABLE ${schema}."enumTable" ALTER COLUMN m TYPE ${schema}.mood_new USING m::text::${schema}.mood_new;
1171
+ `);
1172
+ stream = await table.find({}, { stream: true });
1173
+ stream.on("data", (data) => {
1174
+ expect(data.m).toEqual('sad');
1175
+ });
1176
+ try {
1177
+ await new Promise<any>((resolve, reject) => {
1178
+ stream.on('error', reject);
1179
+ stream.on('close', resolve);
1180
+ });
1181
+ expect(false).toBeTruthy();
1182
+ } catch (e) {
1183
+ expect('' + e).toEqual('Error: [337] Query returns fields with unknown oid.'); //This is not a feature, but a side-effect
1184
+ }
1185
+ await pgdbwt.transactionRollback();
1186
+ }));
1187
+
1188
+ it("Testing output formats of enum array types - query", w(async () => {
1189
+ let pgdbwt = await pgdb.transactionBegin();
1190
+
1191
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1192
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumArrayTable"`);
1193
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1194
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumArrayTable" (m ${schema}.mood[] NULL)`);
1195
+ await pgdbwt.reload();
1196
+ let table = pgdbwt.schemas[schema].tables["enumArrayTable"];
1197
+ await table.insert({ m: ['sad', 'ok'] });
1198
+ let result = await table.findFirst({});
1199
+ expect(result.m).toEqual(['sad', 'ok']);
1200
+
1201
+ await pgdbwt.run(`
1202
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1203
+ ALTER TABLE ${schema}."enumArrayTable" ALTER COLUMN m TYPE ${schema}.mood_new[] USING m::text[]::${schema}.mood_new[];
1204
+ `);
1205
+ result = await table.findFirst({});
1206
+ expect(result.m).toEqual(['sad', 'ok']);
1207
+
1208
+ await pgdbwt.transactionRollback();
1209
+ }));
1210
+
1211
+ it("Testing output formats of enum array types - queryWithOnCursorCallback without stop", w(async () => {
1212
+ let pgdbwt = await pgdb.transactionBegin();
1213
+
1214
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1215
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumArrayTable"`);
1216
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1217
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumArrayTable" (m ${schema}.mood[] NULL)`);
1218
+ await pgdbwt.reload();
1219
+ let table = pgdbwt.schemas[schema].tables["enumArrayTable"];
1220
+ await table.insert([{ m: ['sad', 'ok'] }, { m: ['sad', 'ok'] }]);
1221
+ let counter = 0;
1222
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumArrayTable"`, [], {}, (data) => {
1223
+ expect(data.m).toEqual(['sad', 'ok']);
1224
+ ++counter;
1225
+ });
1226
+ expect(counter).toEqual(2);
1227
+
1228
+ await pgdbwt.run(`
1229
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1230
+ ALTER TABLE ${schema}."enumArrayTable" ALTER COLUMN m TYPE ${schema}.mood_new[] USING m::text[]::${schema}.mood_new[];
1231
+ `);
1232
+ counter = 0;
1233
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumArrayTable"`, [], {}, (data) => {
1234
+ expect(data.m).toEqual(['sad', 'ok']);
1235
+ ++counter;
1236
+ });
1237
+ expect(counter).toEqual(2);
1238
+
1239
+ await pgdbwt.transactionRollback();
1240
+ }));
1241
+
1242
+ it("Testing output formats of enum array types - queryWithOnCursorCallback with stop", w(async () => {
1243
+ let pgdbwt = await pgdb.transactionBegin();
1244
+
1245
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1246
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumArrayTable"`);
1247
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1248
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumArrayTable" (m ${schema}.mood[] NULL)`);
1249
+ await pgdbwt.reload();
1250
+ let table = pgdbwt.schemas[schema].tables["enumArrayTable"];
1251
+ await table.insert([{ m: ['sad', 'ok'] }, { m: ['sad', 'ok'] }]);
1252
+ let counter = 0;
1253
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumArrayTable"`, [], {}, (data) => {
1254
+ expect(data.m).toEqual(['sad', 'ok']);
1255
+ ++counter;
1256
+ return true;
1257
+ });
1258
+ expect(counter).toEqual(1);
1259
+
1260
+ await pgdbwt.run(`
1261
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1262
+ ALTER TABLE ${schema}."enumArrayTable" ALTER COLUMN m TYPE ${schema}.mood_new[] USING m::text[]::${schema}.mood_new[];
1263
+ `);
1264
+ counter = 0;
1265
+ await table.queryWithOnCursorCallback(`select * from ${schema}."enumArrayTable"`, [], {}, (data) => {
1266
+ expect(data.m).toEqual(['sad', 'ok']);
1267
+ ++counter;
1268
+ return true;
1269
+ });
1270
+ expect(counter).toEqual(1);
1271
+
1272
+ await pgdbwt.transactionRollback();
1273
+ }));
1274
+
1275
+ it("Testing output formats of enum array types - queryAsStream", w(async () => {
1276
+ let pgdbwt = await pgdb.transactionBegin();
1277
+
1278
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1279
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumArrayTable"`);
1280
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1281
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumArrayTable" (m ${schema}.mood[] NULL)`);
1282
+ await pgdbwt.reload();
1283
+ let table = pgdbwt.schemas[schema].tables["enumArrayTable"];
1284
+ await table.insert({ m: ['sad', 'ok'] });
1285
+ let stream = await table.find({}, { stream: true });
1286
+ stream.on("data", (data) => {
1287
+ expect(data.m).toEqual(['sad', 'ok']);
1288
+ });
1289
+ await new Promise<any>((resolve, reject) => {
1290
+ stream.on('error', reject);
1291
+ stream.on('close', resolve);
1292
+ });
1293
+ await pgdbwt.run(`
1294
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1295
+ ALTER TABLE ${schema}."enumArrayTable" ALTER COLUMN m TYPE ${schema}.mood_new[] USING m::text[]::${schema}.mood_new[];
1296
+ `);
1297
+ stream = await table.find({}, { stream: true });
1298
+ stream.on("data", (data) => {
1299
+ expect(data.m).toEqual(['sad', 'ok']);
1300
+ });
1301
+ try {
1302
+ await new Promise<any>((resolve, reject) => {
1303
+ stream.on('error', reject);
1304
+ stream.on('close', resolve);
1305
+ });
1306
+ expect(false).toBeTruthy();
1307
+ } catch (e) {
1308
+ expect('' + e).toEqual('Error: [337] Query returns fields with unknown oid.');
1309
+ }
1310
+ await pgdbwt.transactionRollback();
1311
+ }));
1312
+
1313
+ it("Testing output formats of enum array types - query on view", w(async () => {
1314
+ let pgdbwt = await pgdb.transactionBegin();
1315
+
1316
+ await pgdbwt.run(`DROP TYPE IF EXISTS ${schema}.mood CASCADE`);
1317
+ await pgdbwt.run(`DROP TABLE IF EXISTS ${schema}."enumArrayTable"`);
1318
+ await pgdbwt.run(`CREATE TYPE ${schema}.mood AS ENUM ('sad', 'ok', 'happy')`);
1319
+ await pgdbwt.run(`CREATE TABLE ${schema}."enumArrayTable" (m ${schema}.mood[] NULL)`);
1320
+ await pgdbwt.run(`CREATE VIEW ${schema}."enumArrayTableView" as select m from ${schema}."enumArrayTable"`);
1321
+ await pgdbwt.reload();
1322
+ let table = pgdbwt.schemas[schema].tables["enumArrayTable"];
1323
+ await table.insert({ m: ['sad', 'ok'] });
1324
+ let view = pgdbwt.schemas[schema].tables["enumArrayTableView"];
1325
+ let result = await view.findFirst({});
1326
+ expect(result.m).toEqual(['sad', 'ok']);
1327
+
1328
+ await pgdbwt.run(`
1329
+ CREATE TYPE ${schema}.mood_new AS ENUM ('sad', 'ok', 'happy', 'other');
1330
+ DROP VIEW ${schema}."enumArrayTableView";
1331
+ ALTER TABLE ${schema}."enumArrayTable" ALTER COLUMN m TYPE ${schema}.mood_new[] USING m::text[]::${schema}.mood_new[];
1332
+ CREATE VIEW ${schema}."enumArrayTableView" as select m from ${schema}."enumArrayTable"
1333
+ `);
1334
+ result = await view.findFirst({});
1335
+ expect(result.m).toEqual(['sad', 'ok']);
1336
+
1337
+ await pgdbwt.transactionRollback();
1338
+ }));
1339
+ });