@nymphjs/driver-postgresql 1.0.0-beta.9 → 1.0.0-beta.91
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/CHANGELOG.md +401 -0
- package/README.md +1 -1
- package/dist/PostgreSQLDriver.d.ts +66 -20
- package/dist/PostgreSQLDriver.js +710 -542
- package/dist/PostgreSQLDriver.js.map +1 -1
- package/dist/PostgreSQLDriver.test.js +9 -12
- package/dist/PostgreSQLDriver.test.js.map +1 -1
- package/dist/conf/d.d.ts +26 -0
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +1 -3
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -24
- package/dist/index.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +22 -21
- package/src/PostgreSQLDriver.test.ts +5 -3
- package/src/PostgreSQLDriver.ts +1025 -873
- package/src/conf/defaults.ts +1 -1
- package/src/conf/index.ts +2 -2
- package/src/index.ts +2 -2
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
- package/dist/runPostgresqlSync.js +0 -35
- package/src/runPostgresqlSync.js +0 -35
- package/src/testpostgresql.js +0 -59
package/src/PostgreSQLDriver.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Pool, PoolClient, PoolConfig, QueryResult } from 'pg';
|
|
1
|
+
import pg from 'pg';
|
|
2
|
+
import type { Pool, PoolClient, PoolConfig, QueryResult } from 'pg';
|
|
3
3
|
import format from 'pg-format';
|
|
4
|
+
import Cursor from 'pg-cursor';
|
|
4
5
|
import {
|
|
5
6
|
NymphDriver,
|
|
6
|
-
EntityConstructor,
|
|
7
|
-
EntityData,
|
|
8
|
-
EntityInterface,
|
|
9
|
-
|
|
7
|
+
type EntityConstructor,
|
|
8
|
+
type EntityData,
|
|
9
|
+
type EntityInterface,
|
|
10
|
+
type EntityInstanceType,
|
|
11
|
+
type SerializedEntityData,
|
|
12
|
+
type FormattedSelector,
|
|
13
|
+
type Options,
|
|
14
|
+
type Selector,
|
|
15
|
+
EntityUniqueConstraintError,
|
|
10
16
|
InvalidParametersError,
|
|
11
17
|
NotConfiguredError,
|
|
12
18
|
QueryFailedError,
|
|
13
19
|
UnableToConnectError,
|
|
14
|
-
FormattedSelector,
|
|
15
|
-
Options,
|
|
16
|
-
Selector,
|
|
17
20
|
xor,
|
|
18
21
|
} from '@nymphjs/nymph';
|
|
19
22
|
import { makeTableSuffix } from '@nymphjs/guid';
|
|
@@ -21,7 +24,7 @@ import { makeTableSuffix } from '@nymphjs/guid';
|
|
|
21
24
|
import {
|
|
22
25
|
PostgreSQLDriverConfig,
|
|
23
26
|
PostgreSQLDriverConfigDefaults as defaults,
|
|
24
|
-
} from './conf';
|
|
27
|
+
} from './conf/index.js';
|
|
25
28
|
|
|
26
29
|
type PostgreSQLDriverConnection = {
|
|
27
30
|
client: PoolClient;
|
|
@@ -53,10 +56,44 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
53
56
|
return format.literal(input);
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
static escapeNullSequences(input: string) {
|
|
60
|
+
// Postgres doesn't support null bytes in `text`, and it converts strings
|
|
61
|
+
// in JSON to `text`, so we need to escape the escape sequences for null
|
|
62
|
+
// bytes.
|
|
63
|
+
return (
|
|
64
|
+
input
|
|
65
|
+
.replace(/\uFFFD/g, () => '\uFFFD\uFFFD')
|
|
66
|
+
// n so that if there's already an escape, it turns into \n
|
|
67
|
+
// - so that it won't match a \uFFFD that got turned into \uFFFD\uFFFD
|
|
68
|
+
.replace(/\\u0000/g, () => 'nu\uFFFD-')
|
|
69
|
+
.replace(/\\x00/g, () => 'nx\uFFFD-')
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static unescapeNullSequences(input: string) {
|
|
74
|
+
return input
|
|
75
|
+
.replace(/nu\uFFFD-/g, () => '\\u0000')
|
|
76
|
+
.replace(/nx\uFFFD-/g, () => '\\x00')
|
|
77
|
+
.replace(/\uFFFD\uFFFD/g, () => '\uFFFD');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static escapeNulls(input: string) {
|
|
81
|
+
// Postgres doesn't support null bytes in `text`.
|
|
82
|
+
return input
|
|
83
|
+
.replace(/\uFFFD/g, () => '\uFFFD\uFFFD')
|
|
84
|
+
.replace(/\x00/g, () => '-\uFFFD-');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
static unescapeNulls(input: string) {
|
|
88
|
+
return input
|
|
89
|
+
.replace(/-\uFFFD-/g, () => '\x00')
|
|
90
|
+
.replace(/\uFFFD\uFFFD/g, () => '\uFFFD');
|
|
91
|
+
}
|
|
92
|
+
|
|
56
93
|
constructor(
|
|
57
94
|
config: Partial<PostgreSQLDriverConfig>,
|
|
58
95
|
link?: Pool,
|
|
59
|
-
transaction?: PostgreSQLDriverTransaction
|
|
96
|
+
transaction?: PostgreSQLDriverTransaction,
|
|
60
97
|
) {
|
|
61
98
|
super();
|
|
62
99
|
this.config = { ...defaults, ...config };
|
|
@@ -91,18 +128,28 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
91
128
|
return new PostgreSQLDriver(
|
|
92
129
|
this.config,
|
|
93
130
|
this.link,
|
|
94
|
-
this.transaction ?? undefined
|
|
131
|
+
this.transaction ?? undefined,
|
|
95
132
|
);
|
|
96
133
|
}
|
|
97
134
|
|
|
98
|
-
private getConnection(
|
|
99
|
-
|
|
135
|
+
private getConnection(
|
|
136
|
+
outsideTransaction = false,
|
|
137
|
+
): Promise<PostgreSQLDriverConnection> {
|
|
138
|
+
if (
|
|
139
|
+
this.transaction != null &&
|
|
140
|
+
this.transaction.connection != null &&
|
|
141
|
+
!outsideTransaction
|
|
142
|
+
) {
|
|
100
143
|
return Promise.resolve(this.transaction.connection);
|
|
101
144
|
}
|
|
102
145
|
return new Promise((resolve, reject) =>
|
|
103
146
|
this.link.connect((err, client, done) =>
|
|
104
|
-
err
|
|
105
|
-
|
|
147
|
+
err
|
|
148
|
+
? reject(err)
|
|
149
|
+
: client
|
|
150
|
+
? resolve({ client, done })
|
|
151
|
+
: reject('No client returned from connect.'),
|
|
152
|
+
),
|
|
106
153
|
);
|
|
107
154
|
}
|
|
108
155
|
|
|
@@ -118,8 +165,12 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
118
165
|
const connection: PostgreSQLDriverConnection = await new Promise(
|
|
119
166
|
(resolve, reject) =>
|
|
120
167
|
this.link.connect((err, client, done) =>
|
|
121
|
-
err
|
|
122
|
-
|
|
168
|
+
err
|
|
169
|
+
? reject(err)
|
|
170
|
+
: client
|
|
171
|
+
? resolve({ client, done })
|
|
172
|
+
: reject('No client returned from connect.'),
|
|
173
|
+
),
|
|
123
174
|
);
|
|
124
175
|
await new Promise((resolve, reject) =>
|
|
125
176
|
connection.client.query('SELECT 1;', [], (err, res) => {
|
|
@@ -127,7 +178,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
127
178
|
reject(err);
|
|
128
179
|
}
|
|
129
180
|
resolve(0);
|
|
130
|
-
})
|
|
181
|
+
}),
|
|
131
182
|
);
|
|
132
183
|
connection.done();
|
|
133
184
|
}
|
|
@@ -138,7 +189,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
138
189
|
// Connecting, selecting database
|
|
139
190
|
if (!this.connected) {
|
|
140
191
|
try {
|
|
141
|
-
this.link = new Pool(this.postgresqlConfig);
|
|
192
|
+
this.link = new pg.Pool(this.postgresqlConfig);
|
|
142
193
|
this.connected = true;
|
|
143
194
|
} catch (e: any) {
|
|
144
195
|
if (
|
|
@@ -148,7 +199,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
148
199
|
this.postgresqlConfig.database === 'nymph'
|
|
149
200
|
) {
|
|
150
201
|
throw new NotConfiguredError(
|
|
151
|
-
"It seems the config hasn't been set up correctly."
|
|
202
|
+
"It seems the config hasn't been set up correctly.",
|
|
152
203
|
);
|
|
153
204
|
} else {
|
|
154
205
|
throw new UnableToConnectError('Could not connect: ' + e?.message);
|
|
@@ -172,6 +223,9 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
172
223
|
}
|
|
173
224
|
|
|
174
225
|
public async inTransaction() {
|
|
226
|
+
if (this.transaction && this.transaction.count === 0) {
|
|
227
|
+
this.transaction = null;
|
|
228
|
+
}
|
|
175
229
|
return !!this.transaction;
|
|
176
230
|
}
|
|
177
231
|
|
|
@@ -190,203 +244,254 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
190
244
|
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
191
245
|
* @returns True on success, false on failure.
|
|
192
246
|
*/
|
|
193
|
-
private createTables(etype: string | null = null) {
|
|
247
|
+
private async createTables(etype: string | null = null) {
|
|
248
|
+
const connection = await this.getConnection(true);
|
|
194
249
|
if (etype != null) {
|
|
195
250
|
// Create the entity table.
|
|
196
|
-
this.
|
|
251
|
+
await this.queryRun(
|
|
197
252
|
`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
|
|
198
|
-
`${this.prefix}entities_${etype}
|
|
253
|
+
`${this.prefix}entities_${etype}`,
|
|
199
254
|
)} (
|
|
200
255
|
"guid" BYTEA NOT NULL,
|
|
201
256
|
"tags" TEXT[],
|
|
202
257
|
"cdate" DOUBLE PRECISION NOT NULL,
|
|
203
258
|
"mdate" DOUBLE PRECISION NOT NULL,
|
|
204
259
|
PRIMARY KEY ("guid")
|
|
205
|
-
) WITH ( OIDS=FALSE )
|
|
260
|
+
) WITH ( OIDS=FALSE );`,
|
|
261
|
+
{ connection },
|
|
206
262
|
);
|
|
207
|
-
this.
|
|
263
|
+
await this.queryRun(
|
|
208
264
|
`ALTER TABLE ${PostgreSQLDriver.escape(
|
|
209
|
-
`${this.prefix}entities_${etype}
|
|
210
|
-
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)}
|
|
265
|
+
`${this.prefix}entities_${etype}`,
|
|
266
|
+
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
|
|
267
|
+
{ connection },
|
|
211
268
|
);
|
|
212
|
-
this.
|
|
269
|
+
await this.queryRun(
|
|
213
270
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
214
|
-
`${this.prefix}entities_${etype}_id_cdate
|
|
215
|
-
)}
|
|
271
|
+
`${this.prefix}entities_${etype}_id_cdate`,
|
|
272
|
+
)};`,
|
|
273
|
+
{ connection },
|
|
216
274
|
);
|
|
217
|
-
this.
|
|
275
|
+
await this.queryRun(
|
|
218
276
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
219
|
-
`${this.prefix}entities_${etype}_id_cdate
|
|
277
|
+
`${this.prefix}entities_${etype}_id_cdate`,
|
|
220
278
|
)} ON ${PostgreSQLDriver.escape(
|
|
221
|
-
`${this.prefix}entities_${etype}
|
|
222
|
-
)} USING btree ("cdate")
|
|
279
|
+
`${this.prefix}entities_${etype}`,
|
|
280
|
+
)} USING btree ("cdate");`,
|
|
281
|
+
{ connection },
|
|
223
282
|
);
|
|
224
|
-
this.
|
|
283
|
+
await this.queryRun(
|
|
225
284
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
226
|
-
`${this.prefix}entities_${etype}_id_mdate
|
|
227
|
-
)}
|
|
285
|
+
`${this.prefix}entities_${etype}_id_mdate`,
|
|
286
|
+
)};`,
|
|
287
|
+
{ connection },
|
|
228
288
|
);
|
|
229
|
-
this.
|
|
289
|
+
await this.queryRun(
|
|
230
290
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
231
|
-
`${this.prefix}entities_${etype}_id_mdate
|
|
291
|
+
`${this.prefix}entities_${etype}_id_mdate`,
|
|
232
292
|
)} ON ${PostgreSQLDriver.escape(
|
|
233
|
-
`${this.prefix}entities_${etype}
|
|
234
|
-
)} USING btree ("mdate")
|
|
293
|
+
`${this.prefix}entities_${etype}`,
|
|
294
|
+
)} USING btree ("mdate");`,
|
|
295
|
+
{ connection },
|
|
235
296
|
);
|
|
236
|
-
this.
|
|
297
|
+
await this.queryRun(
|
|
237
298
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
238
|
-
`${this.prefix}entities_${etype}_id_tags
|
|
239
|
-
)}
|
|
299
|
+
`${this.prefix}entities_${etype}_id_tags`,
|
|
300
|
+
)};`,
|
|
301
|
+
{ connection },
|
|
240
302
|
);
|
|
241
|
-
this.
|
|
303
|
+
await this.queryRun(
|
|
242
304
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
243
|
-
`${this.prefix}entities_${etype}_id_tags
|
|
305
|
+
`${this.prefix}entities_${etype}_id_tags`,
|
|
244
306
|
)} ON ${PostgreSQLDriver.escape(
|
|
245
|
-
`${this.prefix}entities_${etype}
|
|
246
|
-
)} USING gin ("tags")
|
|
307
|
+
`${this.prefix}entities_${etype}`,
|
|
308
|
+
)} USING gin ("tags");`,
|
|
309
|
+
{ connection },
|
|
247
310
|
);
|
|
248
311
|
// Create the data table.
|
|
249
|
-
this.
|
|
312
|
+
await this.queryRun(
|
|
250
313
|
`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
|
|
251
|
-
`${this.prefix}data_${etype}
|
|
314
|
+
`${this.prefix}data_${etype}`,
|
|
252
315
|
)} (
|
|
253
316
|
"guid" BYTEA NOT NULL,
|
|
254
317
|
"name" TEXT NOT NULL,
|
|
255
|
-
"value"
|
|
318
|
+
"value" CHARACTER(1) NOT NULL,
|
|
319
|
+
"json" JSONB,
|
|
320
|
+
"string" TEXT,
|
|
321
|
+
"number" DOUBLE PRECISION,
|
|
322
|
+
"truthy" BOOLEAN,
|
|
256
323
|
PRIMARY KEY ("guid", "name"),
|
|
257
324
|
FOREIGN KEY ("guid")
|
|
258
325
|
REFERENCES ${PostgreSQLDriver.escape(
|
|
259
|
-
`${this.prefix}entities_${etype}
|
|
326
|
+
`${this.prefix}entities_${etype}`,
|
|
260
327
|
)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
261
|
-
) WITH ( OIDS=FALSE )
|
|
328
|
+
) WITH ( OIDS=FALSE );`,
|
|
329
|
+
{ connection },
|
|
262
330
|
);
|
|
263
|
-
this.
|
|
331
|
+
await this.queryRun(
|
|
264
332
|
`ALTER TABLE ${PostgreSQLDriver.escape(
|
|
265
|
-
`${this.prefix}data_${etype}
|
|
266
|
-
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)}
|
|
333
|
+
`${this.prefix}data_${etype}`,
|
|
334
|
+
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
|
|
335
|
+
{ connection },
|
|
336
|
+
);
|
|
337
|
+
await this.queryRun(
|
|
338
|
+
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
339
|
+
`${this.prefix}data_${etype}_id_guid`,
|
|
340
|
+
)};`,
|
|
341
|
+
{ connection },
|
|
342
|
+
);
|
|
343
|
+
await this.queryRun(
|
|
344
|
+
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
345
|
+
`${this.prefix}data_${etype}_id_guid`,
|
|
346
|
+
)} ON ${PostgreSQLDriver.escape(
|
|
347
|
+
`${this.prefix}data_${etype}`,
|
|
348
|
+
)} USING btree ("guid");`,
|
|
349
|
+
{ connection },
|
|
350
|
+
);
|
|
351
|
+
await this.queryRun(
|
|
352
|
+
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
353
|
+
`${this.prefix}data_${etype}_id_guid_name`,
|
|
354
|
+
)};`,
|
|
355
|
+
{ connection },
|
|
356
|
+
);
|
|
357
|
+
await this.queryRun(
|
|
358
|
+
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
359
|
+
`${this.prefix}data_${etype}_id_guid_name`,
|
|
360
|
+
)} ON ${PostgreSQLDriver.escape(
|
|
361
|
+
`${this.prefix}data_${etype}`,
|
|
362
|
+
)} USING btree ("guid", "name");`,
|
|
363
|
+
{ connection },
|
|
267
364
|
);
|
|
268
|
-
this.
|
|
365
|
+
await this.queryRun(
|
|
269
366
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
270
|
-
`${this.prefix}data_${etype}
|
|
271
|
-
)}
|
|
367
|
+
`${this.prefix}data_${etype}_id_guid_name__user`,
|
|
368
|
+
)};`,
|
|
369
|
+
{ connection },
|
|
272
370
|
);
|
|
273
|
-
this.
|
|
371
|
+
await this.queryRun(
|
|
274
372
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
275
|
-
`${this.prefix}data_${etype}
|
|
373
|
+
`${this.prefix}data_${etype}_id_guid_name__user`,
|
|
276
374
|
)} ON ${PostgreSQLDriver.escape(
|
|
277
|
-
`${this.prefix}data_${etype}
|
|
278
|
-
)} USING btree ("guid")
|
|
375
|
+
`${this.prefix}data_${etype}`,
|
|
376
|
+
)} USING btree ("guid") WHERE "name" = 'user'::text;`,
|
|
377
|
+
{ connection },
|
|
279
378
|
);
|
|
280
|
-
this.
|
|
379
|
+
await this.queryRun(
|
|
281
380
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
282
|
-
`${this.prefix}data_${etype}
|
|
283
|
-
)}
|
|
381
|
+
`${this.prefix}data_${etype}_id_guid_name__group`,
|
|
382
|
+
)};`,
|
|
383
|
+
{ connection },
|
|
284
384
|
);
|
|
285
|
-
this.
|
|
385
|
+
await this.queryRun(
|
|
286
386
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
287
|
-
`${this.prefix}data_${etype}
|
|
387
|
+
`${this.prefix}data_${etype}_id_guid_name__group`,
|
|
288
388
|
)} ON ${PostgreSQLDriver.escape(
|
|
289
|
-
`${this.prefix}data_${etype}
|
|
290
|
-
)} USING btree ("name"
|
|
389
|
+
`${this.prefix}data_${etype}`,
|
|
390
|
+
)} USING btree ("guid") WHERE "name" = 'group'::text;`,
|
|
391
|
+
{ connection },
|
|
291
392
|
);
|
|
292
|
-
this.
|
|
393
|
+
await this.queryRun(
|
|
293
394
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
294
|
-
`${this.prefix}data_${etype}
|
|
295
|
-
)}
|
|
395
|
+
`${this.prefix}data_${etype}_id_name`,
|
|
396
|
+
)};`,
|
|
397
|
+
{ connection },
|
|
296
398
|
);
|
|
297
|
-
this.
|
|
399
|
+
await this.queryRun(
|
|
298
400
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
299
|
-
`${this.prefix}data_${etype}
|
|
401
|
+
`${this.prefix}data_${etype}_id_name`,
|
|
300
402
|
)} ON ${PostgreSQLDriver.escape(
|
|
301
|
-
`${this.prefix}data_${etype}
|
|
302
|
-
)} USING btree ("
|
|
403
|
+
`${this.prefix}data_${etype}`,
|
|
404
|
+
)} USING btree ("name");`,
|
|
405
|
+
{ connection },
|
|
303
406
|
);
|
|
304
|
-
this.
|
|
407
|
+
await this.queryRun(
|
|
305
408
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
306
|
-
`${this.prefix}data_${etype}
|
|
307
|
-
)}
|
|
409
|
+
`${this.prefix}data_${etype}_id_name_string`,
|
|
410
|
+
)};`,
|
|
411
|
+
{ connection },
|
|
308
412
|
);
|
|
309
|
-
this.
|
|
413
|
+
await this.queryRun(
|
|
310
414
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
311
|
-
`${this.prefix}data_${etype}
|
|
415
|
+
`${this.prefix}data_${etype}_id_name_string`,
|
|
312
416
|
)} ON ${PostgreSQLDriver.escape(
|
|
313
|
-
`${this.prefix}data_${etype}
|
|
314
|
-
)} USING btree ("
|
|
417
|
+
`${this.prefix}data_${etype}`,
|
|
418
|
+
)} USING btree ("name", LEFT("string", 512));`,
|
|
419
|
+
{ connection },
|
|
315
420
|
);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
"guid" BYTEA NOT NULL,
|
|
322
|
-
"name" TEXT NOT NULL,
|
|
323
|
-
"truthy" BOOLEAN,
|
|
324
|
-
"string" TEXT,
|
|
325
|
-
"number" DOUBLE PRECISION,
|
|
326
|
-
PRIMARY KEY ("guid", "name"),
|
|
327
|
-
FOREIGN KEY ("guid")
|
|
328
|
-
REFERENCES ${PostgreSQLDriver.escape(
|
|
329
|
-
`${this.prefix}entities_${etype}`
|
|
330
|
-
)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
331
|
-
) WITH ( OIDS=FALSE );`
|
|
421
|
+
await this.queryRun(
|
|
422
|
+
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
423
|
+
`${this.prefix}data_${etype}_id_name_number`,
|
|
424
|
+
)};`,
|
|
425
|
+
{ connection },
|
|
332
426
|
);
|
|
333
|
-
this.
|
|
334
|
-
`
|
|
335
|
-
`${this.prefix}
|
|
336
|
-
)}
|
|
427
|
+
await this.queryRun(
|
|
428
|
+
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
429
|
+
`${this.prefix}data_${etype}_id_name_number`,
|
|
430
|
+
)} ON ${PostgreSQLDriver.escape(
|
|
431
|
+
`${this.prefix}data_${etype}`,
|
|
432
|
+
)} USING btree ("name", "number");`,
|
|
433
|
+
{ connection },
|
|
337
434
|
);
|
|
338
|
-
this.
|
|
435
|
+
await this.queryRun(
|
|
339
436
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
340
|
-
`${this.prefix}
|
|
341
|
-
)}
|
|
437
|
+
`${this.prefix}data_${etype}_id_name_truthy`,
|
|
438
|
+
)};`,
|
|
439
|
+
{ connection },
|
|
342
440
|
);
|
|
343
|
-
this.
|
|
441
|
+
await this.queryRun(
|
|
344
442
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
345
|
-
`${this.prefix}
|
|
443
|
+
`${this.prefix}data_${etype}_id_name_truthy`,
|
|
346
444
|
)} ON ${PostgreSQLDriver.escape(
|
|
347
|
-
`${this.prefix}
|
|
348
|
-
)} USING btree ("
|
|
445
|
+
`${this.prefix}data_${etype}`,
|
|
446
|
+
)} USING btree ("name") WHERE "truthy" = TRUE;`,
|
|
447
|
+
{ connection },
|
|
349
448
|
);
|
|
350
|
-
this.
|
|
449
|
+
await this.queryRun(
|
|
351
450
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
352
|
-
`${this.prefix}
|
|
353
|
-
)}
|
|
451
|
+
`${this.prefix}data_${etype}_id_name_falsy`,
|
|
452
|
+
)};`,
|
|
453
|
+
{ connection },
|
|
354
454
|
);
|
|
355
|
-
this.
|
|
455
|
+
await this.queryRun(
|
|
356
456
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
357
|
-
`${this.prefix}
|
|
457
|
+
`${this.prefix}data_${etype}_id_name_falsy`,
|
|
358
458
|
)} ON ${PostgreSQLDriver.escape(
|
|
359
|
-
`${this.prefix}
|
|
360
|
-
)} USING btree ("name")
|
|
459
|
+
`${this.prefix}data_${etype}`,
|
|
460
|
+
)} USING btree ("name") WHERE "truthy" <> TRUE;`,
|
|
461
|
+
{ connection },
|
|
361
462
|
);
|
|
362
|
-
this.
|
|
463
|
+
await this.queryRun(
|
|
363
464
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
364
|
-
`${this.prefix}
|
|
365
|
-
)}
|
|
465
|
+
`${this.prefix}data_${etype}_id_string`,
|
|
466
|
+
)};`,
|
|
467
|
+
{ connection },
|
|
366
468
|
);
|
|
367
|
-
this.
|
|
469
|
+
await this.queryRun(
|
|
368
470
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
369
|
-
`${this.prefix}
|
|
471
|
+
`${this.prefix}data_${etype}_id_string`,
|
|
370
472
|
)} ON ${PostgreSQLDriver.escape(
|
|
371
|
-
`${this.prefix}
|
|
372
|
-
)} USING
|
|
473
|
+
`${this.prefix}data_${etype}`,
|
|
474
|
+
)} USING gin ("string" gin_trgm_ops);`,
|
|
475
|
+
{ connection },
|
|
373
476
|
);
|
|
374
|
-
this.
|
|
477
|
+
await this.queryRun(
|
|
375
478
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
376
|
-
`${this.prefix}
|
|
377
|
-
)}
|
|
479
|
+
`${this.prefix}data_${etype}_id_json`,
|
|
480
|
+
)};`,
|
|
481
|
+
{ connection },
|
|
378
482
|
);
|
|
379
|
-
this.
|
|
483
|
+
await this.queryRun(
|
|
380
484
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
381
|
-
`${this.prefix}
|
|
485
|
+
`${this.prefix}data_${etype}_id_json`,
|
|
382
486
|
)} ON ${PostgreSQLDriver.escape(
|
|
383
|
-
`${this.prefix}
|
|
384
|
-
)} USING
|
|
487
|
+
`${this.prefix}data_${etype}`,
|
|
488
|
+
)} USING gin ("json");`,
|
|
489
|
+
{ connection },
|
|
385
490
|
);
|
|
386
491
|
// Create the references table.
|
|
387
|
-
this.
|
|
492
|
+
await this.queryRun(
|
|
388
493
|
`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
|
|
389
|
-
`${this.prefix}references_${etype}
|
|
494
|
+
`${this.prefix}references_${etype}`,
|
|
390
495
|
)} (
|
|
391
496
|
"guid" BYTEA NOT NULL,
|
|
392
497
|
"name" TEXT NOT NULL,
|
|
@@ -394,74 +499,106 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
394
499
|
PRIMARY KEY ("guid", "name", "reference"),
|
|
395
500
|
FOREIGN KEY ("guid")
|
|
396
501
|
REFERENCES ${PostgreSQLDriver.escape(
|
|
397
|
-
`${this.prefix}entities_${etype}
|
|
502
|
+
`${this.prefix}entities_${etype}`,
|
|
398
503
|
)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
399
|
-
) WITH ( OIDS=FALSE )
|
|
504
|
+
) WITH ( OIDS=FALSE );`,
|
|
505
|
+
{ connection },
|
|
400
506
|
);
|
|
401
|
-
this.
|
|
507
|
+
await this.queryRun(
|
|
402
508
|
`ALTER TABLE ${PostgreSQLDriver.escape(
|
|
403
|
-
`${this.prefix}references_${etype}
|
|
404
|
-
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)}
|
|
509
|
+
`${this.prefix}references_${etype}`,
|
|
510
|
+
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
|
|
511
|
+
{ connection },
|
|
405
512
|
);
|
|
406
|
-
this.
|
|
513
|
+
await this.queryRun(
|
|
407
514
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
408
|
-
`${this.prefix}references_${etype}_id_guid
|
|
409
|
-
)}
|
|
515
|
+
`${this.prefix}references_${etype}_id_guid`,
|
|
516
|
+
)};`,
|
|
517
|
+
{ connection },
|
|
410
518
|
);
|
|
411
|
-
this.
|
|
519
|
+
await this.queryRun(
|
|
412
520
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
413
|
-
`${this.prefix}references_${etype}_id_guid
|
|
521
|
+
`${this.prefix}references_${etype}_id_guid`,
|
|
414
522
|
)} ON ${PostgreSQLDriver.escape(
|
|
415
|
-
`${this.prefix}references_${etype}
|
|
416
|
-
)} USING btree ("guid")
|
|
523
|
+
`${this.prefix}references_${etype}`,
|
|
524
|
+
)} USING btree ("guid");`,
|
|
525
|
+
{ connection },
|
|
417
526
|
);
|
|
418
|
-
this.
|
|
527
|
+
await this.queryRun(
|
|
419
528
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
420
|
-
`${this.prefix}references_${etype}_id_name
|
|
421
|
-
)}
|
|
529
|
+
`${this.prefix}references_${etype}_id_name`,
|
|
530
|
+
)};`,
|
|
531
|
+
{ connection },
|
|
422
532
|
);
|
|
423
|
-
this.
|
|
533
|
+
await this.queryRun(
|
|
424
534
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
425
|
-
`${this.prefix}references_${etype}_id_name
|
|
535
|
+
`${this.prefix}references_${etype}_id_name`,
|
|
426
536
|
)} ON ${PostgreSQLDriver.escape(
|
|
427
|
-
`${this.prefix}references_${etype}
|
|
428
|
-
)} USING btree ("name")
|
|
537
|
+
`${this.prefix}references_${etype}`,
|
|
538
|
+
)} USING btree ("name");`,
|
|
539
|
+
{ connection },
|
|
429
540
|
);
|
|
430
|
-
this.
|
|
541
|
+
await this.queryRun(
|
|
431
542
|
`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
|
|
432
|
-
`${this.prefix}references_${etype}
|
|
433
|
-
)}
|
|
543
|
+
`${this.prefix}references_${etype}_id_name_reference`,
|
|
544
|
+
)};`,
|
|
545
|
+
{ connection },
|
|
434
546
|
);
|
|
435
|
-
this.
|
|
547
|
+
await this.queryRun(
|
|
436
548
|
`CREATE INDEX ${PostgreSQLDriver.escape(
|
|
437
|
-
`${this.prefix}references_${etype}
|
|
549
|
+
`${this.prefix}references_${etype}_id_name_reference`,
|
|
438
550
|
)} ON ${PostgreSQLDriver.escape(
|
|
439
|
-
`${this.prefix}references_${etype}
|
|
440
|
-
)} USING btree ("reference")
|
|
551
|
+
`${this.prefix}references_${etype}`,
|
|
552
|
+
)} USING btree ("name", "reference");`,
|
|
553
|
+
{ connection },
|
|
554
|
+
);
|
|
555
|
+
// Create the unique strings table.
|
|
556
|
+
await this.queryRun(
|
|
557
|
+
`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
|
|
558
|
+
`${this.prefix}uniques_${etype}`,
|
|
559
|
+
)} (
|
|
560
|
+
"guid" BYTEA NOT NULL,
|
|
561
|
+
"unique" TEXT NOT NULL UNIQUE,
|
|
562
|
+
PRIMARY KEY ("guid", "unique"),
|
|
563
|
+
FOREIGN KEY ("guid")
|
|
564
|
+
REFERENCES ${PostgreSQLDriver.escape(
|
|
565
|
+
`${this.prefix}entities_${etype}`,
|
|
566
|
+
)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
567
|
+
) WITH ( OIDS=FALSE );`,
|
|
568
|
+
{ connection },
|
|
441
569
|
);
|
|
442
570
|
} else {
|
|
571
|
+
// Add trigram extensions.
|
|
572
|
+
try {
|
|
573
|
+
await this.queryRun(`CREATE EXTENSION pg_trgm;`, { connection });
|
|
574
|
+
} catch (e: any) {
|
|
575
|
+
// Ignore errors.
|
|
576
|
+
}
|
|
443
577
|
// Create the UID table.
|
|
444
|
-
this.
|
|
578
|
+
await this.queryRun(
|
|
445
579
|
`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
|
|
446
|
-
`${this.prefix}uids
|
|
580
|
+
`${this.prefix}uids`,
|
|
447
581
|
)} (
|
|
448
582
|
"name" TEXT NOT NULL,
|
|
449
583
|
"cur_uid" BIGINT NOT NULL,
|
|
450
584
|
PRIMARY KEY ("name")
|
|
451
|
-
) WITH ( OIDS = FALSE )
|
|
585
|
+
) WITH ( OIDS = FALSE );`,
|
|
586
|
+
{ connection },
|
|
452
587
|
);
|
|
453
|
-
this.
|
|
588
|
+
await this.queryRun(
|
|
454
589
|
`ALTER TABLE ${PostgreSQLDriver.escape(
|
|
455
|
-
`${this.prefix}uids
|
|
456
|
-
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)}
|
|
590
|
+
`${this.prefix}uids`,
|
|
591
|
+
)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
|
|
592
|
+
{ connection },
|
|
457
593
|
);
|
|
458
594
|
}
|
|
595
|
+
connection.done();
|
|
459
596
|
return true;
|
|
460
597
|
}
|
|
461
598
|
|
|
462
599
|
private translateQuery(
|
|
463
600
|
origQuery: string,
|
|
464
|
-
origParams: { [k: string]: any }
|
|
601
|
+
origParams: { [k: string]: any },
|
|
465
602
|
): { query: string; params: any[] } {
|
|
466
603
|
const params: any[] = [];
|
|
467
604
|
let query = origQuery;
|
|
@@ -481,61 +618,39 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
481
618
|
private async query<T extends () => any>(
|
|
482
619
|
runQuery: T,
|
|
483
620
|
query: string,
|
|
484
|
-
etypes: string[] = []
|
|
621
|
+
etypes: string[] = [],
|
|
485
622
|
// @ts-ignore: The return type of T is a promise.
|
|
486
623
|
): ReturnType<T> {
|
|
487
624
|
try {
|
|
625
|
+
this.nymph.config.debugInfo('postgresql:query', query);
|
|
488
626
|
return await runQuery();
|
|
489
627
|
} catch (e: any) {
|
|
490
628
|
const errorCode = e?.code;
|
|
491
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
629
|
+
if (errorCode === '42P01' && (await this.createTables())) {
|
|
492
630
|
// If the tables don't exist yet, create them.
|
|
493
631
|
for (let etype of etypes) {
|
|
494
|
-
this.createTables(etype);
|
|
632
|
+
await this.createTables(etype);
|
|
495
633
|
}
|
|
496
634
|
try {
|
|
497
635
|
return await runQuery();
|
|
498
636
|
} catch (e2: any) {
|
|
499
637
|
throw new QueryFailedError(
|
|
500
638
|
'Query failed: ' + e2?.code + ' - ' + e2?.message,
|
|
501
|
-
query
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
} else {
|
|
505
|
-
throw e;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
private querySync<T extends () => any>(
|
|
511
|
-
runQuery: T,
|
|
512
|
-
query: string,
|
|
513
|
-
etypes: string[] = []
|
|
514
|
-
): ReturnType<T> {
|
|
515
|
-
try {
|
|
516
|
-
return runQuery();
|
|
517
|
-
} catch (e: any) {
|
|
518
|
-
const errorCode = e?.code;
|
|
519
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
520
|
-
// If the tables don't exist yet, create them.
|
|
521
|
-
for (let etype of etypes) {
|
|
522
|
-
this.createTables(etype);
|
|
523
|
-
}
|
|
524
|
-
try {
|
|
525
|
-
return runQuery();
|
|
526
|
-
} catch (e2: any) {
|
|
527
|
-
throw new QueryFailedError(
|
|
528
|
-
'Query failed: ' + e2?.code + ' - ' + e2?.message,
|
|
529
|
-
query
|
|
639
|
+
query,
|
|
530
640
|
);
|
|
531
641
|
}
|
|
642
|
+
} else if (errorCode === '23505') {
|
|
643
|
+
throw new EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
532
644
|
} else {
|
|
533
|
-
throw
|
|
645
|
+
throw new QueryFailedError(
|
|
646
|
+
'Query failed: ' + e?.code + ' - ' + e?.message,
|
|
647
|
+
query,
|
|
648
|
+
);
|
|
534
649
|
}
|
|
535
650
|
}
|
|
536
651
|
}
|
|
537
652
|
|
|
538
|
-
private
|
|
653
|
+
private queryArray(
|
|
539
654
|
query: string,
|
|
540
655
|
{
|
|
541
656
|
etypes = [],
|
|
@@ -543,11 +658,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
543
658
|
}: {
|
|
544
659
|
etypes?: string[];
|
|
545
660
|
params?: { [k: string]: any };
|
|
546
|
-
} = {}
|
|
661
|
+
} = {},
|
|
547
662
|
) {
|
|
548
663
|
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
549
664
|
query,
|
|
550
|
-
params
|
|
665
|
+
params,
|
|
551
666
|
);
|
|
552
667
|
return this.query(
|
|
553
668
|
async () => {
|
|
@@ -558,64 +673,65 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
558
673
|
.query(newQuery, newParams)
|
|
559
674
|
.then(
|
|
560
675
|
(results) => resolve(results),
|
|
561
|
-
(error) => reject(error)
|
|
676
|
+
(error) => reject(error),
|
|
562
677
|
);
|
|
563
678
|
} catch (e) {
|
|
564
679
|
reject(e);
|
|
565
680
|
}
|
|
566
|
-
}
|
|
681
|
+
},
|
|
567
682
|
);
|
|
568
683
|
return results.rows;
|
|
569
684
|
},
|
|
570
685
|
`${query} -- ${JSON.stringify(params)}`,
|
|
571
|
-
etypes
|
|
686
|
+
etypes,
|
|
572
687
|
);
|
|
573
688
|
}
|
|
574
689
|
|
|
575
|
-
private
|
|
690
|
+
private async queryIter(
|
|
576
691
|
query: string,
|
|
577
692
|
{
|
|
578
693
|
etypes = [],
|
|
579
694
|
params = {},
|
|
580
|
-
}: {
|
|
695
|
+
}: {
|
|
696
|
+
etypes?: string[];
|
|
697
|
+
params?: { [k: string]: any };
|
|
698
|
+
} = {},
|
|
581
699
|
) {
|
|
582
700
|
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
583
701
|
query,
|
|
584
|
-
params
|
|
702
|
+
params,
|
|
585
703
|
);
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
704
|
+
const that = this;
|
|
705
|
+
|
|
706
|
+
return this.query(
|
|
707
|
+
async function* (): AsyncGenerator<any, void, false | undefined> {
|
|
708
|
+
const transaction = !!that.transaction?.connection;
|
|
709
|
+
const connection = await that.getConnection();
|
|
710
|
+
const cursor = new Cursor(newQuery, newParams);
|
|
711
|
+
const iter = connection.client.query(cursor);
|
|
712
|
+
|
|
713
|
+
while (true) {
|
|
714
|
+
const rows = await iter.read(100);
|
|
715
|
+
|
|
716
|
+
if (!rows.length) {
|
|
717
|
+
await new Promise<void>((resolve) => {
|
|
718
|
+
iter.close(() => {
|
|
719
|
+
if (!transaction) {
|
|
720
|
+
connection.done();
|
|
721
|
+
}
|
|
722
|
+
resolve();
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
for (let row of rows) {
|
|
729
|
+
yield row;
|
|
601
730
|
}
|
|
602
|
-
);
|
|
603
|
-
try {
|
|
604
|
-
return JSON.parse(output.stdout).rows;
|
|
605
|
-
} catch (e) {
|
|
606
|
-
// Do nothing.
|
|
607
|
-
}
|
|
608
|
-
if (output.status === 0) {
|
|
609
|
-
throw new Error('Unknown parse error.');
|
|
610
|
-
}
|
|
611
|
-
const err = JSON.parse(output.stderr);
|
|
612
|
-
const e = new Error(err.name);
|
|
613
|
-
for (const name in err) {
|
|
614
|
-
(e as any)[name] = err[name];
|
|
615
731
|
}
|
|
616
732
|
},
|
|
617
733
|
`${query} -- ${JSON.stringify(params)}`,
|
|
618
|
-
etypes
|
|
734
|
+
etypes,
|
|
619
735
|
);
|
|
620
736
|
}
|
|
621
737
|
|
|
@@ -627,11 +743,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
627
743
|
}: {
|
|
628
744
|
etypes?: string[];
|
|
629
745
|
params?: { [k: string]: any };
|
|
630
|
-
} = {}
|
|
746
|
+
} = {},
|
|
631
747
|
) {
|
|
632
748
|
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
633
749
|
query,
|
|
634
|
-
params
|
|
750
|
+
params,
|
|
635
751
|
);
|
|
636
752
|
return this.query(
|
|
637
753
|
async () => {
|
|
@@ -642,17 +758,17 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
642
758
|
.query(newQuery, newParams)
|
|
643
759
|
.then(
|
|
644
760
|
(results) => resolve(results),
|
|
645
|
-
(error) => reject(error)
|
|
761
|
+
(error) => reject(error),
|
|
646
762
|
);
|
|
647
763
|
} catch (e) {
|
|
648
764
|
reject(e);
|
|
649
765
|
}
|
|
650
|
-
}
|
|
766
|
+
},
|
|
651
767
|
);
|
|
652
768
|
return results.rows[0];
|
|
653
769
|
},
|
|
654
770
|
`${query} -- ${JSON.stringify(params)}`,
|
|
655
|
-
etypes
|
|
771
|
+
etypes,
|
|
656
772
|
);
|
|
657
773
|
}
|
|
658
774
|
|
|
@@ -661,90 +777,47 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
661
777
|
{
|
|
662
778
|
etypes = [],
|
|
663
779
|
params = {},
|
|
780
|
+
connection,
|
|
664
781
|
}: {
|
|
665
782
|
etypes?: string[];
|
|
666
783
|
params?: { [k: string]: any };
|
|
667
|
-
|
|
784
|
+
connection?: PostgreSQLDriverConnection;
|
|
785
|
+
} = {},
|
|
668
786
|
) {
|
|
669
787
|
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
670
788
|
query,
|
|
671
|
-
params
|
|
789
|
+
params,
|
|
672
790
|
);
|
|
673
791
|
return this.query(
|
|
674
792
|
async () => {
|
|
675
793
|
const results: QueryResult<any> = await new Promise(
|
|
676
794
|
(resolve, reject) => {
|
|
677
795
|
try {
|
|
678
|
-
(
|
|
796
|
+
(
|
|
797
|
+
(connection ?? this.transaction?.connection)?.client ??
|
|
798
|
+
this.link
|
|
799
|
+
)
|
|
679
800
|
.query(newQuery, newParams)
|
|
680
801
|
.then(
|
|
681
802
|
(results) => resolve(results),
|
|
682
|
-
(error) => reject(error)
|
|
803
|
+
(error) => reject(error),
|
|
683
804
|
);
|
|
684
805
|
} catch (e) {
|
|
685
806
|
reject(e);
|
|
686
807
|
}
|
|
687
|
-
}
|
|
808
|
+
},
|
|
688
809
|
);
|
|
689
810
|
return { rowCount: results.rowCount ?? 0 };
|
|
690
811
|
},
|
|
691
812
|
`${query} -- ${JSON.stringify(params)}`,
|
|
692
|
-
etypes
|
|
693
|
-
);
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
private queryRunSync(
|
|
697
|
-
query: string,
|
|
698
|
-
{
|
|
699
|
-
etypes = [],
|
|
700
|
-
params = {},
|
|
701
|
-
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
702
|
-
) {
|
|
703
|
-
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
704
|
-
query,
|
|
705
|
-
params
|
|
706
|
-
);
|
|
707
|
-
return this.querySync(
|
|
708
|
-
() => {
|
|
709
|
-
const output = cp.spawnSync(
|
|
710
|
-
process.argv0,
|
|
711
|
-
[__dirname + '/runPostgresqlSync.js'],
|
|
712
|
-
{
|
|
713
|
-
input: JSON.stringify({
|
|
714
|
-
postgresqlConfig: this.postgresqlConfig,
|
|
715
|
-
query: newQuery,
|
|
716
|
-
params: newParams,
|
|
717
|
-
}),
|
|
718
|
-
timeout: 30000,
|
|
719
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
720
|
-
encoding: 'utf8',
|
|
721
|
-
windowsHide: true,
|
|
722
|
-
}
|
|
723
|
-
);
|
|
724
|
-
try {
|
|
725
|
-
const results = JSON.parse(output.stdout);
|
|
726
|
-
return { rowCount: results.rowCount ?? 0 };
|
|
727
|
-
} catch (e) {
|
|
728
|
-
// Do nothing.
|
|
729
|
-
}
|
|
730
|
-
if (output.status === 0) {
|
|
731
|
-
throw new Error('Unknown parse error.');
|
|
732
|
-
}
|
|
733
|
-
const err = JSON.parse(output.stderr);
|
|
734
|
-
const e = new Error(err.name);
|
|
735
|
-
for (const name in err) {
|
|
736
|
-
(e as any)[name] = err[name];
|
|
737
|
-
}
|
|
738
|
-
},
|
|
739
|
-
`${query} -- ${JSON.stringify(params)}`,
|
|
740
|
-
etypes
|
|
813
|
+
etypes,
|
|
741
814
|
);
|
|
742
815
|
}
|
|
743
816
|
|
|
744
817
|
public async commit(name: string) {
|
|
745
818
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
746
819
|
throw new InvalidParametersError(
|
|
747
|
-
'Transaction commit attempted without a name.'
|
|
820
|
+
'Transaction commit attempted without a name.',
|
|
748
821
|
);
|
|
749
822
|
}
|
|
750
823
|
if (!this.transaction || this.transaction.count === 0) {
|
|
@@ -764,7 +837,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
764
837
|
|
|
765
838
|
public async deleteEntityByID(
|
|
766
839
|
guid: string,
|
|
767
|
-
className?: EntityConstructor | string | null
|
|
840
|
+
className?: EntityConstructor | string | null,
|
|
768
841
|
) {
|
|
769
842
|
let EntityClass: EntityConstructor;
|
|
770
843
|
if (typeof className === 'string' || className == null) {
|
|
@@ -778,58 +851,60 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
778
851
|
try {
|
|
779
852
|
await this.queryRun(
|
|
780
853
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
781
|
-
`${this.prefix}entities_${etype}
|
|
854
|
+
`${this.prefix}entities_${etype}`,
|
|
782
855
|
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
783
856
|
{
|
|
784
857
|
etypes: [etype],
|
|
785
858
|
params: {
|
|
786
859
|
guid,
|
|
787
860
|
},
|
|
788
|
-
}
|
|
861
|
+
},
|
|
789
862
|
);
|
|
790
863
|
await this.queryRun(
|
|
791
864
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
792
|
-
`${this.prefix}data_${etype}
|
|
865
|
+
`${this.prefix}data_${etype}`,
|
|
793
866
|
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
794
867
|
{
|
|
795
868
|
etypes: [etype],
|
|
796
869
|
params: {
|
|
797
870
|
guid,
|
|
798
871
|
},
|
|
799
|
-
}
|
|
872
|
+
},
|
|
800
873
|
);
|
|
801
874
|
await this.queryRun(
|
|
802
875
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
803
|
-
`${this.prefix}
|
|
876
|
+
`${this.prefix}references_${etype}`,
|
|
804
877
|
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
805
878
|
{
|
|
806
879
|
etypes: [etype],
|
|
807
880
|
params: {
|
|
808
881
|
guid,
|
|
809
882
|
},
|
|
810
|
-
}
|
|
883
|
+
},
|
|
811
884
|
);
|
|
812
885
|
await this.queryRun(
|
|
813
886
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
814
|
-
`${this.prefix}
|
|
887
|
+
`${this.prefix}uniques_${etype}`,
|
|
815
888
|
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
816
889
|
{
|
|
817
890
|
etypes: [etype],
|
|
818
891
|
params: {
|
|
819
892
|
guid,
|
|
820
893
|
},
|
|
821
|
-
}
|
|
894
|
+
},
|
|
822
895
|
);
|
|
823
|
-
await this.commit('nymph-delete');
|
|
824
|
-
// Remove any cached versions of this entity.
|
|
825
|
-
if (this.nymph.config.cache) {
|
|
826
|
-
this.cleanCache(guid);
|
|
827
|
-
}
|
|
828
|
-
return true;
|
|
829
896
|
} catch (e: any) {
|
|
897
|
+
this.nymph.config.debugError('postgresql', `Delete entity error: "${e}"`);
|
|
830
898
|
await this.rollback('nymph-delete');
|
|
831
899
|
throw e;
|
|
832
900
|
}
|
|
901
|
+
|
|
902
|
+
await this.commit('nymph-delete');
|
|
903
|
+
// Remove any cached versions of this entity.
|
|
904
|
+
if (this.nymph.config.cache) {
|
|
905
|
+
this.cleanCache(guid);
|
|
906
|
+
}
|
|
907
|
+
return true;
|
|
833
908
|
}
|
|
834
909
|
|
|
835
910
|
public async deleteUID(name: string) {
|
|
@@ -838,103 +913,143 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
838
913
|
}
|
|
839
914
|
await this.queryRun(
|
|
840
915
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
841
|
-
`${this.prefix}uids
|
|
916
|
+
`${this.prefix}uids`,
|
|
842
917
|
)} WHERE "name"=@name;`,
|
|
843
918
|
{
|
|
844
919
|
params: {
|
|
845
920
|
name,
|
|
846
921
|
},
|
|
847
|
-
}
|
|
922
|
+
},
|
|
848
923
|
);
|
|
849
924
|
return true;
|
|
850
925
|
}
|
|
851
926
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
927
|
+
public async *exportDataIterator(): AsyncGenerator<
|
|
928
|
+
{ type: 'comment' | 'uid' | 'entity'; content: string },
|
|
929
|
+
void,
|
|
930
|
+
false | undefined
|
|
931
|
+
> {
|
|
932
|
+
if (
|
|
933
|
+
yield {
|
|
934
|
+
type: 'comment',
|
|
935
|
+
content: `#nex2
|
|
936
|
+
# Nymph Entity Exchange v2
|
|
937
|
+
# http://nymph.io
|
|
938
|
+
#
|
|
939
|
+
# Generation Time: ${new Date().toLocaleString()}
|
|
940
|
+
`,
|
|
941
|
+
}
|
|
942
|
+
) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if (
|
|
947
|
+
yield {
|
|
948
|
+
type: 'comment',
|
|
949
|
+
content: `
|
|
859
950
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
951
|
+
#
|
|
952
|
+
# UIDs
|
|
953
|
+
#
|
|
954
|
+
|
|
955
|
+
`,
|
|
956
|
+
}
|
|
957
|
+
) {
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
864
960
|
|
|
865
961
|
// Export UIDs.
|
|
866
962
|
let uids = await this.queryIter(
|
|
867
963
|
`SELECT * FROM ${PostgreSQLDriver.escape(
|
|
868
|
-
`${this.prefix}uids
|
|
869
|
-
)} ORDER BY "name"
|
|
964
|
+
`${this.prefix}uids`,
|
|
965
|
+
)} ORDER BY "name";`,
|
|
870
966
|
);
|
|
871
|
-
for (const uid of uids) {
|
|
872
|
-
|
|
967
|
+
for await (const uid of uids) {
|
|
968
|
+
if (yield { type: 'uid', content: `<${uid.name}>[${uid.cur_uid}]\n` }) {
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
873
971
|
}
|
|
874
972
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
973
|
+
if (
|
|
974
|
+
yield {
|
|
975
|
+
type: 'comment',
|
|
976
|
+
content: `
|
|
977
|
+
|
|
978
|
+
#
|
|
979
|
+
# Entities
|
|
980
|
+
#
|
|
981
|
+
|
|
982
|
+
`,
|
|
983
|
+
}
|
|
984
|
+
) {
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
880
987
|
|
|
881
988
|
// Get the etypes.
|
|
882
|
-
const tables = await this.
|
|
883
|
-
'SELECT
|
|
989
|
+
const tables = await this.queryArray(
|
|
990
|
+
'SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix;',
|
|
991
|
+
{
|
|
992
|
+
params: {
|
|
993
|
+
db: this.config.database,
|
|
994
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
995
|
+
},
|
|
996
|
+
},
|
|
884
997
|
);
|
|
885
998
|
const etypes = [];
|
|
886
|
-
for (const
|
|
887
|
-
|
|
888
|
-
if (table.startsWith(this.prefix + 'entities_')) {
|
|
889
|
-
etypes.push(table.substr((this.prefix + 'entities_').length));
|
|
890
|
-
}
|
|
999
|
+
for (const table of tables) {
|
|
1000
|
+
etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
|
|
891
1001
|
}
|
|
892
1002
|
|
|
893
1003
|
for (const etype of etypes) {
|
|
894
1004
|
// Export entities.
|
|
895
|
-
const dataIterator = (
|
|
896
|
-
|
|
897
|
-
`SELECT encode(e."guid", 'hex') AS "guid", e."tags", e."cdate", e."mdate", d."name" AS "dname", d."value" AS "dvalue", c."string", c."number"
|
|
1005
|
+
const dataIterator = await this.queryIter(
|
|
1006
|
+
`SELECT encode(e."guid", 'hex') AS "guid", e."tags", e."cdate", e."mdate", d."name", d."value", d."json", d."string", d."number"
|
|
898
1007
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} e
|
|
899
1008
|
LEFT JOIN ${PostgreSQLDriver.escape(
|
|
900
|
-
`${this.prefix}data_${etype}
|
|
1009
|
+
`${this.prefix}data_${etype}`,
|
|
901
1010
|
)} d ON e."guid"=d."guid"
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
ORDER BY e."guid";`
|
|
906
|
-
)
|
|
907
|
-
)[Symbol.iterator]();
|
|
908
|
-
let datum = dataIterator.next();
|
|
1011
|
+
ORDER BY e."guid";`,
|
|
1012
|
+
);
|
|
1013
|
+
let datum = await dataIterator.next();
|
|
909
1014
|
while (!datum.done) {
|
|
910
1015
|
const guid = datum.value.guid;
|
|
911
|
-
const tags = datum.value.tags.join(',');
|
|
1016
|
+
const tags = datum.value.tags.filter((tag: string) => tag).join(',');
|
|
912
1017
|
const cdate = datum.value.cdate;
|
|
913
1018
|
const mdate = datum.value.mdate;
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1019
|
+
let currentEntityExport: string[] = [];
|
|
1020
|
+
currentEntityExport.push(`{${guid}}<${etype}>[${tags}]`);
|
|
1021
|
+
currentEntityExport.push(`\tcdate=${JSON.stringify(cdate)}`);
|
|
1022
|
+
currentEntityExport.push(`\tmdate=${JSON.stringify(mdate)}`);
|
|
1023
|
+
if (datum.value.name != null) {
|
|
918
1024
|
// This do will keep going and adding the data until the
|
|
919
1025
|
// next entity is reached. $row will end on the next entity.
|
|
920
1026
|
do {
|
|
921
1027
|
const value =
|
|
922
|
-
datum.value.
|
|
1028
|
+
datum.value.value === 'N'
|
|
923
1029
|
? JSON.stringify(Number(datum.value.number))
|
|
924
|
-
: datum.value.
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1030
|
+
: datum.value.value === 'S'
|
|
1031
|
+
? JSON.stringify(
|
|
1032
|
+
PostgreSQLDriver.unescapeNulls(datum.value.string),
|
|
1033
|
+
)
|
|
1034
|
+
: datum.value.value === 'J'
|
|
1035
|
+
? PostgreSQLDriver.unescapeNullSequences(
|
|
1036
|
+
JSON.stringify(datum.value.json),
|
|
1037
|
+
)
|
|
1038
|
+
: datum.value.value;
|
|
1039
|
+
currentEntityExport.push(`\t${datum.value.name}=${value}`);
|
|
1040
|
+
datum = await dataIterator.next();
|
|
929
1041
|
} while (!datum.done && datum.value.guid === guid);
|
|
930
1042
|
} else {
|
|
931
1043
|
// Make sure that datum is incremented :)
|
|
932
|
-
datum = dataIterator.next();
|
|
1044
|
+
datum = await dataIterator.next();
|
|
1045
|
+
}
|
|
1046
|
+
currentEntityExport.push('');
|
|
1047
|
+
|
|
1048
|
+
if (yield { type: 'entity', content: currentEntityExport.join('\n') }) {
|
|
1049
|
+
return;
|
|
933
1050
|
}
|
|
934
1051
|
}
|
|
935
1052
|
}
|
|
936
|
-
|
|
937
|
-
return;
|
|
938
1053
|
}
|
|
939
1054
|
|
|
940
1055
|
/**
|
|
@@ -955,21 +1070,22 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
955
1070
|
params: { [k: string]: any } = {},
|
|
956
1071
|
subquery = false,
|
|
957
1072
|
tableSuffix = '',
|
|
958
|
-
etypes: string[] = []
|
|
1073
|
+
etypes: string[] = [],
|
|
1074
|
+
guidSelector: string | undefined = undefined,
|
|
959
1075
|
) {
|
|
960
1076
|
if (typeof options.class?.alterOptions === 'function') {
|
|
961
1077
|
options = options.class.alterOptions(options);
|
|
962
1078
|
}
|
|
963
1079
|
const eTable = `e${tableSuffix}`;
|
|
964
1080
|
const dTable = `d${tableSuffix}`;
|
|
965
|
-
const cTable = `c${tableSuffix}`;
|
|
966
1081
|
const fTable = `f${tableSuffix}`;
|
|
967
1082
|
const ieTable = `ie${tableSuffix}`;
|
|
968
1083
|
const countTable = `count${tableSuffix}`;
|
|
1084
|
+
const sTable = `s${tableSuffix}`;
|
|
969
1085
|
const sort = options.sort ?? 'cdate';
|
|
970
1086
|
const queryParts = this.iterateSelectorsForQuery(
|
|
971
1087
|
formattedSelectors,
|
|
972
|
-
(key, value, typeIsOr, typeIsNot) => {
|
|
1088
|
+
({ key, value, typeIsOr, typeIsNot }) => {
|
|
973
1089
|
const clauseNot = key.startsWith('!');
|
|
974
1090
|
let curQuery = '';
|
|
975
1091
|
for (const curValue of value) {
|
|
@@ -1013,12 +1129,12 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1013
1129
|
}
|
|
1014
1130
|
const name = `param${++count.i}`;
|
|
1015
1131
|
curQuery +=
|
|
1016
|
-
ieTable +
|
|
1017
|
-
'."guid" ' +
|
|
1018
1132
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1019
|
-
'
|
|
1133
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1020
1134
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1021
|
-
' WHERE "
|
|
1135
|
+
' WHERE "guid"=' +
|
|
1136
|
+
ieTable +
|
|
1137
|
+
'."guid" AND "name"=@' +
|
|
1022
1138
|
name +
|
|
1023
1139
|
')';
|
|
1024
1140
|
params[name] = curVar;
|
|
@@ -1048,12 +1164,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1048
1164
|
const name = `param${++count.i}`;
|
|
1049
1165
|
curQuery +=
|
|
1050
1166
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1167
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1168
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1169
|
+
' WHERE "guid"=' +
|
|
1051
1170
|
ieTable +
|
|
1052
|
-
'."guid"
|
|
1053
|
-
PostgreSQLDriver.escape(
|
|
1054
|
-
this.prefix + 'comparisons_' + etype
|
|
1055
|
-
) +
|
|
1056
|
-
' WHERE "name"=@' +
|
|
1171
|
+
'."guid" AND "name"=@' +
|
|
1057
1172
|
name +
|
|
1058
1173
|
' AND "truthy"=TRUE)';
|
|
1059
1174
|
params[name] = curVar;
|
|
@@ -1098,12 +1213,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1098
1213
|
const value = `param${++count.i}`;
|
|
1099
1214
|
curQuery +=
|
|
1100
1215
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1216
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1217
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1218
|
+
' WHERE "guid"=' +
|
|
1101
1219
|
ieTable +
|
|
1102
|
-
'."guid"
|
|
1103
|
-
PostgreSQLDriver.escape(
|
|
1104
|
-
this.prefix + 'comparisons_' + etype
|
|
1105
|
-
) +
|
|
1106
|
-
' WHERE "name"=@' +
|
|
1220
|
+
'."guid" AND "name"=@' +
|
|
1107
1221
|
name +
|
|
1108
1222
|
' AND "number"=@' +
|
|
1109
1223
|
value +
|
|
@@ -1118,18 +1232,19 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1118
1232
|
const value = `param${++count.i}`;
|
|
1119
1233
|
curQuery +=
|
|
1120
1234
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1235
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1236
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1237
|
+
' WHERE "guid"=' +
|
|
1121
1238
|
ieTable +
|
|
1122
|
-
'."guid"
|
|
1123
|
-
PostgreSQLDriver.escape(
|
|
1124
|
-
this.prefix + 'comparisons_' + etype
|
|
1125
|
-
) +
|
|
1126
|
-
' WHERE "name"=@' +
|
|
1239
|
+
'."guid" AND "name"=@' +
|
|
1127
1240
|
name +
|
|
1128
|
-
' AND "string"
|
|
1129
|
-
|
|
1241
|
+
' AND "string"=' +
|
|
1242
|
+
(curValue[1].length < 512
|
|
1243
|
+
? 'LEFT(@' + value + ', 512)'
|
|
1244
|
+
: '@' + value) +
|
|
1130
1245
|
')';
|
|
1131
1246
|
params[name] = curValue[0];
|
|
1132
|
-
params[value] = curValue[1];
|
|
1247
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
1133
1248
|
} else {
|
|
1134
1249
|
if (curQuery) {
|
|
1135
1250
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1147,16 +1262,17 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1147
1262
|
const value = `param${++count.i}`;
|
|
1148
1263
|
curQuery +=
|
|
1149
1264
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1150
|
-
|
|
1151
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1265
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1152
1266
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1153
|
-
' WHERE "
|
|
1267
|
+
' WHERE "guid"=' +
|
|
1268
|
+
ieTable +
|
|
1269
|
+
'."guid" AND "name"=@' +
|
|
1154
1270
|
name +
|
|
1155
|
-
' AND "
|
|
1271
|
+
' AND "json"=@' +
|
|
1156
1272
|
value +
|
|
1157
1273
|
')';
|
|
1158
1274
|
params[name] = curValue[0];
|
|
1159
|
-
params[value] = svalue;
|
|
1275
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
1160
1276
|
}
|
|
1161
1277
|
break;
|
|
1162
1278
|
case 'contain':
|
|
@@ -1169,7 +1285,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1169
1285
|
curQuery +=
|
|
1170
1286
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1171
1287
|
ieTable +
|
|
1172
|
-
'."cdate"
|
|
1288
|
+
'."cdate"=@' +
|
|
1173
1289
|
cdate;
|
|
1174
1290
|
params[cdate] = isNaN(Number(curValue[1]))
|
|
1175
1291
|
? null
|
|
@@ -1183,7 +1299,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1183
1299
|
curQuery +=
|
|
1184
1300
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1185
1301
|
ieTable +
|
|
1186
|
-
'."mdate"
|
|
1302
|
+
'."mdate"=@' +
|
|
1187
1303
|
mdate;
|
|
1188
1304
|
params[mdate] = isNaN(Number(curValue[1]))
|
|
1189
1305
|
? null
|
|
@@ -1194,57 +1310,34 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1194
1310
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1195
1311
|
}
|
|
1196
1312
|
let svalue: string;
|
|
1197
|
-
let stringValue: string;
|
|
1198
1313
|
if (
|
|
1199
1314
|
curValue[1] instanceof Object &&
|
|
1200
1315
|
typeof curValue[1].toReference === 'function'
|
|
1201
1316
|
) {
|
|
1202
1317
|
svalue = JSON.stringify(curValue[1].toReference());
|
|
1203
|
-
|
|
1204
|
-
|
|
1318
|
+
} else if (
|
|
1319
|
+
typeof curValue[1] === 'string' ||
|
|
1320
|
+
typeof curValue[1] === 'number'
|
|
1321
|
+
) {
|
|
1205
1322
|
svalue = JSON.stringify(curValue[1]);
|
|
1206
|
-
|
|
1323
|
+
} else {
|
|
1324
|
+
svalue = JSON.stringify([curValue[1]]);
|
|
1207
1325
|
}
|
|
1208
1326
|
const name = `param${++count.i}`;
|
|
1209
1327
|
const value = `param${++count.i}`;
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
value +
|
|
1222
|
-
' IN "value")>0) OR ' +
|
|
1223
|
-
ieTable +
|
|
1224
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1225
|
-
PostgreSQLDriver.escape(
|
|
1226
|
-
this.prefix + 'comparisons_' + etype
|
|
1227
|
-
) +
|
|
1228
|
-
' WHERE "name"=@' +
|
|
1229
|
-
name +
|
|
1230
|
-
' AND "string"=@' +
|
|
1231
|
-
stringParam +
|
|
1232
|
-
'))';
|
|
1233
|
-
params[stringParam] = stringValue;
|
|
1234
|
-
} else {
|
|
1235
|
-
curQuery +=
|
|
1236
|
-
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1237
|
-
ieTable +
|
|
1238
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1239
|
-
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1240
|
-
' WHERE "name"=@' +
|
|
1241
|
-
name +
|
|
1242
|
-
' AND position(@' +
|
|
1243
|
-
value +
|
|
1244
|
-
' IN "value")>0)';
|
|
1245
|
-
}
|
|
1328
|
+
curQuery +=
|
|
1329
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1330
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1331
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1332
|
+
' WHERE "guid"=' +
|
|
1333
|
+
ieTable +
|
|
1334
|
+
'."guid" AND "name"=@' +
|
|
1335
|
+
name +
|
|
1336
|
+
' AND "json" @> @' +
|
|
1337
|
+
value +
|
|
1338
|
+
')';
|
|
1246
1339
|
params[name] = curValue[0];
|
|
1247
|
-
params[value] = svalue;
|
|
1340
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
1248
1341
|
}
|
|
1249
1342
|
break;
|
|
1250
1343
|
case 'match':
|
|
@@ -1285,18 +1378,17 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1285
1378
|
const value = `param${++count.i}`;
|
|
1286
1379
|
curQuery +=
|
|
1287
1380
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1381
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1382
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1383
|
+
' WHERE "guid"=' +
|
|
1288
1384
|
ieTable +
|
|
1289
|
-
'."guid"
|
|
1290
|
-
PostgreSQLDriver.escape(
|
|
1291
|
-
this.prefix + 'comparisons_' + etype
|
|
1292
|
-
) +
|
|
1293
|
-
' WHERE "name"=@' +
|
|
1385
|
+
'."guid" AND "name"=@' +
|
|
1294
1386
|
name +
|
|
1295
1387
|
' AND "string" ~ @' +
|
|
1296
1388
|
value +
|
|
1297
1389
|
')';
|
|
1298
1390
|
params[name] = curValue[0];
|
|
1299
|
-
params[value] = curValue[1];
|
|
1391
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
1300
1392
|
}
|
|
1301
1393
|
break;
|
|
1302
1394
|
case 'imatch':
|
|
@@ -1337,18 +1429,17 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1337
1429
|
const value = `param${++count.i}`;
|
|
1338
1430
|
curQuery +=
|
|
1339
1431
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1432
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1433
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1434
|
+
' WHERE "guid"=' +
|
|
1340
1435
|
ieTable +
|
|
1341
|
-
'."guid"
|
|
1342
|
-
PostgreSQLDriver.escape(
|
|
1343
|
-
this.prefix + 'comparisons_' + etype
|
|
1344
|
-
) +
|
|
1345
|
-
' WHERE "name"=@' +
|
|
1436
|
+
'."guid" AND "name"=@' +
|
|
1346
1437
|
name +
|
|
1347
1438
|
' AND "string" ~* @' +
|
|
1348
1439
|
value +
|
|
1349
1440
|
')';
|
|
1350
1441
|
params[name] = curValue[0];
|
|
1351
|
-
params[value] = curValue[1];
|
|
1442
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
1352
1443
|
}
|
|
1353
1444
|
break;
|
|
1354
1445
|
case 'like':
|
|
@@ -1389,18 +1480,17 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1389
1480
|
const value = `param${++count.i}`;
|
|
1390
1481
|
curQuery +=
|
|
1391
1482
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1483
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1484
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1485
|
+
' WHERE "guid"=' +
|
|
1392
1486
|
ieTable +
|
|
1393
|
-
'."guid"
|
|
1394
|
-
PostgreSQLDriver.escape(
|
|
1395
|
-
this.prefix + 'comparisons_' + etype
|
|
1396
|
-
) +
|
|
1397
|
-
' WHERE "name"=@' +
|
|
1487
|
+
'."guid" AND "name"=@' +
|
|
1398
1488
|
name +
|
|
1399
1489
|
' AND "string" LIKE @' +
|
|
1400
1490
|
value +
|
|
1401
1491
|
')';
|
|
1402
1492
|
params[name] = curValue[0];
|
|
1403
|
-
params[value] = curValue[1];
|
|
1493
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
1404
1494
|
}
|
|
1405
1495
|
break;
|
|
1406
1496
|
case 'ilike':
|
|
@@ -1441,18 +1531,17 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1441
1531
|
const value = `param${++count.i}`;
|
|
1442
1532
|
curQuery +=
|
|
1443
1533
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1534
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1535
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1536
|
+
' WHERE "guid"=' +
|
|
1444
1537
|
ieTable +
|
|
1445
|
-
'."guid"
|
|
1446
|
-
PostgreSQLDriver.escape(
|
|
1447
|
-
this.prefix + 'comparisons_' + etype
|
|
1448
|
-
) +
|
|
1449
|
-
' WHERE "name"=@' +
|
|
1538
|
+
'."guid" AND "name"=@' +
|
|
1450
1539
|
name +
|
|
1451
1540
|
' AND "string" ILIKE @' +
|
|
1452
1541
|
value +
|
|
1453
1542
|
')';
|
|
1454
1543
|
params[name] = curValue[0];
|
|
1455
|
-
params[value] = curValue[1];
|
|
1544
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
1456
1545
|
}
|
|
1457
1546
|
break;
|
|
1458
1547
|
case 'gt':
|
|
@@ -1493,12 +1582,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1493
1582
|
const value = `param${++count.i}`;
|
|
1494
1583
|
curQuery +=
|
|
1495
1584
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1585
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1586
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1587
|
+
' WHERE "guid"=' +
|
|
1496
1588
|
ieTable +
|
|
1497
|
-
'."guid"
|
|
1498
|
-
PostgreSQLDriver.escape(
|
|
1499
|
-
this.prefix + 'comparisons_' + etype
|
|
1500
|
-
) +
|
|
1501
|
-
' WHERE "name"=@' +
|
|
1589
|
+
'."guid" AND "name"=@' +
|
|
1502
1590
|
name +
|
|
1503
1591
|
' AND "number">@' +
|
|
1504
1592
|
value +
|
|
@@ -1547,12 +1635,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1547
1635
|
const value = `param${++count.i}`;
|
|
1548
1636
|
curQuery +=
|
|
1549
1637
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1638
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1639
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1640
|
+
' WHERE "guid"=' +
|
|
1550
1641
|
ieTable +
|
|
1551
|
-
'."guid"
|
|
1552
|
-
PostgreSQLDriver.escape(
|
|
1553
|
-
this.prefix + 'comparisons_' + etype
|
|
1554
|
-
) +
|
|
1555
|
-
' WHERE "name"=@' +
|
|
1642
|
+
'."guid" AND "name"=@' +
|
|
1556
1643
|
name +
|
|
1557
1644
|
' AND "number">=@' +
|
|
1558
1645
|
value +
|
|
@@ -1601,12 +1688,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1601
1688
|
const value = `param${++count.i}`;
|
|
1602
1689
|
curQuery +=
|
|
1603
1690
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1691
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1692
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1693
|
+
' WHERE "guid"=' +
|
|
1604
1694
|
ieTable +
|
|
1605
|
-
'."guid"
|
|
1606
|
-
PostgreSQLDriver.escape(
|
|
1607
|
-
this.prefix + 'comparisons_' + etype
|
|
1608
|
-
) +
|
|
1609
|
-
' WHERE "name"=@' +
|
|
1695
|
+
'."guid" AND "name"=@' +
|
|
1610
1696
|
name +
|
|
1611
1697
|
' AND "number"<@' +
|
|
1612
1698
|
value +
|
|
@@ -1655,12 +1741,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1655
1741
|
const value = `param${++count.i}`;
|
|
1656
1742
|
curQuery +=
|
|
1657
1743
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1744
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1745
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1746
|
+
' WHERE "guid"=' +
|
|
1658
1747
|
ieTable +
|
|
1659
|
-
'."guid"
|
|
1660
|
-
PostgreSQLDriver.escape(
|
|
1661
|
-
this.prefix + 'comparisons_' + etype
|
|
1662
|
-
) +
|
|
1663
|
-
' WHERE "name"=@' +
|
|
1748
|
+
'."guid" AND "name"=@' +
|
|
1664
1749
|
name +
|
|
1665
1750
|
' AND "number"<=@' +
|
|
1666
1751
|
value +
|
|
@@ -1688,10 +1773,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1688
1773
|
const guid = `param${++count.i}`;
|
|
1689
1774
|
curQuery +=
|
|
1690
1775
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1691
|
-
|
|
1692
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1776
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1693
1777
|
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1694
|
-
' WHERE "
|
|
1778
|
+
' WHERE "guid"=' +
|
|
1779
|
+
ieTable +
|
|
1780
|
+
'."guid" AND "name"=@' +
|
|
1695
1781
|
name +
|
|
1696
1782
|
' AND "reference"=decode(@' +
|
|
1697
1783
|
guid +
|
|
@@ -1709,7 +1795,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1709
1795
|
params,
|
|
1710
1796
|
true,
|
|
1711
1797
|
tableSuffix,
|
|
1712
|
-
etypes
|
|
1798
|
+
etypes,
|
|
1713
1799
|
);
|
|
1714
1800
|
if (curQuery) {
|
|
1715
1801
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1722,9 +1808,10 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1722
1808
|
break;
|
|
1723
1809
|
case 'qref':
|
|
1724
1810
|
case '!qref':
|
|
1811
|
+
const referenceTableSuffix = makeTableSuffix();
|
|
1725
1812
|
const [qrefOptions, ...qrefSelectors] = curValue[1] as [
|
|
1726
1813
|
Options,
|
|
1727
|
-
...FormattedSelector[]
|
|
1814
|
+
...FormattedSelector[],
|
|
1728
1815
|
];
|
|
1729
1816
|
const QrefEntityClass = qrefOptions.class as EntityConstructor;
|
|
1730
1817
|
etypes.push(QrefEntityClass.ETYPE);
|
|
@@ -1736,7 +1823,8 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1736
1823
|
params,
|
|
1737
1824
|
false,
|
|
1738
1825
|
makeTableSuffix(),
|
|
1739
|
-
etypes
|
|
1826
|
+
etypes,
|
|
1827
|
+
'r' + referenceTableSuffix + '."reference"',
|
|
1740
1828
|
);
|
|
1741
1829
|
if (curQuery) {
|
|
1742
1830
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1744,12 +1832,19 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1744
1832
|
const qrefName = `param${++count.i}`;
|
|
1745
1833
|
curQuery +=
|
|
1746
1834
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1747
|
-
|
|
1748
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1835
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1749
1836
|
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1750
|
-
'
|
|
1837
|
+
' r' +
|
|
1838
|
+
referenceTableSuffix +
|
|
1839
|
+
' WHERE r' +
|
|
1840
|
+
referenceTableSuffix +
|
|
1841
|
+
'."guid"=' +
|
|
1842
|
+
ieTable +
|
|
1843
|
+
'."guid" AND r' +
|
|
1844
|
+
referenceTableSuffix +
|
|
1845
|
+
'."name"=@' +
|
|
1751
1846
|
qrefName +
|
|
1752
|
-
' AND
|
|
1847
|
+
' AND EXISTS (' +
|
|
1753
1848
|
qrefQuery.query +
|
|
1754
1849
|
'))';
|
|
1755
1850
|
params[qrefName] = curValue[0];
|
|
@@ -1757,22 +1852,42 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1757
1852
|
}
|
|
1758
1853
|
}
|
|
1759
1854
|
return curQuery;
|
|
1760
|
-
}
|
|
1855
|
+
},
|
|
1761
1856
|
);
|
|
1762
1857
|
|
|
1763
1858
|
let sortBy: string;
|
|
1859
|
+
let sortByInner: string;
|
|
1860
|
+
let sortJoin = '';
|
|
1861
|
+
let sortJoinInner = '';
|
|
1862
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1764
1863
|
switch (sort) {
|
|
1765
1864
|
case 'mdate':
|
|
1766
|
-
sortBy =
|
|
1865
|
+
sortBy = `${eTable}."mdate"${order}`;
|
|
1866
|
+
sortByInner = `${ieTable}."mdate"${order}`;
|
|
1767
1867
|
break;
|
|
1768
1868
|
case 'cdate':
|
|
1869
|
+
sortBy = `${eTable}."cdate"${order}`;
|
|
1870
|
+
sortByInner = `${ieTable}."cdate"${order}`;
|
|
1871
|
+
break;
|
|
1769
1872
|
default:
|
|
1770
|
-
|
|
1873
|
+
const name = `param${++count.i}`;
|
|
1874
|
+
sortJoin = `LEFT JOIN (
|
|
1875
|
+
SELECT "guid", "string", "number"
|
|
1876
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1877
|
+
WHERE "name"=@${name}
|
|
1878
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1879
|
+
) ${sTable} ON ${eTable}."guid"=${sTable}."guid"`;
|
|
1880
|
+
sortJoinInner = `LEFT JOIN (
|
|
1881
|
+
SELECT "guid", "string", "number"
|
|
1882
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1883
|
+
WHERE "name"=@${name}
|
|
1884
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1885
|
+
) ${sTable} ON ${ieTable}."guid"=${sTable}."guid"`;
|
|
1886
|
+
sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1887
|
+
sortByInner = sortBy;
|
|
1888
|
+
params[name] = sort;
|
|
1771
1889
|
break;
|
|
1772
1890
|
}
|
|
1773
|
-
if (options.reverse) {
|
|
1774
|
-
sortBy += ' DESC';
|
|
1775
|
-
}
|
|
1776
1891
|
|
|
1777
1892
|
let query: string;
|
|
1778
1893
|
if (queryParts.length) {
|
|
@@ -1782,31 +1897,34 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1782
1897
|
let limit = '';
|
|
1783
1898
|
if ('limit' in options) {
|
|
1784
1899
|
limit = ` LIMIT ${Math.floor(
|
|
1785
|
-
isNaN(Number(options.limit)) ? 0 : Number(options.limit)
|
|
1900
|
+
isNaN(Number(options.limit)) ? 0 : Number(options.limit),
|
|
1786
1901
|
)}`;
|
|
1787
1902
|
}
|
|
1788
1903
|
let offset = '';
|
|
1789
1904
|
if ('offset' in options) {
|
|
1790
1905
|
offset = ` OFFSET ${Math.floor(
|
|
1791
|
-
isNaN(Number(options.offset)) ? 0 : Number(options.offset)
|
|
1906
|
+
isNaN(Number(options.offset)) ? 0 : Number(options.offset),
|
|
1792
1907
|
)}`;
|
|
1793
1908
|
}
|
|
1794
1909
|
const whereClause = queryParts.join(') AND (');
|
|
1910
|
+
const guidClause = guidSelector
|
|
1911
|
+
? `${ieTable}."guid"=${guidSelector} AND `
|
|
1912
|
+
: '';
|
|
1795
1913
|
if (options.return === 'count') {
|
|
1796
1914
|
if (limit || offset) {
|
|
1797
1915
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1798
|
-
SELECT
|
|
1916
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1799
1917
|
FROM ${PostgreSQLDriver.escape(
|
|
1800
|
-
`${this.prefix}entities_${etype}
|
|
1918
|
+
`${this.prefix}entities_${etype}`,
|
|
1801
1919
|
)} ${ieTable}
|
|
1802
|
-
WHERE (${whereClause})${limit}${offset}
|
|
1920
|
+
WHERE ${guidClause}(${whereClause})${limit}${offset}
|
|
1803
1921
|
) ${countTable}`;
|
|
1804
1922
|
} else {
|
|
1805
1923
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1806
1924
|
FROM ${PostgreSQLDriver.escape(
|
|
1807
|
-
`${this.prefix}entities_${etype}
|
|
1925
|
+
`${this.prefix}entities_${etype}`,
|
|
1808
1926
|
)} ${ieTable}
|
|
1809
|
-
WHERE (${whereClause})`;
|
|
1927
|
+
WHERE ${guidClause}(${whereClause})`;
|
|
1810
1928
|
}
|
|
1811
1929
|
} else if (options.return === 'guid') {
|
|
1812
1930
|
const guidColumn =
|
|
@@ -1815,10 +1933,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1815
1933
|
: `${ieTable}."guid"`;
|
|
1816
1934
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1817
1935
|
FROM ${PostgreSQLDriver.escape(
|
|
1818
|
-
`${this.prefix}entities_${etype}
|
|
1936
|
+
`${this.prefix}entities_${etype}`,
|
|
1819
1937
|
)} ${ieTable}
|
|
1820
|
-
|
|
1821
|
-
|
|
1938
|
+
${sortJoinInner}
|
|
1939
|
+
WHERE ${guidClause}(${whereClause})
|
|
1940
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1822
1941
|
} else {
|
|
1823
1942
|
query = `SELECT
|
|
1824
1943
|
encode(${eTable}."guid", 'hex') AS "guid",
|
|
@@ -1827,26 +1946,26 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1827
1946
|
${eTable}."mdate",
|
|
1828
1947
|
${dTable}."name",
|
|
1829
1948
|
${dTable}."value",
|
|
1830
|
-
${
|
|
1831
|
-
${
|
|
1949
|
+
${dTable}."json",
|
|
1950
|
+
${dTable}."string",
|
|
1951
|
+
${dTable}."number"
|
|
1832
1952
|
FROM ${PostgreSQLDriver.escape(
|
|
1833
|
-
`${this.prefix}entities_${etype}
|
|
1953
|
+
`${this.prefix}entities_${etype}`,
|
|
1834
1954
|
)} ${eTable}
|
|
1835
1955
|
LEFT JOIN ${PostgreSQLDriver.escape(
|
|
1836
|
-
`${this.prefix}data_${etype}
|
|
1956
|
+
`${this.prefix}data_${etype}`,
|
|
1837
1957
|
)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1838
|
-
|
|
1839
|
-
`${this.prefix}comparisons_${etype}`
|
|
1840
|
-
)} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
|
|
1958
|
+
${sortJoin}
|
|
1841
1959
|
INNER JOIN (
|
|
1842
1960
|
SELECT ${ieTable}."guid"
|
|
1843
1961
|
FROM ${PostgreSQLDriver.escape(
|
|
1844
|
-
`${this.prefix}entities_${etype}
|
|
1962
|
+
`${this.prefix}entities_${etype}`,
|
|
1845
1963
|
)} ${ieTable}
|
|
1846
|
-
|
|
1847
|
-
|
|
1964
|
+
${sortJoinInner}
|
|
1965
|
+
WHERE ${guidClause}(${whereClause})
|
|
1966
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1848
1967
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1849
|
-
ORDER BY ${
|
|
1968
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1850
1969
|
}
|
|
1851
1970
|
}
|
|
1852
1971
|
} else {
|
|
@@ -1856,28 +1975,31 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1856
1975
|
let limit = '';
|
|
1857
1976
|
if ('limit' in options) {
|
|
1858
1977
|
limit = ` LIMIT ${Math.floor(
|
|
1859
|
-
isNaN(Number(options.limit)) ? 0 : Number(options.limit)
|
|
1978
|
+
isNaN(Number(options.limit)) ? 0 : Number(options.limit),
|
|
1860
1979
|
)}`;
|
|
1861
1980
|
}
|
|
1862
1981
|
let offset = '';
|
|
1863
1982
|
if ('offset' in options) {
|
|
1864
1983
|
offset = ` OFFSET ${Math.floor(
|
|
1865
|
-
isNaN(Number(options.offset)) ? 0 : Number(options.offset)
|
|
1984
|
+
isNaN(Number(options.offset)) ? 0 : Number(options.offset),
|
|
1866
1985
|
)}`;
|
|
1867
1986
|
}
|
|
1987
|
+
const guidClause = guidSelector
|
|
1988
|
+
? ` WHERE ${ieTable}."guid"=${guidSelector}`
|
|
1989
|
+
: '';
|
|
1868
1990
|
if (options.return === 'count') {
|
|
1869
1991
|
if (limit || offset) {
|
|
1870
1992
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1871
|
-
SELECT
|
|
1993
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1872
1994
|
FROM ${PostgreSQLDriver.escape(
|
|
1873
|
-
`${this.prefix}entities_${etype}
|
|
1874
|
-
)} ${ieTable}${limit}${offset}
|
|
1995
|
+
`${this.prefix}entities_${etype}`,
|
|
1996
|
+
)} ${ieTable}${guidClause}${limit}${offset}
|
|
1875
1997
|
) ${countTable}`;
|
|
1876
1998
|
} else {
|
|
1877
1999
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1878
2000
|
FROM ${PostgreSQLDriver.escape(
|
|
1879
|
-
`${this.prefix}entities_${etype}
|
|
1880
|
-
)} ${ieTable}`;
|
|
2001
|
+
`${this.prefix}entities_${etype}`,
|
|
2002
|
+
)} ${ieTable}${guidClause}`;
|
|
1881
2003
|
}
|
|
1882
2004
|
} else if (options.return === 'guid') {
|
|
1883
2005
|
const guidColumn =
|
|
@@ -1886,9 +2008,11 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1886
2008
|
: `${ieTable}."guid"`;
|
|
1887
2009
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1888
2010
|
FROM ${PostgreSQLDriver.escape(
|
|
1889
|
-
`${this.prefix}entities_${etype}
|
|
2011
|
+
`${this.prefix}entities_${etype}`,
|
|
1890
2012
|
)} ${ieTable}
|
|
1891
|
-
|
|
2013
|
+
${sortJoinInner}
|
|
2014
|
+
${guidClause}
|
|
2015
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1892
2016
|
} else {
|
|
1893
2017
|
if (limit || offset) {
|
|
1894
2018
|
query = `SELECT
|
|
@@ -1898,25 +2022,26 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1898
2022
|
${eTable}."mdate",
|
|
1899
2023
|
${dTable}."name",
|
|
1900
2024
|
${dTable}."value",
|
|
1901
|
-
${
|
|
1902
|
-
${
|
|
2025
|
+
${dTable}."json",
|
|
2026
|
+
${dTable}."string",
|
|
2027
|
+
${dTable}."number"
|
|
1903
2028
|
FROM ${PostgreSQLDriver.escape(
|
|
1904
|
-
`${this.prefix}entities_${etype}
|
|
2029
|
+
`${this.prefix}entities_${etype}`,
|
|
1905
2030
|
)} ${eTable}
|
|
1906
2031
|
LEFT JOIN ${PostgreSQLDriver.escape(
|
|
1907
|
-
`${this.prefix}data_${etype}
|
|
2032
|
+
`${this.prefix}data_${etype}`,
|
|
1908
2033
|
)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1909
|
-
|
|
1910
|
-
`${this.prefix}comparisons_${etype}`
|
|
1911
|
-
)} ${cTable} ON ${dTable}."guid"=${cTable}."guid" AND ${dTable}."name"=${cTable}."name"
|
|
2034
|
+
${sortJoin}
|
|
1912
2035
|
INNER JOIN (
|
|
1913
2036
|
SELECT ${ieTable}."guid"
|
|
1914
2037
|
FROM ${PostgreSQLDriver.escape(
|
|
1915
|
-
`${this.prefix}entities_${etype}
|
|
2038
|
+
`${this.prefix}entities_${etype}`,
|
|
1916
2039
|
)} ${ieTable}
|
|
1917
|
-
|
|
2040
|
+
${sortJoinInner}
|
|
2041
|
+
${guidClause}
|
|
2042
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1918
2043
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1919
|
-
ORDER BY ${
|
|
2044
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1920
2045
|
} else {
|
|
1921
2046
|
query = `SELECT
|
|
1922
2047
|
encode(${eTable}."guid", 'hex') AS "guid",
|
|
@@ -1925,18 +2050,18 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1925
2050
|
${eTable}."mdate",
|
|
1926
2051
|
${dTable}."name",
|
|
1927
2052
|
${dTable}."value",
|
|
1928
|
-
${
|
|
1929
|
-
${
|
|
2053
|
+
${dTable}."json",
|
|
2054
|
+
${dTable}."string",
|
|
2055
|
+
${dTable}."number"
|
|
1930
2056
|
FROM ${PostgreSQLDriver.escape(
|
|
1931
|
-
`${this.prefix}entities_${etype}
|
|
2057
|
+
`${this.prefix}entities_${etype}`,
|
|
1932
2058
|
)} ${eTable}
|
|
1933
2059
|
LEFT JOIN ${PostgreSQLDriver.escape(
|
|
1934
|
-
`${this.prefix}data_${etype}
|
|
2060
|
+
`${this.prefix}data_${etype}`,
|
|
1935
2061
|
)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
ORDER BY ${eTable}.${sortBy}`;
|
|
2062
|
+
${sortJoin}
|
|
2063
|
+
${guidSelector ? `WHERE ${eTable}."guid"=${guidSelector}` : ''}
|
|
2064
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1940
2065
|
}
|
|
1941
2066
|
}
|
|
1942
2067
|
}
|
|
@@ -1956,43 +2081,23 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
1956
2081
|
protected performQuery(
|
|
1957
2082
|
options: Options,
|
|
1958
2083
|
formattedSelectors: FormattedSelector[],
|
|
1959
|
-
etype: string
|
|
2084
|
+
etype: string,
|
|
1960
2085
|
): {
|
|
1961
2086
|
result: any;
|
|
1962
2087
|
} {
|
|
1963
2088
|
const { query, params, etypes } = this.makeEntityQuery(
|
|
1964
2089
|
options,
|
|
1965
2090
|
formattedSelectors,
|
|
1966
|
-
etype
|
|
2091
|
+
etype,
|
|
1967
2092
|
);
|
|
1968
|
-
const result = this.
|
|
1969
|
-
val[Symbol.iterator]()
|
|
2093
|
+
const result = this.queryArray(query, { etypes, params }).then((val) =>
|
|
2094
|
+
val[Symbol.iterator](),
|
|
1970
2095
|
);
|
|
1971
2096
|
return {
|
|
1972
2097
|
result,
|
|
1973
2098
|
};
|
|
1974
2099
|
}
|
|
1975
2100
|
|
|
1976
|
-
protected performQuerySync(
|
|
1977
|
-
options: Options,
|
|
1978
|
-
formattedSelectors: FormattedSelector[],
|
|
1979
|
-
etype: string
|
|
1980
|
-
): {
|
|
1981
|
-
result: any;
|
|
1982
|
-
} {
|
|
1983
|
-
const { query, params, etypes } = this.makeEntityQuery(
|
|
1984
|
-
options,
|
|
1985
|
-
formattedSelectors,
|
|
1986
|
-
etype
|
|
1987
|
-
);
|
|
1988
|
-
const result = (this.queryIterSync(query, { etypes, params }) || [])[
|
|
1989
|
-
Symbol.iterator
|
|
1990
|
-
]();
|
|
1991
|
-
return {
|
|
1992
|
-
result,
|
|
1993
|
-
};
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
2101
|
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
1997
2102
|
options: Options<T> & { return: 'count' },
|
|
1998
2103
|
...selectors: Selector[]
|
|
@@ -2004,17 +2109,17 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2004
2109
|
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
2005
2110
|
options?: Options<T>,
|
|
2006
2111
|
...selectors: Selector[]
|
|
2007
|
-
): Promise<
|
|
2112
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
2008
2113
|
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
2009
2114
|
options: Options<T> = {},
|
|
2010
2115
|
...selectors: Selector[]
|
|
2011
|
-
): Promise<
|
|
2012
|
-
const { result: resultPromise, process } = this.
|
|
2116
|
+
): Promise<EntityInstanceType<T>[] | string[] | number> {
|
|
2117
|
+
const { result: resultPromise, process } = this.getEntitiesRowLike<T>(
|
|
2013
2118
|
// @ts-ignore: options is correct here.
|
|
2014
2119
|
options,
|
|
2015
2120
|
selectors,
|
|
2016
|
-
(options,
|
|
2017
|
-
this.performQuery(options,
|
|
2121
|
+
({ options, selectors, etype }) =>
|
|
2122
|
+
this.performQuery(options, selectors, etype),
|
|
2018
2123
|
() => {
|
|
2019
2124
|
const next: any = result.next();
|
|
2020
2125
|
return next.done ? null : next.value;
|
|
@@ -2023,7 +2128,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2023
2128
|
(row) => Number(row.count),
|
|
2024
2129
|
(row) => row.guid,
|
|
2025
2130
|
(row) => ({
|
|
2026
|
-
tags: row.tags,
|
|
2131
|
+
tags: row.tags.filter((tag: string) => tag),
|
|
2027
2132
|
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
2028
2133
|
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
2029
2134
|
}),
|
|
@@ -2033,9 +2138,13 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2033
2138
|
row.value === 'N'
|
|
2034
2139
|
? JSON.stringify(Number(row.number))
|
|
2035
2140
|
: row.value === 'S'
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2141
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(row.string))
|
|
2142
|
+
: row.value === 'J'
|
|
2143
|
+
? PostgreSQLDriver.unescapeNullSequences(
|
|
2144
|
+
JSON.stringify(row.json),
|
|
2145
|
+
)
|
|
2146
|
+
: row.value,
|
|
2147
|
+
}),
|
|
2039
2148
|
);
|
|
2040
2149
|
|
|
2041
2150
|
const result = await resultPromise;
|
|
@@ -2046,252 +2155,228 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2046
2155
|
return value;
|
|
2047
2156
|
}
|
|
2048
2157
|
|
|
2049
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
2050
|
-
options: Options<T> & { return: 'count' },
|
|
2051
|
-
...selectors: Selector[]
|
|
2052
|
-
): number;
|
|
2053
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
2054
|
-
options: Options<T> & { return: 'guid' },
|
|
2055
|
-
...selectors: Selector[]
|
|
2056
|
-
): string[];
|
|
2057
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
2058
|
-
options?: Options<T>,
|
|
2059
|
-
...selectors: Selector[]
|
|
2060
|
-
): ReturnType<T['factorySync']>[];
|
|
2061
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
2062
|
-
options: Options<T> = {},
|
|
2063
|
-
...selectors: Selector[]
|
|
2064
|
-
): ReturnType<T['factorySync']>[] | string[] | number {
|
|
2065
|
-
const { result, process } = this.getEntitesRowLike<T>(
|
|
2066
|
-
// @ts-ignore: options is correct here.
|
|
2067
|
-
options,
|
|
2068
|
-
selectors,
|
|
2069
|
-
(options, formattedSelectors, etype) =>
|
|
2070
|
-
this.performQuerySync(options, formattedSelectors, etype),
|
|
2071
|
-
() => {
|
|
2072
|
-
const next: any = result.next();
|
|
2073
|
-
return next.done ? null : next.value;
|
|
2074
|
-
},
|
|
2075
|
-
() => undefined,
|
|
2076
|
-
(row) => Number(row.count),
|
|
2077
|
-
(row) => row.guid,
|
|
2078
|
-
(row) => ({
|
|
2079
|
-
tags: row.tags,
|
|
2080
|
-
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
2081
|
-
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
2082
|
-
}),
|
|
2083
|
-
(row) => ({
|
|
2084
|
-
name: row.name,
|
|
2085
|
-
svalue:
|
|
2086
|
-
row.value === 'N'
|
|
2087
|
-
? JSON.stringify(Number(row.number))
|
|
2088
|
-
: row.value === 'S'
|
|
2089
|
-
? JSON.stringify(row.string)
|
|
2090
|
-
: row.value,
|
|
2091
|
-
})
|
|
2092
|
-
);
|
|
2093
|
-
const value = process();
|
|
2094
|
-
if (value instanceof Error) {
|
|
2095
|
-
throw value;
|
|
2096
|
-
}
|
|
2097
|
-
return value;
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
2158
|
public async getUID(name: string) {
|
|
2101
2159
|
if (name == null) {
|
|
2102
2160
|
throw new InvalidParametersError('Name not given for UID.');
|
|
2103
2161
|
}
|
|
2104
2162
|
const result = await this.queryGet(
|
|
2105
2163
|
`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(
|
|
2106
|
-
`${this.prefix}uids
|
|
2164
|
+
`${this.prefix}uids`,
|
|
2107
2165
|
)} WHERE "name"=@name;`,
|
|
2108
2166
|
{
|
|
2109
2167
|
params: {
|
|
2110
2168
|
name: name,
|
|
2111
2169
|
},
|
|
2112
|
-
}
|
|
2170
|
+
},
|
|
2113
2171
|
);
|
|
2114
2172
|
return result?.cur_uid == null ? null : Number(result.cur_uid);
|
|
2115
2173
|
}
|
|
2116
2174
|
|
|
2117
|
-
public async
|
|
2175
|
+
public async importEntity({
|
|
2176
|
+
guid,
|
|
2177
|
+
cdate,
|
|
2178
|
+
mdate,
|
|
2179
|
+
tags,
|
|
2180
|
+
sdata,
|
|
2181
|
+
etype,
|
|
2182
|
+
}: {
|
|
2183
|
+
guid: string;
|
|
2184
|
+
cdate: number;
|
|
2185
|
+
mdate: number;
|
|
2186
|
+
tags: string[];
|
|
2187
|
+
sdata: SerializedEntityData;
|
|
2188
|
+
etype: string;
|
|
2189
|
+
}) {
|
|
2118
2190
|
try {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2191
|
+
let promises = [];
|
|
2192
|
+
promises.push(
|
|
2193
|
+
this.queryRun(
|
|
2194
|
+
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2195
|
+
`${this.prefix}entities_${etype}`,
|
|
2196
|
+
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2197
|
+
{
|
|
2198
|
+
etypes: [etype],
|
|
2199
|
+
params: {
|
|
2200
|
+
guid,
|
|
2201
|
+
},
|
|
2202
|
+
},
|
|
2203
|
+
),
|
|
2204
|
+
);
|
|
2205
|
+
promises.push(
|
|
2206
|
+
this.queryRun(
|
|
2207
|
+
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2208
|
+
`${this.prefix}data_${etype}`,
|
|
2209
|
+
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2210
|
+
{
|
|
2211
|
+
etypes: [etype],
|
|
2212
|
+
params: {
|
|
2213
|
+
guid,
|
|
2214
|
+
},
|
|
2215
|
+
},
|
|
2216
|
+
),
|
|
2217
|
+
);
|
|
2218
|
+
promises.push(
|
|
2219
|
+
this.queryRun(
|
|
2220
|
+
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2221
|
+
`${this.prefix}references_${etype}`,
|
|
2222
|
+
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2223
|
+
{
|
|
2224
|
+
etypes: [etype],
|
|
2225
|
+
params: {
|
|
2226
|
+
guid,
|
|
2227
|
+
},
|
|
2228
|
+
},
|
|
2229
|
+
),
|
|
2230
|
+
);
|
|
2231
|
+
promises.push(
|
|
2232
|
+
this.queryRun(
|
|
2233
|
+
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2234
|
+
`${this.prefix}uniques_${etype}`,
|
|
2235
|
+
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2236
|
+
{
|
|
2237
|
+
etypes: [etype],
|
|
2238
|
+
params: {
|
|
2239
|
+
guid,
|
|
2240
|
+
},
|
|
2241
|
+
},
|
|
2242
|
+
),
|
|
2243
|
+
);
|
|
2244
|
+
await Promise.all(promises);
|
|
2245
|
+
promises = [];
|
|
2246
|
+
await this.queryRun(
|
|
2247
|
+
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2248
|
+
`${this.prefix}entities_${etype}`,
|
|
2249
|
+
)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`,
|
|
2250
|
+
{
|
|
2251
|
+
etypes: [etype],
|
|
2252
|
+
params: {
|
|
2253
|
+
guid,
|
|
2254
|
+
tags,
|
|
2255
|
+
cdate: isNaN(cdate) ? null : cdate,
|
|
2256
|
+
mdate: isNaN(mdate) ? null : mdate,
|
|
2257
|
+
},
|
|
2258
|
+
},
|
|
2259
|
+
);
|
|
2260
|
+
for (const name in sdata) {
|
|
2261
|
+
const value = sdata[name];
|
|
2262
|
+
const uvalue = JSON.parse(value);
|
|
2263
|
+
if (value === undefined) {
|
|
2264
|
+
continue;
|
|
2265
|
+
}
|
|
2266
|
+
const storageValue =
|
|
2267
|
+
typeof uvalue === 'number'
|
|
2268
|
+
? 'N'
|
|
2269
|
+
: typeof uvalue === 'string'
|
|
2270
|
+
? 'S'
|
|
2271
|
+
: 'J';
|
|
2272
|
+
const jsonValue =
|
|
2273
|
+
storageValue === 'J'
|
|
2274
|
+
? PostgreSQLDriver.escapeNullSequences(value)
|
|
2275
|
+
: null;
|
|
2276
|
+
promises.push(
|
|
2277
|
+
this.queryRun(
|
|
2134
2278
|
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2135
|
-
`${this.prefix}
|
|
2136
|
-
)} ("guid", "
|
|
2279
|
+
`${this.prefix}data_${etype}`,
|
|
2280
|
+
)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`,
|
|
2137
2281
|
{
|
|
2138
2282
|
etypes: [etype],
|
|
2139
2283
|
params: {
|
|
2140
2284
|
guid,
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2285
|
+
name,
|
|
2286
|
+
storageValue,
|
|
2287
|
+
jsonValue,
|
|
2288
|
+
string:
|
|
2289
|
+
storageValue === 'J'
|
|
2290
|
+
? null
|
|
2291
|
+
: PostgreSQLDriver.escapeNulls(`${uvalue}`),
|
|
2292
|
+
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
2293
|
+
truthy: !!uvalue,
|
|
2148
2294
|
},
|
|
2149
|
-
}
|
|
2150
|
-
)
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2155
|
-
`${this.prefix}data_${etype}`
|
|
2156
|
-
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2157
|
-
{
|
|
2158
|
-
etypes: [etype],
|
|
2159
|
-
params: {
|
|
2160
|
-
guid,
|
|
2161
|
-
},
|
|
2162
|
-
}
|
|
2163
|
-
)
|
|
2164
|
-
);
|
|
2165
|
-
promises.push(
|
|
2166
|
-
this.queryRun(
|
|
2167
|
-
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2168
|
-
`${this.prefix}comparisons_${etype}`
|
|
2169
|
-
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2170
|
-
{
|
|
2171
|
-
etypes: [etype],
|
|
2172
|
-
params: {
|
|
2173
|
-
guid,
|
|
2174
|
-
},
|
|
2175
|
-
}
|
|
2176
|
-
)
|
|
2177
|
-
);
|
|
2295
|
+
},
|
|
2296
|
+
),
|
|
2297
|
+
);
|
|
2298
|
+
const references = this.findReferences(value);
|
|
2299
|
+
for (const reference of references) {
|
|
2178
2300
|
promises.push(
|
|
2179
2301
|
this.queryRun(
|
|
2180
|
-
`
|
|
2181
|
-
`${this.prefix}references_${etype}
|
|
2182
|
-
)}
|
|
2302
|
+
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2303
|
+
`${this.prefix}references_${etype}`,
|
|
2304
|
+
)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`,
|
|
2183
2305
|
{
|
|
2184
2306
|
etypes: [etype],
|
|
2185
2307
|
params: {
|
|
2186
2308
|
guid,
|
|
2309
|
+
name,
|
|
2310
|
+
reference,
|
|
2187
2311
|
},
|
|
2188
|
-
}
|
|
2189
|
-
)
|
|
2190
|
-
);
|
|
2191
|
-
await Promise.all(promises);
|
|
2192
|
-
delete sdata.cdate;
|
|
2193
|
-
delete sdata.mdate;
|
|
2194
|
-
for (const name in sdata) {
|
|
2195
|
-
const value = sdata[name];
|
|
2196
|
-
const uvalue = JSON.parse(value);
|
|
2197
|
-
if (value === undefined) {
|
|
2198
|
-
continue;
|
|
2199
|
-
}
|
|
2200
|
-
const storageValue =
|
|
2201
|
-
typeof uvalue === 'number'
|
|
2202
|
-
? 'N'
|
|
2203
|
-
: typeof uvalue === 'string'
|
|
2204
|
-
? 'S'
|
|
2205
|
-
: value;
|
|
2206
|
-
const promises = [];
|
|
2207
|
-
promises.push(
|
|
2208
|
-
this.queryRun(
|
|
2209
|
-
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2210
|
-
`${this.prefix}data_${etype}`
|
|
2211
|
-
)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`,
|
|
2212
|
-
{
|
|
2213
|
-
etypes: [etype],
|
|
2214
|
-
params: {
|
|
2215
|
-
guid,
|
|
2216
|
-
name,
|
|
2217
|
-
storageValue,
|
|
2218
|
-
},
|
|
2219
|
-
}
|
|
2220
|
-
)
|
|
2221
|
-
);
|
|
2222
|
-
promises.push(
|
|
2223
|
-
this.queryRun(
|
|
2224
|
-
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2225
|
-
`${this.prefix}comparisons_${etype}`
|
|
2226
|
-
)} ("guid", "name", "truthy", "string", "number") VALUES (decode(@guid, 'hex'), @name, @truthy, @string, @number);`,
|
|
2227
|
-
{
|
|
2228
|
-
etypes: [etype],
|
|
2229
|
-
params: {
|
|
2230
|
-
guid,
|
|
2231
|
-
name,
|
|
2232
|
-
truthy: !!uvalue,
|
|
2233
|
-
string: `${uvalue}`,
|
|
2234
|
-
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
2235
|
-
},
|
|
2236
|
-
}
|
|
2237
|
-
)
|
|
2238
|
-
);
|
|
2239
|
-
const references = this.findReferences(value);
|
|
2240
|
-
for (const reference of references) {
|
|
2241
|
-
promises.push(
|
|
2242
|
-
this.queryRun(
|
|
2243
|
-
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2244
|
-
`${this.prefix}references_${etype}`
|
|
2245
|
-
)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`,
|
|
2246
|
-
{
|
|
2247
|
-
etypes: [etype],
|
|
2248
|
-
params: {
|
|
2249
|
-
guid,
|
|
2250
|
-
name,
|
|
2251
|
-
reference,
|
|
2252
|
-
},
|
|
2253
|
-
}
|
|
2254
|
-
)
|
|
2255
|
-
);
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
await Promise.all(promises);
|
|
2259
|
-
},
|
|
2260
|
-
async (name, curUid) => {
|
|
2261
|
-
await this.queryRun(
|
|
2262
|
-
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2263
|
-
`${this.prefix}uids`
|
|
2264
|
-
)} WHERE "name"=@name;`,
|
|
2265
|
-
{
|
|
2266
|
-
params: {
|
|
2267
|
-
name,
|
|
2268
2312
|
},
|
|
2269
|
-
|
|
2313
|
+
),
|
|
2270
2314
|
);
|
|
2271
|
-
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
const uniques = await this.nymph
|
|
2318
|
+
.getEntityClassByEtype(etype)
|
|
2319
|
+
.getUniques({ guid, cdate, mdate, tags, data: {}, sdata });
|
|
2320
|
+
for (const unique of uniques) {
|
|
2321
|
+
promises.push(
|
|
2322
|
+
this.queryRun(
|
|
2272
2323
|
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2273
|
-
`${this.prefix}
|
|
2274
|
-
)} ("
|
|
2324
|
+
`${this.prefix}uniques_${etype}`,
|
|
2325
|
+
)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`,
|
|
2275
2326
|
{
|
|
2327
|
+
etypes: [etype],
|
|
2276
2328
|
params: {
|
|
2277
|
-
|
|
2278
|
-
|
|
2329
|
+
guid,
|
|
2330
|
+
unique,
|
|
2279
2331
|
},
|
|
2332
|
+
},
|
|
2333
|
+
).catch((e: any) => {
|
|
2334
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2335
|
+
this.nymph.config.debugError(
|
|
2336
|
+
'postgresql',
|
|
2337
|
+
`Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
|
|
2338
|
+
);
|
|
2280
2339
|
}
|
|
2281
|
-
|
|
2340
|
+
return e;
|
|
2341
|
+
}),
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
await Promise.all(promises);
|
|
2345
|
+
} catch (e: any) {
|
|
2346
|
+
this.nymph.config.debugError('postgresql', `Import entity error: "${e}"`);
|
|
2347
|
+
throw e;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
public async importUID({ name, value }: { name: string; value: number }) {
|
|
2352
|
+
try {
|
|
2353
|
+
await this.internalTransaction(`nymph-import-uid-${name}`);
|
|
2354
|
+
await this.queryRun(
|
|
2355
|
+
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2356
|
+
`${this.prefix}uids`,
|
|
2357
|
+
)} WHERE "name"=@name;`,
|
|
2358
|
+
{
|
|
2359
|
+
params: {
|
|
2360
|
+
name,
|
|
2361
|
+
},
|
|
2282
2362
|
},
|
|
2283
|
-
|
|
2284
|
-
|
|
2363
|
+
);
|
|
2364
|
+
await this.queryRun(
|
|
2365
|
+
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2366
|
+
`${this.prefix}uids`,
|
|
2367
|
+
)} ("name", "cur_uid") VALUES (@name, @value);`,
|
|
2368
|
+
{
|
|
2369
|
+
params: {
|
|
2370
|
+
name,
|
|
2371
|
+
value,
|
|
2372
|
+
},
|
|
2285
2373
|
},
|
|
2286
|
-
async () => {
|
|
2287
|
-
await this.commit('nymph-import');
|
|
2288
|
-
}
|
|
2289
2374
|
);
|
|
2290
|
-
|
|
2291
|
-
return result;
|
|
2375
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
2292
2376
|
} catch (e: any) {
|
|
2293
|
-
|
|
2294
|
-
|
|
2377
|
+
this.nymph.config.debugError('postgresql', `Import UID error: "${e}"`);
|
|
2378
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
2379
|
+
throw e;
|
|
2295
2380
|
}
|
|
2296
2381
|
}
|
|
2297
2382
|
|
|
@@ -2300,52 +2385,54 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2300
2385
|
throw new InvalidParametersError('Name not given for UID.');
|
|
2301
2386
|
}
|
|
2302
2387
|
await this.internalTransaction('nymph-newuid');
|
|
2388
|
+
let curUid: number | undefined = undefined;
|
|
2303
2389
|
try {
|
|
2304
2390
|
const lock = await this.queryGet(
|
|
2305
2391
|
`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(
|
|
2306
|
-
`${this.prefix}uids
|
|
2392
|
+
`${this.prefix}uids`,
|
|
2307
2393
|
)} WHERE "name"=@name FOR UPDATE;`,
|
|
2308
2394
|
{
|
|
2309
2395
|
params: {
|
|
2310
2396
|
name,
|
|
2311
2397
|
},
|
|
2312
|
-
}
|
|
2398
|
+
},
|
|
2313
2399
|
);
|
|
2314
|
-
|
|
2315
|
-
lock?.cur_uid == null ? undefined : Number(lock.cur_uid);
|
|
2400
|
+
curUid = lock?.cur_uid == null ? undefined : Number(lock.cur_uid);
|
|
2316
2401
|
if (curUid == null) {
|
|
2317
2402
|
curUid = 1;
|
|
2318
2403
|
await this.queryRun(
|
|
2319
2404
|
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2320
|
-
`${this.prefix}uids
|
|
2405
|
+
`${this.prefix}uids`,
|
|
2321
2406
|
)} ("name", "cur_uid") VALUES (@name, @curUid);`,
|
|
2322
2407
|
{
|
|
2323
2408
|
params: {
|
|
2324
2409
|
name,
|
|
2325
2410
|
curUid,
|
|
2326
2411
|
},
|
|
2327
|
-
}
|
|
2412
|
+
},
|
|
2328
2413
|
);
|
|
2329
2414
|
} else {
|
|
2330
2415
|
curUid++;
|
|
2331
2416
|
await this.queryRun(
|
|
2332
2417
|
`UPDATE ${PostgreSQLDriver.escape(
|
|
2333
|
-
`${this.prefix}uids
|
|
2418
|
+
`${this.prefix}uids`,
|
|
2334
2419
|
)} SET "cur_uid"=@curUid WHERE "name"=@name;`,
|
|
2335
2420
|
{
|
|
2336
2421
|
params: {
|
|
2337
2422
|
name,
|
|
2338
2423
|
curUid,
|
|
2339
2424
|
},
|
|
2340
|
-
}
|
|
2425
|
+
},
|
|
2341
2426
|
);
|
|
2342
2427
|
}
|
|
2343
|
-
await this.commit('nymph-newuid');
|
|
2344
|
-
return curUid;
|
|
2345
2428
|
} catch (e: any) {
|
|
2429
|
+
this.nymph.config.debugError('postgresql', `New UID error: "${e}"`);
|
|
2346
2430
|
await this.rollback('nymph-newuid');
|
|
2347
2431
|
throw e;
|
|
2348
2432
|
}
|
|
2433
|
+
|
|
2434
|
+
await this.commit('nymph-newuid');
|
|
2435
|
+
return curUid;
|
|
2349
2436
|
}
|
|
2350
2437
|
|
|
2351
2438
|
public async renameUID(oldName: string, newName: string) {
|
|
@@ -2354,14 +2441,14 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2354
2441
|
}
|
|
2355
2442
|
await this.queryRun(
|
|
2356
2443
|
`UPDATE ${PostgreSQLDriver.escape(
|
|
2357
|
-
`${this.prefix}uids
|
|
2444
|
+
`${this.prefix}uids`,
|
|
2358
2445
|
)} SET "name"=@newName WHERE "name"=@oldName;`,
|
|
2359
2446
|
{
|
|
2360
2447
|
params: {
|
|
2361
2448
|
newName,
|
|
2362
2449
|
oldName,
|
|
2363
2450
|
},
|
|
2364
|
-
}
|
|
2451
|
+
},
|
|
2365
2452
|
);
|
|
2366
2453
|
return true;
|
|
2367
2454
|
}
|
|
@@ -2369,7 +2456,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2369
2456
|
public async rollback(name: string) {
|
|
2370
2457
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
2371
2458
|
throw new InvalidParametersError(
|
|
2372
|
-
'Transaction rollback attempted without a name.'
|
|
2459
|
+
'Transaction rollback attempted without a name.',
|
|
2373
2460
|
);
|
|
2374
2461
|
}
|
|
2375
2462
|
if (!this.transaction || this.transaction.count === 0) {
|
|
@@ -2377,7 +2464,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2377
2464
|
return true;
|
|
2378
2465
|
}
|
|
2379
2466
|
await this.queryRun(
|
|
2380
|
-
`ROLLBACK TO SAVEPOINT ${PostgreSQLDriver.escape(name)}
|
|
2467
|
+
`ROLLBACK TO SAVEPOINT ${PostgreSQLDriver.escape(name)};`,
|
|
2381
2468
|
);
|
|
2382
2469
|
this.transaction.count--;
|
|
2383
2470
|
if (this.transaction.count === 0) {
|
|
@@ -2394,12 +2481,13 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2394
2481
|
guid: string,
|
|
2395
2482
|
data: EntityData,
|
|
2396
2483
|
sdata: SerializedEntityData,
|
|
2397
|
-
|
|
2484
|
+
uniques: string[],
|
|
2485
|
+
etype: string,
|
|
2398
2486
|
) => {
|
|
2399
2487
|
const runInsertQuery = async (
|
|
2400
2488
|
name: string,
|
|
2401
2489
|
value: any,
|
|
2402
|
-
svalue: string
|
|
2490
|
+
svalue: string,
|
|
2403
2491
|
) => {
|
|
2404
2492
|
if (value === undefined) {
|
|
2405
2493
|
return;
|
|
@@ -2408,47 +2496,41 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2408
2496
|
typeof value === 'number'
|
|
2409
2497
|
? 'N'
|
|
2410
2498
|
: typeof value === 'string'
|
|
2411
|
-
|
|
2412
|
-
|
|
2499
|
+
? 'S'
|
|
2500
|
+
: 'J';
|
|
2501
|
+
const jsonValue =
|
|
2502
|
+
storageValue === 'J'
|
|
2503
|
+
? PostgreSQLDriver.escapeNullSequences(svalue)
|
|
2504
|
+
: null;
|
|
2413
2505
|
const promises = [];
|
|
2414
2506
|
promises.push(
|
|
2415
2507
|
this.queryRun(
|
|
2416
2508
|
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2417
|
-
`${this.prefix}data_${etype}
|
|
2418
|
-
)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`,
|
|
2509
|
+
`${this.prefix}data_${etype}`,
|
|
2510
|
+
)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`,
|
|
2419
2511
|
{
|
|
2420
2512
|
etypes: [etype],
|
|
2421
2513
|
params: {
|
|
2422
2514
|
guid,
|
|
2423
2515
|
name,
|
|
2424
2516
|
storageValue,
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
this.queryRun(
|
|
2431
|
-
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2432
|
-
`${this.prefix}comparisons_${etype}`
|
|
2433
|
-
)} ("guid", "name", "truthy", "string", "number") VALUES (decode(@guid, 'hex'), @name, @truthy, @string, @number);`,
|
|
2434
|
-
{
|
|
2435
|
-
etypes: [etype],
|
|
2436
|
-
params: {
|
|
2437
|
-
guid,
|
|
2438
|
-
name,
|
|
2439
|
-
truthy: !!value,
|
|
2440
|
-
string: `${value}`,
|
|
2517
|
+
jsonValue,
|
|
2518
|
+
string:
|
|
2519
|
+
storageValue === 'J'
|
|
2520
|
+
? null
|
|
2521
|
+
: PostgreSQLDriver.escapeNulls(`${value}`),
|
|
2441
2522
|
number: isNaN(Number(value)) ? null : Number(value),
|
|
2523
|
+
truthy: !!value,
|
|
2442
2524
|
},
|
|
2443
|
-
}
|
|
2444
|
-
)
|
|
2525
|
+
},
|
|
2526
|
+
),
|
|
2445
2527
|
);
|
|
2446
2528
|
const references = this.findReferences(svalue);
|
|
2447
2529
|
for (const reference of references) {
|
|
2448
2530
|
promises.push(
|
|
2449
2531
|
this.queryRun(
|
|
2450
2532
|
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2451
|
-
`${this.prefix}references_${etype}
|
|
2533
|
+
`${this.prefix}references_${etype}`,
|
|
2452
2534
|
)} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`,
|
|
2453
2535
|
{
|
|
2454
2536
|
etypes: [etype],
|
|
@@ -2457,12 +2539,36 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2457
2539
|
name,
|
|
2458
2540
|
reference,
|
|
2459
2541
|
},
|
|
2460
|
-
}
|
|
2461
|
-
)
|
|
2542
|
+
},
|
|
2543
|
+
),
|
|
2462
2544
|
);
|
|
2463
2545
|
}
|
|
2464
2546
|
await Promise.all(promises);
|
|
2465
2547
|
};
|
|
2548
|
+
for (const unique of uniques) {
|
|
2549
|
+
try {
|
|
2550
|
+
await this.queryRun(
|
|
2551
|
+
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2552
|
+
`${this.prefix}uniques_${etype}`,
|
|
2553
|
+
)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`,
|
|
2554
|
+
{
|
|
2555
|
+
etypes: [etype],
|
|
2556
|
+
params: {
|
|
2557
|
+
guid,
|
|
2558
|
+
unique,
|
|
2559
|
+
},
|
|
2560
|
+
},
|
|
2561
|
+
);
|
|
2562
|
+
} catch (e: any) {
|
|
2563
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2564
|
+
this.nymph.config.debugError(
|
|
2565
|
+
'postgresql',
|
|
2566
|
+
`Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
throw e;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2466
2572
|
for (const name in data) {
|
|
2467
2573
|
await runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
2468
2574
|
}
|
|
@@ -2470,13 +2576,20 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2470
2576
|
await runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
2471
2577
|
}
|
|
2472
2578
|
};
|
|
2579
|
+
let inTransaction = false;
|
|
2473
2580
|
try {
|
|
2474
2581
|
const result = await this.saveEntityRowLike(
|
|
2475
2582
|
entity,
|
|
2476
|
-
async (
|
|
2583
|
+
async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
2584
|
+
if (
|
|
2585
|
+
Object.keys(data).length === 0 &&
|
|
2586
|
+
Object.keys(sdata).length === 0
|
|
2587
|
+
) {
|
|
2588
|
+
return false;
|
|
2589
|
+
}
|
|
2477
2590
|
await this.queryRun(
|
|
2478
2591
|
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2479
|
-
`${this.prefix}entities_${etype}
|
|
2592
|
+
`${this.prefix}entities_${etype}`,
|
|
2480
2593
|
)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @cdate);`,
|
|
2481
2594
|
{
|
|
2482
2595
|
etypes: [etype],
|
|
@@ -2485,69 +2598,75 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2485
2598
|
tags,
|
|
2486
2599
|
cdate,
|
|
2487
2600
|
},
|
|
2488
|
-
}
|
|
2601
|
+
},
|
|
2489
2602
|
);
|
|
2490
|
-
await insertData(guid, data, sdata, etype);
|
|
2603
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
2491
2604
|
return true;
|
|
2492
2605
|
},
|
|
2493
|
-
async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
2606
|
+
async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
2607
|
+
if (
|
|
2608
|
+
Object.keys(data).length === 0 &&
|
|
2609
|
+
Object.keys(sdata).length === 0
|
|
2610
|
+
) {
|
|
2611
|
+
return false;
|
|
2612
|
+
}
|
|
2494
2613
|
const promises = [];
|
|
2495
2614
|
promises.push(
|
|
2496
2615
|
this.queryRun(
|
|
2497
2616
|
`SELECT 1 FROM ${PostgreSQLDriver.escape(
|
|
2498
|
-
`${this.prefix}entities_${etype}
|
|
2617
|
+
`${this.prefix}entities_${etype}`,
|
|
2499
2618
|
)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`,
|
|
2500
2619
|
{
|
|
2501
2620
|
etypes: [etype],
|
|
2502
2621
|
params: {
|
|
2503
2622
|
guid,
|
|
2504
2623
|
},
|
|
2505
|
-
}
|
|
2506
|
-
)
|
|
2624
|
+
},
|
|
2625
|
+
),
|
|
2507
2626
|
);
|
|
2508
2627
|
promises.push(
|
|
2509
2628
|
this.queryRun(
|
|
2510
2629
|
`SELECT 1 FROM ${PostgreSQLDriver.escape(
|
|
2511
|
-
`${this.prefix}data_${etype}
|
|
2630
|
+
`${this.prefix}data_${etype}`,
|
|
2512
2631
|
)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`,
|
|
2513
2632
|
{
|
|
2514
2633
|
etypes: [etype],
|
|
2515
2634
|
params: {
|
|
2516
2635
|
guid,
|
|
2517
2636
|
},
|
|
2518
|
-
}
|
|
2519
|
-
)
|
|
2637
|
+
},
|
|
2638
|
+
),
|
|
2520
2639
|
);
|
|
2521
2640
|
promises.push(
|
|
2522
2641
|
this.queryRun(
|
|
2523
2642
|
`SELECT 1 FROM ${PostgreSQLDriver.escape(
|
|
2524
|
-
`${this.prefix}
|
|
2643
|
+
`${this.prefix}references_${etype}`,
|
|
2525
2644
|
)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`,
|
|
2526
2645
|
{
|
|
2527
2646
|
etypes: [etype],
|
|
2528
2647
|
params: {
|
|
2529
2648
|
guid,
|
|
2530
2649
|
},
|
|
2531
|
-
}
|
|
2532
|
-
)
|
|
2650
|
+
},
|
|
2651
|
+
),
|
|
2533
2652
|
);
|
|
2534
2653
|
promises.push(
|
|
2535
2654
|
this.queryRun(
|
|
2536
2655
|
`SELECT 1 FROM ${PostgreSQLDriver.escape(
|
|
2537
|
-
`${this.prefix}
|
|
2656
|
+
`${this.prefix}uniques_${etype}`,
|
|
2538
2657
|
)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`,
|
|
2539
2658
|
{
|
|
2540
2659
|
etypes: [etype],
|
|
2541
2660
|
params: {
|
|
2542
2661
|
guid,
|
|
2543
2662
|
},
|
|
2544
|
-
}
|
|
2545
|
-
)
|
|
2663
|
+
},
|
|
2664
|
+
),
|
|
2546
2665
|
);
|
|
2547
2666
|
await Promise.all(promises);
|
|
2548
2667
|
const info = await this.queryRun(
|
|
2549
2668
|
`UPDATE ${PostgreSQLDriver.escape(
|
|
2550
|
-
`${this.prefix}entities_${etype}
|
|
2669
|
+
`${this.prefix}entities_${etype}`,
|
|
2551
2670
|
)} SET "tags"=@tags, "mdate"=@mdate WHERE "guid"=decode(@guid, 'hex') AND "mdate" <= @emdate;`,
|
|
2552
2671
|
{
|
|
2553
2672
|
etypes: [etype],
|
|
@@ -2557,7 +2676,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2557
2676
|
guid,
|
|
2558
2677
|
emdate: isNaN(Number(entity.mdate)) ? 0 : Number(entity.mdate),
|
|
2559
2678
|
},
|
|
2560
|
-
}
|
|
2679
|
+
},
|
|
2561
2680
|
);
|
|
2562
2681
|
let success = false;
|
|
2563
2682
|
if (info.rowCount === 1) {
|
|
@@ -2565,64 +2684,71 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2565
2684
|
promises.push(
|
|
2566
2685
|
this.queryRun(
|
|
2567
2686
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2568
|
-
`${this.prefix}data_${etype}
|
|
2687
|
+
`${this.prefix}data_${etype}`,
|
|
2569
2688
|
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2570
2689
|
{
|
|
2571
2690
|
etypes: [etype],
|
|
2572
2691
|
params: {
|
|
2573
2692
|
guid,
|
|
2574
2693
|
},
|
|
2575
|
-
}
|
|
2576
|
-
)
|
|
2694
|
+
},
|
|
2695
|
+
),
|
|
2577
2696
|
);
|
|
2578
2697
|
promises.push(
|
|
2579
2698
|
this.queryRun(
|
|
2580
2699
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2581
|
-
`${this.prefix}
|
|
2700
|
+
`${this.prefix}references_${etype}`,
|
|
2582
2701
|
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2583
2702
|
{
|
|
2584
2703
|
etypes: [etype],
|
|
2585
2704
|
params: {
|
|
2586
2705
|
guid,
|
|
2587
2706
|
},
|
|
2588
|
-
}
|
|
2589
|
-
)
|
|
2707
|
+
},
|
|
2708
|
+
),
|
|
2590
2709
|
);
|
|
2591
2710
|
promises.push(
|
|
2592
2711
|
this.queryRun(
|
|
2593
2712
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2594
|
-
`${this.prefix}
|
|
2713
|
+
`${this.prefix}uniques_${etype}`,
|
|
2595
2714
|
)} WHERE "guid"=decode(@guid, 'hex');`,
|
|
2596
2715
|
{
|
|
2597
2716
|
etypes: [etype],
|
|
2598
2717
|
params: {
|
|
2599
2718
|
guid,
|
|
2600
2719
|
},
|
|
2601
|
-
}
|
|
2602
|
-
)
|
|
2720
|
+
},
|
|
2721
|
+
),
|
|
2603
2722
|
);
|
|
2604
2723
|
await Promise.all(promises);
|
|
2605
|
-
await insertData(guid, data, sdata, etype);
|
|
2724
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
2606
2725
|
success = true;
|
|
2607
2726
|
}
|
|
2608
2727
|
return success;
|
|
2609
2728
|
},
|
|
2610
2729
|
async () => {
|
|
2611
2730
|
await this.internalTransaction('nymph-save');
|
|
2731
|
+
inTransaction = true;
|
|
2612
2732
|
},
|
|
2613
2733
|
async (success) => {
|
|
2614
|
-
if (
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2734
|
+
if (inTransaction) {
|
|
2735
|
+
inTransaction = false;
|
|
2736
|
+
if (success) {
|
|
2737
|
+
await this.commit('nymph-save');
|
|
2738
|
+
} else {
|
|
2739
|
+
await this.rollback('nymph-save');
|
|
2740
|
+
}
|
|
2618
2741
|
}
|
|
2619
2742
|
return success;
|
|
2620
|
-
}
|
|
2743
|
+
},
|
|
2621
2744
|
);
|
|
2622
2745
|
|
|
2623
2746
|
return result;
|
|
2624
2747
|
} catch (e: any) {
|
|
2625
|
-
|
|
2748
|
+
this.nymph.config.debugError('postgresql', `Save entity error: "${e}"`);
|
|
2749
|
+
if (inTransaction) {
|
|
2750
|
+
await this.rollback('nymph-save');
|
|
2751
|
+
}
|
|
2626
2752
|
throw e;
|
|
2627
2753
|
}
|
|
2628
2754
|
}
|
|
@@ -2635,38 +2761,39 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2635
2761
|
try {
|
|
2636
2762
|
await this.queryRun(
|
|
2637
2763
|
`DELETE FROM ${PostgreSQLDriver.escape(
|
|
2638
|
-
`${this.prefix}uids
|
|
2764
|
+
`${this.prefix}uids`,
|
|
2639
2765
|
)} WHERE "name"=@name;`,
|
|
2640
2766
|
{
|
|
2641
2767
|
params: {
|
|
2642
2768
|
name,
|
|
2643
2769
|
curUid,
|
|
2644
2770
|
},
|
|
2645
|
-
}
|
|
2771
|
+
},
|
|
2646
2772
|
);
|
|
2647
2773
|
await this.queryRun(
|
|
2648
2774
|
`INSERT INTO ${PostgreSQLDriver.escape(
|
|
2649
|
-
`${this.prefix}uids
|
|
2775
|
+
`${this.prefix}uids`,
|
|
2650
2776
|
)} ("name", "cur_uid") VALUES (@name, @curUid);`,
|
|
2651
2777
|
{
|
|
2652
2778
|
params: {
|
|
2653
2779
|
name,
|
|
2654
2780
|
curUid,
|
|
2655
2781
|
},
|
|
2656
|
-
}
|
|
2782
|
+
},
|
|
2657
2783
|
);
|
|
2658
|
-
await this.commit('nymph-setuid');
|
|
2659
|
-
return true;
|
|
2660
2784
|
} catch (e: any) {
|
|
2661
2785
|
await this.rollback('nymph-setuid');
|
|
2662
2786
|
throw e;
|
|
2663
2787
|
}
|
|
2788
|
+
|
|
2789
|
+
await this.commit('nymph-setuid');
|
|
2790
|
+
return true;
|
|
2664
2791
|
}
|
|
2665
2792
|
|
|
2666
|
-
|
|
2793
|
+
protected async internalTransaction(name: string) {
|
|
2667
2794
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
2668
2795
|
throw new InvalidParametersError(
|
|
2669
|
-
'Transaction start attempted without a name.'
|
|
2796
|
+
'Transaction start attempted without a name.',
|
|
2670
2797
|
);
|
|
2671
2798
|
}
|
|
2672
2799
|
|
|
@@ -2688,7 +2815,7 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2688
2815
|
}
|
|
2689
2816
|
|
|
2690
2817
|
public async startTransaction(name: string) {
|
|
2691
|
-
const inTransaction = this.inTransaction();
|
|
2818
|
+
const inTransaction = await this.inTransaction();
|
|
2692
2819
|
const transaction = await this.internalTransaction(name);
|
|
2693
2820
|
if (!inTransaction) {
|
|
2694
2821
|
this.transaction = null;
|
|
@@ -2699,4 +2826,29 @@ export default class PostgreSQLDriver extends NymphDriver {
|
|
|
2699
2826
|
|
|
2700
2827
|
return nymph;
|
|
2701
2828
|
}
|
|
2829
|
+
|
|
2830
|
+
public async needsMigration(): Promise<boolean> {
|
|
2831
|
+
const table = await this.queryGet(
|
|
2832
|
+
'SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix LIMIT 1;',
|
|
2833
|
+
{
|
|
2834
|
+
params: {
|
|
2835
|
+
db: this.config.database,
|
|
2836
|
+
prefix: this.prefix + 'data_' + '%',
|
|
2837
|
+
},
|
|
2838
|
+
},
|
|
2839
|
+
);
|
|
2840
|
+
if (table?.table_name) {
|
|
2841
|
+
const result = await this.queryGet(
|
|
2842
|
+
'SELECT 1 AS "exists" FROM "information_schema"."columns" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name"=@table AND "column_name"=\'json\';',
|
|
2843
|
+
{
|
|
2844
|
+
params: {
|
|
2845
|
+
db: this.config.database,
|
|
2846
|
+
table: table.table_name,
|
|
2847
|
+
},
|
|
2848
|
+
},
|
|
2849
|
+
);
|
|
2850
|
+
return !result?.exists;
|
|
2851
|
+
}
|
|
2852
|
+
return false;
|
|
2853
|
+
}
|
|
2702
2854
|
}
|