@nymphjs/driver-postgresql 1.0.0-beta.1 → 1.0.0-beta.100
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 +489 -0
- package/README.md +1 -1
- package/dist/PostgreSQLDriver.d.ts +86 -20
- package/dist/PostgreSQLDriver.js +1000 -505
- package/dist/PostgreSQLDriver.js.map +1 -1
- package/dist/PostgreSQLDriver.test.js +9 -12
- package/dist/PostgreSQLDriver.test.js.map +1 -1
- package/dist/conf/d.d.ts +26 -0
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +1 -3
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -24
- package/dist/index.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +23 -21
- package/src/PostgreSQLDriver.test.ts +5 -3
- package/src/PostgreSQLDriver.ts +1773 -945
- package/src/conf/defaults.ts +1 -1
- package/src/conf/index.ts +2 -2
- package/src/index.ts +2 -2
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
- package/dist/runPostgresqlSync.js +0 -35
- package/src/runPostgresqlSync.js +0 -35
- package/src/testpostgresql.js +0 -59
package/dist/PostgreSQLDriver.js
CHANGED
|
@@ -1,26 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import pg from 'pg';
|
|
2
|
+
import format from 'pg-format';
|
|
3
|
+
import Cursor from 'pg-cursor';
|
|
4
|
+
import { NymphDriver, EntityUniqueConstraintError, InvalidParametersError, NotConfiguredError, QueryFailedError, UnableToConnectError, xor, } from '@nymphjs/nymph';
|
|
5
|
+
import { makeTableSuffix } from '@nymphjs/guid';
|
|
6
|
+
import { PostgreSQLDriverConfigDefaults as defaults, } from './conf/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* The PostgreSQL Nymph database driver.
|
|
9
|
+
*/
|
|
10
|
+
export default class PostgreSQLDriver extends NymphDriver {
|
|
11
|
+
config;
|
|
12
|
+
postgresqlConfig;
|
|
13
|
+
prefix;
|
|
14
|
+
connected = false;
|
|
15
|
+
// @ts-ignore: this is assigned in connect(), which is called by the constructor.
|
|
16
|
+
link;
|
|
17
|
+
transaction = null;
|
|
13
18
|
static escape(input) {
|
|
14
|
-
return
|
|
19
|
+
return format.ident(input);
|
|
15
20
|
}
|
|
16
21
|
static escapeValue(input) {
|
|
17
|
-
return
|
|
22
|
+
return format.literal(input);
|
|
23
|
+
}
|
|
24
|
+
static escapeNullSequences(input) {
|
|
25
|
+
// Postgres doesn't support null bytes in `text`, and it converts strings
|
|
26
|
+
// in JSON to `text`, so we need to escape the escape sequences for null
|
|
27
|
+
// bytes.
|
|
28
|
+
return (input
|
|
29
|
+
.replace(/\uFFFD/g, () => '\uFFFD\uFFFD')
|
|
30
|
+
// n so that if there's already an escape, it turns into \n
|
|
31
|
+
// - so that it won't match a \uFFFD that got turned into \uFFFD\uFFFD
|
|
32
|
+
.replace(/\\u0000/g, () => 'nu\uFFFD-')
|
|
33
|
+
.replace(/\\x00/g, () => 'nx\uFFFD-'));
|
|
34
|
+
}
|
|
35
|
+
static unescapeNullSequences(input) {
|
|
36
|
+
return input
|
|
37
|
+
.replace(/nu\uFFFD-/g, () => '\\u0000')
|
|
38
|
+
.replace(/nx\uFFFD-/g, () => '\\x00')
|
|
39
|
+
.replace(/\uFFFD\uFFFD/g, () => '\uFFFD');
|
|
40
|
+
}
|
|
41
|
+
static escapeNulls(input) {
|
|
42
|
+
// Postgres doesn't support null bytes in `text`.
|
|
43
|
+
return input
|
|
44
|
+
.replace(/\uFFFD/g, () => '\uFFFD\uFFFD')
|
|
45
|
+
.replace(/\x00/g, () => '-\uFFFD-');
|
|
46
|
+
}
|
|
47
|
+
static unescapeNulls(input) {
|
|
48
|
+
return input
|
|
49
|
+
.replace(/-\uFFFD-/g, () => '\x00')
|
|
50
|
+
.replace(/\uFFFD\uFFFD/g, () => '\uFFFD');
|
|
18
51
|
}
|
|
19
52
|
constructor(config, link, transaction) {
|
|
20
53
|
super();
|
|
21
|
-
this.
|
|
22
|
-
this.transaction = null;
|
|
23
|
-
this.config = { ...conf_1.PostgreSQLDriverConfigDefaults, ...config };
|
|
54
|
+
this.config = { ...defaults, ...config };
|
|
24
55
|
const { host, user, password, database, port, customPoolConfig } = this.config;
|
|
25
56
|
this.postgresqlConfig = customPoolConfig ?? {
|
|
26
57
|
host,
|
|
@@ -41,16 +72,40 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
41
72
|
this.connect();
|
|
42
73
|
}
|
|
43
74
|
}
|
|
44
|
-
|
|
45
|
-
|
|
75
|
+
/**
|
|
76
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
77
|
+
*
|
|
78
|
+
* @returns A clone of this instance.
|
|
79
|
+
*/
|
|
80
|
+
clone() {
|
|
81
|
+
return new PostgreSQLDriver(this.config, this.link, this.transaction ?? undefined);
|
|
82
|
+
}
|
|
83
|
+
getConnection(outsideTransaction = false) {
|
|
84
|
+
if (this.transaction != null &&
|
|
85
|
+
this.transaction.connection != null &&
|
|
86
|
+
!outsideTransaction) {
|
|
46
87
|
return Promise.resolve(this.transaction.connection);
|
|
47
88
|
}
|
|
48
|
-
return new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
89
|
+
return new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
90
|
+
? reject(err)
|
|
91
|
+
: client
|
|
92
|
+
? resolve({ client, done })
|
|
93
|
+
: reject('No client returned from connect.')));
|
|
49
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Connect to the PostgreSQL database.
|
|
97
|
+
*
|
|
98
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
99
|
+
*/
|
|
50
100
|
async connect() {
|
|
101
|
+
// If we think we're connected, try pinging the server.
|
|
51
102
|
try {
|
|
52
103
|
if (this.connected) {
|
|
53
|
-
const connection = await new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
104
|
+
const connection = await new Promise((resolve, reject) => this.link.connect((err, client, done) => err
|
|
105
|
+
? reject(err)
|
|
106
|
+
: client
|
|
107
|
+
? resolve({ client, done })
|
|
108
|
+
: reject('No client returned from connect.')));
|
|
54
109
|
await new Promise((resolve, reject) => connection.client.query('SELECT 1;', [], (err, res) => {
|
|
55
110
|
if (err) {
|
|
56
111
|
reject(err);
|
|
@@ -63,9 +118,10 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
63
118
|
catch (e) {
|
|
64
119
|
this.connected = false;
|
|
65
120
|
}
|
|
121
|
+
// Connecting, selecting database
|
|
66
122
|
if (!this.connected) {
|
|
67
123
|
try {
|
|
68
|
-
this.link = new
|
|
124
|
+
this.link = new pg.Pool(this.postgresqlConfig);
|
|
69
125
|
this.connected = true;
|
|
70
126
|
}
|
|
71
127
|
catch (e) {
|
|
@@ -73,15 +129,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
73
129
|
this.postgresqlConfig.user === 'nymph' &&
|
|
74
130
|
this.postgresqlConfig.password === 'password' &&
|
|
75
131
|
this.postgresqlConfig.database === 'nymph') {
|
|
76
|
-
throw new
|
|
132
|
+
throw new NotConfiguredError("It seems the config hasn't been set up correctly.");
|
|
77
133
|
}
|
|
78
134
|
else {
|
|
79
|
-
throw new
|
|
135
|
+
throw new UnableToConnectError('Could not connect: ' + e?.message);
|
|
80
136
|
}
|
|
81
137
|
}
|
|
82
138
|
}
|
|
83
139
|
return this.connected;
|
|
84
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Disconnect from the PostgreSQL database.
|
|
143
|
+
*
|
|
144
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
145
|
+
*/
|
|
85
146
|
async disconnect() {
|
|
86
147
|
if (this.connected) {
|
|
87
148
|
await new Promise((resolve) => this.link.end(() => resolve(0)));
|
|
@@ -90,87 +151,179 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
90
151
|
return this.connected;
|
|
91
152
|
}
|
|
92
153
|
async inTransaction() {
|
|
154
|
+
if (this.transaction && this.transaction.count === 0) {
|
|
155
|
+
this.transaction = null;
|
|
156
|
+
}
|
|
93
157
|
return !!this.transaction;
|
|
94
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Check connection status.
|
|
161
|
+
*
|
|
162
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
163
|
+
*/
|
|
95
164
|
isConnected() {
|
|
96
165
|
return this.connected;
|
|
97
166
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
167
|
+
async createEntitiesTable(etype, connection) {
|
|
168
|
+
// Create the entity table.
|
|
169
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} (
|
|
101
170
|
"guid" BYTEA NOT NULL,
|
|
102
171
|
"tags" TEXT[],
|
|
103
172
|
"cdate" DOUBLE PRECISION NOT NULL,
|
|
104
173
|
"mdate" DOUBLE PRECISION NOT NULL,
|
|
105
174
|
PRIMARY KEY ("guid")
|
|
106
|
-
) WITH ( OIDS=FALSE )
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
FOREIGN KEY ("guid")
|
|
120
|
-
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
121
|
-
) WITH ( OIDS=FALSE );`);
|
|
122
|
-
this.queryRunSync(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`);
|
|
123
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`);
|
|
124
|
-
this.queryRunSync(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`);
|
|
125
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`);
|
|
126
|
-
this.queryRunSync(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`);
|
|
127
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)};`);
|
|
128
|
-
this.queryRunSync(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'user'::text;`);
|
|
129
|
-
this.queryRunSync(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)};`);
|
|
130
|
-
this.queryRunSync(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'group'::text;`);
|
|
131
|
-
this.queryRunSync(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}comparisons_${etype}`)} (
|
|
175
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
176
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
177
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)};`, { connection });
|
|
178
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("cdate");`, { connection });
|
|
179
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)};`, { connection });
|
|
180
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("mdate");`, { connection });
|
|
181
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)};`, { connection });
|
|
182
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("tags");`, { connection });
|
|
183
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
|
|
184
|
+
}
|
|
185
|
+
async createDataTable(etype, connection) {
|
|
186
|
+
// Create the data table.
|
|
187
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} (
|
|
132
188
|
"guid" BYTEA NOT NULL,
|
|
133
189
|
"name" TEXT NOT NULL,
|
|
134
|
-
"
|
|
190
|
+
"value" CHARACTER(1) NOT NULL,
|
|
191
|
+
"json" JSONB,
|
|
135
192
|
"string" TEXT,
|
|
136
193
|
"number" DOUBLE PRECISION,
|
|
194
|
+
"truthy" BOOLEAN,
|
|
137
195
|
PRIMARY KEY ("guid", "name"),
|
|
138
196
|
FOREIGN KEY ("guid")
|
|
139
197
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
140
|
-
) WITH ( OIDS=FALSE )
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
198
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
199
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
200
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`, { connection });
|
|
201
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`, { connection });
|
|
202
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)};`, { connection });
|
|
203
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name");`, { connection });
|
|
204
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)};`, { connection });
|
|
205
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__user`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'user'::text;`, { connection });
|
|
206
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)};`, { connection });
|
|
207
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name__group`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name" = 'group'::text;`, { connection });
|
|
208
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`, { connection });
|
|
209
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`, { connection });
|
|
210
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)};`, { connection });
|
|
211
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", LEFT("string", 512));`, { connection });
|
|
212
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)};`, { connection });
|
|
213
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", "number");`, { connection });
|
|
214
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_number`)};`, { connection });
|
|
215
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_number`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name", "number");`, { connection });
|
|
216
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)};`, { connection });
|
|
217
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name", "truthy");`, { connection });
|
|
218
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_truthy`)};`, { connection });
|
|
219
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid", "name", "truthy");`, { connection });
|
|
220
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)};`, { connection });
|
|
221
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("string" gin_trgm_ops);`, { connection });
|
|
222
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)};`, { connection });
|
|
223
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("json");`, { connection });
|
|
224
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuserread`)};`, { connection });
|
|
225
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuserread`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='acUser' AND "number" >= 1;`, { connection });
|
|
226
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroupread`)};`, { connection });
|
|
227
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroupread`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='acGroup' AND "number" >= 1;`, { connection });
|
|
228
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acotherread`)};`, { connection });
|
|
229
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acotherread`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='acOther' AND "number" >= 1;`, { connection });
|
|
230
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuser`)};`, { connection });
|
|
231
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acuser`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='user';`, { connection });
|
|
232
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroup`)};`, { connection });
|
|
233
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_acgroup`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid") WHERE "name"='group';`, { connection });
|
|
234
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
|
|
235
|
+
}
|
|
236
|
+
async createReferencesTable(etype, connection) {
|
|
237
|
+
// Create the references table.
|
|
238
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} (
|
|
151
239
|
"guid" BYTEA NOT NULL,
|
|
152
240
|
"name" TEXT NOT NULL,
|
|
153
241
|
"reference" BYTEA NOT NULL,
|
|
154
242
|
PRIMARY KEY ("guid", "name", "reference"),
|
|
155
243
|
FOREIGN KEY ("guid")
|
|
156
244
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
157
|
-
) WITH ( OIDS=FALSE )
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
245
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
246
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
247
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)};`, { connection });
|
|
248
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid");`, { connection });
|
|
249
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)};`, { connection });
|
|
250
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name");`, { connection });
|
|
251
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)};`, { connection });
|
|
252
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name", "reference");`, { connection });
|
|
253
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)};`, { connection });
|
|
254
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference");`, { connection });
|
|
255
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_name_reference`)};`, { connection });
|
|
256
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_name_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid", "name", "reference");`, { connection });
|
|
257
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_name_guid`)};`, { connection });
|
|
258
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_name_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference", "name", "guid");`, { connection });
|
|
259
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_guid_name`)};`, { connection });
|
|
260
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_guid_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference", "guid", "name");`, { connection });
|
|
261
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_nameuser`)};`, { connection });
|
|
262
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_nameuser`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid", "reference") WHERE "name"='user';`, { connection });
|
|
263
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_namegroup`)};`, { connection });
|
|
264
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_reference_namegroup`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid", "reference") WHERE "name"='group';`, { connection });
|
|
265
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
|
|
266
|
+
}
|
|
267
|
+
async createTokensTable(etype, connection) {
|
|
268
|
+
// Create the tokens table.
|
|
269
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} (
|
|
270
|
+
"guid" BYTEA NOT NULL,
|
|
271
|
+
"name" TEXT NOT NULL,
|
|
272
|
+
"token" INTEGER NOT NULL,
|
|
273
|
+
"position" INTEGER NOT NULL,
|
|
274
|
+
"stem" BOOLEAN NOT NULL,
|
|
275
|
+
PRIMARY KEY ("guid", "name", "token", "position"),
|
|
276
|
+
FOREIGN KEY ("guid")
|
|
277
|
+
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
278
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
279
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
280
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_name_token`)};`, { connection });
|
|
281
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_name_token`)} ON ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} USING btree ("name", "token");`, { connection });
|
|
282
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} SET ( autovacuum_vacuum_scale_factor = 0.05, autovacuum_analyze_scale_factor = 0.05 );`, { connection });
|
|
283
|
+
}
|
|
284
|
+
async createUniquesTable(etype, connection) {
|
|
285
|
+
// Create the unique strings table.
|
|
286
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} (
|
|
287
|
+
"guid" BYTEA NOT NULL,
|
|
288
|
+
"unique" TEXT NOT NULL UNIQUE,
|
|
289
|
+
PRIMARY KEY ("guid", "unique"),
|
|
290
|
+
FOREIGN KEY ("guid")
|
|
291
|
+
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
292
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
293
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Create entity tables in the database.
|
|
297
|
+
*
|
|
298
|
+
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
299
|
+
* @returns True on success, false on failure.
|
|
300
|
+
*/
|
|
301
|
+
async createTables(etype = null) {
|
|
302
|
+
const connection = await this.getConnection(true);
|
|
303
|
+
if (etype != null) {
|
|
304
|
+
await this.createEntitiesTable(etype, connection);
|
|
305
|
+
await this.createDataTable(etype, connection);
|
|
306
|
+
await this.createReferencesTable(etype, connection);
|
|
307
|
+
await this.createTokensTable(etype, connection);
|
|
308
|
+
await this.createUniquesTable(etype, connection);
|
|
165
309
|
}
|
|
166
310
|
else {
|
|
167
|
-
|
|
311
|
+
// Add trigram extensions.
|
|
312
|
+
try {
|
|
313
|
+
await this.queryRun(`CREATE EXTENSION pg_trgm;`, { connection });
|
|
314
|
+
}
|
|
315
|
+
catch (e) {
|
|
316
|
+
// Ignore errors.
|
|
317
|
+
}
|
|
318
|
+
// Create the UID table.
|
|
319
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uids`)} (
|
|
168
320
|
"name" TEXT NOT NULL,
|
|
169
321
|
"cur_uid" BIGINT NOT NULL,
|
|
170
322
|
PRIMARY KEY ("name")
|
|
171
|
-
) WITH ( OIDS = FALSE )
|
|
172
|
-
this.
|
|
323
|
+
) WITH ( OIDS = FALSE );`, { connection });
|
|
324
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}uids`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
173
325
|
}
|
|
326
|
+
connection.done();
|
|
174
327
|
return true;
|
|
175
328
|
}
|
|
176
329
|
translateQuery(origQuery, origParams) {
|
|
@@ -188,49 +341,32 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
188
341
|
}
|
|
189
342
|
async query(runQuery, query, etypes = []) {
|
|
190
343
|
try {
|
|
344
|
+
this.nymph.config.debugInfo('postgresql:query', query);
|
|
191
345
|
return await runQuery();
|
|
192
346
|
}
|
|
193
347
|
catch (e) {
|
|
194
348
|
const errorCode = e?.code;
|
|
195
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
349
|
+
if (errorCode === '42P01' && (await this.createTables())) {
|
|
350
|
+
// If the tables don't exist yet, create them.
|
|
196
351
|
for (let etype of etypes) {
|
|
197
|
-
this.createTables(etype);
|
|
352
|
+
await this.createTables(etype);
|
|
198
353
|
}
|
|
199
354
|
try {
|
|
200
355
|
return await runQuery();
|
|
201
356
|
}
|
|
202
357
|
catch (e2) {
|
|
203
|
-
throw new
|
|
358
|
+
throw new QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query);
|
|
204
359
|
}
|
|
205
360
|
}
|
|
206
|
-
else {
|
|
207
|
-
throw
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
querySync(runQuery, query, etypes = []) {
|
|
212
|
-
try {
|
|
213
|
-
return runQuery();
|
|
214
|
-
}
|
|
215
|
-
catch (e) {
|
|
216
|
-
const errorCode = e?.code;
|
|
217
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
218
|
-
for (let etype of etypes) {
|
|
219
|
-
this.createTables(etype);
|
|
220
|
-
}
|
|
221
|
-
try {
|
|
222
|
-
return runQuery();
|
|
223
|
-
}
|
|
224
|
-
catch (e2) {
|
|
225
|
-
throw new nymph_1.QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query);
|
|
226
|
-
}
|
|
361
|
+
else if (errorCode === '23505') {
|
|
362
|
+
throw new EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
227
363
|
}
|
|
228
364
|
else {
|
|
229
|
-
throw e;
|
|
365
|
+
throw new QueryFailedError('Query failed: ' + e?.code + ' - ' + e?.message, query);
|
|
230
366
|
}
|
|
231
367
|
}
|
|
232
368
|
}
|
|
233
|
-
|
|
369
|
+
queryArray(query, { etypes = [], params = {}, } = {}) {
|
|
234
370
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
235
371
|
return this.query(async () => {
|
|
236
372
|
const results = await new Promise((resolve, reject) => {
|
|
@@ -246,32 +382,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
246
382
|
return results.rows;
|
|
247
383
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
248
384
|
}
|
|
249
|
-
|
|
385
|
+
async queryIter(query, { etypes = [], params = {}, } = {}) {
|
|
250
386
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
for (const name in err) {
|
|
274
|
-
e[name] = err[name];
|
|
387
|
+
const that = this;
|
|
388
|
+
return this.query(async function* () {
|
|
389
|
+
const transaction = !!that.transaction?.connection;
|
|
390
|
+
const connection = await that.getConnection();
|
|
391
|
+
const cursor = new Cursor(newQuery, newParams);
|
|
392
|
+
const iter = connection.client.query(cursor);
|
|
393
|
+
while (true) {
|
|
394
|
+
const rows = await iter.read(100);
|
|
395
|
+
if (!rows.length) {
|
|
396
|
+
await new Promise((resolve) => {
|
|
397
|
+
iter.close(() => {
|
|
398
|
+
if (!transaction) {
|
|
399
|
+
connection.done();
|
|
400
|
+
}
|
|
401
|
+
resolve();
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
for (let row of rows) {
|
|
407
|
+
yield row;
|
|
408
|
+
}
|
|
275
409
|
}
|
|
276
410
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
277
411
|
}
|
|
@@ -291,12 +425,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
291
425
|
return results.rows[0];
|
|
292
426
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
293
427
|
}
|
|
294
|
-
queryRun(query, { etypes = [], params = {}, } = {}) {
|
|
428
|
+
queryRun(query, { etypes = [], params = {}, connection, } = {}) {
|
|
295
429
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
296
430
|
return this.query(async () => {
|
|
297
431
|
const results = await new Promise((resolve, reject) => {
|
|
298
432
|
try {
|
|
299
|
-
(this.transaction?.connection?.client ??
|
|
433
|
+
((connection ?? this.transaction?.connection)?.client ??
|
|
434
|
+
this.link)
|
|
300
435
|
.query(newQuery, newParams)
|
|
301
436
|
.then((results) => resolve(results), (error) => reject(error));
|
|
302
437
|
}
|
|
@@ -307,39 +442,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
307
442
|
return { rowCount: results.rowCount ?? 0 };
|
|
308
443
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
309
444
|
}
|
|
310
|
-
queryRunSync(query, { etypes = [], params = {}, } = {}) {
|
|
311
|
-
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
312
|
-
return this.querySync(() => {
|
|
313
|
-
const output = child_process_1.default.spawnSync(process.argv0, [__dirname + '/runPostgresqlSync.js'], {
|
|
314
|
-
input: JSON.stringify({
|
|
315
|
-
postgresqlConfig: this.postgresqlConfig,
|
|
316
|
-
query: newQuery,
|
|
317
|
-
params: newParams,
|
|
318
|
-
}),
|
|
319
|
-
timeout: 30000,
|
|
320
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
321
|
-
encoding: 'utf8',
|
|
322
|
-
windowsHide: true,
|
|
323
|
-
});
|
|
324
|
-
try {
|
|
325
|
-
const results = JSON.parse(output.stdout);
|
|
326
|
-
return { rowCount: results.rowCount ?? 0 };
|
|
327
|
-
}
|
|
328
|
-
catch (e) {
|
|
329
|
-
}
|
|
330
|
-
if (output.status === 0) {
|
|
331
|
-
throw new Error('Unknown parse error.');
|
|
332
|
-
}
|
|
333
|
-
const err = JSON.parse(output.stderr);
|
|
334
|
-
const e = new Error(err.name);
|
|
335
|
-
for (const name in err) {
|
|
336
|
-
e[name] = err[name];
|
|
337
|
-
}
|
|
338
|
-
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
339
|
-
}
|
|
340
445
|
async commit(name) {
|
|
341
446
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
342
|
-
throw new
|
|
447
|
+
throw new InvalidParametersError('Transaction commit attempted without a name.');
|
|
343
448
|
}
|
|
344
449
|
if (!this.transaction || this.transaction.count === 0) {
|
|
345
450
|
this.transaction = null;
|
|
@@ -379,32 +484,40 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
379
484
|
guid,
|
|
380
485
|
},
|
|
381
486
|
});
|
|
382
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
487
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
383
488
|
etypes: [etype],
|
|
384
489
|
params: {
|
|
385
490
|
guid,
|
|
386
491
|
},
|
|
387
492
|
});
|
|
388
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
493
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
494
|
+
etypes: [etype],
|
|
495
|
+
params: {
|
|
496
|
+
guid,
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
389
500
|
etypes: [etype],
|
|
390
501
|
params: {
|
|
391
502
|
guid,
|
|
392
503
|
},
|
|
393
504
|
});
|
|
394
|
-
await this.commit('nymph-delete');
|
|
395
|
-
if (this.nymph.config.cache) {
|
|
396
|
-
this.cleanCache(guid);
|
|
397
|
-
}
|
|
398
|
-
return true;
|
|
399
505
|
}
|
|
400
506
|
catch (e) {
|
|
507
|
+
this.nymph.config.debugError('postgresql', `Delete entity error: "${e}"`);
|
|
401
508
|
await this.rollback('nymph-delete');
|
|
402
509
|
throw e;
|
|
403
510
|
}
|
|
511
|
+
await this.commit('nymph-delete');
|
|
512
|
+
// Remove any cached versions of this entity.
|
|
513
|
+
if (this.nymph.config.cache) {
|
|
514
|
+
this.cleanCache(guid);
|
|
515
|
+
}
|
|
516
|
+
return true;
|
|
404
517
|
}
|
|
405
518
|
async deleteUID(name) {
|
|
406
519
|
if (!name) {
|
|
407
|
-
throw new
|
|
520
|
+
throw new InvalidParametersError('Name not given for UID');
|
|
408
521
|
}
|
|
409
522
|
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
410
523
|
params: {
|
|
@@ -413,79 +526,128 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
413
526
|
});
|
|
414
527
|
return true;
|
|
415
528
|
}
|
|
416
|
-
async
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
writeLine('#');
|
|
424
|
-
writeLine('# UIDs');
|
|
425
|
-
writeLine('#');
|
|
426
|
-
writeLine('');
|
|
427
|
-
let uids = await this.queryIter(`SELECT * FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ORDER BY "name";`);
|
|
428
|
-
for (const uid of uids) {
|
|
429
|
-
writeLine(`<${uid.name}>[${uid.cur_uid}]`);
|
|
430
|
-
}
|
|
431
|
-
writeLine('');
|
|
432
|
-
writeLine('#');
|
|
433
|
-
writeLine('# Entities');
|
|
434
|
-
writeLine('#');
|
|
435
|
-
writeLine('');
|
|
436
|
-
const tables = await this.queryIter('SELECT relname FROM pg_stat_user_tables ORDER BY relname;');
|
|
529
|
+
async getEtypes() {
|
|
530
|
+
const tables = await this.queryArray('SELECT "table_name" AS "table_name" FROM "information_schema"."tables" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name" LIKE @prefix;', {
|
|
531
|
+
params: {
|
|
532
|
+
db: this.config.database,
|
|
533
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
534
|
+
},
|
|
535
|
+
});
|
|
437
536
|
const etypes = [];
|
|
438
|
-
for (const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
537
|
+
for (const table of tables) {
|
|
538
|
+
etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
|
|
539
|
+
}
|
|
540
|
+
return etypes;
|
|
541
|
+
}
|
|
542
|
+
async *exportDataIterator() {
|
|
543
|
+
if (yield {
|
|
544
|
+
type: 'comment',
|
|
545
|
+
content: `#nex2
|
|
546
|
+
# Nymph Entity Exchange v2
|
|
547
|
+
# http://nymph.io
|
|
548
|
+
#
|
|
549
|
+
# Generation Time: ${new Date().toLocaleString()}
|
|
550
|
+
`,
|
|
551
|
+
}) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (yield {
|
|
555
|
+
type: 'comment',
|
|
556
|
+
content: `
|
|
557
|
+
|
|
558
|
+
#
|
|
559
|
+
# UIDs
|
|
560
|
+
#
|
|
561
|
+
|
|
562
|
+
`,
|
|
563
|
+
}) {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
// Export UIDs.
|
|
567
|
+
let uids = await this.queryIter(`SELECT * FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ORDER BY "name";`);
|
|
568
|
+
for await (const uid of uids) {
|
|
569
|
+
if (yield { type: 'uid', content: `<${uid.name}>[${uid.cur_uid}]\n` }) {
|
|
570
|
+
return;
|
|
442
571
|
}
|
|
443
572
|
}
|
|
573
|
+
if (yield {
|
|
574
|
+
type: 'comment',
|
|
575
|
+
content: `
|
|
576
|
+
|
|
577
|
+
#
|
|
578
|
+
# Entities
|
|
579
|
+
#
|
|
580
|
+
|
|
581
|
+
`,
|
|
582
|
+
}) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
// Get the etypes.
|
|
586
|
+
const etypes = await this.getEtypes();
|
|
444
587
|
for (const etype of etypes) {
|
|
445
|
-
|
|
588
|
+
// Export entities.
|
|
589
|
+
const dataIterator = await this.queryIter(`SELECT encode(e."guid", 'hex') AS "guid", e."tags", e."cdate", e."mdate", d."name", d."value", d."json", d."string", d."number"
|
|
446
590
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} e
|
|
447
591
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} d ON e."guid"=d."guid"
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
let datum = dataIterator.next();
|
|
592
|
+
ORDER BY e."guid";`);
|
|
593
|
+
let datum = await dataIterator.next();
|
|
451
594
|
while (!datum.done) {
|
|
452
595
|
const guid = datum.value.guid;
|
|
453
|
-
const tags = datum.value.tags.join(',');
|
|
596
|
+
const tags = datum.value.tags.filter((tag) => tag).join(',');
|
|
454
597
|
const cdate = datum.value.cdate;
|
|
455
598
|
const mdate = datum.value.mdate;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
599
|
+
let currentEntityExport = [];
|
|
600
|
+
currentEntityExport.push(`{${guid}}<${etype}>[${tags}]`);
|
|
601
|
+
currentEntityExport.push(`\tcdate=${JSON.stringify(cdate)}`);
|
|
602
|
+
currentEntityExport.push(`\tmdate=${JSON.stringify(mdate)}`);
|
|
603
|
+
if (datum.value.name != null) {
|
|
604
|
+
// This do will keep going and adding the data until the
|
|
605
|
+
// next entity is reached. $row will end on the next entity.
|
|
460
606
|
do {
|
|
461
|
-
const value = datum.value.
|
|
607
|
+
const value = datum.value.value === 'N'
|
|
462
608
|
? JSON.stringify(Number(datum.value.number))
|
|
463
|
-
: datum.value.
|
|
464
|
-
? JSON.stringify(datum.value.string)
|
|
465
|
-
: datum.value.
|
|
466
|
-
|
|
467
|
-
|
|
609
|
+
: datum.value.value === 'S'
|
|
610
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(datum.value.string))
|
|
611
|
+
: datum.value.value === 'J'
|
|
612
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(datum.value.json))
|
|
613
|
+
: datum.value.value;
|
|
614
|
+
currentEntityExport.push(`\t${datum.value.name}=${value}`);
|
|
615
|
+
datum = await dataIterator.next();
|
|
468
616
|
} while (!datum.done && datum.value.guid === guid);
|
|
469
617
|
}
|
|
470
618
|
else {
|
|
471
|
-
datum
|
|
619
|
+
// Make sure that datum is incremented :)
|
|
620
|
+
datum = await dataIterator.next();
|
|
621
|
+
}
|
|
622
|
+
currentEntityExport.push('');
|
|
623
|
+
if (yield { type: 'entity', content: currentEntityExport.join('\n') }) {
|
|
624
|
+
return;
|
|
472
625
|
}
|
|
473
626
|
}
|
|
474
627
|
}
|
|
475
|
-
return;
|
|
476
628
|
}
|
|
477
|
-
|
|
629
|
+
/**
|
|
630
|
+
* Generate the PostgreSQL query.
|
|
631
|
+
* @param options The options array.
|
|
632
|
+
* @param formattedSelectors The formatted selector array.
|
|
633
|
+
* @param etype
|
|
634
|
+
* @param count Used to track internal params.
|
|
635
|
+
* @param params Used to store internal params.
|
|
636
|
+
* @param subquery Whether only a subquery should be returned.
|
|
637
|
+
* @returns The SQL query.
|
|
638
|
+
*/
|
|
639
|
+
makeEntityQuery(options, formattedSelectors, etype, count = { i: 0 }, params = {}, subquery = false, tableSuffix = '', etypes = [], guidSelector = undefined) {
|
|
478
640
|
if (typeof options.class?.alterOptions === 'function') {
|
|
479
641
|
options = options.class.alterOptions(options);
|
|
480
642
|
}
|
|
481
643
|
const eTable = `e${tableSuffix}`;
|
|
482
644
|
const dTable = `d${tableSuffix}`;
|
|
483
|
-
const cTable = `c${tableSuffix}`;
|
|
484
645
|
const fTable = `f${tableSuffix}`;
|
|
485
646
|
const ieTable = `ie${tableSuffix}`;
|
|
486
647
|
const countTable = `count${tableSuffix}`;
|
|
648
|
+
const sTable = `s${tableSuffix}`;
|
|
487
649
|
const sort = options.sort ?? 'cdate';
|
|
488
|
-
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, (key, value, typeIsOr, typeIsNot) => {
|
|
650
|
+
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, ({ key, value, typeIsOr, typeIsNot }) => {
|
|
489
651
|
const clauseNot = key.startsWith('!');
|
|
490
652
|
let curQuery = '';
|
|
491
653
|
for (const curValue of value) {
|
|
@@ -498,7 +660,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
498
660
|
}
|
|
499
661
|
const guid = `param${++count.i}`;
|
|
500
662
|
curQuery +=
|
|
501
|
-
(
|
|
663
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
502
664
|
ieTable +
|
|
503
665
|
'."guid"=decode(@' +
|
|
504
666
|
guid +
|
|
@@ -514,7 +676,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
514
676
|
}
|
|
515
677
|
const tag = `param${++count.i}`;
|
|
516
678
|
curQuery +=
|
|
517
|
-
(
|
|
679
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
518
680
|
'@' +
|
|
519
681
|
tag +
|
|
520
682
|
' <@ ie."tags"';
|
|
@@ -529,12 +691,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
529
691
|
}
|
|
530
692
|
const name = `param${++count.i}`;
|
|
531
693
|
curQuery +=
|
|
532
|
-
|
|
533
|
-
'
|
|
534
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
535
|
-
'IN (SELECT "guid" FROM ' +
|
|
694
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
695
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
536
696
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
537
|
-
' WHERE "
|
|
697
|
+
' WHERE "guid"=' +
|
|
698
|
+
ieTable +
|
|
699
|
+
'."guid" AND "name"=@' +
|
|
538
700
|
name +
|
|
539
701
|
')';
|
|
540
702
|
params[name] = curVar;
|
|
@@ -548,7 +710,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
548
710
|
}
|
|
549
711
|
if (curVar === 'cdate') {
|
|
550
712
|
curQuery +=
|
|
551
|
-
(
|
|
713
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
552
714
|
'(' +
|
|
553
715
|
ieTable +
|
|
554
716
|
'."cdate" NOT NULL)';
|
|
@@ -556,7 +718,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
556
718
|
}
|
|
557
719
|
else if (curVar === 'mdate') {
|
|
558
720
|
curQuery +=
|
|
559
|
-
(
|
|
721
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
560
722
|
'(' +
|
|
561
723
|
ieTable +
|
|
562
724
|
'."mdate" NOT NULL)';
|
|
@@ -565,11 +727,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
565
727
|
else {
|
|
566
728
|
const name = `param${++count.i}`;
|
|
567
729
|
curQuery +=
|
|
568
|
-
(
|
|
730
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
731
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
732
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
733
|
+
' WHERE "guid"=' +
|
|
569
734
|
ieTable +
|
|
570
|
-
'."guid"
|
|
571
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
572
|
-
' WHERE "name"=@' +
|
|
735
|
+
'."guid" AND "name"=@' +
|
|
573
736
|
name +
|
|
574
737
|
' AND "truthy"=TRUE)';
|
|
575
738
|
params[name] = curVar;
|
|
@@ -584,7 +747,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
584
747
|
}
|
|
585
748
|
const cdate = `param${++count.i}`;
|
|
586
749
|
curQuery +=
|
|
587
|
-
(
|
|
750
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
588
751
|
ieTable +
|
|
589
752
|
'."cdate"=@' +
|
|
590
753
|
cdate;
|
|
@@ -599,7 +762,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
599
762
|
}
|
|
600
763
|
const mdate = `param${++count.i}`;
|
|
601
764
|
curQuery +=
|
|
602
|
-
(
|
|
765
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
603
766
|
ieTable +
|
|
604
767
|
'."mdate"=@' +
|
|
605
768
|
mdate;
|
|
@@ -615,11 +778,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
615
778
|
const name = `param${++count.i}`;
|
|
616
779
|
const value = `param${++count.i}`;
|
|
617
780
|
curQuery +=
|
|
618
|
-
(
|
|
781
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
782
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
783
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
784
|
+
' WHERE "guid"=' +
|
|
619
785
|
ieTable +
|
|
620
|
-
'."guid"
|
|
621
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
622
|
-
' WHERE "name"=@' +
|
|
786
|
+
'."guid" AND "name"=@' +
|
|
623
787
|
name +
|
|
624
788
|
' AND "number"=@' +
|
|
625
789
|
value +
|
|
@@ -634,17 +798,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
634
798
|
const name = `param${++count.i}`;
|
|
635
799
|
const value = `param${++count.i}`;
|
|
636
800
|
curQuery +=
|
|
637
|
-
(
|
|
801
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
802
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
803
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
804
|
+
' WHERE "guid"=' +
|
|
638
805
|
ieTable +
|
|
639
|
-
'."guid"
|
|
640
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
641
|
-
' WHERE "name"=@' +
|
|
806
|
+
'."guid" AND "name"=@' +
|
|
642
807
|
name +
|
|
643
|
-
' AND "string"
|
|
644
|
-
|
|
808
|
+
' AND "string"=' +
|
|
809
|
+
(curValue[1].length < 512
|
|
810
|
+
? 'LEFT(@' + value + ', 512)'
|
|
811
|
+
: '@' + value) +
|
|
645
812
|
')';
|
|
646
813
|
params[name] = curValue[0];
|
|
647
|
-
params[value] = curValue[1];
|
|
814
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
648
815
|
}
|
|
649
816
|
else {
|
|
650
817
|
if (curQuery) {
|
|
@@ -661,17 +828,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
661
828
|
const name = `param${++count.i}`;
|
|
662
829
|
const value = `param${++count.i}`;
|
|
663
830
|
curQuery +=
|
|
664
|
-
(
|
|
665
|
-
|
|
666
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
831
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
832
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
667
833
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
668
|
-
' WHERE "
|
|
834
|
+
' WHERE "guid"=' +
|
|
835
|
+
ieTable +
|
|
836
|
+
'."guid" AND "name"=@' +
|
|
669
837
|
name +
|
|
670
|
-
' AND "
|
|
838
|
+
' AND "json"=@' +
|
|
671
839
|
value +
|
|
672
840
|
')';
|
|
673
841
|
params[name] = curValue[0];
|
|
674
|
-
params[value] = svalue;
|
|
842
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
675
843
|
}
|
|
676
844
|
break;
|
|
677
845
|
case 'contain':
|
|
@@ -682,9 +850,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
682
850
|
}
|
|
683
851
|
const cdate = `param${++count.i}`;
|
|
684
852
|
curQuery +=
|
|
685
|
-
(
|
|
853
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
686
854
|
ieTable +
|
|
687
|
-
'."cdate"
|
|
855
|
+
'."cdate"=@' +
|
|
688
856
|
cdate;
|
|
689
857
|
params[cdate] = isNaN(Number(curValue[1]))
|
|
690
858
|
? null
|
|
@@ -697,9 +865,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
697
865
|
}
|
|
698
866
|
const mdate = `param${++count.i}`;
|
|
699
867
|
curQuery +=
|
|
700
|
-
(
|
|
868
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
701
869
|
ieTable +
|
|
702
|
-
'."mdate"
|
|
870
|
+
'."mdate"=@' +
|
|
703
871
|
mdate;
|
|
704
872
|
params[mdate] = isNaN(Number(curValue[1]))
|
|
705
873
|
? null
|
|
@@ -711,55 +879,151 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
711
879
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
712
880
|
}
|
|
713
881
|
let svalue;
|
|
714
|
-
let stringValue;
|
|
715
882
|
if (curValue[1] instanceof Object &&
|
|
716
883
|
typeof curValue[1].toReference === 'function') {
|
|
717
884
|
svalue = JSON.stringify(curValue[1].toReference());
|
|
718
|
-
stringValue = `${curValue[1].toReference()}`;
|
|
719
885
|
}
|
|
720
|
-
else
|
|
886
|
+
else if (typeof curValue[1] === 'string' ||
|
|
887
|
+
typeof curValue[1] === 'number') {
|
|
721
888
|
svalue = JSON.stringify(curValue[1]);
|
|
722
|
-
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
svalue = JSON.stringify([curValue[1]]);
|
|
723
892
|
}
|
|
724
893
|
const name = `param${++count.i}`;
|
|
725
894
|
const value = `param${++count.i}`;
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
(
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
'))';
|
|
747
|
-
params[stringParam] = stringValue;
|
|
895
|
+
curQuery +=
|
|
896
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
897
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
898
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
899
|
+
' WHERE "guid"=' +
|
|
900
|
+
ieTable +
|
|
901
|
+
'."guid" AND "name"=@' +
|
|
902
|
+
name +
|
|
903
|
+
' AND "json" @> @' +
|
|
904
|
+
value +
|
|
905
|
+
')';
|
|
906
|
+
params[name] = curValue[0];
|
|
907
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
908
|
+
}
|
|
909
|
+
break;
|
|
910
|
+
case 'search':
|
|
911
|
+
case '!search':
|
|
912
|
+
if (curValue[0] === 'cdate' || curValue[0] === 'mdate') {
|
|
913
|
+
if (curQuery) {
|
|
914
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
748
915
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
name +
|
|
757
|
-
' AND position(@' +
|
|
758
|
-
value +
|
|
759
|
-
' IN "value")>0)';
|
|
916
|
+
curQuery +=
|
|
917
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
|
|
918
|
+
break;
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
if (curQuery) {
|
|
922
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
760
923
|
}
|
|
924
|
+
const name = `param${++count.i}`;
|
|
925
|
+
const queryPartToken = (term) => {
|
|
926
|
+
const value = `param${++count.i}`;
|
|
927
|
+
params[value] = term.token;
|
|
928
|
+
return ('EXISTS (SELECT "guid" FROM ' +
|
|
929
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
930
|
+
' WHERE "guid"=' +
|
|
931
|
+
ieTable +
|
|
932
|
+
'."guid" AND "name"=@' +
|
|
933
|
+
name +
|
|
934
|
+
' AND "token"=@' +
|
|
935
|
+
value +
|
|
936
|
+
(term.nostemmed ? ' AND "stem"=FALSE' : '') +
|
|
937
|
+
')');
|
|
938
|
+
};
|
|
939
|
+
const queryPartSeries = (series) => {
|
|
940
|
+
const tokenTableSuffix = makeTableSuffix();
|
|
941
|
+
const tokenParts = series.tokens.map((token, i) => {
|
|
942
|
+
const value = `param${++count.i}`;
|
|
943
|
+
params[value] = token.token;
|
|
944
|
+
return {
|
|
945
|
+
fromClause: i === 0
|
|
946
|
+
? 'FROM ' +
|
|
947
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
948
|
+
' t' +
|
|
949
|
+
tokenTableSuffix +
|
|
950
|
+
'0'
|
|
951
|
+
: 'JOIN ' +
|
|
952
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
953
|
+
' t' +
|
|
954
|
+
tokenTableSuffix +
|
|
955
|
+
i +
|
|
956
|
+
' ON t' +
|
|
957
|
+
tokenTableSuffix +
|
|
958
|
+
i +
|
|
959
|
+
'."guid" = t' +
|
|
960
|
+
tokenTableSuffix +
|
|
961
|
+
'0."guid" AND t' +
|
|
962
|
+
tokenTableSuffix +
|
|
963
|
+
i +
|
|
964
|
+
'."name" = t' +
|
|
965
|
+
tokenTableSuffix +
|
|
966
|
+
'0."name" AND t' +
|
|
967
|
+
tokenTableSuffix +
|
|
968
|
+
i +
|
|
969
|
+
'."position" = t' +
|
|
970
|
+
tokenTableSuffix +
|
|
971
|
+
'0."position" + ' +
|
|
972
|
+
i,
|
|
973
|
+
whereClause: 't' +
|
|
974
|
+
tokenTableSuffix +
|
|
975
|
+
i +
|
|
976
|
+
'."token"=@' +
|
|
977
|
+
value +
|
|
978
|
+
(token.nostemmed
|
|
979
|
+
? ' AND t' + tokenTableSuffix + i + '."stem"=FALSE'
|
|
980
|
+
: ''),
|
|
981
|
+
};
|
|
982
|
+
});
|
|
983
|
+
return ('EXISTS (SELECT t' +
|
|
984
|
+
tokenTableSuffix +
|
|
985
|
+
'0."guid" ' +
|
|
986
|
+
tokenParts.map((part) => part.fromClause).join(' ') +
|
|
987
|
+
' WHERE t' +
|
|
988
|
+
tokenTableSuffix +
|
|
989
|
+
'0."guid"=' +
|
|
990
|
+
ieTable +
|
|
991
|
+
'."guid" AND t' +
|
|
992
|
+
tokenTableSuffix +
|
|
993
|
+
'0."name"=@' +
|
|
994
|
+
name +
|
|
995
|
+
' AND ' +
|
|
996
|
+
tokenParts.map((part) => part.whereClause).join(' AND ') +
|
|
997
|
+
')');
|
|
998
|
+
};
|
|
999
|
+
const queryPartTerm = (term) => {
|
|
1000
|
+
if (term.type === 'series') {
|
|
1001
|
+
return queryPartSeries(term);
|
|
1002
|
+
}
|
|
1003
|
+
else if (term.type === 'not') {
|
|
1004
|
+
return 'NOT ' + queryPartTerm(term.operand);
|
|
1005
|
+
}
|
|
1006
|
+
else if (term.type === 'or') {
|
|
1007
|
+
let queryParts = [];
|
|
1008
|
+
for (let operand of term.operands) {
|
|
1009
|
+
queryParts.push(queryPartTerm(operand));
|
|
1010
|
+
}
|
|
1011
|
+
return '(' + queryParts.join(' OR ') + ')';
|
|
1012
|
+
}
|
|
1013
|
+
return queryPartToken(term);
|
|
1014
|
+
};
|
|
1015
|
+
const parsedFTSQuery = this.tokenizer.parseSearchQuery(curValue[1]);
|
|
1016
|
+
// Run through the query and add terms.
|
|
1017
|
+
let termStrings = [];
|
|
1018
|
+
for (let term of parsedFTSQuery) {
|
|
1019
|
+
termStrings.push(queryPartTerm(term));
|
|
1020
|
+
}
|
|
1021
|
+
curQuery +=
|
|
1022
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1023
|
+
'(' +
|
|
1024
|
+
termStrings.join(' AND ') +
|
|
1025
|
+
')';
|
|
761
1026
|
params[name] = curValue[0];
|
|
762
|
-
params[value] = svalue;
|
|
763
1027
|
}
|
|
764
1028
|
break;
|
|
765
1029
|
case 'match':
|
|
@@ -770,7 +1034,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
770
1034
|
}
|
|
771
1035
|
const cdate = `param${++count.i}`;
|
|
772
1036
|
curQuery +=
|
|
773
|
-
(
|
|
1037
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
774
1038
|
'(' +
|
|
775
1039
|
ieTable +
|
|
776
1040
|
'."cdate" ~ @' +
|
|
@@ -785,7 +1049,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
785
1049
|
}
|
|
786
1050
|
const mdate = `param${++count.i}`;
|
|
787
1051
|
curQuery +=
|
|
788
|
-
(
|
|
1052
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
789
1053
|
'(' +
|
|
790
1054
|
ieTable +
|
|
791
1055
|
'."mdate" ~ @' +
|
|
@@ -801,17 +1065,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
801
1065
|
const name = `param${++count.i}`;
|
|
802
1066
|
const value = `param${++count.i}`;
|
|
803
1067
|
curQuery +=
|
|
804
|
-
(
|
|
1068
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1069
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1070
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1071
|
+
' WHERE "guid"=' +
|
|
805
1072
|
ieTable +
|
|
806
|
-
'."guid"
|
|
807
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
808
|
-
' WHERE "name"=@' +
|
|
1073
|
+
'."guid" AND "name"=@' +
|
|
809
1074
|
name +
|
|
810
1075
|
' AND "string" ~ @' +
|
|
811
1076
|
value +
|
|
812
1077
|
')';
|
|
813
1078
|
params[name] = curValue[0];
|
|
814
|
-
params[value] = curValue[1];
|
|
1079
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
815
1080
|
}
|
|
816
1081
|
break;
|
|
817
1082
|
case 'imatch':
|
|
@@ -822,7 +1087,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
822
1087
|
}
|
|
823
1088
|
const cdate = `param${++count.i}`;
|
|
824
1089
|
curQuery +=
|
|
825
|
-
(
|
|
1090
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
826
1091
|
'(' +
|
|
827
1092
|
ieTable +
|
|
828
1093
|
'."cdate" ~* @' +
|
|
@@ -837,7 +1102,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
837
1102
|
}
|
|
838
1103
|
const mdate = `param${++count.i}`;
|
|
839
1104
|
curQuery +=
|
|
840
|
-
(
|
|
1105
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
841
1106
|
'(' +
|
|
842
1107
|
ieTable +
|
|
843
1108
|
'."mdate" ~* @' +
|
|
@@ -853,17 +1118,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
853
1118
|
const name = `param${++count.i}`;
|
|
854
1119
|
const value = `param${++count.i}`;
|
|
855
1120
|
curQuery +=
|
|
856
|
-
(
|
|
1121
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1122
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1123
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1124
|
+
' WHERE "guid"=' +
|
|
857
1125
|
ieTable +
|
|
858
|
-
'."guid"
|
|
859
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
860
|
-
' WHERE "name"=@' +
|
|
1126
|
+
'."guid" AND "name"=@' +
|
|
861
1127
|
name +
|
|
862
1128
|
' AND "string" ~* @' +
|
|
863
1129
|
value +
|
|
864
1130
|
')';
|
|
865
1131
|
params[name] = curValue[0];
|
|
866
|
-
params[value] = curValue[1];
|
|
1132
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
867
1133
|
}
|
|
868
1134
|
break;
|
|
869
1135
|
case 'like':
|
|
@@ -874,7 +1140,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
874
1140
|
}
|
|
875
1141
|
const cdate = `param${++count.i}`;
|
|
876
1142
|
curQuery +=
|
|
877
|
-
(
|
|
1143
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
878
1144
|
'(' +
|
|
879
1145
|
ieTable +
|
|
880
1146
|
'."cdate" LIKE @' +
|
|
@@ -889,7 +1155,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
889
1155
|
}
|
|
890
1156
|
const mdate = `param${++count.i}`;
|
|
891
1157
|
curQuery +=
|
|
892
|
-
(
|
|
1158
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
893
1159
|
'(' +
|
|
894
1160
|
ieTable +
|
|
895
1161
|
'."mdate" LIKE @' +
|
|
@@ -905,17 +1171,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
905
1171
|
const name = `param${++count.i}`;
|
|
906
1172
|
const value = `param${++count.i}`;
|
|
907
1173
|
curQuery +=
|
|
908
|
-
(
|
|
1174
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1175
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1176
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1177
|
+
' WHERE "guid"=' +
|
|
909
1178
|
ieTable +
|
|
910
|
-
'."guid"
|
|
911
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
912
|
-
' WHERE "name"=@' +
|
|
1179
|
+
'."guid" AND "name"=@' +
|
|
913
1180
|
name +
|
|
914
1181
|
' AND "string" LIKE @' +
|
|
915
1182
|
value +
|
|
916
1183
|
')';
|
|
917
1184
|
params[name] = curValue[0];
|
|
918
|
-
params[value] = curValue[1];
|
|
1185
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
919
1186
|
}
|
|
920
1187
|
break;
|
|
921
1188
|
case 'ilike':
|
|
@@ -926,7 +1193,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
926
1193
|
}
|
|
927
1194
|
const cdate = `param${++count.i}`;
|
|
928
1195
|
curQuery +=
|
|
929
|
-
(
|
|
1196
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
930
1197
|
'(' +
|
|
931
1198
|
ieTable +
|
|
932
1199
|
'."cdate" ILIKE @' +
|
|
@@ -941,7 +1208,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
941
1208
|
}
|
|
942
1209
|
const mdate = `param${++count.i}`;
|
|
943
1210
|
curQuery +=
|
|
944
|
-
(
|
|
1211
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
945
1212
|
'(' +
|
|
946
1213
|
ieTable +
|
|
947
1214
|
'."mdate" ILIKE @' +
|
|
@@ -957,17 +1224,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
957
1224
|
const name = `param${++count.i}`;
|
|
958
1225
|
const value = `param${++count.i}`;
|
|
959
1226
|
curQuery +=
|
|
960
|
-
(
|
|
1227
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1228
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1229
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1230
|
+
' WHERE "guid"=' +
|
|
961
1231
|
ieTable +
|
|
962
|
-
'."guid"
|
|
963
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
964
|
-
' WHERE "name"=@' +
|
|
1232
|
+
'."guid" AND "name"=@' +
|
|
965
1233
|
name +
|
|
966
1234
|
' AND "string" ILIKE @' +
|
|
967
1235
|
value +
|
|
968
1236
|
')';
|
|
969
1237
|
params[name] = curValue[0];
|
|
970
|
-
params[value] = curValue[1];
|
|
1238
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
971
1239
|
}
|
|
972
1240
|
break;
|
|
973
1241
|
case 'gt':
|
|
@@ -978,7 +1246,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
978
1246
|
}
|
|
979
1247
|
const cdate = `param${++count.i}`;
|
|
980
1248
|
curQuery +=
|
|
981
|
-
(
|
|
1249
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
982
1250
|
ieTable +
|
|
983
1251
|
'."cdate">@' +
|
|
984
1252
|
cdate;
|
|
@@ -993,7 +1261,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
993
1261
|
}
|
|
994
1262
|
const mdate = `param${++count.i}`;
|
|
995
1263
|
curQuery +=
|
|
996
|
-
(
|
|
1264
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
997
1265
|
ieTable +
|
|
998
1266
|
'."mdate">@' +
|
|
999
1267
|
mdate;
|
|
@@ -1009,11 +1277,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1009
1277
|
const name = `param${++count.i}`;
|
|
1010
1278
|
const value = `param${++count.i}`;
|
|
1011
1279
|
curQuery +=
|
|
1012
|
-
(
|
|
1280
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1281
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1282
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1283
|
+
' WHERE "guid"=' +
|
|
1013
1284
|
ieTable +
|
|
1014
|
-
'."guid"
|
|
1015
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1016
|
-
' WHERE "name"=@' +
|
|
1285
|
+
'."guid" AND "name"=@' +
|
|
1017
1286
|
name +
|
|
1018
1287
|
' AND "number">@' +
|
|
1019
1288
|
value +
|
|
@@ -1032,7 +1301,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1032
1301
|
}
|
|
1033
1302
|
const cdate = `param${++count.i}`;
|
|
1034
1303
|
curQuery +=
|
|
1035
|
-
(
|
|
1304
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1036
1305
|
ieTable +
|
|
1037
1306
|
'."cdate">=@' +
|
|
1038
1307
|
cdate;
|
|
@@ -1047,7 +1316,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1047
1316
|
}
|
|
1048
1317
|
const mdate = `param${++count.i}`;
|
|
1049
1318
|
curQuery +=
|
|
1050
|
-
(
|
|
1319
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1051
1320
|
ieTable +
|
|
1052
1321
|
'."mdate">=@' +
|
|
1053
1322
|
mdate;
|
|
@@ -1063,11 +1332,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1063
1332
|
const name = `param${++count.i}`;
|
|
1064
1333
|
const value = `param${++count.i}`;
|
|
1065
1334
|
curQuery +=
|
|
1066
|
-
(
|
|
1335
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1336
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1337
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1338
|
+
' WHERE "guid"=' +
|
|
1067
1339
|
ieTable +
|
|
1068
|
-
'."guid"
|
|
1069
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1070
|
-
' WHERE "name"=@' +
|
|
1340
|
+
'."guid" AND "name"=@' +
|
|
1071
1341
|
name +
|
|
1072
1342
|
' AND "number">=@' +
|
|
1073
1343
|
value +
|
|
@@ -1086,7 +1356,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1086
1356
|
}
|
|
1087
1357
|
const cdate = `param${++count.i}`;
|
|
1088
1358
|
curQuery +=
|
|
1089
|
-
(
|
|
1359
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1090
1360
|
ieTable +
|
|
1091
1361
|
'."cdate"<@' +
|
|
1092
1362
|
cdate;
|
|
@@ -1101,7 +1371,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1101
1371
|
}
|
|
1102
1372
|
const mdate = `param${++count.i}`;
|
|
1103
1373
|
curQuery +=
|
|
1104
|
-
(
|
|
1374
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1105
1375
|
ieTable +
|
|
1106
1376
|
'."mdate"<@' +
|
|
1107
1377
|
mdate;
|
|
@@ -1117,11 +1387,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1117
1387
|
const name = `param${++count.i}`;
|
|
1118
1388
|
const value = `param${++count.i}`;
|
|
1119
1389
|
curQuery +=
|
|
1120
|
-
(
|
|
1390
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1391
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1392
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1393
|
+
' WHERE "guid"=' +
|
|
1121
1394
|
ieTable +
|
|
1122
|
-
'."guid"
|
|
1123
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1124
|
-
' WHERE "name"=@' +
|
|
1395
|
+
'."guid" AND "name"=@' +
|
|
1125
1396
|
name +
|
|
1126
1397
|
' AND "number"<@' +
|
|
1127
1398
|
value +
|
|
@@ -1140,7 +1411,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1140
1411
|
}
|
|
1141
1412
|
const cdate = `param${++count.i}`;
|
|
1142
1413
|
curQuery +=
|
|
1143
|
-
(
|
|
1414
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1144
1415
|
ieTable +
|
|
1145
1416
|
'."cdate"<=@' +
|
|
1146
1417
|
cdate;
|
|
@@ -1155,7 +1426,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1155
1426
|
}
|
|
1156
1427
|
const mdate = `param${++count.i}`;
|
|
1157
1428
|
curQuery +=
|
|
1158
|
-
(
|
|
1429
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1159
1430
|
ieTable +
|
|
1160
1431
|
'."mdate"<=@' +
|
|
1161
1432
|
mdate;
|
|
@@ -1171,11 +1442,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1171
1442
|
const name = `param${++count.i}`;
|
|
1172
1443
|
const value = `param${++count.i}`;
|
|
1173
1444
|
curQuery +=
|
|
1174
|
-
(
|
|
1445
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1446
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1447
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1448
|
+
' WHERE "guid"=' +
|
|
1175
1449
|
ieTable +
|
|
1176
|
-
'."guid"
|
|
1177
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1178
|
-
' WHERE "name"=@' +
|
|
1450
|
+
'."guid" AND "name"=@' +
|
|
1179
1451
|
name +
|
|
1180
1452
|
' AND "number"<=@' +
|
|
1181
1453
|
value +
|
|
@@ -1204,11 +1476,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1204
1476
|
const name = `param${++count.i}`;
|
|
1205
1477
|
const guid = `param${++count.i}`;
|
|
1206
1478
|
curQuery +=
|
|
1207
|
-
(
|
|
1208
|
-
|
|
1209
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1479
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1480
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1210
1481
|
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1211
|
-
' WHERE "
|
|
1482
|
+
' WHERE "guid"=' +
|
|
1483
|
+
ieTable +
|
|
1484
|
+
'."guid" AND "name"=@' +
|
|
1212
1485
|
name +
|
|
1213
1486
|
' AND "reference"=decode(@' +
|
|
1214
1487
|
guid +
|
|
@@ -1223,29 +1496,37 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1223
1496
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1224
1497
|
}
|
|
1225
1498
|
curQuery +=
|
|
1226
|
-
(
|
|
1499
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1227
1500
|
'(' +
|
|
1228
1501
|
subquery.query +
|
|
1229
1502
|
')';
|
|
1230
1503
|
break;
|
|
1231
1504
|
case 'qref':
|
|
1232
1505
|
case '!qref':
|
|
1506
|
+
const referenceTableSuffix = makeTableSuffix();
|
|
1233
1507
|
const [qrefOptions, ...qrefSelectors] = curValue[1];
|
|
1234
1508
|
const QrefEntityClass = qrefOptions.class;
|
|
1235
1509
|
etypes.push(QrefEntityClass.ETYPE);
|
|
1236
|
-
const qrefQuery = this.makeEntityQuery({ ...qrefOptions, return: 'guid', class: QrefEntityClass }, qrefSelectors, QrefEntityClass.ETYPE, count, params, false,
|
|
1510
|
+
const qrefQuery = this.makeEntityQuery({ ...qrefOptions, return: 'guid', class: QrefEntityClass }, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' + referenceTableSuffix + '."reference"');
|
|
1237
1511
|
if (curQuery) {
|
|
1238
1512
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1239
1513
|
}
|
|
1240
1514
|
const qrefName = `param${++count.i}`;
|
|
1241
1515
|
curQuery +=
|
|
1242
|
-
(
|
|
1243
|
-
|
|
1244
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1516
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1517
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1245
1518
|
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1246
|
-
'
|
|
1519
|
+
' r' +
|
|
1520
|
+
referenceTableSuffix +
|
|
1521
|
+
' WHERE r' +
|
|
1522
|
+
referenceTableSuffix +
|
|
1523
|
+
'."guid"=' +
|
|
1524
|
+
ieTable +
|
|
1525
|
+
'."guid" AND r' +
|
|
1526
|
+
referenceTableSuffix +
|
|
1527
|
+
'."name"=@' +
|
|
1247
1528
|
qrefName +
|
|
1248
|
-
' AND
|
|
1529
|
+
' AND EXISTS (' +
|
|
1249
1530
|
qrefQuery.query +
|
|
1250
1531
|
'))';
|
|
1251
1532
|
params[qrefName] = curValue[0];
|
|
@@ -1255,18 +1536,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1255
1536
|
return curQuery;
|
|
1256
1537
|
});
|
|
1257
1538
|
let sortBy;
|
|
1539
|
+
let sortByInner;
|
|
1540
|
+
let sortJoin = '';
|
|
1541
|
+
let sortJoinInner = '';
|
|
1542
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1258
1543
|
switch (sort) {
|
|
1259
1544
|
case 'mdate':
|
|
1260
|
-
sortBy =
|
|
1545
|
+
sortBy = `${eTable}."mdate"${order}`;
|
|
1546
|
+
sortByInner = `${ieTable}."mdate"${order}`;
|
|
1261
1547
|
break;
|
|
1262
1548
|
case 'cdate':
|
|
1549
|
+
sortBy = `${eTable}."cdate"${order}`;
|
|
1550
|
+
sortByInner = `${ieTable}."cdate"${order}`;
|
|
1551
|
+
break;
|
|
1263
1552
|
default:
|
|
1264
|
-
|
|
1553
|
+
const name = `param${++count.i}`;
|
|
1554
|
+
sortJoin = `LEFT JOIN (
|
|
1555
|
+
SELECT "guid", "string", "number"
|
|
1556
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1557
|
+
WHERE "name"=@${name}
|
|
1558
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1559
|
+
) ${sTable} ON ${eTable}."guid"=${sTable}."guid"`;
|
|
1560
|
+
sortJoinInner = `LEFT JOIN (
|
|
1561
|
+
SELECT "guid", "string", "number"
|
|
1562
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1563
|
+
WHERE "name"=@${name}
|
|
1564
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1565
|
+
) ${sTable} ON ${ieTable}."guid"=${sTable}."guid"`;
|
|
1566
|
+
sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1567
|
+
sortByInner = sortBy;
|
|
1568
|
+
params[name] = sort;
|
|
1265
1569
|
break;
|
|
1266
1570
|
}
|
|
1267
|
-
if (options.reverse) {
|
|
1268
|
-
sortBy += ' DESC';
|
|
1269
|
-
}
|
|
1270
1571
|
let query;
|
|
1271
1572
|
if (queryParts.length) {
|
|
1272
1573
|
if (subquery) {
|
|
@@ -1282,18 +1583,21 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1282
1583
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1283
1584
|
}
|
|
1284
1585
|
const whereClause = queryParts.join(') AND (');
|
|
1586
|
+
const guidClause = guidSelector
|
|
1587
|
+
? `${ieTable}."guid"=${guidSelector} AND `
|
|
1588
|
+
: '';
|
|
1285
1589
|
if (options.return === 'count') {
|
|
1286
1590
|
if (limit || offset) {
|
|
1287
1591
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1288
|
-
SELECT
|
|
1592
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1289
1593
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1290
|
-
WHERE (${whereClause})${limit}${offset}
|
|
1594
|
+
WHERE ${guidClause}(${whereClause})${limit}${offset}
|
|
1291
1595
|
) ${countTable}`;
|
|
1292
1596
|
}
|
|
1293
1597
|
else {
|
|
1294
1598
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1295
1599
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1296
|
-
WHERE (${whereClause})`;
|
|
1600
|
+
WHERE ${guidClause}(${whereClause})`;
|
|
1297
1601
|
}
|
|
1298
1602
|
}
|
|
1299
1603
|
else if (options.return === 'guid') {
|
|
@@ -1302,8 +1606,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1302
1606
|
: `${ieTable}."guid"`;
|
|
1303
1607
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1304
1608
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1305
|
-
|
|
1306
|
-
|
|
1609
|
+
${sortJoinInner}
|
|
1610
|
+
WHERE ${guidClause}(${whereClause})
|
|
1611
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1307
1612
|
}
|
|
1308
1613
|
else {
|
|
1309
1614
|
query = `SELECT
|
|
@@ -1313,18 +1618,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1313
1618
|
${eTable}."mdate",
|
|
1314
1619
|
${dTable}."name",
|
|
1315
1620
|
${dTable}."value",
|
|
1316
|
-
${
|
|
1317
|
-
${
|
|
1621
|
+
${dTable}."json",
|
|
1622
|
+
${dTable}."string",
|
|
1623
|
+
${dTable}."number"
|
|
1318
1624
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1319
1625
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1320
|
-
|
|
1626
|
+
${sortJoin}
|
|
1321
1627
|
INNER JOIN (
|
|
1322
1628
|
SELECT ${ieTable}."guid"
|
|
1323
1629
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1324
|
-
|
|
1325
|
-
|
|
1630
|
+
${sortJoinInner}
|
|
1631
|
+
WHERE ${guidClause}(${whereClause})
|
|
1632
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1326
1633
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1327
|
-
ORDER BY ${
|
|
1634
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1328
1635
|
}
|
|
1329
1636
|
}
|
|
1330
1637
|
}
|
|
@@ -1341,16 +1648,19 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1341
1648
|
if ('offset' in options) {
|
|
1342
1649
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1343
1650
|
}
|
|
1651
|
+
const guidClause = guidSelector
|
|
1652
|
+
? ` WHERE ${ieTable}."guid"=${guidSelector}`
|
|
1653
|
+
: '';
|
|
1344
1654
|
if (options.return === 'count') {
|
|
1345
1655
|
if (limit || offset) {
|
|
1346
1656
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1347
|
-
SELECT
|
|
1348
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${limit}${offset}
|
|
1657
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1658
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}${limit}${offset}
|
|
1349
1659
|
) ${countTable}`;
|
|
1350
1660
|
}
|
|
1351
1661
|
else {
|
|
1352
1662
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1353
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}`;
|
|
1663
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}`;
|
|
1354
1664
|
}
|
|
1355
1665
|
}
|
|
1356
1666
|
else if (options.return === 'guid') {
|
|
@@ -1359,7 +1669,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1359
1669
|
: `${ieTable}."guid"`;
|
|
1360
1670
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1361
1671
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1362
|
-
|
|
1672
|
+
${sortJoinInner}
|
|
1673
|
+
${guidClause}
|
|
1674
|
+
ORDER BY ${sortByInner}, ${ieTable}."guid"${limit}${offset}`;
|
|
1363
1675
|
}
|
|
1364
1676
|
else {
|
|
1365
1677
|
if (limit || offset) {
|
|
@@ -1370,17 +1682,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1370
1682
|
${eTable}."mdate",
|
|
1371
1683
|
${dTable}."name",
|
|
1372
1684
|
${dTable}."value",
|
|
1373
|
-
${
|
|
1374
|
-
${
|
|
1685
|
+
${dTable}."json",
|
|
1686
|
+
${dTable}."string",
|
|
1687
|
+
${dTable}."number"
|
|
1375
1688
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1376
1689
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1377
|
-
|
|
1690
|
+
${sortJoin}
|
|
1378
1691
|
INNER JOIN (
|
|
1379
1692
|
SELECT ${ieTable}."guid"
|
|
1380
1693
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1381
|
-
|
|
1694
|
+
${sortJoinInner}
|
|
1695
|
+
${guidClause}
|
|
1696
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1382
1697
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1383
|
-
ORDER BY ${
|
|
1698
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1384
1699
|
}
|
|
1385
1700
|
else {
|
|
1386
1701
|
query = `SELECT
|
|
@@ -1390,12 +1705,14 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1390
1705
|
${eTable}."mdate",
|
|
1391
1706
|
${dTable}."name",
|
|
1392
1707
|
${dTable}."value",
|
|
1393
|
-
${
|
|
1394
|
-
${
|
|
1708
|
+
${dTable}."json",
|
|
1709
|
+
${dTable}."string",
|
|
1710
|
+
${dTable}."number"
|
|
1395
1711
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1396
1712
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1397
|
-
|
|
1398
|
-
|
|
1713
|
+
${sortJoin}
|
|
1714
|
+
${guidSelector ? `WHERE ${eTable}."guid"=${guidSelector}` : ''}
|
|
1715
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1399
1716
|
}
|
|
1400
1717
|
}
|
|
1401
1718
|
}
|
|
@@ -1411,58 +1728,41 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1411
1728
|
}
|
|
1412
1729
|
performQuery(options, formattedSelectors, etype) {
|
|
1413
1730
|
const { query, params, etypes } = this.makeEntityQuery(options, formattedSelectors, etype);
|
|
1414
|
-
const result = this.
|
|
1415
|
-
return {
|
|
1416
|
-
result,
|
|
1417
|
-
};
|
|
1418
|
-
}
|
|
1419
|
-
performQuerySync(options, formattedSelectors, etype) {
|
|
1420
|
-
const { query, params, etypes } = this.makeEntityQuery(options, formattedSelectors, etype);
|
|
1421
|
-
const result = (this.queryIterSync(query, { etypes, params }) || [])[Symbol.iterator]();
|
|
1731
|
+
const result = this.queryArray(query, { etypes, params }).then((val) => val[Symbol.iterator]());
|
|
1422
1732
|
return {
|
|
1423
1733
|
result,
|
|
1424
1734
|
};
|
|
1425
1735
|
}
|
|
1426
1736
|
async getEntities(options = {}, ...selectors) {
|
|
1427
|
-
const { result: resultPromise, process } = this.
|
|
1737
|
+
const { result: resultPromise, process } = this.getEntitiesRowLike(
|
|
1738
|
+
// @ts-ignore: options is correct here.
|
|
1739
|
+
options, selectors, ({ options, selectors, etype }) => this.performQuery(options, selectors, etype), () => {
|
|
1428
1740
|
const next = result.next();
|
|
1429
1741
|
return next.done ? null : next.value;
|
|
1430
1742
|
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1431
|
-
tags: row.tags,
|
|
1432
|
-
cdate: isNaN(Number(row.cdate)) ?
|
|
1433
|
-
mdate: isNaN(Number(row.mdate)) ?
|
|
1743
|
+
tags: row.tags.filter((tag) => tag),
|
|
1744
|
+
cdate: isNaN(Number(row.cdate)) ? Date.now() : Number(row.cdate),
|
|
1745
|
+
mdate: isNaN(Number(row.mdate)) ? Date.now() : Number(row.mdate),
|
|
1434
1746
|
}), (row) => ({
|
|
1435
1747
|
name: row.name,
|
|
1436
1748
|
svalue: row.value === 'N'
|
|
1437
1749
|
? JSON.stringify(Number(row.number))
|
|
1438
1750
|
: row.value === 'S'
|
|
1439
|
-
? JSON.stringify(row.string)
|
|
1440
|
-
: row.value
|
|
1751
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(row.string))
|
|
1752
|
+
: row.value === 'J'
|
|
1753
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(row.json))
|
|
1754
|
+
: row.value,
|
|
1441
1755
|
}));
|
|
1442
1756
|
const result = await resultPromise;
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
return next.done ? null : next.value;
|
|
1449
|
-
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1450
|
-
tags: row.tags,
|
|
1451
|
-
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
1452
|
-
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
1453
|
-
}), (row) => ({
|
|
1454
|
-
name: row.name,
|
|
1455
|
-
svalue: row.value === 'N'
|
|
1456
|
-
? JSON.stringify(Number(row.number))
|
|
1457
|
-
: row.value === 'S'
|
|
1458
|
-
? JSON.stringify(row.string)
|
|
1459
|
-
: row.value,
|
|
1460
|
-
}));
|
|
1461
|
-
return process();
|
|
1757
|
+
const value = process();
|
|
1758
|
+
if (value instanceof Error) {
|
|
1759
|
+
throw value;
|
|
1760
|
+
}
|
|
1761
|
+
return value;
|
|
1462
1762
|
}
|
|
1463
1763
|
async getUID(name) {
|
|
1464
1764
|
if (name == null) {
|
|
1465
|
-
throw new
|
|
1765
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1466
1766
|
}
|
|
1467
1767
|
const result = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
1468
1768
|
params: {
|
|
@@ -1471,50 +1771,61 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1471
1771
|
});
|
|
1472
1772
|
return result?.cur_uid == null ? null : Number(result.cur_uid);
|
|
1473
1773
|
}
|
|
1474
|
-
async
|
|
1774
|
+
async importEntity(entity) {
|
|
1775
|
+
return await this.importEntityInternal(entity, false);
|
|
1776
|
+
}
|
|
1777
|
+
async importEntityTokens(entity) {
|
|
1778
|
+
return await this.importEntityInternal(entity, true);
|
|
1779
|
+
}
|
|
1780
|
+
async importEntityInternal({ guid, cdate, mdate, tags, sdata, etype, }, onlyTokens) {
|
|
1475
1781
|
try {
|
|
1476
|
-
|
|
1477
|
-
|
|
1782
|
+
let promises = [];
|
|
1783
|
+
if (!onlyTokens) {
|
|
1784
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1478
1785
|
etypes: [etype],
|
|
1479
1786
|
params: {
|
|
1480
1787
|
guid,
|
|
1481
1788
|
},
|
|
1482
|
-
});
|
|
1483
|
-
|
|
1789
|
+
}));
|
|
1790
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1484
1791
|
etypes: [etype],
|
|
1485
1792
|
params: {
|
|
1486
1793
|
guid,
|
|
1487
|
-
tags,
|
|
1488
|
-
cdate: isNaN(Number(JSON.parse(sdata.cdate)))
|
|
1489
|
-
? null
|
|
1490
|
-
: Number(JSON.parse(sdata.cdate)),
|
|
1491
|
-
mdate: isNaN(Number(JSON.parse(sdata.mdate)))
|
|
1492
|
-
? null
|
|
1493
|
-
: Number(JSON.parse(sdata.mdate)),
|
|
1494
1794
|
},
|
|
1495
|
-
});
|
|
1496
|
-
|
|
1497
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1795
|
+
}));
|
|
1796
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1498
1797
|
etypes: [etype],
|
|
1499
1798
|
params: {
|
|
1500
1799
|
guid,
|
|
1501
1800
|
},
|
|
1502
1801
|
}));
|
|
1503
|
-
|
|
1802
|
+
}
|
|
1803
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1804
|
+
etypes: [etype],
|
|
1805
|
+
params: {
|
|
1806
|
+
guid,
|
|
1807
|
+
},
|
|
1808
|
+
}));
|
|
1809
|
+
if (!onlyTokens) {
|
|
1810
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1504
1811
|
etypes: [etype],
|
|
1505
1812
|
params: {
|
|
1506
1813
|
guid,
|
|
1507
1814
|
},
|
|
1508
1815
|
}));
|
|
1509
|
-
|
|
1816
|
+
}
|
|
1817
|
+
await Promise.all(promises);
|
|
1818
|
+
promises = [];
|
|
1819
|
+
if (!onlyTokens) {
|
|
1820
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate);`, {
|
|
1510
1821
|
etypes: [etype],
|
|
1511
1822
|
params: {
|
|
1512
1823
|
guid,
|
|
1824
|
+
tags,
|
|
1825
|
+
cdate: isNaN(cdate) ? null : cdate,
|
|
1826
|
+
mdate: isNaN(mdate) ? null : mdate,
|
|
1513
1827
|
},
|
|
1514
|
-
})
|
|
1515
|
-
await Promise.all(promises);
|
|
1516
|
-
delete sdata.cdate;
|
|
1517
|
-
delete sdata.mdate;
|
|
1828
|
+
});
|
|
1518
1829
|
for (const name in sdata) {
|
|
1519
1830
|
const value = sdata[name];
|
|
1520
1831
|
const uvalue = JSON.parse(value);
|
|
@@ -1525,24 +1836,22 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1525
1836
|
? 'N'
|
|
1526
1837
|
: typeof uvalue === 'string'
|
|
1527
1838
|
? 'S'
|
|
1528
|
-
:
|
|
1529
|
-
const
|
|
1530
|
-
|
|
1839
|
+
: 'J';
|
|
1840
|
+
const jsonValue = storageValue === 'J'
|
|
1841
|
+
? PostgreSQLDriver.escapeNullSequences(value)
|
|
1842
|
+
: null;
|
|
1843
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`, {
|
|
1531
1844
|
etypes: [etype],
|
|
1532
1845
|
params: {
|
|
1533
1846
|
guid,
|
|
1534
1847
|
name,
|
|
1535
1848
|
storageValue,
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
params: {
|
|
1541
|
-
guid,
|
|
1542
|
-
name,
|
|
1543
|
-
truthy: !!uvalue,
|
|
1544
|
-
string: `${uvalue}`,
|
|
1849
|
+
jsonValue,
|
|
1850
|
+
string: storageValue === 'J'
|
|
1851
|
+
? null
|
|
1852
|
+
: PostgreSQLDriver.escapeNulls(`${uvalue}`),
|
|
1545
1853
|
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
1854
|
+
truthy: !!uvalue,
|
|
1546
1855
|
},
|
|
1547
1856
|
}));
|
|
1548
1857
|
const references = this.findReferences(value);
|
|
@@ -1557,43 +1866,111 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1557
1866
|
}));
|
|
1558
1867
|
}
|
|
1559
1868
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1869
|
+
}
|
|
1870
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
1871
|
+
for (let name in sdata) {
|
|
1872
|
+
let tokenString = null;
|
|
1873
|
+
try {
|
|
1874
|
+
tokenString = EntityClass.getFTSText(name, JSON.parse(sdata[name]));
|
|
1875
|
+
}
|
|
1876
|
+
catch (e) {
|
|
1877
|
+
// Ignore error.
|
|
1878
|
+
}
|
|
1879
|
+
if (tokenString != null) {
|
|
1880
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
1881
|
+
while (tokens.length) {
|
|
1882
|
+
const currentTokens = tokens.splice(0, 100);
|
|
1883
|
+
const params = {
|
|
1884
|
+
guid,
|
|
1885
|
+
name,
|
|
1886
|
+
};
|
|
1887
|
+
const values = [];
|
|
1888
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
1889
|
+
const token = currentTokens[i];
|
|
1890
|
+
params['token' + i] = token.token;
|
|
1891
|
+
params['position' + i] = token.position;
|
|
1892
|
+
params['stem' + i] = token.stem;
|
|
1893
|
+
values.push("(decode(@guid, 'hex'), @name, @token" +
|
|
1894
|
+
i +
|
|
1895
|
+
', @position' +
|
|
1896
|
+
i +
|
|
1897
|
+
', @stem' +
|
|
1898
|
+
i +
|
|
1899
|
+
')');
|
|
1900
|
+
}
|
|
1901
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
1902
|
+
etypes: [etype],
|
|
1903
|
+
params,
|
|
1904
|
+
}));
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
if (!onlyTokens) {
|
|
1909
|
+
const uniques = await EntityClass.getUniques({
|
|
1910
|
+
guid,
|
|
1911
|
+
cdate,
|
|
1912
|
+
mdate,
|
|
1913
|
+
tags,
|
|
1914
|
+
data: {},
|
|
1915
|
+
sdata,
|
|
1572
1916
|
});
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1917
|
+
for (const unique of uniques) {
|
|
1918
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
1919
|
+
etypes: [etype],
|
|
1920
|
+
params: {
|
|
1921
|
+
guid,
|
|
1922
|
+
unique,
|
|
1923
|
+
},
|
|
1924
|
+
}).catch((e) => {
|
|
1925
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
1926
|
+
this.nymph.config.debugError('postgresql', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
1927
|
+
}
|
|
1928
|
+
return e;
|
|
1929
|
+
}));
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
await Promise.all(promises);
|
|
1933
|
+
}
|
|
1934
|
+
catch (e) {
|
|
1935
|
+
this.nymph.config.debugError('postgresql', `Import entity error: "${e}"`);
|
|
1936
|
+
throw e;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
async importUID({ name, value }) {
|
|
1940
|
+
try {
|
|
1941
|
+
await this.internalTransaction(`nymph-import-uid-${name}`);
|
|
1942
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
1943
|
+
params: {
|
|
1944
|
+
name,
|
|
1945
|
+
},
|
|
1577
1946
|
});
|
|
1578
|
-
|
|
1947
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @value);`, {
|
|
1948
|
+
params: {
|
|
1949
|
+
name,
|
|
1950
|
+
value,
|
|
1951
|
+
},
|
|
1952
|
+
});
|
|
1953
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
1579
1954
|
}
|
|
1580
1955
|
catch (e) {
|
|
1581
|
-
|
|
1582
|
-
|
|
1956
|
+
this.nymph.config.debugError('postgresql', `Import UID error: "${e}"`);
|
|
1957
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
1958
|
+
throw e;
|
|
1583
1959
|
}
|
|
1584
1960
|
}
|
|
1585
1961
|
async newUID(name) {
|
|
1586
1962
|
if (name == null) {
|
|
1587
|
-
throw new
|
|
1963
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1588
1964
|
}
|
|
1589
1965
|
await this.internalTransaction('nymph-newuid');
|
|
1966
|
+
let curUid = undefined;
|
|
1590
1967
|
try {
|
|
1591
1968
|
const lock = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name FOR UPDATE;`, {
|
|
1592
1969
|
params: {
|
|
1593
1970
|
name,
|
|
1594
1971
|
},
|
|
1595
1972
|
});
|
|
1596
|
-
|
|
1973
|
+
curUid = lock?.cur_uid == null ? undefined : Number(lock.cur_uid);
|
|
1597
1974
|
if (curUid == null) {
|
|
1598
1975
|
curUid = 1;
|
|
1599
1976
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
@@ -1612,17 +1989,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1612
1989
|
},
|
|
1613
1990
|
});
|
|
1614
1991
|
}
|
|
1615
|
-
await this.commit('nymph-newuid');
|
|
1616
|
-
return curUid;
|
|
1617
1992
|
}
|
|
1618
1993
|
catch (e) {
|
|
1994
|
+
this.nymph.config.debugError('postgresql', `New UID error: "${e}"`);
|
|
1619
1995
|
await this.rollback('nymph-newuid');
|
|
1620
1996
|
throw e;
|
|
1621
1997
|
}
|
|
1998
|
+
await this.commit('nymph-newuid');
|
|
1999
|
+
return curUid;
|
|
1622
2000
|
}
|
|
1623
2001
|
async renameUID(oldName, newName) {
|
|
1624
2002
|
if (oldName == null || newName == null) {
|
|
1625
|
-
throw new
|
|
2003
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1626
2004
|
}
|
|
1627
2005
|
await this.queryRun(`UPDATE ${PostgreSQLDriver.escape(`${this.prefix}uids`)} SET "name"=@newName WHERE "name"=@oldName;`, {
|
|
1628
2006
|
params: {
|
|
@@ -1634,7 +2012,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1634
2012
|
}
|
|
1635
2013
|
async rollback(name) {
|
|
1636
2014
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1637
|
-
throw new
|
|
2015
|
+
throw new InvalidParametersError('Transaction rollback attempted without a name.');
|
|
1638
2016
|
}
|
|
1639
2017
|
if (!this.transaction || this.transaction.count === 0) {
|
|
1640
2018
|
this.transaction = null;
|
|
@@ -1651,7 +2029,8 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1651
2029
|
return true;
|
|
1652
2030
|
}
|
|
1653
2031
|
async saveEntity(entity) {
|
|
1654
|
-
const insertData = async (guid, data, sdata, etype) => {
|
|
2032
|
+
const insertData = async (guid, data, sdata, uniques, etype) => {
|
|
2033
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
1655
2034
|
const runInsertQuery = async (name, value, svalue) => {
|
|
1656
2035
|
if (value === undefined) {
|
|
1657
2036
|
return;
|
|
@@ -1660,24 +2039,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1660
2039
|
? 'N'
|
|
1661
2040
|
: typeof value === 'string'
|
|
1662
2041
|
? 'S'
|
|
1663
|
-
:
|
|
2042
|
+
: 'J';
|
|
2043
|
+
const jsonValue = storageValue === 'J'
|
|
2044
|
+
? PostgreSQLDriver.escapeNullSequences(svalue)
|
|
2045
|
+
: null;
|
|
1664
2046
|
const promises = [];
|
|
1665
|
-
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`, {
|
|
2047
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (decode(@guid, 'hex'), @name, @storageValue, @jsonValue, @string, @number, @truthy);`, {
|
|
1666
2048
|
etypes: [etype],
|
|
1667
2049
|
params: {
|
|
1668
2050
|
guid,
|
|
1669
2051
|
name,
|
|
1670
2052
|
storageValue,
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
params: {
|
|
1676
|
-
guid,
|
|
1677
|
-
name,
|
|
1678
|
-
truthy: !!value,
|
|
1679
|
-
string: `${value}`,
|
|
2053
|
+
jsonValue,
|
|
2054
|
+
string: storageValue === 'J'
|
|
2055
|
+
? null
|
|
2056
|
+
: PostgreSQLDriver.escapeNulls(`${value}`),
|
|
1680
2057
|
number: isNaN(Number(value)) ? null : Number(value),
|
|
2058
|
+
truthy: !!value,
|
|
1681
2059
|
},
|
|
1682
2060
|
}));
|
|
1683
2061
|
const references = this.findReferences(svalue);
|
|
@@ -1691,8 +2069,60 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1691
2069
|
},
|
|
1692
2070
|
}));
|
|
1693
2071
|
}
|
|
2072
|
+
let tokenString = null;
|
|
2073
|
+
try {
|
|
2074
|
+
tokenString = EntityClass.getFTSText(name, value);
|
|
2075
|
+
}
|
|
2076
|
+
catch (e) {
|
|
2077
|
+
// Ignore error.
|
|
2078
|
+
}
|
|
2079
|
+
if (tokenString != null) {
|
|
2080
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2081
|
+
while (tokens.length) {
|
|
2082
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2083
|
+
const params = {
|
|
2084
|
+
guid,
|
|
2085
|
+
name,
|
|
2086
|
+
};
|
|
2087
|
+
const values = [];
|
|
2088
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2089
|
+
const token = currentTokens[i];
|
|
2090
|
+
params['token' + i] = token.token;
|
|
2091
|
+
params['position' + i] = token.position;
|
|
2092
|
+
params['stem' + i] = token.stem;
|
|
2093
|
+
values.push("(decode(@guid, 'hex'), @name, @token" +
|
|
2094
|
+
i +
|
|
2095
|
+
', @position' +
|
|
2096
|
+
i +
|
|
2097
|
+
', @stem' +
|
|
2098
|
+
i +
|
|
2099
|
+
')');
|
|
2100
|
+
}
|
|
2101
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
2102
|
+
etypes: [etype],
|
|
2103
|
+
params,
|
|
2104
|
+
}));
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
1694
2107
|
await Promise.all(promises);
|
|
1695
2108
|
};
|
|
2109
|
+
for (const unique of uniques) {
|
|
2110
|
+
try {
|
|
2111
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
2112
|
+
etypes: [etype],
|
|
2113
|
+
params: {
|
|
2114
|
+
guid,
|
|
2115
|
+
unique,
|
|
2116
|
+
},
|
|
2117
|
+
});
|
|
2118
|
+
}
|
|
2119
|
+
catch (e) {
|
|
2120
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2121
|
+
this.nymph.config.debugError('postgresql', `Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
2122
|
+
}
|
|
2123
|
+
throw e;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
1696
2126
|
for (const name in data) {
|
|
1697
2127
|
await runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
1698
2128
|
}
|
|
@@ -1700,8 +2130,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1700
2130
|
await runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
1701
2131
|
}
|
|
1702
2132
|
};
|
|
2133
|
+
let inTransaction = false;
|
|
1703
2134
|
try {
|
|
1704
|
-
const result = await this.saveEntityRowLike(entity, async (
|
|
2135
|
+
const result = await this.saveEntityRowLike(entity, async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
2136
|
+
if (Object.keys(data).length === 0 &&
|
|
2137
|
+
Object.keys(sdata).length === 0) {
|
|
2138
|
+
return false;
|
|
2139
|
+
}
|
|
1705
2140
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (decode(@guid, 'hex'), @tags, @cdate, @cdate);`, {
|
|
1706
2141
|
etypes: [etype],
|
|
1707
2142
|
params: {
|
|
@@ -1710,9 +2145,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1710
2145
|
cdate,
|
|
1711
2146
|
},
|
|
1712
2147
|
});
|
|
1713
|
-
await insertData(guid, data, sdata, etype);
|
|
2148
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1714
2149
|
return true;
|
|
1715
|
-
}, async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
2150
|
+
}, async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
2151
|
+
if (Object.keys(data).length === 0 &&
|
|
2152
|
+
Object.keys(sdata).length === 0) {
|
|
2153
|
+
return false;
|
|
2154
|
+
}
|
|
1716
2155
|
const promises = [];
|
|
1717
2156
|
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1718
2157
|
etypes: [etype],
|
|
@@ -1726,13 +2165,19 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1726
2165
|
guid,
|
|
1727
2166
|
},
|
|
1728
2167
|
}));
|
|
1729
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2168
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1730
2169
|
etypes: [etype],
|
|
1731
2170
|
params: {
|
|
1732
2171
|
guid,
|
|
1733
2172
|
},
|
|
1734
2173
|
}));
|
|
1735
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2174
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
2175
|
+
etypes: [etype],
|
|
2176
|
+
params: {
|
|
2177
|
+
guid,
|
|
2178
|
+
},
|
|
2179
|
+
}));
|
|
2180
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1736
2181
|
etypes: [etype],
|
|
1737
2182
|
params: {
|
|
1738
2183
|
guid,
|
|
@@ -1757,44 +2202,57 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1757
2202
|
guid,
|
|
1758
2203
|
},
|
|
1759
2204
|
}));
|
|
1760
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2205
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1761
2206
|
etypes: [etype],
|
|
1762
2207
|
params: {
|
|
1763
2208
|
guid,
|
|
1764
2209
|
},
|
|
1765
2210
|
}));
|
|
1766
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2211
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
2212
|
+
etypes: [etype],
|
|
2213
|
+
params: {
|
|
2214
|
+
guid,
|
|
2215
|
+
},
|
|
2216
|
+
}));
|
|
2217
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1767
2218
|
etypes: [etype],
|
|
1768
2219
|
params: {
|
|
1769
2220
|
guid,
|
|
1770
2221
|
},
|
|
1771
2222
|
}));
|
|
1772
2223
|
await Promise.all(promises);
|
|
1773
|
-
await insertData(guid, data, sdata, etype);
|
|
2224
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1774
2225
|
success = true;
|
|
1775
2226
|
}
|
|
1776
2227
|
return success;
|
|
1777
2228
|
}, async () => {
|
|
1778
2229
|
await this.internalTransaction('nymph-save');
|
|
2230
|
+
inTransaction = true;
|
|
1779
2231
|
}, async (success) => {
|
|
1780
|
-
if (
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
2232
|
+
if (inTransaction) {
|
|
2233
|
+
inTransaction = false;
|
|
2234
|
+
if (success) {
|
|
2235
|
+
await this.commit('nymph-save');
|
|
2236
|
+
}
|
|
2237
|
+
else {
|
|
2238
|
+
await this.rollback('nymph-save');
|
|
2239
|
+
}
|
|
1785
2240
|
}
|
|
1786
2241
|
return success;
|
|
1787
2242
|
});
|
|
1788
2243
|
return result;
|
|
1789
2244
|
}
|
|
1790
2245
|
catch (e) {
|
|
1791
|
-
|
|
2246
|
+
this.nymph.config.debugError('postgresql', `Save entity error: "${e}"`);
|
|
2247
|
+
if (inTransaction) {
|
|
2248
|
+
await this.rollback('nymph-save');
|
|
2249
|
+
}
|
|
1792
2250
|
throw e;
|
|
1793
2251
|
}
|
|
1794
2252
|
}
|
|
1795
2253
|
async setUID(name, curUid) {
|
|
1796
2254
|
if (name == null) {
|
|
1797
|
-
throw new
|
|
2255
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1798
2256
|
}
|
|
1799
2257
|
await this.internalTransaction('nymph-setuid');
|
|
1800
2258
|
try {
|
|
@@ -1810,23 +2268,25 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1810
2268
|
curUid,
|
|
1811
2269
|
},
|
|
1812
2270
|
});
|
|
1813
|
-
await this.commit('nymph-setuid');
|
|
1814
|
-
return true;
|
|
1815
2271
|
}
|
|
1816
2272
|
catch (e) {
|
|
1817
2273
|
await this.rollback('nymph-setuid');
|
|
1818
2274
|
throw e;
|
|
1819
2275
|
}
|
|
2276
|
+
await this.commit('nymph-setuid');
|
|
2277
|
+
return true;
|
|
1820
2278
|
}
|
|
1821
2279
|
async internalTransaction(name) {
|
|
1822
2280
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1823
|
-
throw new
|
|
2281
|
+
throw new InvalidParametersError('Transaction start attempted without a name.');
|
|
1824
2282
|
}
|
|
1825
2283
|
if (!this.transaction || this.transaction.count === 0) {
|
|
2284
|
+
// Lock to one connection.
|
|
1826
2285
|
this.transaction = {
|
|
1827
2286
|
count: 0,
|
|
1828
2287
|
connection: await this.getConnection(),
|
|
1829
2288
|
};
|
|
2289
|
+
// We're not in a transaction yet, so start one.
|
|
1830
2290
|
await this.queryRun('BEGIN;');
|
|
1831
2291
|
}
|
|
1832
2292
|
await this.queryRun(`SAVEPOINT ${PostgreSQLDriver.escape(name)};`);
|
|
@@ -1834,16 +2294,51 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1834
2294
|
return this.transaction;
|
|
1835
2295
|
}
|
|
1836
2296
|
async startTransaction(name) {
|
|
1837
|
-
const inTransaction = this.inTransaction();
|
|
2297
|
+
const inTransaction = await this.inTransaction();
|
|
1838
2298
|
const transaction = await this.internalTransaction(name);
|
|
1839
2299
|
if (!inTransaction) {
|
|
1840
2300
|
this.transaction = null;
|
|
1841
2301
|
}
|
|
1842
2302
|
const nymph = this.nymph.clone();
|
|
1843
|
-
nymph.driver =
|
|
1844
|
-
nymph.driver.init(nymph);
|
|
2303
|
+
nymph.driver.transaction = transaction;
|
|
1845
2304
|
return nymph;
|
|
1846
2305
|
}
|
|
2306
|
+
async needsMigration() {
|
|
2307
|
+
const table = await this.queryGet('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;', {
|
|
2308
|
+
params: {
|
|
2309
|
+
db: this.config.database,
|
|
2310
|
+
prefix: this.prefix + 'data_' + '%',
|
|
2311
|
+
},
|
|
2312
|
+
});
|
|
2313
|
+
if (table?.table_name) {
|
|
2314
|
+
const result = await this.queryGet('SELECT 1 AS "exists" FROM "information_schema"."columns" WHERE "table_catalog"=@db AND "table_schema"=\'public\' AND "table_name"=@table AND "column_name"=\'json\';', {
|
|
2315
|
+
params: {
|
|
2316
|
+
db: this.config.database,
|
|
2317
|
+
table: table.table_name,
|
|
2318
|
+
},
|
|
2319
|
+
});
|
|
2320
|
+
if (!result?.exists) {
|
|
2321
|
+
return 'json';
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
const table2 = await this.queryGet('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;', {
|
|
2325
|
+
params: {
|
|
2326
|
+
db: this.config.database,
|
|
2327
|
+
tokenTable: this.prefix + 'tokens_' + '%',
|
|
2328
|
+
},
|
|
2329
|
+
});
|
|
2330
|
+
if (!table2 || !table2.table_name) {
|
|
2331
|
+
return 'tokens';
|
|
2332
|
+
}
|
|
2333
|
+
return false;
|
|
2334
|
+
}
|
|
2335
|
+
async liveMigration(_migrationType) {
|
|
2336
|
+
const etypes = await this.getEtypes();
|
|
2337
|
+
const connection = await this.getConnection(true);
|
|
2338
|
+
for (let etype of etypes) {
|
|
2339
|
+
await this.createTokensTable(etype, connection);
|
|
2340
|
+
}
|
|
2341
|
+
connection.done();
|
|
2342
|
+
}
|
|
1847
2343
|
}
|
|
1848
|
-
exports.default = PostgreSQLDriver;
|
|
1849
2344
|
//# sourceMappingURL=PostgreSQLDriver.js.map
|