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