@nymphjs/driver-postgresql 1.0.0-beta.98 → 1.0.0-beta.99

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.
@@ -2,6 +2,12 @@ import pg from 'pg';
2
2
  import type { Pool, PoolClient, PoolConfig, QueryResult } from 'pg';
3
3
  import format from 'pg-format';
4
4
  import Cursor from 'pg-cursor';
5
+ import type {
6
+ SearchTerm,
7
+ SearchOrTerm,
8
+ SearchNotTerm,
9
+ SearchSeriesTerm,
10
+ } from '@sciactive/tokenizer';
5
11
  import {
6
12
  NymphDriver,
7
13
  type EntityConstructor,
@@ -239,87 +245,88 @@ export default class PostgreSQLDriver extends NymphDriver {
239
245
  return this.connected;
240
246
  }
241
247
 
242
- /**
243
- * Create entity tables in the database.
244
- *
245
- * @param etype The entity type to create a table for. If this is blank, the default tables are created.
246
- * @returns True on success, false on failure.
247
- */
248
- private async createTables(etype: string | null = null) {
249
- const connection = await this.getConnection(true);
250
- if (etype != null) {
251
- // Create the entity table.
252
- await this.queryRun(
253
- `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
254
- `${this.prefix}entities_${etype}`,
255
- )} (
248
+ private async createEntitiesTable(
249
+ etype: string,
250
+ connection: PostgreSQLDriverConnection,
251
+ ) {
252
+ // Create the entity table.
253
+ await this.queryRun(
254
+ `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
255
+ `${this.prefix}entities_${etype}`,
256
+ )} (
256
257
  "guid" BYTEA NOT NULL,
257
258
  "tags" TEXT[],
258
259
  "cdate" DOUBLE PRECISION NOT NULL,
259
260
  "mdate" DOUBLE PRECISION NOT NULL,
260
261
  PRIMARY KEY ("guid")
261
262
  ) WITH ( OIDS=FALSE );`,
262
- { connection },
263
- );
264
- await this.queryRun(
265
- `ALTER TABLE ${PostgreSQLDriver.escape(
266
- `${this.prefix}entities_${etype}`,
267
- )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
268
- { connection },
269
- );
270
- await this.queryRun(
271
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
272
- `${this.prefix}entities_${etype}_id_cdate`,
273
- )};`,
274
- { connection },
275
- );
276
- await this.queryRun(
277
- `CREATE INDEX ${PostgreSQLDriver.escape(
278
- `${this.prefix}entities_${etype}_id_cdate`,
279
- )} ON ${PostgreSQLDriver.escape(
280
- `${this.prefix}entities_${etype}`,
281
- )} USING btree ("cdate");`,
282
- { connection },
283
- );
284
- await this.queryRun(
285
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
286
- `${this.prefix}entities_${etype}_id_mdate`,
287
- )};`,
288
- { connection },
289
- );
290
- await this.queryRun(
291
- `CREATE INDEX ${PostgreSQLDriver.escape(
292
- `${this.prefix}entities_${etype}_id_mdate`,
293
- )} ON ${PostgreSQLDriver.escape(
294
- `${this.prefix}entities_${etype}`,
295
- )} USING btree ("mdate");`,
296
- { connection },
297
- );
298
- await this.queryRun(
299
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
300
- `${this.prefix}entities_${etype}_id_tags`,
301
- )};`,
302
- { connection },
303
- );
304
- await this.queryRun(
305
- `CREATE INDEX ${PostgreSQLDriver.escape(
306
- `${this.prefix}entities_${etype}_id_tags`,
307
- )} ON ${PostgreSQLDriver.escape(
308
- `${this.prefix}entities_${etype}`,
309
- )} USING gin ("tags");`,
310
- { connection },
311
- );
312
- await this.queryRun(
313
- `ALTER TABLE ${PostgreSQLDriver.escape(
314
- `${this.prefix}entities_${etype}`,
315
- )} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`,
316
- { connection },
317
- );
318
- // Create the data table.
319
- await this.queryRun(
320
- `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
321
- `${this.prefix}data_${etype}`,
322
- )} (
263
+ { connection },
264
+ );
265
+ await this.queryRun(
266
+ `ALTER TABLE ${PostgreSQLDriver.escape(
267
+ `${this.prefix}entities_${etype}`,
268
+ )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
269
+ { connection },
270
+ );
271
+ await this.queryRun(
272
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
273
+ `${this.prefix}entities_${etype}_id_cdate`,
274
+ )};`,
275
+ { connection },
276
+ );
277
+ await this.queryRun(
278
+ `CREATE INDEX ${PostgreSQLDriver.escape(
279
+ `${this.prefix}entities_${etype}_id_cdate`,
280
+ )} ON ${PostgreSQLDriver.escape(
281
+ `${this.prefix}entities_${etype}`,
282
+ )} USING btree ("cdate");`,
283
+ { connection },
284
+ );
285
+ await this.queryRun(
286
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
287
+ `${this.prefix}entities_${etype}_id_mdate`,
288
+ )};`,
289
+ { connection },
290
+ );
291
+ await this.queryRun(
292
+ `CREATE INDEX ${PostgreSQLDriver.escape(
293
+ `${this.prefix}entities_${etype}_id_mdate`,
294
+ )} ON ${PostgreSQLDriver.escape(
295
+ `${this.prefix}entities_${etype}`,
296
+ )} USING btree ("mdate");`,
297
+ { connection },
298
+ );
299
+ await this.queryRun(
300
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
301
+ `${this.prefix}entities_${etype}_id_tags`,
302
+ )};`,
303
+ { connection },
304
+ );
305
+ await this.queryRun(
306
+ `CREATE INDEX ${PostgreSQLDriver.escape(
307
+ `${this.prefix}entities_${etype}_id_tags`,
308
+ )} ON ${PostgreSQLDriver.escape(
309
+ `${this.prefix}entities_${etype}`,
310
+ )} USING gin ("tags");`,
311
+ { connection },
312
+ );
313
+ await this.queryRun(
314
+ `ALTER TABLE ${PostgreSQLDriver.escape(
315
+ `${this.prefix}entities_${etype}`,
316
+ )} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`,
317
+ { connection },
318
+ );
319
+ }
320
+
321
+ private async createDataTable(
322
+ etype: string,
323
+ connection: PostgreSQLDriverConnection,
324
+ ) {
325
+ // Create the data table.
326
+ await this.queryRun(
327
+ `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
328
+ `${this.prefix}data_${etype}`,
329
+ )} (
323
330
  "guid" BYTEA NOT NULL,
324
331
  "name" TEXT NOT NULL,
325
332
  "value" CHARACTER(1) NOT NULL,
@@ -333,263 +340,269 @@ export default class PostgreSQLDriver extends NymphDriver {
333
340
  `${this.prefix}entities_${etype}`,
334
341
  )} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
335
342
  ) WITH ( OIDS=FALSE );`,
336
- { connection },
337
- );
338
- await this.queryRun(
339
- `ALTER TABLE ${PostgreSQLDriver.escape(
340
- `${this.prefix}data_${etype}`,
341
- )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
342
- { connection },
343
- );
344
- await this.queryRun(
345
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
346
- `${this.prefix}data_${etype}_id_guid`,
347
- )};`,
348
- { connection },
349
- );
350
- await this.queryRun(
351
- `CREATE INDEX ${PostgreSQLDriver.escape(
352
- `${this.prefix}data_${etype}_id_guid`,
353
- )} ON ${PostgreSQLDriver.escape(
354
- `${this.prefix}data_${etype}`,
355
- )} USING btree ("guid");`,
356
- { connection },
357
- );
358
- await this.queryRun(
359
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
360
- `${this.prefix}data_${etype}_id_guid_name`,
361
- )};`,
362
- { connection },
363
- );
364
- await this.queryRun(
365
- `CREATE INDEX ${PostgreSQLDriver.escape(
366
- `${this.prefix}data_${etype}_id_guid_name`,
367
- )} ON ${PostgreSQLDriver.escape(
368
- `${this.prefix}data_${etype}`,
369
- )} USING btree ("guid", "name");`,
370
- { connection },
371
- );
372
- await this.queryRun(
373
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
374
- `${this.prefix}data_${etype}_id_guid_name__user`,
375
- )};`,
376
- { connection },
377
- );
378
- await this.queryRun(
379
- `CREATE INDEX ${PostgreSQLDriver.escape(
380
- `${this.prefix}data_${etype}_id_guid_name__user`,
381
- )} ON ${PostgreSQLDriver.escape(
382
- `${this.prefix}data_${etype}`,
383
- )} USING btree ("guid") WHERE "name" = 'user'::text;`,
384
- { connection },
385
- );
386
- await this.queryRun(
387
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
388
- `${this.prefix}data_${etype}_id_guid_name__group`,
389
- )};`,
390
- { connection },
391
- );
392
- await this.queryRun(
393
- `CREATE INDEX ${PostgreSQLDriver.escape(
394
- `${this.prefix}data_${etype}_id_guid_name__group`,
395
- )} ON ${PostgreSQLDriver.escape(
396
- `${this.prefix}data_${etype}`,
397
- )} USING btree ("guid") WHERE "name" = 'group'::text;`,
398
- { connection },
399
- );
400
- await this.queryRun(
401
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
402
- `${this.prefix}data_${etype}_id_name`,
403
- )};`,
404
- { connection },
405
- );
406
- await this.queryRun(
407
- `CREATE INDEX ${PostgreSQLDriver.escape(
408
- `${this.prefix}data_${etype}_id_name`,
409
- )} ON ${PostgreSQLDriver.escape(
410
- `${this.prefix}data_${etype}`,
411
- )} USING btree ("name");`,
412
- { connection },
413
- );
414
- await this.queryRun(
415
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
416
- `${this.prefix}data_${etype}_id_name_string`,
417
- )};`,
418
- { connection },
419
- );
420
- await this.queryRun(
421
- `CREATE INDEX ${PostgreSQLDriver.escape(
422
- `${this.prefix}data_${etype}_id_name_string`,
423
- )} ON ${PostgreSQLDriver.escape(
424
- `${this.prefix}data_${etype}`,
425
- )} USING btree ("name", LEFT("string", 512));`,
426
- { connection },
427
- );
428
- await this.queryRun(
429
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
430
- `${this.prefix}data_${etype}_id_name_number`,
431
- )};`,
432
- { connection },
433
- );
434
- await this.queryRun(
435
- `CREATE INDEX ${PostgreSQLDriver.escape(
436
- `${this.prefix}data_${etype}_id_name_number`,
437
- )} ON ${PostgreSQLDriver.escape(
438
- `${this.prefix}data_${etype}`,
439
- )} USING btree ("name", "number");`,
440
- { connection },
441
- );
442
- await this.queryRun(
443
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
444
- `${this.prefix}data_${etype}_id_guid_name_number`,
445
- )};`,
446
- { connection },
447
- );
448
- await this.queryRun(
449
- `CREATE INDEX ${PostgreSQLDriver.escape(
450
- `${this.prefix}data_${etype}_id_guid_name_number`,
451
- )} ON ${PostgreSQLDriver.escape(
452
- `${this.prefix}data_${etype}`,
453
- )} USING btree ("guid", "name", "number");`,
454
- { connection },
455
- );
456
- await this.queryRun(
457
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
458
- `${this.prefix}data_${etype}_id_name_truthy`,
459
- )};`,
460
- { connection },
461
- );
462
- await this.queryRun(
463
- `CREATE INDEX ${PostgreSQLDriver.escape(
464
- `${this.prefix}data_${etype}_id_name_truthy`,
465
- )} ON ${PostgreSQLDriver.escape(
466
- `${this.prefix}data_${etype}`,
467
- )} USING btree ("name", "truthy");`,
468
- { connection },
469
- );
470
- await this.queryRun(
471
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
472
- `${this.prefix}data_${etype}_id_guid_name_truthy`,
473
- )};`,
474
- { connection },
475
- );
476
- await this.queryRun(
477
- `CREATE INDEX ${PostgreSQLDriver.escape(
478
- `${this.prefix}data_${etype}_id_guid_name_truthy`,
479
- )} ON ${PostgreSQLDriver.escape(
480
- `${this.prefix}data_${etype}`,
481
- )} USING btree ("guid", "name", "truthy");`,
482
- { connection },
483
- );
484
- await this.queryRun(
485
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
486
- `${this.prefix}data_${etype}_id_string`,
487
- )};`,
488
- { connection },
489
- );
490
- await this.queryRun(
491
- `CREATE INDEX ${PostgreSQLDriver.escape(
492
- `${this.prefix}data_${etype}_id_string`,
493
- )} ON ${PostgreSQLDriver.escape(
494
- `${this.prefix}data_${etype}`,
495
- )} USING gin ("string" gin_trgm_ops);`,
496
- { connection },
497
- );
498
- await this.queryRun(
499
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
500
- `${this.prefix}data_${etype}_id_json`,
501
- )};`,
502
- { connection },
503
- );
504
- await this.queryRun(
505
- `CREATE INDEX ${PostgreSQLDriver.escape(
506
- `${this.prefix}data_${etype}_id_json`,
507
- )} ON ${PostgreSQLDriver.escape(
508
- `${this.prefix}data_${etype}`,
509
- )} USING gin ("json");`,
510
- { connection },
511
- );
512
- await this.queryRun(
513
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
514
- `${this.prefix}data_${etype}_id_acuserread`,
515
- )};`,
516
- { connection },
517
- );
518
- await this.queryRun(
519
- `CREATE INDEX ${PostgreSQLDriver.escape(
520
- `${this.prefix}data_${etype}_id_acuserread`,
521
- )} ON ${PostgreSQLDriver.escape(
522
- `${this.prefix}data_${etype}`,
523
- )} USING btree ("guid") WHERE "name"='acUser' AND "number" >= 1;`,
524
- { connection },
525
- );
526
- await this.queryRun(
527
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
528
- `${this.prefix}data_${etype}_id_acgroupread`,
529
- )};`,
530
- { connection },
531
- );
532
- await this.queryRun(
533
- `CREATE INDEX ${PostgreSQLDriver.escape(
534
- `${this.prefix}data_${etype}_id_acgroupread`,
535
- )} ON ${PostgreSQLDriver.escape(
536
- `${this.prefix}data_${etype}`,
537
- )} USING btree ("guid") WHERE "name"='acGroup' AND "number" >= 1;`,
538
- { connection },
539
- );
540
- await this.queryRun(
541
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
542
- `${this.prefix}data_${etype}_id_acotherread`,
543
- )};`,
544
- { connection },
545
- );
546
- await this.queryRun(
547
- `CREATE INDEX ${PostgreSQLDriver.escape(
548
- `${this.prefix}data_${etype}_id_acotherread`,
549
- )} ON ${PostgreSQLDriver.escape(
550
- `${this.prefix}data_${etype}`,
551
- )} USING btree ("guid") WHERE "name"='acOther' AND "number" >= 1;`,
552
- { connection },
553
- );
554
- await this.queryRun(
555
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
556
- `${this.prefix}data_${etype}_id_acuser`,
557
- )};`,
558
- { connection },
559
- );
560
- await this.queryRun(
561
- `CREATE INDEX ${PostgreSQLDriver.escape(
562
- `${this.prefix}data_${etype}_id_acuser`,
563
- )} ON ${PostgreSQLDriver.escape(
564
- `${this.prefix}data_${etype}`,
565
- )} USING btree ("guid") WHERE "name"='user';`,
566
- { connection },
567
- );
568
- await this.queryRun(
569
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
570
- `${this.prefix}data_${etype}_id_acgroup`,
571
- )};`,
572
- { connection },
573
- );
574
- await this.queryRun(
575
- `CREATE INDEX ${PostgreSQLDriver.escape(
576
- `${this.prefix}data_${etype}_id_acgroup`,
577
- )} ON ${PostgreSQLDriver.escape(
578
- `${this.prefix}data_${etype}`,
579
- )} USING btree ("guid") WHERE "name"='group';`,
580
- { connection },
581
- );
582
- await this.queryRun(
583
- `ALTER TABLE ${PostgreSQLDriver.escape(
584
- `${this.prefix}data_${etype}`,
585
- )} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`,
586
- { connection },
587
- );
588
- // Create the references table.
589
- await this.queryRun(
590
- `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
591
- `${this.prefix}references_${etype}`,
592
- )} (
343
+ { connection },
344
+ );
345
+ await this.queryRun(
346
+ `ALTER TABLE ${PostgreSQLDriver.escape(
347
+ `${this.prefix}data_${etype}`,
348
+ )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
349
+ { connection },
350
+ );
351
+ await this.queryRun(
352
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
353
+ `${this.prefix}data_${etype}_id_guid`,
354
+ )};`,
355
+ { connection },
356
+ );
357
+ await this.queryRun(
358
+ `CREATE INDEX ${PostgreSQLDriver.escape(
359
+ `${this.prefix}data_${etype}_id_guid`,
360
+ )} ON ${PostgreSQLDriver.escape(
361
+ `${this.prefix}data_${etype}`,
362
+ )} USING btree ("guid");`,
363
+ { connection },
364
+ );
365
+ await this.queryRun(
366
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
367
+ `${this.prefix}data_${etype}_id_guid_name`,
368
+ )};`,
369
+ { connection },
370
+ );
371
+ await this.queryRun(
372
+ `CREATE INDEX ${PostgreSQLDriver.escape(
373
+ `${this.prefix}data_${etype}_id_guid_name`,
374
+ )} ON ${PostgreSQLDriver.escape(
375
+ `${this.prefix}data_${etype}`,
376
+ )} USING btree ("guid", "name");`,
377
+ { connection },
378
+ );
379
+ await this.queryRun(
380
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
381
+ `${this.prefix}data_${etype}_id_guid_name__user`,
382
+ )};`,
383
+ { connection },
384
+ );
385
+ await this.queryRun(
386
+ `CREATE INDEX ${PostgreSQLDriver.escape(
387
+ `${this.prefix}data_${etype}_id_guid_name__user`,
388
+ )} ON ${PostgreSQLDriver.escape(
389
+ `${this.prefix}data_${etype}`,
390
+ )} USING btree ("guid") WHERE "name" = 'user'::text;`,
391
+ { connection },
392
+ );
393
+ await this.queryRun(
394
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
395
+ `${this.prefix}data_${etype}_id_guid_name__group`,
396
+ )};`,
397
+ { connection },
398
+ );
399
+ await this.queryRun(
400
+ `CREATE INDEX ${PostgreSQLDriver.escape(
401
+ `${this.prefix}data_${etype}_id_guid_name__group`,
402
+ )} ON ${PostgreSQLDriver.escape(
403
+ `${this.prefix}data_${etype}`,
404
+ )} USING btree ("guid") WHERE "name" = 'group'::text;`,
405
+ { connection },
406
+ );
407
+ await this.queryRun(
408
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
409
+ `${this.prefix}data_${etype}_id_name`,
410
+ )};`,
411
+ { connection },
412
+ );
413
+ await this.queryRun(
414
+ `CREATE INDEX ${PostgreSQLDriver.escape(
415
+ `${this.prefix}data_${etype}_id_name`,
416
+ )} ON ${PostgreSQLDriver.escape(
417
+ `${this.prefix}data_${etype}`,
418
+ )} USING btree ("name");`,
419
+ { connection },
420
+ );
421
+ await this.queryRun(
422
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
423
+ `${this.prefix}data_${etype}_id_name_string`,
424
+ )};`,
425
+ { connection },
426
+ );
427
+ await this.queryRun(
428
+ `CREATE INDEX ${PostgreSQLDriver.escape(
429
+ `${this.prefix}data_${etype}_id_name_string`,
430
+ )} ON ${PostgreSQLDriver.escape(
431
+ `${this.prefix}data_${etype}`,
432
+ )} USING btree ("name", LEFT("string", 512));`,
433
+ { connection },
434
+ );
435
+ await this.queryRun(
436
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
437
+ `${this.prefix}data_${etype}_id_name_number`,
438
+ )};`,
439
+ { connection },
440
+ );
441
+ await this.queryRun(
442
+ `CREATE INDEX ${PostgreSQLDriver.escape(
443
+ `${this.prefix}data_${etype}_id_name_number`,
444
+ )} ON ${PostgreSQLDriver.escape(
445
+ `${this.prefix}data_${etype}`,
446
+ )} USING btree ("name", "number");`,
447
+ { connection },
448
+ );
449
+ await this.queryRun(
450
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
451
+ `${this.prefix}data_${etype}_id_guid_name_number`,
452
+ )};`,
453
+ { connection },
454
+ );
455
+ await this.queryRun(
456
+ `CREATE INDEX ${PostgreSQLDriver.escape(
457
+ `${this.prefix}data_${etype}_id_guid_name_number`,
458
+ )} ON ${PostgreSQLDriver.escape(
459
+ `${this.prefix}data_${etype}`,
460
+ )} USING btree ("guid", "name", "number");`,
461
+ { connection },
462
+ );
463
+ await this.queryRun(
464
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
465
+ `${this.prefix}data_${etype}_id_name_truthy`,
466
+ )};`,
467
+ { connection },
468
+ );
469
+ await this.queryRun(
470
+ `CREATE INDEX ${PostgreSQLDriver.escape(
471
+ `${this.prefix}data_${etype}_id_name_truthy`,
472
+ )} ON ${PostgreSQLDriver.escape(
473
+ `${this.prefix}data_${etype}`,
474
+ )} USING btree ("name", "truthy");`,
475
+ { connection },
476
+ );
477
+ await this.queryRun(
478
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
479
+ `${this.prefix}data_${etype}_id_guid_name_truthy`,
480
+ )};`,
481
+ { connection },
482
+ );
483
+ await this.queryRun(
484
+ `CREATE INDEX ${PostgreSQLDriver.escape(
485
+ `${this.prefix}data_${etype}_id_guid_name_truthy`,
486
+ )} ON ${PostgreSQLDriver.escape(
487
+ `${this.prefix}data_${etype}`,
488
+ )} USING btree ("guid", "name", "truthy");`,
489
+ { connection },
490
+ );
491
+ await this.queryRun(
492
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
493
+ `${this.prefix}data_${etype}_id_string`,
494
+ )};`,
495
+ { connection },
496
+ );
497
+ await this.queryRun(
498
+ `CREATE INDEX ${PostgreSQLDriver.escape(
499
+ `${this.prefix}data_${etype}_id_string`,
500
+ )} ON ${PostgreSQLDriver.escape(
501
+ `${this.prefix}data_${etype}`,
502
+ )} USING gin ("string" gin_trgm_ops);`,
503
+ { connection },
504
+ );
505
+ await this.queryRun(
506
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
507
+ `${this.prefix}data_${etype}_id_json`,
508
+ )};`,
509
+ { connection },
510
+ );
511
+ await this.queryRun(
512
+ `CREATE INDEX ${PostgreSQLDriver.escape(
513
+ `${this.prefix}data_${etype}_id_json`,
514
+ )} ON ${PostgreSQLDriver.escape(
515
+ `${this.prefix}data_${etype}`,
516
+ )} USING gin ("json");`,
517
+ { connection },
518
+ );
519
+ await this.queryRun(
520
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
521
+ `${this.prefix}data_${etype}_id_acuserread`,
522
+ )};`,
523
+ { connection },
524
+ );
525
+ await this.queryRun(
526
+ `CREATE INDEX ${PostgreSQLDriver.escape(
527
+ `${this.prefix}data_${etype}_id_acuserread`,
528
+ )} ON ${PostgreSQLDriver.escape(
529
+ `${this.prefix}data_${etype}`,
530
+ )} USING btree ("guid") WHERE "name"='acUser' AND "number" >= 1;`,
531
+ { connection },
532
+ );
533
+ await this.queryRun(
534
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
535
+ `${this.prefix}data_${etype}_id_acgroupread`,
536
+ )};`,
537
+ { connection },
538
+ );
539
+ await this.queryRun(
540
+ `CREATE INDEX ${PostgreSQLDriver.escape(
541
+ `${this.prefix}data_${etype}_id_acgroupread`,
542
+ )} ON ${PostgreSQLDriver.escape(
543
+ `${this.prefix}data_${etype}`,
544
+ )} USING btree ("guid") WHERE "name"='acGroup' AND "number" >= 1;`,
545
+ { connection },
546
+ );
547
+ await this.queryRun(
548
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
549
+ `${this.prefix}data_${etype}_id_acotherread`,
550
+ )};`,
551
+ { connection },
552
+ );
553
+ await this.queryRun(
554
+ `CREATE INDEX ${PostgreSQLDriver.escape(
555
+ `${this.prefix}data_${etype}_id_acotherread`,
556
+ )} ON ${PostgreSQLDriver.escape(
557
+ `${this.prefix}data_${etype}`,
558
+ )} USING btree ("guid") WHERE "name"='acOther' AND "number" >= 1;`,
559
+ { connection },
560
+ );
561
+ await this.queryRun(
562
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
563
+ `${this.prefix}data_${etype}_id_acuser`,
564
+ )};`,
565
+ { connection },
566
+ );
567
+ await this.queryRun(
568
+ `CREATE INDEX ${PostgreSQLDriver.escape(
569
+ `${this.prefix}data_${etype}_id_acuser`,
570
+ )} ON ${PostgreSQLDriver.escape(
571
+ `${this.prefix}data_${etype}`,
572
+ )} USING btree ("guid") WHERE "name"='user';`,
573
+ { connection },
574
+ );
575
+ await this.queryRun(
576
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
577
+ `${this.prefix}data_${etype}_id_acgroup`,
578
+ )};`,
579
+ { connection },
580
+ );
581
+ await this.queryRun(
582
+ `CREATE INDEX ${PostgreSQLDriver.escape(
583
+ `${this.prefix}data_${etype}_id_acgroup`,
584
+ )} ON ${PostgreSQLDriver.escape(
585
+ `${this.prefix}data_${etype}`,
586
+ )} USING btree ("guid") WHERE "name"='group';`,
587
+ { connection },
588
+ );
589
+ await this.queryRun(
590
+ `ALTER TABLE ${PostgreSQLDriver.escape(
591
+ `${this.prefix}data_${etype}`,
592
+ )} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`,
593
+ { connection },
594
+ );
595
+ }
596
+
597
+ private async createReferencesTable(
598
+ etype: string,
599
+ connection: PostgreSQLDriverConnection,
600
+ ) {
601
+ // Create the references table.
602
+ await this.queryRun(
603
+ `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
604
+ `${this.prefix}references_${etype}`,
605
+ )} (
593
606
  "guid" BYTEA NOT NULL,
594
607
  "name" TEXT NOT NULL,
595
608
  "reference" BYTEA NOT NULL,
@@ -599,151 +612,207 @@ export default class PostgreSQLDriver extends NymphDriver {
599
612
  `${this.prefix}entities_${etype}`,
600
613
  )} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
601
614
  ) WITH ( OIDS=FALSE );`,
602
- { connection },
603
- );
604
- await this.queryRun(
605
- `ALTER TABLE ${PostgreSQLDriver.escape(
606
- `${this.prefix}references_${etype}`,
607
- )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
608
- { connection },
609
- );
610
- await this.queryRun(
611
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
612
- `${this.prefix}references_${etype}_id_guid`,
613
- )};`,
614
- { connection },
615
- );
616
- await this.queryRun(
617
- `CREATE INDEX ${PostgreSQLDriver.escape(
618
- `${this.prefix}references_${etype}_id_guid`,
619
- )} ON ${PostgreSQLDriver.escape(
620
- `${this.prefix}references_${etype}`,
621
- )} USING btree ("guid");`,
622
- { connection },
623
- );
624
- await this.queryRun(
625
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
626
- `${this.prefix}references_${etype}_id_name`,
627
- )};`,
628
- { connection },
629
- );
630
- await this.queryRun(
631
- `CREATE INDEX ${PostgreSQLDriver.escape(
632
- `${this.prefix}references_${etype}_id_name`,
633
- )} ON ${PostgreSQLDriver.escape(
634
- `${this.prefix}references_${etype}`,
635
- )} USING btree ("name");`,
636
- { connection },
637
- );
638
- await this.queryRun(
639
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
640
- `${this.prefix}references_${etype}_id_name_reference`,
641
- )};`,
642
- { connection },
643
- );
644
- await this.queryRun(
645
- `CREATE INDEX ${PostgreSQLDriver.escape(
646
- `${this.prefix}references_${etype}_id_name_reference`,
647
- )} ON ${PostgreSQLDriver.escape(
648
- `${this.prefix}references_${etype}`,
649
- )} USING btree ("name", "reference");`,
650
- { connection },
651
- );
652
- await this.queryRun(
653
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
654
- `${this.prefix}references_${etype}_id_reference`,
655
- )};`,
656
- { connection },
657
- );
658
- await this.queryRun(
659
- `CREATE INDEX ${PostgreSQLDriver.escape(
660
- `${this.prefix}references_${etype}_id_reference`,
661
- )} ON ${PostgreSQLDriver.escape(
662
- `${this.prefix}references_${etype}`,
663
- )} USING btree ("reference");`,
664
- { connection },
665
- );
666
- await this.queryRun(
667
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
668
- `${this.prefix}references_${etype}_id_guid_name_reference`,
669
- )};`,
670
- { connection },
671
- );
672
- await this.queryRun(
673
- `CREATE INDEX ${PostgreSQLDriver.escape(
674
- `${this.prefix}references_${etype}_id_guid_name_reference`,
675
- )} ON ${PostgreSQLDriver.escape(
676
- `${this.prefix}references_${etype}`,
677
- )} USING btree ("guid", "name", "reference");`,
678
- { connection },
679
- );
680
- await this.queryRun(
681
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
682
- `${this.prefix}references_${etype}_id_reference_name_guid`,
683
- )};`,
684
- { connection },
685
- );
686
- await this.queryRun(
687
- `CREATE INDEX ${PostgreSQLDriver.escape(
688
- `${this.prefix}references_${etype}_id_reference_name_guid`,
689
- )} ON ${PostgreSQLDriver.escape(
690
- `${this.prefix}references_${etype}`,
691
- )} USING btree ("reference", "name", "guid");`,
692
- { connection },
693
- );
694
- await this.queryRun(
695
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
696
- `${this.prefix}references_${etype}_id_reference_guid_name`,
697
- )};`,
698
- { connection },
699
- );
700
- await this.queryRun(
701
- `CREATE INDEX ${PostgreSQLDriver.escape(
702
- `${this.prefix}references_${etype}_id_reference_guid_name`,
703
- )} ON ${PostgreSQLDriver.escape(
704
- `${this.prefix}references_${etype}`,
705
- )} USING btree ("reference", "guid", "name");`,
706
- { connection },
707
- );
708
- await this.queryRun(
709
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
710
- `${this.prefix}references_${etype}_id_guid_reference_nameuser`,
711
- )};`,
712
- { connection },
713
- );
714
- await this.queryRun(
715
- `CREATE INDEX ${PostgreSQLDriver.escape(
716
- `${this.prefix}references_${etype}_id_guid_reference_nameuser`,
717
- )} ON ${PostgreSQLDriver.escape(
718
- `${this.prefix}references_${etype}`,
719
- )} USING btree ("guid", "reference") WHERE "name"='user';`,
720
- { connection },
721
- );
722
- await this.queryRun(
723
- `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
724
- `${this.prefix}references_${etype}_id_guid_reference_namegroup`,
725
- )};`,
726
- { connection },
727
- );
728
- await this.queryRun(
729
- `CREATE INDEX ${PostgreSQLDriver.escape(
730
- `${this.prefix}references_${etype}_id_guid_reference_namegroup`,
731
- )} ON ${PostgreSQLDriver.escape(
732
- `${this.prefix}references_${etype}`,
733
- )} USING btree ("guid", "reference") WHERE "name"='group';`,
734
- { connection },
735
- );
736
- await this.queryRun(
737
- `ALTER TABLE ${PostgreSQLDriver.escape(
738
- `${this.prefix}references_${etype}`,
739
- )} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`,
740
- { connection },
741
- );
742
- // Create the unique strings table.
743
- await this.queryRun(
744
- `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
745
- `${this.prefix}uniques_${etype}`,
746
- )} (
615
+ { connection },
616
+ );
617
+ await this.queryRun(
618
+ `ALTER TABLE ${PostgreSQLDriver.escape(
619
+ `${this.prefix}references_${etype}`,
620
+ )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
621
+ { connection },
622
+ );
623
+ await this.queryRun(
624
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
625
+ `${this.prefix}references_${etype}_id_guid`,
626
+ )};`,
627
+ { connection },
628
+ );
629
+ await this.queryRun(
630
+ `CREATE INDEX ${PostgreSQLDriver.escape(
631
+ `${this.prefix}references_${etype}_id_guid`,
632
+ )} ON ${PostgreSQLDriver.escape(
633
+ `${this.prefix}references_${etype}`,
634
+ )} USING btree ("guid");`,
635
+ { connection },
636
+ );
637
+ await this.queryRun(
638
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
639
+ `${this.prefix}references_${etype}_id_name`,
640
+ )};`,
641
+ { connection },
642
+ );
643
+ await this.queryRun(
644
+ `CREATE INDEX ${PostgreSQLDriver.escape(
645
+ `${this.prefix}references_${etype}_id_name`,
646
+ )} ON ${PostgreSQLDriver.escape(
647
+ `${this.prefix}references_${etype}`,
648
+ )} USING btree ("name");`,
649
+ { connection },
650
+ );
651
+ await this.queryRun(
652
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
653
+ `${this.prefix}references_${etype}_id_name_reference`,
654
+ )};`,
655
+ { connection },
656
+ );
657
+ await this.queryRun(
658
+ `CREATE INDEX ${PostgreSQLDriver.escape(
659
+ `${this.prefix}references_${etype}_id_name_reference`,
660
+ )} ON ${PostgreSQLDriver.escape(
661
+ `${this.prefix}references_${etype}`,
662
+ )} USING btree ("name", "reference");`,
663
+ { connection },
664
+ );
665
+ await this.queryRun(
666
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
667
+ `${this.prefix}references_${etype}_id_reference`,
668
+ )};`,
669
+ { connection },
670
+ );
671
+ await this.queryRun(
672
+ `CREATE INDEX ${PostgreSQLDriver.escape(
673
+ `${this.prefix}references_${etype}_id_reference`,
674
+ )} ON ${PostgreSQLDriver.escape(
675
+ `${this.prefix}references_${etype}`,
676
+ )} USING btree ("reference");`,
677
+ { connection },
678
+ );
679
+ await this.queryRun(
680
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
681
+ `${this.prefix}references_${etype}_id_guid_name_reference`,
682
+ )};`,
683
+ { connection },
684
+ );
685
+ await this.queryRun(
686
+ `CREATE INDEX ${PostgreSQLDriver.escape(
687
+ `${this.prefix}references_${etype}_id_guid_name_reference`,
688
+ )} ON ${PostgreSQLDriver.escape(
689
+ `${this.prefix}references_${etype}`,
690
+ )} USING btree ("guid", "name", "reference");`,
691
+ { connection },
692
+ );
693
+ await this.queryRun(
694
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
695
+ `${this.prefix}references_${etype}_id_reference_name_guid`,
696
+ )};`,
697
+ { connection },
698
+ );
699
+ await this.queryRun(
700
+ `CREATE INDEX ${PostgreSQLDriver.escape(
701
+ `${this.prefix}references_${etype}_id_reference_name_guid`,
702
+ )} ON ${PostgreSQLDriver.escape(
703
+ `${this.prefix}references_${etype}`,
704
+ )} USING btree ("reference", "name", "guid");`,
705
+ { connection },
706
+ );
707
+ await this.queryRun(
708
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
709
+ `${this.prefix}references_${etype}_id_reference_guid_name`,
710
+ )};`,
711
+ { connection },
712
+ );
713
+ await this.queryRun(
714
+ `CREATE INDEX ${PostgreSQLDriver.escape(
715
+ `${this.prefix}references_${etype}_id_reference_guid_name`,
716
+ )} ON ${PostgreSQLDriver.escape(
717
+ `${this.prefix}references_${etype}`,
718
+ )} USING btree ("reference", "guid", "name");`,
719
+ { connection },
720
+ );
721
+ await this.queryRun(
722
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
723
+ `${this.prefix}references_${etype}_id_guid_reference_nameuser`,
724
+ )};`,
725
+ { connection },
726
+ );
727
+ await this.queryRun(
728
+ `CREATE INDEX ${PostgreSQLDriver.escape(
729
+ `${this.prefix}references_${etype}_id_guid_reference_nameuser`,
730
+ )} ON ${PostgreSQLDriver.escape(
731
+ `${this.prefix}references_${etype}`,
732
+ )} USING btree ("guid", "reference") WHERE "name"='user';`,
733
+ { connection },
734
+ );
735
+ await this.queryRun(
736
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
737
+ `${this.prefix}references_${etype}_id_guid_reference_namegroup`,
738
+ )};`,
739
+ { connection },
740
+ );
741
+ await this.queryRun(
742
+ `CREATE INDEX ${PostgreSQLDriver.escape(
743
+ `${this.prefix}references_${etype}_id_guid_reference_namegroup`,
744
+ )} ON ${PostgreSQLDriver.escape(
745
+ `${this.prefix}references_${etype}`,
746
+ )} USING btree ("guid", "reference") WHERE "name"='group';`,
747
+ { connection },
748
+ );
749
+ await this.queryRun(
750
+ `ALTER TABLE ${PostgreSQLDriver.escape(
751
+ `${this.prefix}references_${etype}`,
752
+ )} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`,
753
+ { connection },
754
+ );
755
+ }
756
+
757
+ private async createTokensTable(
758
+ etype: string,
759
+ connection: PostgreSQLDriverConnection,
760
+ ) {
761
+ // Create the tokens table.
762
+ await this.queryRun(
763
+ `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
764
+ `${this.prefix}tokens_${etype}`,
765
+ )} (
766
+ "guid" BYTEA NOT NULL,
767
+ "name" TEXT NOT NULL,
768
+ "token" INTEGER NOT NULL,
769
+ "position" INTEGER NOT NULL,
770
+ "stem" BOOLEAN NOT NULL,
771
+ PRIMARY KEY ("guid", "name", "token", "position"),
772
+ FOREIGN KEY ("guid")
773
+ REFERENCES ${PostgreSQLDriver.escape(
774
+ `${this.prefix}entities_${etype}`,
775
+ )} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
776
+ ) WITH ( OIDS=FALSE );`,
777
+ { connection },
778
+ );
779
+ await this.queryRun(
780
+ `ALTER TABLE ${PostgreSQLDriver.escape(
781
+ `${this.prefix}tokens_${etype}`,
782
+ )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
783
+ { connection },
784
+ );
785
+ await this.queryRun(
786
+ `DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(
787
+ `${this.prefix}tokens_${etype}_id_name_token`,
788
+ )};`,
789
+ { connection },
790
+ );
791
+ await this.queryRun(
792
+ `CREATE INDEX ${PostgreSQLDriver.escape(
793
+ `${this.prefix}tokens_${etype}_id_name_token`,
794
+ )} ON ${PostgreSQLDriver.escape(
795
+ `${this.prefix}tokens_${etype}`,
796
+ )} USING btree ("name", "token");`,
797
+ { connection },
798
+ );
799
+ await this.queryRun(
800
+ `ALTER TABLE ${PostgreSQLDriver.escape(
801
+ `${this.prefix}tokens_${etype}`,
802
+ )} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`,
803
+ { connection },
804
+ );
805
+ }
806
+
807
+ private async createUniquesTable(
808
+ etype: string,
809
+ connection: PostgreSQLDriverConnection,
810
+ ) {
811
+ // Create the unique strings table.
812
+ await this.queryRun(
813
+ `CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(
814
+ `${this.prefix}uniques_${etype}`,
815
+ )} (
747
816
  "guid" BYTEA NOT NULL,
748
817
  "unique" TEXT NOT NULL UNIQUE,
749
818
  PRIMARY KEY ("guid", "unique"),
@@ -752,14 +821,30 @@ export default class PostgreSQLDriver extends NymphDriver {
752
821
  `${this.prefix}entities_${etype}`,
753
822
  )} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
754
823
  ) WITH ( OIDS=FALSE );`,
755
- { connection },
756
- );
757
- await this.queryRun(
758
- `ALTER TABLE ${PostgreSQLDriver.escape(
759
- `${this.prefix}uniques_${etype}`,
760
- )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
761
- { connection },
762
- );
824
+ { connection },
825
+ );
826
+ await this.queryRun(
827
+ `ALTER TABLE ${PostgreSQLDriver.escape(
828
+ `${this.prefix}uniques_${etype}`,
829
+ )} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`,
830
+ { connection },
831
+ );
832
+ }
833
+
834
+ /**
835
+ * Create entity tables in the database.
836
+ *
837
+ * @param etype The entity type to create a table for. If this is blank, the default tables are created.
838
+ * @returns True on success, false on failure.
839
+ */
840
+ private async createTables(etype: string | null = null) {
841
+ const connection = await this.getConnection(true);
842
+ if (etype != null) {
843
+ await this.createEntitiesTable(etype, connection);
844
+ await this.createDataTable(etype, connection);
845
+ await this.createReferencesTable(etype, connection);
846
+ await this.createTokensTable(etype, connection);
847
+ await this.createUniquesTable(etype, connection);
763
848
  } else {
764
849
  // Add trigram extensions.
765
850
  try {
@@ -1075,6 +1160,17 @@ export default class PostgreSQLDriver extends NymphDriver {
1075
1160
  },
1076
1161
  },
1077
1162
  );
1163
+ await this.queryRun(
1164
+ `DELETE FROM ${PostgreSQLDriver.escape(
1165
+ `${this.prefix}tokens_${etype}`,
1166
+ )} WHERE "guid"=decode(@guid, 'hex');`,
1167
+ {
1168
+ etypes: [etype],
1169
+ params: {
1170
+ guid,
1171
+ },
1172
+ },
1173
+ );
1078
1174
  await this.queryRun(
1079
1175
  `DELETE FROM ${PostgreSQLDriver.escape(
1080
1176
  `${this.prefix}uniques_${etype}`,
@@ -1117,6 +1213,24 @@ export default class PostgreSQLDriver extends NymphDriver {
1117
1213
  return true;
1118
1214
  }
1119
1215
 
1216
+ public async getEtypes() {
1217
+ const tables = await this.queryArray(
1218
+ 'SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix;',
1219
+ {
1220
+ params: {
1221
+ db: this.config.database,
1222
+ prefix: this.prefix + 'entities_' + '%',
1223
+ },
1224
+ },
1225
+ );
1226
+ const etypes: string[] = [];
1227
+ for (const table of tables) {
1228
+ etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
1229
+ }
1230
+
1231
+ return etypes;
1232
+ }
1233
+
1120
1234
  public async *exportDataIterator(): AsyncGenerator<
1121
1235
  { type: 'comment' | 'uid' | 'entity'; content: string },
1122
1236
  void,
@@ -1179,19 +1293,7 @@ export default class PostgreSQLDriver extends NymphDriver {
1179
1293
  }
1180
1294
 
1181
1295
  // Get the etypes.
1182
- const tables = await this.queryArray(
1183
- 'SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix;',
1184
- {
1185
- params: {
1186
- db: this.config.database,
1187
- prefix: this.prefix + 'entities_' + '%',
1188
- },
1189
- },
1190
- );
1191
- const etypes = [];
1192
- for (const table of tables) {
1193
- etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
1194
- }
1296
+ const etypes = await this.getEtypes();
1195
1297
 
1196
1298
  for (const etype of etypes) {
1197
1299
  // Export entities.
@@ -1533,6 +1635,148 @@ export default class PostgreSQLDriver extends NymphDriver {
1533
1635
  params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
1534
1636
  }
1535
1637
  break;
1638
+ case 'search':
1639
+ case '!search':
1640
+ if (curValue[0] === 'cdate' || curValue[0] === 'mdate') {
1641
+ if (curQuery) {
1642
+ curQuery += typeIsOr ? ' OR ' : ' AND ';
1643
+ }
1644
+ curQuery +=
1645
+ (xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
1646
+ break;
1647
+ } else {
1648
+ if (curQuery) {
1649
+ curQuery += typeIsOr ? ' OR ' : ' AND ';
1650
+ }
1651
+
1652
+ const name = `param${++count.i}`;
1653
+
1654
+ const queryPartToken = (term: SearchTerm) => {
1655
+ const value = `param${++count.i}`;
1656
+ params[value] = term.token;
1657
+ return (
1658
+ 'EXISTS (SELECT "guid" FROM ' +
1659
+ PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
1660
+ ' WHERE "guid"=' +
1661
+ ieTable +
1662
+ '."guid" AND "name"=@' +
1663
+ name +
1664
+ ' AND "token"=@' +
1665
+ value +
1666
+ (term.nostemmed ? ' AND "stem"=FALSE' : '') +
1667
+ ')'
1668
+ );
1669
+ };
1670
+
1671
+ const queryPartSeries = (series: SearchSeriesTerm) => {
1672
+ const tokenTableSuffix = makeTableSuffix();
1673
+ const tokenParts = series.tokens.map((token, i) => {
1674
+ const value = `param${++count.i}`;
1675
+ params[value] = token.token;
1676
+ return {
1677
+ fromClause:
1678
+ i === 0
1679
+ ? 'FROM ' +
1680
+ PostgreSQLDriver.escape(
1681
+ this.prefix + 'tokens_' + etype,
1682
+ ) +
1683
+ ' t' +
1684
+ tokenTableSuffix +
1685
+ '0'
1686
+ : 'JOIN ' +
1687
+ PostgreSQLDriver.escape(
1688
+ this.prefix + 'tokens_' + etype,
1689
+ ) +
1690
+ ' t' +
1691
+ tokenTableSuffix +
1692
+ i +
1693
+ ' ON t' +
1694
+ tokenTableSuffix +
1695
+ i +
1696
+ '."guid" = t' +
1697
+ tokenTableSuffix +
1698
+ '0."guid" AND t' +
1699
+ tokenTableSuffix +
1700
+ i +
1701
+ '."name" = t' +
1702
+ tokenTableSuffix +
1703
+ '0."name" AND t' +
1704
+ tokenTableSuffix +
1705
+ i +
1706
+ '."position" = t' +
1707
+ tokenTableSuffix +
1708
+ '0."position" + ' +
1709
+ i,
1710
+ whereClause:
1711
+ 't' +
1712
+ tokenTableSuffix +
1713
+ i +
1714
+ '."token"=@' +
1715
+ value +
1716
+ (token.nostemmed
1717
+ ? ' AND t' + tokenTableSuffix + i + '."stem"=FALSE'
1718
+ : ''),
1719
+ };
1720
+ });
1721
+ return (
1722
+ 'EXISTS (SELECT t' +
1723
+ tokenTableSuffix +
1724
+ '0."guid" ' +
1725
+ tokenParts.map((part) => part.fromClause).join(' ') +
1726
+ ' WHERE t' +
1727
+ tokenTableSuffix +
1728
+ '0."guid"=' +
1729
+ ieTable +
1730
+ '."guid" AND t' +
1731
+ tokenTableSuffix +
1732
+ '0."name"=@' +
1733
+ name +
1734
+ ' AND ' +
1735
+ tokenParts.map((part) => part.whereClause).join(' AND ') +
1736
+ ')'
1737
+ );
1738
+ };
1739
+
1740
+ const queryPartTerm = (
1741
+ term:
1742
+ | SearchTerm
1743
+ | SearchOrTerm
1744
+ | SearchNotTerm
1745
+ | SearchSeriesTerm,
1746
+ ): string => {
1747
+ if (term.type === 'series') {
1748
+ return queryPartSeries(term);
1749
+ } else if (term.type === 'not') {
1750
+ return 'NOT ' + queryPartTerm(term.operand);
1751
+ } else if (term.type === 'or') {
1752
+ let queryParts: string[] = [];
1753
+ for (let operand of term.operands) {
1754
+ queryParts.push(queryPartTerm(operand));
1755
+ }
1756
+ return '(' + queryParts.join(' OR ') + ')';
1757
+ }
1758
+ return queryPartToken(term);
1759
+ };
1760
+
1761
+ const parsedFTSQuery = this.tokenizer.parseSearchQuery(
1762
+ curValue[1],
1763
+ );
1764
+
1765
+ // Run through the query and add terms.
1766
+ let termStrings: string[] = [];
1767
+ for (let term of parsedFTSQuery) {
1768
+ termStrings.push(queryPartTerm(term));
1769
+ }
1770
+
1771
+ curQuery +=
1772
+ (xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
1773
+ '(' +
1774
+ termStrings.join(' AND ') +
1775
+ ')';
1776
+
1777
+ params[name] = curValue[0];
1778
+ }
1779
+ break;
1536
1780
  case 'match':
1537
1781
  case '!match':
1538
1782
  if (curValue[0] === 'cdate') {
@@ -2371,14 +2615,18 @@ export default class PostgreSQLDriver extends NymphDriver {
2371
2615
  return result?.cur_uid == null ? null : Number(result.cur_uid);
2372
2616
  }
2373
2617
 
2374
- public async importEntity({
2375
- guid,
2376
- cdate,
2377
- mdate,
2378
- tags,
2379
- sdata,
2380
- etype,
2381
- }: {
2618
+ public async importEntity(entity: {
2619
+ guid: string;
2620
+ cdate: number;
2621
+ mdate: number;
2622
+ tags: string[];
2623
+ sdata: SerializedEntityData;
2624
+ etype: string;
2625
+ }) {
2626
+ return await this.importEntityInternal(entity, false);
2627
+ }
2628
+
2629
+ public async importEntityTokens(entity: {
2382
2630
  guid: string;
2383
2631
  cdate: number;
2384
2632
  mdate: number;
@@ -2386,51 +2634,74 @@ export default class PostgreSQLDriver extends NymphDriver {
2386
2634
  sdata: SerializedEntityData;
2387
2635
  etype: string;
2388
2636
  }) {
2637
+ return await this.importEntityInternal(entity, true);
2638
+ }
2639
+
2640
+ private async importEntityInternal(
2641
+ {
2642
+ guid,
2643
+ cdate,
2644
+ mdate,
2645
+ tags,
2646
+ sdata,
2647
+ etype,
2648
+ }: {
2649
+ guid: string;
2650
+ cdate: number;
2651
+ mdate: number;
2652
+ tags: string[];
2653
+ sdata: SerializedEntityData;
2654
+ etype: string;
2655
+ },
2656
+ onlyTokens: boolean,
2657
+ ) {
2389
2658
  try {
2390
2659
  let promises = [];
2391
- promises.push(
2392
- this.queryRun(
2393
- `DELETE FROM ${PostgreSQLDriver.escape(
2394
- `${this.prefix}entities_${etype}`,
2395
- )} WHERE "guid"=decode(@guid, 'hex');`,
2396
- {
2397
- etypes: [etype],
2398
- params: {
2399
- guid,
2660
+ if (!onlyTokens) {
2661
+ promises.push(
2662
+ this.queryRun(
2663
+ `DELETE FROM ${PostgreSQLDriver.escape(
2664
+ `${this.prefix}entities_${etype}`,
2665
+ )} WHERE "guid"=decode(@guid, 'hex');`,
2666
+ {
2667
+ etypes: [etype],
2668
+ params: {
2669
+ guid,
2670
+ },
2400
2671
  },
2401
- },
2402
- ),
2403
- );
2404
- promises.push(
2405
- this.queryRun(
2406
- `DELETE FROM ${PostgreSQLDriver.escape(
2407
- `${this.prefix}data_${etype}`,
2408
- )} WHERE "guid"=decode(@guid, 'hex');`,
2409
- {
2410
- etypes: [etype],
2411
- params: {
2412
- guid,
2672
+ ),
2673
+ );
2674
+ promises.push(
2675
+ this.queryRun(
2676
+ `DELETE FROM ${PostgreSQLDriver.escape(
2677
+ `${this.prefix}data_${etype}`,
2678
+ )} WHERE "guid"=decode(@guid, 'hex');`,
2679
+ {
2680
+ etypes: [etype],
2681
+ params: {
2682
+ guid,
2683
+ },
2413
2684
  },
2414
- },
2415
- ),
2416
- );
2417
- promises.push(
2418
- this.queryRun(
2419
- `DELETE FROM ${PostgreSQLDriver.escape(
2420
- `${this.prefix}references_${etype}`,
2421
- )} WHERE "guid"=decode(@guid, 'hex');`,
2422
- {
2423
- etypes: [etype],
2424
- params: {
2425
- guid,
2685
+ ),
2686
+ );
2687
+ promises.push(
2688
+ this.queryRun(
2689
+ `DELETE FROM ${PostgreSQLDriver.escape(
2690
+ `${this.prefix}references_${etype}`,
2691
+ )} WHERE "guid"=decode(@guid, 'hex');`,
2692
+ {
2693
+ etypes: [etype],
2694
+ params: {
2695
+ guid,
2696
+ },
2426
2697
  },
2427
- },
2428
- ),
2429
- );
2698
+ ),
2699
+ );
2700
+ }
2430
2701
  promises.push(
2431
2702
  this.queryRun(
2432
2703
  `DELETE FROM ${PostgreSQLDriver.escape(
2433
- `${this.prefix}uniques_${etype}`,
2704
+ `${this.prefix}tokens_${etype}`,
2434
2705
  )} WHERE "guid"=decode(@guid, 'hex');`,
2435
2706
  {
2436
2707
  etypes: [etype],
@@ -2440,106 +2711,188 @@ export default class PostgreSQLDriver extends NymphDriver {
2440
2711
  },
2441
2712
  ),
2442
2713
  );
2443
- await Promise.all(promises);
2444
- promises = [];
2445
- await this.queryRun(
2446
- `INSERT INTO ${PostgreSQLDriver.escape(
2447
- `${this.prefix}entities_${etype}`,
2448
- )} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`,
2449
- {
2450
- etypes: [etype],
2451
- params: {
2452
- guid,
2453
- tags,
2454
- cdate: isNaN(cdate) ? null : cdate,
2455
- mdate: isNaN(mdate) ? null : mdate,
2456
- },
2457
- },
2458
- );
2459
- for (const name in sdata) {
2460
- const value = sdata[name];
2461
- const uvalue = JSON.parse(value);
2462
- if (value === undefined) {
2463
- continue;
2464
- }
2465
- const storageValue =
2466
- typeof uvalue === 'number'
2467
- ? 'N'
2468
- : typeof uvalue === 'string'
2469
- ? 'S'
2470
- : 'J';
2471
- const jsonValue =
2472
- storageValue === 'J'
2473
- ? PostgreSQLDriver.escapeNullSequences(value)
2474
- : null;
2714
+ if (!onlyTokens) {
2475
2715
  promises.push(
2476
2716
  this.queryRun(
2477
- `INSERT INTO ${PostgreSQLDriver.escape(
2478
- `${this.prefix}data_${etype}`,
2479
- )} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`,
2717
+ `DELETE FROM ${PostgreSQLDriver.escape(
2718
+ `${this.prefix}uniques_${etype}`,
2719
+ )} WHERE "guid"=decode(@guid, 'hex');`,
2480
2720
  {
2481
2721
  etypes: [etype],
2482
2722
  params: {
2483
2723
  guid,
2484
- name,
2485
- storageValue,
2486
- jsonValue,
2487
- string:
2488
- storageValue === 'J'
2489
- ? null
2490
- : PostgreSQLDriver.escapeNulls(`${uvalue}`),
2491
- number: isNaN(Number(uvalue)) ? null : Number(uvalue),
2492
- truthy: !!uvalue,
2493
2724
  },
2494
2725
  },
2495
2726
  ),
2496
2727
  );
2497
- const references = this.findReferences(value);
2498
- for (const reference of references) {
2728
+ }
2729
+
2730
+ await Promise.all(promises);
2731
+ promises = [];
2732
+
2733
+ if (!onlyTokens) {
2734
+ await this.queryRun(
2735
+ `INSERT INTO ${PostgreSQLDriver.escape(
2736
+ `${this.prefix}entities_${etype}`,
2737
+ )} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`,
2738
+ {
2739
+ etypes: [etype],
2740
+ params: {
2741
+ guid,
2742
+ tags,
2743
+ cdate: isNaN(cdate) ? null : cdate,
2744
+ mdate: isNaN(mdate) ? null : mdate,
2745
+ },
2746
+ },
2747
+ );
2748
+
2749
+ for (const name in sdata) {
2750
+ const value = sdata[name];
2751
+ const uvalue = JSON.parse(value);
2752
+ if (value === undefined) {
2753
+ continue;
2754
+ }
2755
+ const storageValue =
2756
+ typeof uvalue === 'number'
2757
+ ? 'N'
2758
+ : typeof uvalue === 'string'
2759
+ ? 'S'
2760
+ : 'J';
2761
+ const jsonValue =
2762
+ storageValue === 'J'
2763
+ ? PostgreSQLDriver.escapeNullSequences(value)
2764
+ : null;
2765
+
2499
2766
  promises.push(
2500
2767
  this.queryRun(
2501
2768
  `INSERT INTO ${PostgreSQLDriver.escape(
2502
- `${this.prefix}references_${etype}`,
2503
- )} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`,
2769
+ `${this.prefix}data_${etype}`,
2770
+ )} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`,
2504
2771
  {
2505
2772
  etypes: [etype],
2506
2773
  params: {
2507
2774
  guid,
2508
2775
  name,
2509
- reference,
2776
+ storageValue,
2777
+ jsonValue,
2778
+ string:
2779
+ storageValue === 'J'
2780
+ ? null
2781
+ : PostgreSQLDriver.escapeNulls(`${uvalue}`),
2782
+ number: isNaN(Number(uvalue)) ? null : Number(uvalue),
2783
+ truthy: !!uvalue,
2510
2784
  },
2511
2785
  },
2512
2786
  ),
2513
2787
  );
2788
+
2789
+ const references = this.findReferences(value);
2790
+ for (const reference of references) {
2791
+ promises.push(
2792
+ this.queryRun(
2793
+ `INSERT INTO ${PostgreSQLDriver.escape(
2794
+ `${this.prefix}references_${etype}`,
2795
+ )} ("guid", "name", "reference") VALUES (decode(@guid, 'hex'), @name, decode(@reference, 'hex'));`,
2796
+ {
2797
+ etypes: [etype],
2798
+ params: {
2799
+ guid,
2800
+ name,
2801
+ reference,
2802
+ },
2803
+ },
2804
+ ),
2805
+ );
2806
+ }
2514
2807
  }
2515
2808
  }
2516
- const uniques = await this.nymph
2517
- .getEntityClassByEtype(etype)
2518
- .getUniques({ guid, cdate, mdate, tags, data: {}, sdata });
2519
- for (const unique of uniques) {
2520
- promises.push(
2521
- this.queryRun(
2522
- `INSERT INTO ${PostgreSQLDriver.escape(
2523
- `${this.prefix}uniques_${etype}`,
2524
- )} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`,
2525
- {
2526
- etypes: [etype],
2527
- params: {
2528
- guid,
2529
- unique,
2530
- },
2531
- },
2532
- ).catch((e: any) => {
2533
- if (e instanceof EntityUniqueConstraintError) {
2534
- this.nymph.config.debugError(
2535
- 'postgresql',
2536
- `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
2809
+
2810
+ const EntityClass = this.nymph.getEntityClassByEtype(etype);
2811
+
2812
+ for (let name in sdata) {
2813
+ let tokenString: string | null = null;
2814
+ try {
2815
+ tokenString = EntityClass.getFTSText(name, JSON.parse(sdata[name]));
2816
+ } catch (e: any) {
2817
+ // Ignore error.
2818
+ }
2819
+
2820
+ if (tokenString != null) {
2821
+ const tokens = this.tokenizer.tokenize(tokenString);
2822
+ while (tokens.length) {
2823
+ const currentTokens = tokens.splice(0, 100);
2824
+ const params: { [k: string]: any } = {
2825
+ guid,
2826
+ name,
2827
+ };
2828
+ const values: string[] = [];
2829
+
2830
+ for (let i = 0; i < currentTokens.length; i++) {
2831
+ const token = currentTokens[i];
2832
+ params['token' + i] = token.token;
2833
+ params['position' + i] = token.position;
2834
+ params['stem' + i] = token.stem;
2835
+ values.push(
2836
+ "(decode(@guid, 'hex'), @name, @token" +
2837
+ i +
2838
+ ', @position' +
2839
+ i +
2840
+ ', @stem' +
2841
+ i +
2842
+ ')',
2537
2843
  );
2538
2844
  }
2539
- return e;
2540
- }),
2541
- );
2845
+
2846
+ promises.push(
2847
+ this.queryRun(
2848
+ `INSERT INTO ${PostgreSQLDriver.escape(
2849
+ `${this.prefix}tokens_${etype}`,
2850
+ )} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`,
2851
+ {
2852
+ etypes: [etype],
2853
+ params,
2854
+ },
2855
+ ),
2856
+ );
2857
+ }
2858
+ }
2859
+ }
2860
+
2861
+ if (!onlyTokens) {
2862
+ const uniques = await EntityClass.getUniques({
2863
+ guid,
2864
+ cdate,
2865
+ mdate,
2866
+ tags,
2867
+ data: {},
2868
+ sdata,
2869
+ });
2870
+ for (const unique of uniques) {
2871
+ promises.push(
2872
+ this.queryRun(
2873
+ `INSERT INTO ${PostgreSQLDriver.escape(
2874
+ `${this.prefix}uniques_${etype}`,
2875
+ )} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`,
2876
+ {
2877
+ etypes: [etype],
2878
+ params: {
2879
+ guid,
2880
+ unique,
2881
+ },
2882
+ },
2883
+ ).catch((e: any) => {
2884
+ if (e instanceof EntityUniqueConstraintError) {
2885
+ this.nymph.config.debugError(
2886
+ 'postgresql',
2887
+ `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
2888
+ );
2889
+ }
2890
+ return e;
2891
+ }),
2892
+ );
2893
+ }
2542
2894
  }
2895
+
2543
2896
  await Promise.all(promises);
2544
2897
  } catch (e: any) {
2545
2898
  this.nymph.config.debugError('postgresql', `Import entity error: "${e}"`);
@@ -2683,6 +3036,8 @@ export default class PostgreSQLDriver extends NymphDriver {
2683
3036
  uniques: string[],
2684
3037
  etype: string,
2685
3038
  ) => {
3039
+ const EntityClass = this.nymph.getEntityClassByEtype(etype);
3040
+
2686
3041
  const runInsertQuery = async (
2687
3042
  name: string,
2688
3043
  value: any,
@@ -2701,7 +3056,9 @@ export default class PostgreSQLDriver extends NymphDriver {
2701
3056
  storageValue === 'J'
2702
3057
  ? PostgreSQLDriver.escapeNullSequences(svalue)
2703
3058
  : null;
3059
+
2704
3060
  const promises = [];
3061
+
2705
3062
  promises.push(
2706
3063
  this.queryRun(
2707
3064
  `INSERT INTO ${PostgreSQLDriver.escape(
@@ -2724,6 +3081,7 @@ export default class PostgreSQLDriver extends NymphDriver {
2724
3081
  },
2725
3082
  ),
2726
3083
  );
3084
+
2727
3085
  const references = this.findReferences(svalue);
2728
3086
  for (const reference of references) {
2729
3087
  promises.push(
@@ -2742,8 +3100,57 @@ export default class PostgreSQLDriver extends NymphDriver {
2742
3100
  ),
2743
3101
  );
2744
3102
  }
3103
+
3104
+ let tokenString: string | null = null;
3105
+ try {
3106
+ tokenString = EntityClass.getFTSText(name, value);
3107
+ } catch (e: any) {
3108
+ // Ignore error.
3109
+ }
3110
+
3111
+ if (tokenString != null) {
3112
+ const tokens = this.tokenizer.tokenize(tokenString);
3113
+ while (tokens.length) {
3114
+ const currentTokens = tokens.splice(0, 100);
3115
+ const params: { [k: string]: any } = {
3116
+ guid,
3117
+ name,
3118
+ };
3119
+ const values: string[] = [];
3120
+
3121
+ for (let i = 0; i < currentTokens.length; i++) {
3122
+ const token = currentTokens[i];
3123
+ params['token' + i] = token.token;
3124
+ params['position' + i] = token.position;
3125
+ params['stem' + i] = token.stem;
3126
+ values.push(
3127
+ "(decode(@guid, 'hex'), @name, @token" +
3128
+ i +
3129
+ ', @position' +
3130
+ i +
3131
+ ', @stem' +
3132
+ i +
3133
+ ')',
3134
+ );
3135
+ }
3136
+
3137
+ promises.push(
3138
+ this.queryRun(
3139
+ `INSERT INTO ${PostgreSQLDriver.escape(
3140
+ `${this.prefix}tokens_${etype}`,
3141
+ )} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`,
3142
+ {
3143
+ etypes: [etype],
3144
+ params,
3145
+ },
3146
+ ),
3147
+ );
3148
+ }
3149
+ }
3150
+
2745
3151
  await Promise.all(promises);
2746
3152
  };
3153
+
2747
3154
  for (const unique of uniques) {
2748
3155
  try {
2749
3156
  await this.queryRun(
@@ -2849,6 +3256,19 @@ export default class PostgreSQLDriver extends NymphDriver {
2849
3256
  },
2850
3257
  ),
2851
3258
  );
3259
+ promises.push(
3260
+ this.queryRun(
3261
+ `SELECT 1 FROM ${PostgreSQLDriver.escape(
3262
+ `${this.prefix}tokens_${etype}`,
3263
+ )} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`,
3264
+ {
3265
+ etypes: [etype],
3266
+ params: {
3267
+ guid,
3268
+ },
3269
+ },
3270
+ ),
3271
+ );
2852
3272
  promises.push(
2853
3273
  this.queryRun(
2854
3274
  `SELECT 1 FROM ${PostgreSQLDriver.escape(
@@ -2906,6 +3326,19 @@ export default class PostgreSQLDriver extends NymphDriver {
2906
3326
  },
2907
3327
  ),
2908
3328
  );
3329
+ promises.push(
3330
+ this.queryRun(
3331
+ `DELETE FROM ${PostgreSQLDriver.escape(
3332
+ `${this.prefix}tokens_${etype}`,
3333
+ )} WHERE "guid"=decode(@guid, 'hex');`,
3334
+ {
3335
+ etypes: [etype],
3336
+ params: {
3337
+ guid,
3338
+ },
3339
+ },
3340
+ ),
3341
+ );
2909
3342
  promises.push(
2910
3343
  this.queryRun(
2911
3344
  `DELETE FROM ${PostgreSQLDriver.escape(
@@ -3026,7 +3459,7 @@ export default class PostgreSQLDriver extends NymphDriver {
3026
3459
  return nymph;
3027
3460
  }
3028
3461
 
3029
- public async needsMigration(): Promise<boolean> {
3462
+ public async needsMigration(): Promise<'json' | 'tokens' | false> {
3030
3463
  const table = await this.queryGet(
3031
3464
  '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;',
3032
3465
  {
@@ -3046,8 +3479,32 @@ export default class PostgreSQLDriver extends NymphDriver {
3046
3479
  },
3047
3480
  },
3048
3481
  );
3049
- return !result?.exists;
3482
+ if (!result?.exists) {
3483
+ return 'json';
3484
+ }
3485
+ }
3486
+ const table2 = await this.queryGet(
3487
+ 'SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @tokenTable LIMIT 1;',
3488
+ {
3489
+ params: {
3490
+ db: this.config.database,
3491
+ tokenTable: this.prefix + 'tokens_' + '%',
3492
+ },
3493
+ },
3494
+ );
3495
+ if (!table2 || !table2.table_name) {
3496
+ return 'tokens';
3050
3497
  }
3051
3498
  return false;
3052
3499
  }
3500
+
3501
+ public async liveMigration(_migrationType: 'tokenTables') {
3502
+ const etypes = await this.getEtypes();
3503
+
3504
+ const connection = await this.getConnection(true);
3505
+ for (let etype of etypes) {
3506
+ await this.createTokensTable(etype, connection);
3507
+ }
3508
+ connection.done();
3509
+ }
3053
3510
  }