@nymphjs/driver-postgresql 1.0.0-beta.11 → 1.0.0-beta.111
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 +508 -0
- package/README.md +1 -1
- package/dist/PostgreSQLDriver.d.ts +107 -20
- package/dist/PostgreSQLDriver.js +1647 -730
- package/dist/PostgreSQLDriver.js.map +1 -1
- package/dist/PostgreSQLDriver.test.js +12 -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 +24 -21
- package/src/PostgreSQLDriver.test.ts +9 -3
- package/src/PostgreSQLDriver.ts +2695 -1199
- 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,19 +72,40 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
41
72
|
this.connect();
|
|
42
73
|
}
|
|
43
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
77
|
+
*
|
|
78
|
+
* @returns A clone of this instance.
|
|
79
|
+
*/
|
|
44
80
|
clone() {
|
|
45
81
|
return new PostgreSQLDriver(this.config, this.link, this.transaction ?? undefined);
|
|
46
82
|
}
|
|
47
|
-
getConnection() {
|
|
48
|
-
if (this.transaction != null &&
|
|
83
|
+
getConnection(outsideTransaction = false) {
|
|
84
|
+
if (this.transaction != null &&
|
|
85
|
+
this.transaction.connection != null &&
|
|
86
|
+
!outsideTransaction) {
|
|
49
87
|
return Promise.resolve(this.transaction.connection);
|
|
50
88
|
}
|
|
51
|
-
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.')));
|
|
52
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Connect to the PostgreSQL database.
|
|
97
|
+
*
|
|
98
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
99
|
+
*/
|
|
53
100
|
async connect() {
|
|
101
|
+
// If we think we're connected, try pinging the server.
|
|
54
102
|
try {
|
|
55
103
|
if (this.connected) {
|
|
56
|
-
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.')));
|
|
57
109
|
await new Promise((resolve, reject) => connection.client.query('SELECT 1;', [], (err, res) => {
|
|
58
110
|
if (err) {
|
|
59
111
|
reject(err);
|
|
@@ -66,9 +118,10 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
66
118
|
catch (e) {
|
|
67
119
|
this.connected = false;
|
|
68
120
|
}
|
|
121
|
+
// Connecting, selecting database
|
|
69
122
|
if (!this.connected) {
|
|
70
123
|
try {
|
|
71
|
-
this.link = new
|
|
124
|
+
this.link = new pg.Pool(this.postgresqlConfig);
|
|
72
125
|
this.connected = true;
|
|
73
126
|
}
|
|
74
127
|
catch (e) {
|
|
@@ -76,15 +129,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
76
129
|
this.postgresqlConfig.user === 'nymph' &&
|
|
77
130
|
this.postgresqlConfig.password === 'password' &&
|
|
78
131
|
this.postgresqlConfig.database === 'nymph') {
|
|
79
|
-
throw new
|
|
132
|
+
throw new NotConfiguredError("It seems the config hasn't been set up correctly.");
|
|
80
133
|
}
|
|
81
134
|
else {
|
|
82
|
-
throw new
|
|
135
|
+
throw new UnableToConnectError('Could not connect: ' + e?.message);
|
|
83
136
|
}
|
|
84
137
|
}
|
|
85
138
|
}
|
|
86
139
|
return this.connected;
|
|
87
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Disconnect from the PostgreSQL database.
|
|
143
|
+
*
|
|
144
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
145
|
+
*/
|
|
88
146
|
async disconnect() {
|
|
89
147
|
if (this.connected) {
|
|
90
148
|
await new Promise((resolve) => this.link.end(() => resolve(0)));
|
|
@@ -93,87 +151,199 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
93
151
|
return this.connected;
|
|
94
152
|
}
|
|
95
153
|
async inTransaction() {
|
|
154
|
+
if (this.transaction && this.transaction.count === 0) {
|
|
155
|
+
this.transaction = null;
|
|
156
|
+
}
|
|
96
157
|
return !!this.transaction;
|
|
97
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Check connection status.
|
|
161
|
+
*
|
|
162
|
+
* @returns Whether this instance is connected to a PostgreSQL database.
|
|
163
|
+
*/
|
|
98
164
|
isConnected() {
|
|
99
165
|
return this.connected;
|
|
100
166
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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}`)} (
|
|
104
170
|
"guid" BYTEA NOT NULL,
|
|
105
171
|
"tags" TEXT[],
|
|
106
172
|
"cdate" DOUBLE PRECISION NOT NULL,
|
|
107
173
|
"mdate" DOUBLE PRECISION NOT NULL,
|
|
174
|
+
"user" BYTEA,
|
|
175
|
+
"group" BYTEA,
|
|
176
|
+
"acUser" SMALLINT,
|
|
177
|
+
"acGroup" SMALLINT,
|
|
178
|
+
"acOther" SMALLINT,
|
|
179
|
+
"acRead" BYTEA[],
|
|
180
|
+
"acWrite" BYTEA[],
|
|
181
|
+
"acFull" BYTEA[],
|
|
108
182
|
PRIMARY KEY ("guid")
|
|
109
|
-
) WITH ( OIDS=FALSE )
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
183
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
184
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
185
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)};`, { connection });
|
|
186
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_cdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("cdate");`, { connection });
|
|
187
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)};`, { connection });
|
|
188
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_mdate`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("mdate");`, { connection });
|
|
189
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)};`, { connection });
|
|
190
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_tags`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("tags");`, { connection });
|
|
191
|
+
await this.createEntitiesTilmeldIndexes(etype, connection);
|
|
192
|
+
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 });
|
|
193
|
+
}
|
|
194
|
+
async addTilmeldColumnsAndIndexes(etype, connection) {
|
|
195
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN IF NOT EXISTS "user" BYTEA,
|
|
196
|
+
ADD COLUMN IF NOT EXISTS "group" BYTEA,
|
|
197
|
+
ADD COLUMN IF NOT EXISTS "acUser" SMALLINT,
|
|
198
|
+
ADD COLUMN IF NOT EXISTS "acGroup" SMALLINT,
|
|
199
|
+
ADD COLUMN IF NOT EXISTS "acOther" SMALLINT,
|
|
200
|
+
ADD COLUMN IF NOT EXISTS "acRead" BYTEA[],
|
|
201
|
+
ADD COLUMN IF NOT EXISTS "acWrite" BYTEA[],
|
|
202
|
+
ADD COLUMN IF NOT EXISTS "acFull" BYTEA[];`);
|
|
203
|
+
await this.createEntitiesTilmeldIndexes(etype, connection);
|
|
204
|
+
}
|
|
205
|
+
async createEntitiesTilmeldIndexes(etype, connection) {
|
|
206
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_user_acUser`)};`, { connection });
|
|
207
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_user_acUser`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("user", "acUser");`, { connection });
|
|
208
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_group_acGroup`)};`, { connection });
|
|
209
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_group_acGroup`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("group", "acGroup");`, { connection });
|
|
210
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acUser`)};`, { connection });
|
|
211
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acUser`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("acUser");`, { connection });
|
|
212
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acGroup`)};`, { connection });
|
|
213
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acGroup`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("acGroup");`, { connection });
|
|
214
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acOther`)};`, { connection });
|
|
215
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acOther`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING btree ("acOther");`, { connection });
|
|
216
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acRead`)};`, { connection });
|
|
217
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acRead`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("acRead");`, { connection });
|
|
218
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acWrite`)};`, { connection });
|
|
219
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acWrite`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("acWrite");`, { connection });
|
|
220
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acFull`)};`, { connection });
|
|
221
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}_id_acFull`)} ON ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} USING gin ("acFull");`, { connection });
|
|
222
|
+
}
|
|
223
|
+
async createDataTable(etype, connection) {
|
|
224
|
+
// Create the data table.
|
|
225
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} (
|
|
135
226
|
"guid" BYTEA NOT NULL,
|
|
136
227
|
"name" TEXT NOT NULL,
|
|
137
|
-
"
|
|
228
|
+
"value" CHARACTER(1) NOT NULL,
|
|
229
|
+
"json" JSONB,
|
|
138
230
|
"string" TEXT,
|
|
139
231
|
"number" DOUBLE PRECISION,
|
|
232
|
+
"truthy" BOOLEAN,
|
|
140
233
|
PRIMARY KEY ("guid", "name"),
|
|
141
234
|
FOREIGN KEY ("guid")
|
|
142
235
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
143
|
-
) WITH ( OIDS=FALSE )
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
236
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
237
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
238
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)};`, { connection });
|
|
239
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("guid");`, { connection });
|
|
240
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name`)};`, { connection });
|
|
241
|
+
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 });
|
|
242
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)};`, { connection });
|
|
243
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("name");`, { connection });
|
|
244
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_string`)};`, { connection });
|
|
245
|
+
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 });
|
|
246
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_number`)};`, { connection });
|
|
247
|
+
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 });
|
|
248
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_number`)};`, { connection });
|
|
249
|
+
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 });
|
|
250
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_name_truthy`)};`, { connection });
|
|
251
|
+
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 });
|
|
252
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_guid_name_truthy`)};`, { connection });
|
|
253
|
+
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 });
|
|
254
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_string`)};`, { connection });
|
|
255
|
+
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 });
|
|
256
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)};`, { connection });
|
|
257
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_json`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("json");`, { connection });
|
|
258
|
+
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 });
|
|
259
|
+
}
|
|
260
|
+
async createReferencesTable(etype, connection) {
|
|
261
|
+
// Create the references table.
|
|
262
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} (
|
|
154
263
|
"guid" BYTEA NOT NULL,
|
|
155
264
|
"name" TEXT NOT NULL,
|
|
156
265
|
"reference" BYTEA NOT NULL,
|
|
157
266
|
PRIMARY KEY ("guid", "name", "reference"),
|
|
158
267
|
FOREIGN KEY ("guid")
|
|
159
268
|
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
160
|
-
) WITH ( OIDS=FALSE )
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
269
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
270
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
271
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)};`, { connection });
|
|
272
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("guid");`, { connection });
|
|
273
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)};`, { connection });
|
|
274
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("name");`, { connection });
|
|
275
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_name_reference`)};`, { connection });
|
|
276
|
+
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 });
|
|
277
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)};`, { connection });
|
|
278
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference");`, { connection });
|
|
279
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_guid_name_reference`)};`, { connection });
|
|
280
|
+
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 });
|
|
281
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_name_guid`)};`, { connection });
|
|
282
|
+
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 });
|
|
283
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_reference_guid_name`)};`, { connection });
|
|
284
|
+
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 });
|
|
285
|
+
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 });
|
|
286
|
+
}
|
|
287
|
+
async createTokensTable(etype, connection) {
|
|
288
|
+
// Create the tokens table.
|
|
289
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} (
|
|
290
|
+
"guid" BYTEA NOT NULL,
|
|
291
|
+
"name" TEXT NOT NULL,
|
|
292
|
+
"token" INTEGER NOT NULL,
|
|
293
|
+
"position" INTEGER NOT NULL,
|
|
294
|
+
"stem" BOOLEAN NOT NULL,
|
|
295
|
+
PRIMARY KEY ("guid", "name", "token", "position"),
|
|
296
|
+
FOREIGN KEY ("guid")
|
|
297
|
+
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
298
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
299
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
300
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_name_token`)};`, { connection });
|
|
301
|
+
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 });
|
|
302
|
+
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 });
|
|
303
|
+
}
|
|
304
|
+
async createUniquesTable(etype, connection) {
|
|
305
|
+
// Create the unique strings table.
|
|
306
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} (
|
|
307
|
+
"guid" BYTEA NOT NULL,
|
|
308
|
+
"unique" TEXT NOT NULL UNIQUE,
|
|
309
|
+
PRIMARY KEY ("guid", "unique"),
|
|
310
|
+
FOREIGN KEY ("guid")
|
|
311
|
+
REFERENCES ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid") MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
|
312
|
+
) WITH ( OIDS=FALSE );`, { connection });
|
|
313
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Create entity tables in the database.
|
|
317
|
+
*
|
|
318
|
+
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
319
|
+
* @returns True on success, false on failure.
|
|
320
|
+
*/
|
|
321
|
+
async createTables(etype = null) {
|
|
322
|
+
const connection = await this.getConnection(true);
|
|
323
|
+
if (etype != null) {
|
|
324
|
+
await this.createEntitiesTable(etype, connection);
|
|
325
|
+
await this.createDataTable(etype, connection);
|
|
326
|
+
await this.createReferencesTable(etype, connection);
|
|
327
|
+
await this.createTokensTable(etype, connection);
|
|
328
|
+
await this.createUniquesTable(etype, connection);
|
|
168
329
|
}
|
|
169
330
|
else {
|
|
170
|
-
|
|
331
|
+
// Add trigram extensions.
|
|
332
|
+
try {
|
|
333
|
+
await this.queryRun(`CREATE EXTENSION pg_trgm;`, { connection });
|
|
334
|
+
}
|
|
335
|
+
catch (e) {
|
|
336
|
+
// Ignore errors.
|
|
337
|
+
}
|
|
338
|
+
// Create the UID table.
|
|
339
|
+
await this.queryRun(`CREATE TABLE IF NOT EXISTS ${PostgreSQLDriver.escape(`${this.prefix}uids`)} (
|
|
171
340
|
"name" TEXT NOT NULL,
|
|
172
341
|
"cur_uid" BIGINT NOT NULL,
|
|
173
342
|
PRIMARY KEY ("name")
|
|
174
|
-
) WITH ( OIDS = FALSE )
|
|
175
|
-
this.
|
|
343
|
+
) WITH ( OIDS = FALSE );`, { connection });
|
|
344
|
+
await this.queryRun(`ALTER TABLE ${PostgreSQLDriver.escape(`${this.prefix}uids`)} OWNER TO ${PostgreSQLDriver.escape(this.config.user)};`, { connection });
|
|
176
345
|
}
|
|
346
|
+
connection.done();
|
|
177
347
|
return true;
|
|
178
348
|
}
|
|
179
349
|
translateQuery(origQuery, origParams) {
|
|
@@ -191,49 +361,32 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
191
361
|
}
|
|
192
362
|
async query(runQuery, query, etypes = []) {
|
|
193
363
|
try {
|
|
364
|
+
this.nymph.config.debugInfo('postgresql:query', query);
|
|
194
365
|
return await runQuery();
|
|
195
366
|
}
|
|
196
367
|
catch (e) {
|
|
197
368
|
const errorCode = e?.code;
|
|
198
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
369
|
+
if (errorCode === '42P01' && (await this.createTables())) {
|
|
370
|
+
// If the tables don't exist yet, create them.
|
|
199
371
|
for (let etype of etypes) {
|
|
200
|
-
this.createTables(etype);
|
|
372
|
+
await this.createTables(etype);
|
|
201
373
|
}
|
|
202
374
|
try {
|
|
203
375
|
return await runQuery();
|
|
204
376
|
}
|
|
205
377
|
catch (e2) {
|
|
206
|
-
throw new
|
|
378
|
+
throw new QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query, e2?.code);
|
|
207
379
|
}
|
|
208
380
|
}
|
|
209
|
-
else {
|
|
210
|
-
throw
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
querySync(runQuery, query, etypes = []) {
|
|
215
|
-
try {
|
|
216
|
-
return runQuery();
|
|
217
|
-
}
|
|
218
|
-
catch (e) {
|
|
219
|
-
const errorCode = e?.code;
|
|
220
|
-
if (errorCode === '42P01' && this.createTables()) {
|
|
221
|
-
for (let etype of etypes) {
|
|
222
|
-
this.createTables(etype);
|
|
223
|
-
}
|
|
224
|
-
try {
|
|
225
|
-
return runQuery();
|
|
226
|
-
}
|
|
227
|
-
catch (e2) {
|
|
228
|
-
throw new nymph_1.QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query);
|
|
229
|
-
}
|
|
381
|
+
else if (errorCode === '23505') {
|
|
382
|
+
throw new EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
230
383
|
}
|
|
231
384
|
else {
|
|
232
|
-
throw e;
|
|
385
|
+
throw new QueryFailedError('Query failed: ' + e?.code + ' - ' + e?.message, query, e?.code);
|
|
233
386
|
}
|
|
234
387
|
}
|
|
235
388
|
}
|
|
236
|
-
|
|
389
|
+
queryArray(query, { etypes = [], params = {}, } = {}) {
|
|
237
390
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
238
391
|
return this.query(async () => {
|
|
239
392
|
const results = await new Promise((resolve, reject) => {
|
|
@@ -249,32 +402,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
249
402
|
return results.rows;
|
|
250
403
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
251
404
|
}
|
|
252
|
-
|
|
405
|
+
async queryIter(query, { etypes = [], params = {}, } = {}) {
|
|
253
406
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
for (const name in err) {
|
|
277
|
-
e[name] = err[name];
|
|
407
|
+
const that = this;
|
|
408
|
+
return this.query(async function* () {
|
|
409
|
+
const transaction = !!that.transaction?.connection;
|
|
410
|
+
const connection = await that.getConnection();
|
|
411
|
+
const cursor = new Cursor(newQuery, newParams);
|
|
412
|
+
const iter = connection.client.query(cursor);
|
|
413
|
+
while (true) {
|
|
414
|
+
const rows = await iter.read(100);
|
|
415
|
+
if (!rows.length) {
|
|
416
|
+
await new Promise((resolve) => {
|
|
417
|
+
iter.close(() => {
|
|
418
|
+
if (!transaction) {
|
|
419
|
+
connection.done();
|
|
420
|
+
}
|
|
421
|
+
resolve();
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
for (let row of rows) {
|
|
427
|
+
yield row;
|
|
428
|
+
}
|
|
278
429
|
}
|
|
279
430
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
280
431
|
}
|
|
@@ -294,12 +445,13 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
294
445
|
return results.rows[0];
|
|
295
446
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
296
447
|
}
|
|
297
|
-
queryRun(query, { etypes = [], params = {}, } = {}) {
|
|
448
|
+
queryRun(query, { etypes = [], params = {}, connection, } = {}) {
|
|
298
449
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
299
450
|
return this.query(async () => {
|
|
300
451
|
const results = await new Promise((resolve, reject) => {
|
|
301
452
|
try {
|
|
302
|
-
(this.transaction?.connection?.client ??
|
|
453
|
+
((connection ?? this.transaction?.connection)?.client ??
|
|
454
|
+
this.link)
|
|
303
455
|
.query(newQuery, newParams)
|
|
304
456
|
.then((results) => resolve(results), (error) => reject(error));
|
|
305
457
|
}
|
|
@@ -310,39 +462,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
310
462
|
return { rowCount: results.rowCount ?? 0 };
|
|
311
463
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
312
464
|
}
|
|
313
|
-
queryRunSync(query, { etypes = [], params = {}, } = {}) {
|
|
314
|
-
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
315
|
-
return this.querySync(() => {
|
|
316
|
-
const output = child_process_1.default.spawnSync(process.argv0, [__dirname + '/runPostgresqlSync.js'], {
|
|
317
|
-
input: JSON.stringify({
|
|
318
|
-
postgresqlConfig: this.postgresqlConfig,
|
|
319
|
-
query: newQuery,
|
|
320
|
-
params: newParams,
|
|
321
|
-
}),
|
|
322
|
-
timeout: 30000,
|
|
323
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
324
|
-
encoding: 'utf8',
|
|
325
|
-
windowsHide: true,
|
|
326
|
-
});
|
|
327
|
-
try {
|
|
328
|
-
const results = JSON.parse(output.stdout);
|
|
329
|
-
return { rowCount: results.rowCount ?? 0 };
|
|
330
|
-
}
|
|
331
|
-
catch (e) {
|
|
332
|
-
}
|
|
333
|
-
if (output.status === 0) {
|
|
334
|
-
throw new Error('Unknown parse error.');
|
|
335
|
-
}
|
|
336
|
-
const err = JSON.parse(output.stderr);
|
|
337
|
-
const e = new Error(err.name);
|
|
338
|
-
for (const name in err) {
|
|
339
|
-
e[name] = err[name];
|
|
340
|
-
}
|
|
341
|
-
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
342
|
-
}
|
|
343
465
|
async commit(name) {
|
|
344
466
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
345
|
-
throw new
|
|
467
|
+
throw new InvalidParametersError('Transaction commit attempted without a name.');
|
|
346
468
|
}
|
|
347
469
|
if (!this.transaction || this.transaction.count === 0) {
|
|
348
470
|
this.transaction = null;
|
|
@@ -382,32 +504,40 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
382
504
|
guid,
|
|
383
505
|
},
|
|
384
506
|
});
|
|
385
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
507
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
386
508
|
etypes: [etype],
|
|
387
509
|
params: {
|
|
388
510
|
guid,
|
|
389
511
|
},
|
|
390
512
|
});
|
|
391
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
513
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
514
|
+
etypes: [etype],
|
|
515
|
+
params: {
|
|
516
|
+
guid,
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
392
520
|
etypes: [etype],
|
|
393
521
|
params: {
|
|
394
522
|
guid,
|
|
395
523
|
},
|
|
396
524
|
});
|
|
397
|
-
await this.commit('nymph-delete');
|
|
398
|
-
if (this.nymph.config.cache) {
|
|
399
|
-
this.cleanCache(guid);
|
|
400
|
-
}
|
|
401
|
-
return true;
|
|
402
525
|
}
|
|
403
526
|
catch (e) {
|
|
527
|
+
this.nymph.config.debugError('postgresql', `Delete entity error: "${e}"`);
|
|
404
528
|
await this.rollback('nymph-delete');
|
|
405
529
|
throw e;
|
|
406
530
|
}
|
|
531
|
+
await this.commit('nymph-delete');
|
|
532
|
+
// Remove any cached versions of this entity.
|
|
533
|
+
if (this.nymph.config.cache) {
|
|
534
|
+
this.cleanCache(guid);
|
|
535
|
+
}
|
|
536
|
+
return true;
|
|
407
537
|
}
|
|
408
538
|
async deleteUID(name) {
|
|
409
539
|
if (!name) {
|
|
410
|
-
throw new
|
|
540
|
+
throw new InvalidParametersError('Name not given for UID');
|
|
411
541
|
}
|
|
412
542
|
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
413
543
|
params: {
|
|
@@ -416,79 +546,223 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
416
546
|
});
|
|
417
547
|
return true;
|
|
418
548
|
}
|
|
419
|
-
async
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
549
|
+
async getIndexes(etype) {
|
|
550
|
+
const indexes = [];
|
|
551
|
+
for (let [scope, suffix] of [
|
|
552
|
+
['data', '_json'],
|
|
553
|
+
['references', '_reference_guid'],
|
|
554
|
+
['tokens', '_token_position_stem'],
|
|
555
|
+
]) {
|
|
556
|
+
const indexDefinitions = await this.queryArray(`SELECT * FROM "pg_indexes" WHERE "indexname" LIKE @pattern;`, {
|
|
557
|
+
params: {
|
|
558
|
+
pattern: `${this.prefix}${scope}_${etype}_id_custom_%${suffix}`,
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
for (const indexDefinition of indexDefinitions) {
|
|
562
|
+
indexes.push({
|
|
563
|
+
scope,
|
|
564
|
+
name: indexDefinition.indexname.substring(`${this.prefix}${scope}_${etype}_id_custom_`.length, indexDefinition.indexname.length - suffix.length),
|
|
565
|
+
property: (indexDefinition.indexdef.match(/WHERE\s+\(?\s*name\s*=\s*'(.*)'/) ?? [])[1] ?? '',
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return indexes;
|
|
570
|
+
}
|
|
571
|
+
async addIndex(etype, definition) {
|
|
572
|
+
this.checkIndexName(definition.name);
|
|
573
|
+
await this.deleteIndex(etype, definition.scope, definition.name);
|
|
574
|
+
const connection = await this.getConnection(true);
|
|
575
|
+
if (definition.scope === 'data') {
|
|
576
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_json`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("json") WHERE "name"=${PostgreSQLDriver.escapeValue(definition.property)};`, { connection });
|
|
577
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_string_gin`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING gin ("string" gin_trgm_ops) WHERE "name"=${PostgreSQLDriver.escapeValue(definition.property)};`, { connection });
|
|
578
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_string_btree`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree (LEFT("string", 1024)) WHERE "name"=${PostgreSQLDriver.escapeValue(definition.property)};`, { connection });
|
|
579
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_number`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("number") WHERE "name"=${PostgreSQLDriver.escapeValue(definition.property)};`, { connection });
|
|
580
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_truthy`)} ON ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} USING btree ("truthy") WHERE "name"=${PostgreSQLDriver.escapeValue(definition.property)};`, { connection });
|
|
581
|
+
}
|
|
582
|
+
else if (definition.scope === 'references') {
|
|
583
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_custom_${definition.name}_reference_guid`)} ON ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} USING btree ("reference", "guid") WHERE "name"=${PostgreSQLDriver.escapeValue(definition.property)};`, { connection });
|
|
584
|
+
}
|
|
585
|
+
else if (definition.scope === 'tokens') {
|
|
586
|
+
await this.queryRun(`CREATE INDEX ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_custom_${definition.name}_token_position_stem`)} ON ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} USING btree ("token", "position", "stem") WHERE "name"=${PostgreSQLDriver.escapeValue(definition.property)};`, { connection });
|
|
587
|
+
}
|
|
588
|
+
connection.done();
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
591
|
+
async deleteIndex(etype, scope, name) {
|
|
592
|
+
this.checkIndexName(name);
|
|
593
|
+
const connection = await this.getConnection(true);
|
|
594
|
+
if (scope === 'data') {
|
|
595
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_json`)};`, { connection });
|
|
596
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_string_gin`)};`, { connection });
|
|
597
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_string_btree`)};`, { connection });
|
|
598
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_number`)};`, { connection });
|
|
599
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_truthy`)};`, { connection });
|
|
600
|
+
}
|
|
601
|
+
else if (scope === 'references') {
|
|
602
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_custom_${name}_reference_guid`)};`, { connection });
|
|
603
|
+
}
|
|
604
|
+
else if (scope === 'tokens') {
|
|
605
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_custom_${name}_token_position_stem`)};`, { connection });
|
|
606
|
+
}
|
|
607
|
+
connection.done();
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
async getEtypes() {
|
|
611
|
+
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;', {
|
|
612
|
+
params: {
|
|
613
|
+
db: this.config.database,
|
|
614
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
615
|
+
},
|
|
616
|
+
});
|
|
440
617
|
const etypes = [];
|
|
441
|
-
for (const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
618
|
+
for (const table of tables) {
|
|
619
|
+
etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
|
|
620
|
+
}
|
|
621
|
+
return etypes;
|
|
622
|
+
}
|
|
623
|
+
async *exportDataIterator() {
|
|
624
|
+
if (yield {
|
|
625
|
+
type: 'comment',
|
|
626
|
+
content: `#nex2
|
|
627
|
+
# Nymph Entity Exchange v2
|
|
628
|
+
# http://nymph.io
|
|
629
|
+
#
|
|
630
|
+
# Generation Time: ${new Date().toLocaleString()}
|
|
631
|
+
`,
|
|
632
|
+
}) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (yield {
|
|
636
|
+
type: 'comment',
|
|
637
|
+
content: `
|
|
638
|
+
|
|
639
|
+
#
|
|
640
|
+
# UIDs
|
|
641
|
+
#
|
|
642
|
+
|
|
643
|
+
`,
|
|
644
|
+
}) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
// Export UIDs.
|
|
648
|
+
let uids = await this.queryIter(`SELECT * FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ORDER BY "name";`);
|
|
649
|
+
for await (const uid of uids) {
|
|
650
|
+
if (yield { type: 'uid', content: `<${uid.name}>[${uid.cur_uid}]\n` }) {
|
|
651
|
+
return;
|
|
445
652
|
}
|
|
446
653
|
}
|
|
654
|
+
if (yield {
|
|
655
|
+
type: 'comment',
|
|
656
|
+
content: `
|
|
657
|
+
|
|
658
|
+
#
|
|
659
|
+
# Entities
|
|
660
|
+
#
|
|
661
|
+
|
|
662
|
+
`,
|
|
663
|
+
}) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
// Get the etypes.
|
|
667
|
+
const etypes = await this.getEtypes();
|
|
447
668
|
for (const etype of etypes) {
|
|
448
|
-
|
|
669
|
+
// Export entities.
|
|
670
|
+
const dataIterator = await this.queryIter(`SELECT encode(e."guid", 'hex') AS "guid", e."tags", e."cdate", e."mdate", encode(e."user", 'hex') AS "user", encode(e."group", 'hex') AS "group", e."acUser", e."acGroup", e."acOther", array(SELECT encode(n, 'hex') FROM unnest(e."acRead") AS n) as "acRead", array(SELECT encode(n, 'hex') FROM unnest(e."acWrite") AS n) as "acWrite", array(SELECT encode(n, 'hex') FROM unnest(e."acFull") AS n) as "acFull", d."name", d."value", d."json", d."string", d."number"
|
|
449
671
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} e
|
|
450
672
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} d ON e."guid"=d."guid"
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
let datum = dataIterator.next();
|
|
673
|
+
ORDER BY e."guid";`);
|
|
674
|
+
let datum = await dataIterator.next();
|
|
454
675
|
while (!datum.done) {
|
|
455
676
|
const guid = datum.value.guid;
|
|
456
|
-
const tags = datum.value.tags.join(',');
|
|
677
|
+
const tags = datum.value.tags.filter((tag) => tag).join(',');
|
|
457
678
|
const cdate = datum.value.cdate;
|
|
458
679
|
const mdate = datum.value.mdate;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
680
|
+
const user = datum.value.user;
|
|
681
|
+
const group = datum.value.group;
|
|
682
|
+
const acUser = datum.value.acUser;
|
|
683
|
+
const acGroup = datum.value.acGroup;
|
|
684
|
+
const acOther = datum.value.acOther;
|
|
685
|
+
const acRead = datum.value.acRead?.filter((guid) => guid);
|
|
686
|
+
const acWrite = datum.value.acWrite?.filter((guid) => guid);
|
|
687
|
+
const acFull = datum.value.acFull?.filter((guid) => guid);
|
|
688
|
+
let currentEntityExport = [];
|
|
689
|
+
currentEntityExport.push(`{${guid}}<${etype}>[${tags}]`);
|
|
690
|
+
currentEntityExport.push(`\tcdate=${JSON.stringify(cdate)}`);
|
|
691
|
+
currentEntityExport.push(`\tmdate=${JSON.stringify(mdate)}`);
|
|
692
|
+
if (this.nymph.tilmeld != null) {
|
|
693
|
+
if (user != null) {
|
|
694
|
+
currentEntityExport.push(`\tuser=${JSON.stringify(['nymph_entity_reference', user, 'User'])}`);
|
|
695
|
+
}
|
|
696
|
+
if (group != null) {
|
|
697
|
+
currentEntityExport.push(`\tgroup=${JSON.stringify(['nymph_entity_reference', group, 'Group'])}`);
|
|
698
|
+
}
|
|
699
|
+
if (acUser != null) {
|
|
700
|
+
currentEntityExport.push(`\tacUser=${JSON.stringify(acUser)}`);
|
|
701
|
+
}
|
|
702
|
+
if (acGroup != null) {
|
|
703
|
+
currentEntityExport.push(`\tacGroup=${JSON.stringify(acGroup)}`);
|
|
704
|
+
}
|
|
705
|
+
if (acOther != null) {
|
|
706
|
+
currentEntityExport.push(`\tacOther=${JSON.stringify(acOther)}`);
|
|
707
|
+
}
|
|
708
|
+
if (acRead != null) {
|
|
709
|
+
currentEntityExport.push(`\tacRead=${JSON.stringify(acRead)}`);
|
|
710
|
+
}
|
|
711
|
+
if (acWrite != null) {
|
|
712
|
+
currentEntityExport.push(`\tacWrite=${JSON.stringify(acWrite)}`);
|
|
713
|
+
}
|
|
714
|
+
if (acFull != null) {
|
|
715
|
+
currentEntityExport.push(`\tacFull=${JSON.stringify(acFull)}`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (datum.value.name != null) {
|
|
719
|
+
// This do will keep going and adding the data until the
|
|
720
|
+
// next entity is reached. $row will end on the next entity.
|
|
463
721
|
do {
|
|
464
|
-
const value = datum.value.
|
|
722
|
+
const value = datum.value.value === 'N'
|
|
465
723
|
? JSON.stringify(Number(datum.value.number))
|
|
466
|
-
: datum.value.
|
|
467
|
-
? JSON.stringify(datum.value.string)
|
|
468
|
-
: datum.value.
|
|
469
|
-
|
|
470
|
-
|
|
724
|
+
: datum.value.value === 'S'
|
|
725
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(datum.value.string))
|
|
726
|
+
: datum.value.value === 'J'
|
|
727
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(datum.value.json))
|
|
728
|
+
: datum.value.value;
|
|
729
|
+
currentEntityExport.push(`\t${datum.value.name}=${value}`);
|
|
730
|
+
datum = await dataIterator.next();
|
|
471
731
|
} while (!datum.done && datum.value.guid === guid);
|
|
472
732
|
}
|
|
473
733
|
else {
|
|
474
|
-
datum
|
|
734
|
+
// Make sure that datum is incremented :)
|
|
735
|
+
datum = await dataIterator.next();
|
|
736
|
+
}
|
|
737
|
+
currentEntityExport.push('');
|
|
738
|
+
if (yield { type: 'entity', content: currentEntityExport.join('\n') }) {
|
|
739
|
+
return;
|
|
475
740
|
}
|
|
476
741
|
}
|
|
477
742
|
}
|
|
478
|
-
return;
|
|
479
743
|
}
|
|
480
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Generate the PostgreSQL query.
|
|
746
|
+
* @param options The options array.
|
|
747
|
+
* @param formattedSelectors The formatted selector array.
|
|
748
|
+
* @param etype
|
|
749
|
+
* @param count Used to track internal params.
|
|
750
|
+
* @param params Used to store internal params.
|
|
751
|
+
* @param subquery Whether only a subquery should be returned.
|
|
752
|
+
* @returns The SQL query.
|
|
753
|
+
*/
|
|
754
|
+
makeEntityQuery(options, formattedSelectors, etype, count = { i: 0 }, params = {}, subquery = false, tableSuffix = '', etypes = [], guidSelector = undefined, guidExplicitSelector = undefined) {
|
|
481
755
|
if (typeof options.class?.alterOptions === 'function') {
|
|
482
756
|
options = options.class.alterOptions(options);
|
|
483
757
|
}
|
|
484
758
|
const eTable = `e${tableSuffix}`;
|
|
485
759
|
const dTable = `d${tableSuffix}`;
|
|
486
|
-
const cTable = `c${tableSuffix}`;
|
|
487
760
|
const fTable = `f${tableSuffix}`;
|
|
488
761
|
const ieTable = `ie${tableSuffix}`;
|
|
489
762
|
const countTable = `count${tableSuffix}`;
|
|
490
|
-
const
|
|
491
|
-
const
|
|
763
|
+
const sTable = `s${tableSuffix}`;
|
|
764
|
+
const sort = options.sort === undefined ? 'cdate' : options.sort;
|
|
765
|
+
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, ({ key, value, typeIsOr, typeIsNot }) => {
|
|
492
766
|
const clauseNot = key.startsWith('!');
|
|
493
767
|
let curQuery = '';
|
|
494
768
|
for (const curValue of value) {
|
|
@@ -501,7 +775,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
501
775
|
}
|
|
502
776
|
const guid = `param${++count.i}`;
|
|
503
777
|
curQuery +=
|
|
504
|
-
(
|
|
778
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
505
779
|
ieTable +
|
|
506
780
|
'."guid"=decode(@' +
|
|
507
781
|
guid +
|
|
@@ -517,7 +791,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
517
791
|
}
|
|
518
792
|
const tag = `param${++count.i}`;
|
|
519
793
|
curQuery +=
|
|
520
|
-
(
|
|
794
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
521
795
|
'@' +
|
|
522
796
|
tag +
|
|
523
797
|
' <@ ie."tags"';
|
|
@@ -530,17 +804,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
530
804
|
if (curQuery) {
|
|
531
805
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
532
806
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
807
|
+
if (curVar === 'cdate' ||
|
|
808
|
+
curVar === 'mdate' ||
|
|
809
|
+
(this.nymph.tilmeld != null &&
|
|
810
|
+
(curVar === 'user' ||
|
|
811
|
+
curVar === 'group' ||
|
|
812
|
+
curVar === 'acUser' ||
|
|
813
|
+
curVar === 'acGroup' ||
|
|
814
|
+
curVar === 'acOther' ||
|
|
815
|
+
curVar === 'acRead' ||
|
|
816
|
+
curVar === 'acWrite' ||
|
|
817
|
+
curVar === 'acFull'))) {
|
|
818
|
+
curQuery +=
|
|
819
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
820
|
+
'(' +
|
|
821
|
+
ieTable +
|
|
822
|
+
'.' +
|
|
823
|
+
PostgreSQLDriver.escape(curVar) +
|
|
824
|
+
' IS NOT NULL)';
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
const name = `param${++count.i}`;
|
|
828
|
+
curQuery +=
|
|
829
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
830
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
831
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
832
|
+
' WHERE "guid"=' +
|
|
833
|
+
ieTable +
|
|
834
|
+
'."guid" AND "name"=@' +
|
|
835
|
+
name +
|
|
836
|
+
')';
|
|
837
|
+
params[name] = curVar;
|
|
838
|
+
}
|
|
544
839
|
}
|
|
545
840
|
break;
|
|
546
841
|
case 'truthy':
|
|
@@ -549,30 +844,34 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
549
844
|
if (curQuery) {
|
|
550
845
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
551
846
|
}
|
|
552
|
-
if (curVar === 'cdate'
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
847
|
+
if (curVar === 'cdate' ||
|
|
848
|
+
curVar === 'mdate' ||
|
|
849
|
+
(this.nymph.tilmeld != null &&
|
|
850
|
+
(curVar === 'user' ||
|
|
851
|
+
curVar === 'group' ||
|
|
852
|
+
curVar === 'acUser' ||
|
|
853
|
+
curVar === 'acGroup' ||
|
|
854
|
+
curVar === 'acOther' ||
|
|
855
|
+
curVar === 'acRead' ||
|
|
856
|
+
curVar === 'acWrite' ||
|
|
857
|
+
curVar === 'acFull'))) {
|
|
561
858
|
curQuery +=
|
|
562
|
-
(
|
|
859
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
563
860
|
'(' +
|
|
564
861
|
ieTable +
|
|
565
|
-
'.
|
|
566
|
-
|
|
862
|
+
'.' +
|
|
863
|
+
PostgreSQLDriver.escape(curVar) +
|
|
864
|
+
' IS NOT NULL)';
|
|
567
865
|
}
|
|
568
866
|
else {
|
|
569
867
|
const name = `param${++count.i}`;
|
|
570
868
|
curQuery +=
|
|
571
|
-
(
|
|
869
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
870
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
871
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
872
|
+
' WHERE "guid"=' +
|
|
572
873
|
ieTable +
|
|
573
|
-
'."guid"
|
|
574
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
575
|
-
' WHERE "name"=@' +
|
|
874
|
+
'."guid" AND "name"=@' +
|
|
576
875
|
name +
|
|
577
876
|
' AND "truthy"=TRUE)';
|
|
578
877
|
params[name] = curVar;
|
|
@@ -581,35 +880,63 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
581
880
|
break;
|
|
582
881
|
case 'equal':
|
|
583
882
|
case '!equal':
|
|
584
|
-
if (curValue[0] === 'cdate'
|
|
883
|
+
if (curValue[0] === 'cdate' ||
|
|
884
|
+
curValue[0] === 'mdate' ||
|
|
885
|
+
(this.nymph.tilmeld != null &&
|
|
886
|
+
(curValue[0] === 'acUser' ||
|
|
887
|
+
curValue[0] === 'acGroup' ||
|
|
888
|
+
curValue[0] === 'acOther'))) {
|
|
585
889
|
if (curQuery) {
|
|
586
890
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
587
891
|
}
|
|
588
|
-
const
|
|
892
|
+
const value = `param${++count.i}`;
|
|
589
893
|
curQuery +=
|
|
590
|
-
(
|
|
894
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
591
895
|
ieTable +
|
|
592
|
-
'.
|
|
593
|
-
|
|
594
|
-
|
|
896
|
+
'.' +
|
|
897
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
898
|
+
'=@' +
|
|
899
|
+
value;
|
|
900
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
595
901
|
? null
|
|
596
902
|
: Number(curValue[1]);
|
|
597
|
-
break;
|
|
598
903
|
}
|
|
599
|
-
else if (
|
|
904
|
+
else if (this.nymph.tilmeld != null &&
|
|
905
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
600
906
|
if (curQuery) {
|
|
601
907
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
602
908
|
}
|
|
603
|
-
const
|
|
909
|
+
const value = `param${++count.i}`;
|
|
604
910
|
curQuery +=
|
|
605
|
-
(
|
|
911
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
606
912
|
ieTable +
|
|
607
|
-
'.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
913
|
+
'.' +
|
|
914
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
915
|
+
'=decode(@' +
|
|
916
|
+
value +
|
|
917
|
+
", 'hex')";
|
|
918
|
+
params[value] = `${curValue[1]}`;
|
|
919
|
+
}
|
|
920
|
+
else if (this.nymph.tilmeld != null &&
|
|
921
|
+
(curValue[0] === 'acRead' ||
|
|
922
|
+
curValue[0] === 'acWrite' ||
|
|
923
|
+
curValue[0] === 'acFull')) {
|
|
924
|
+
if (curQuery) {
|
|
925
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
926
|
+
}
|
|
927
|
+
const value = `param${++count.i}`;
|
|
928
|
+
curQuery +=
|
|
929
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
930
|
+
ieTable +
|
|
931
|
+
'.' +
|
|
932
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
933
|
+
'=' +
|
|
934
|
+
!curValue[1]?.length
|
|
935
|
+
? '@' + value
|
|
936
|
+
: "array(SELECT decode(n, 'hex') FROM unnest(@" +
|
|
937
|
+
value +
|
|
938
|
+
'::text[]) AS n)';
|
|
939
|
+
params[value] = Array.isArray(curValue[1]) ? curValue[1] : '';
|
|
613
940
|
}
|
|
614
941
|
else if (typeof curValue[1] === 'number') {
|
|
615
942
|
if (curQuery) {
|
|
@@ -618,11 +945,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
618
945
|
const name = `param${++count.i}`;
|
|
619
946
|
const value = `param${++count.i}`;
|
|
620
947
|
curQuery +=
|
|
621
|
-
(
|
|
948
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
949
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
950
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
951
|
+
' WHERE "guid"=' +
|
|
622
952
|
ieTable +
|
|
623
|
-
'."guid"
|
|
624
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
625
|
-
' WHERE "name"=@' +
|
|
953
|
+
'."guid" AND "name"=@' +
|
|
626
954
|
name +
|
|
627
955
|
' AND "number"=@' +
|
|
628
956
|
value +
|
|
@@ -637,17 +965,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
637
965
|
const name = `param${++count.i}`;
|
|
638
966
|
const value = `param${++count.i}`;
|
|
639
967
|
curQuery +=
|
|
640
|
-
(
|
|
968
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
969
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
970
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
971
|
+
' WHERE "guid"=' +
|
|
641
972
|
ieTable +
|
|
642
|
-
'."guid"
|
|
643
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
644
|
-
' WHERE "name"=@' +
|
|
973
|
+
'."guid" AND "name"=@' +
|
|
645
974
|
name +
|
|
646
|
-
' AND "string"
|
|
647
|
-
|
|
975
|
+
' AND "string"=' +
|
|
976
|
+
(curValue[1].length < 512
|
|
977
|
+
? 'LEFT(@' + value + ', 512)'
|
|
978
|
+
: '@' + value) +
|
|
648
979
|
')';
|
|
649
980
|
params[name] = curValue[0];
|
|
650
|
-
params[value] = curValue[1];
|
|
981
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
651
982
|
}
|
|
652
983
|
else {
|
|
653
984
|
if (curQuery) {
|
|
@@ -664,138 +995,272 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
664
995
|
const name = `param${++count.i}`;
|
|
665
996
|
const value = `param${++count.i}`;
|
|
666
997
|
curQuery +=
|
|
667
|
-
(
|
|
668
|
-
|
|
669
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
998
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
999
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
670
1000
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
671
|
-
' WHERE "
|
|
1001
|
+
' WHERE "guid"=' +
|
|
1002
|
+
ieTable +
|
|
1003
|
+
'."guid" AND "name"=@' +
|
|
672
1004
|
name +
|
|
673
|
-
' AND "
|
|
1005
|
+
' AND "json"=@' +
|
|
674
1006
|
value +
|
|
675
1007
|
')';
|
|
676
1008
|
params[name] = curValue[0];
|
|
677
|
-
params[value] = svalue;
|
|
1009
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
678
1010
|
}
|
|
679
1011
|
break;
|
|
680
1012
|
case 'contain':
|
|
681
1013
|
case '!contain':
|
|
682
|
-
if (curValue[0] === 'cdate'
|
|
1014
|
+
if (curValue[0] === 'cdate' ||
|
|
1015
|
+
curValue[0] === 'mdate' ||
|
|
1016
|
+
(this.nymph.tilmeld != null &&
|
|
1017
|
+
(curValue[0] === 'acUser' ||
|
|
1018
|
+
curValue[0] === 'acGroup' ||
|
|
1019
|
+
curValue[0] === 'acOther'))) {
|
|
683
1020
|
if (curQuery) {
|
|
684
1021
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
685
1022
|
}
|
|
686
|
-
const
|
|
1023
|
+
const value = `param${++count.i}`;
|
|
687
1024
|
curQuery +=
|
|
688
|
-
(
|
|
1025
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
689
1026
|
ieTable +
|
|
690
|
-
'.
|
|
691
|
-
|
|
692
|
-
|
|
1027
|
+
'.' +
|
|
1028
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1029
|
+
'=@' +
|
|
1030
|
+
value;
|
|
1031
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
693
1032
|
? null
|
|
694
1033
|
: Number(curValue[1]);
|
|
695
|
-
break;
|
|
696
1034
|
}
|
|
697
|
-
else if (
|
|
1035
|
+
else if (this.nymph.tilmeld != null &&
|
|
1036
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
698
1037
|
if (curQuery) {
|
|
699
1038
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
700
1039
|
}
|
|
701
|
-
const
|
|
1040
|
+
const value = `param${++count.i}`;
|
|
702
1041
|
curQuery +=
|
|
703
|
-
(
|
|
1042
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
704
1043
|
ieTable +
|
|
705
|
-
'.
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1044
|
+
'.' +
|
|
1045
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1046
|
+
'=decode(@' +
|
|
1047
|
+
value +
|
|
1048
|
+
", 'hex')";
|
|
1049
|
+
params[value] = `${curValue[1]}`;
|
|
1050
|
+
}
|
|
1051
|
+
else if (this.nymph.tilmeld != null &&
|
|
1052
|
+
(curValue[0] === 'acRead' ||
|
|
1053
|
+
curValue[0] === 'acWrite' ||
|
|
1054
|
+
curValue[0] === 'acFull')) {
|
|
1055
|
+
if (curQuery) {
|
|
1056
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1057
|
+
}
|
|
1058
|
+
const value = `param${++count.i}`;
|
|
1059
|
+
curQuery +=
|
|
1060
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1061
|
+
'decode(@' +
|
|
1062
|
+
value +
|
|
1063
|
+
", 'hex')=ANY(" +
|
|
1064
|
+
ieTable +
|
|
1065
|
+
'.' +
|
|
1066
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1067
|
+
')';
|
|
1068
|
+
params[value] = `${curValue[1]}`;
|
|
711
1069
|
}
|
|
712
1070
|
else {
|
|
713
1071
|
if (curQuery) {
|
|
714
1072
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
715
1073
|
}
|
|
716
1074
|
let svalue;
|
|
717
|
-
let stringValue;
|
|
718
1075
|
if (curValue[1] instanceof Object &&
|
|
719
1076
|
typeof curValue[1].toReference === 'function') {
|
|
720
1077
|
svalue = JSON.stringify(curValue[1].toReference());
|
|
721
|
-
stringValue = `${curValue[1].toReference()}`;
|
|
722
1078
|
}
|
|
723
|
-
else
|
|
1079
|
+
else if (typeof curValue[1] === 'string' ||
|
|
1080
|
+
typeof curValue[1] === 'number') {
|
|
724
1081
|
svalue = JSON.stringify(curValue[1]);
|
|
725
|
-
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
svalue = JSON.stringify([curValue[1]]);
|
|
726
1085
|
}
|
|
727
1086
|
const name = `param${++count.i}`;
|
|
728
1087
|
const value = `param${++count.i}`;
|
|
729
|
-
|
|
730
|
-
|
|
1088
|
+
curQuery +=
|
|
1089
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1090
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1091
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1092
|
+
' WHERE "guid"=' +
|
|
1093
|
+
ieTable +
|
|
1094
|
+
'."guid" AND "name"=@' +
|
|
1095
|
+
name +
|
|
1096
|
+
' AND "json" @> @' +
|
|
1097
|
+
value +
|
|
1098
|
+
')';
|
|
1099
|
+
params[name] = curValue[0];
|
|
1100
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
1101
|
+
}
|
|
1102
|
+
break;
|
|
1103
|
+
case 'search':
|
|
1104
|
+
case '!search':
|
|
1105
|
+
if (curValue[0] === 'cdate' ||
|
|
1106
|
+
curValue[0] === 'mdate' ||
|
|
1107
|
+
(this.nymph.tilmeld != null &&
|
|
1108
|
+
(curValue[0] === 'user' ||
|
|
1109
|
+
curValue[0] === 'group' ||
|
|
1110
|
+
curValue[0] === 'acUser' ||
|
|
1111
|
+
curValue[0] === 'acGroup' ||
|
|
1112
|
+
curValue[0] === 'acOther' ||
|
|
1113
|
+
curValue[0] === 'acRead' ||
|
|
1114
|
+
curValue[0] === 'acWrite' ||
|
|
1115
|
+
curValue[0] === 'acFull'))) {
|
|
1116
|
+
if (curQuery) {
|
|
1117
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1118
|
+
}
|
|
1119
|
+
curQuery +=
|
|
1120
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
|
|
1121
|
+
}
|
|
1122
|
+
else {
|
|
1123
|
+
if (curQuery) {
|
|
1124
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1125
|
+
}
|
|
1126
|
+
const parsedFTSQuery = this.tokenizer.parseSearchQuery(curValue[1]);
|
|
1127
|
+
if (!parsedFTSQuery.length) {
|
|
731
1128
|
curQuery +=
|
|
732
|
-
(
|
|
733
|
-
|
|
1129
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
|
|
1130
|
+
}
|
|
1131
|
+
else {
|
|
1132
|
+
const name = `param${++count.i}`;
|
|
1133
|
+
const queryPartToken = (term) => {
|
|
1134
|
+
const value = `param${++count.i}`;
|
|
1135
|
+
params[value] = term.token;
|
|
1136
|
+
return ('EXISTS (SELECT "guid" FROM ' +
|
|
1137
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
1138
|
+
' WHERE "guid"=' +
|
|
734
1139
|
ieTable +
|
|
735
|
-
'."guid"
|
|
736
|
-
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
737
|
-
' WHERE "name"=@' +
|
|
1140
|
+
'."guid" AND "name"=@' +
|
|
738
1141
|
name +
|
|
739
|
-
' AND
|
|
1142
|
+
' AND "token"=@' +
|
|
740
1143
|
value +
|
|
741
|
-
'
|
|
1144
|
+
(term.nostemmed ? ' AND "stem"=FALSE' : '') +
|
|
1145
|
+
')');
|
|
1146
|
+
};
|
|
1147
|
+
const queryPartSeries = (series) => {
|
|
1148
|
+
const tokenTableSuffix = makeTableSuffix();
|
|
1149
|
+
const tokenParts = series.tokens.map((token, i) => {
|
|
1150
|
+
const value = `param${++count.i}`;
|
|
1151
|
+
params[value] = token.token;
|
|
1152
|
+
return {
|
|
1153
|
+
fromClause: i === 0
|
|
1154
|
+
? 'FROM ' +
|
|
1155
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
1156
|
+
' t' +
|
|
1157
|
+
tokenTableSuffix +
|
|
1158
|
+
'0'
|
|
1159
|
+
: 'JOIN ' +
|
|
1160
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
1161
|
+
' t' +
|
|
1162
|
+
tokenTableSuffix +
|
|
1163
|
+
i +
|
|
1164
|
+
' ON t' +
|
|
1165
|
+
tokenTableSuffix +
|
|
1166
|
+
i +
|
|
1167
|
+
'."guid" = t' +
|
|
1168
|
+
tokenTableSuffix +
|
|
1169
|
+
'0."guid" AND t' +
|
|
1170
|
+
tokenTableSuffix +
|
|
1171
|
+
i +
|
|
1172
|
+
'."name" = t' +
|
|
1173
|
+
tokenTableSuffix +
|
|
1174
|
+
'0."name" AND t' +
|
|
1175
|
+
tokenTableSuffix +
|
|
1176
|
+
i +
|
|
1177
|
+
'."position" = t' +
|
|
1178
|
+
tokenTableSuffix +
|
|
1179
|
+
'0."position" + ' +
|
|
1180
|
+
i,
|
|
1181
|
+
whereClause: 't' +
|
|
1182
|
+
tokenTableSuffix +
|
|
1183
|
+
i +
|
|
1184
|
+
'."token"=@' +
|
|
1185
|
+
value +
|
|
1186
|
+
(token.nostemmed
|
|
1187
|
+
? ' AND t' + tokenTableSuffix + i + '."stem"=FALSE'
|
|
1188
|
+
: ''),
|
|
1189
|
+
};
|
|
1190
|
+
});
|
|
1191
|
+
return ('EXISTS (SELECT t' +
|
|
1192
|
+
tokenTableSuffix +
|
|
1193
|
+
'0."guid" ' +
|
|
1194
|
+
tokenParts.map((part) => part.fromClause).join(' ') +
|
|
1195
|
+
' WHERE t' +
|
|
1196
|
+
tokenTableSuffix +
|
|
1197
|
+
'0."guid"=' +
|
|
742
1198
|
ieTable +
|
|
743
|
-
'."guid"
|
|
744
|
-
|
|
745
|
-
'
|
|
1199
|
+
'."guid" AND t' +
|
|
1200
|
+
tokenTableSuffix +
|
|
1201
|
+
'0."name"=@' +
|
|
746
1202
|
name +
|
|
747
|
-
' AND
|
|
748
|
-
|
|
749
|
-
'))
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1203
|
+
' AND ' +
|
|
1204
|
+
tokenParts.map((part) => part.whereClause).join(' AND ') +
|
|
1205
|
+
')');
|
|
1206
|
+
};
|
|
1207
|
+
const queryPartTerm = (term) => {
|
|
1208
|
+
if (term.type === 'series') {
|
|
1209
|
+
return queryPartSeries(term);
|
|
1210
|
+
}
|
|
1211
|
+
else if (term.type === 'not') {
|
|
1212
|
+
return 'NOT ' + queryPartTerm(term.operand);
|
|
1213
|
+
}
|
|
1214
|
+
else if (term.type === 'or') {
|
|
1215
|
+
let queryParts = [];
|
|
1216
|
+
for (let operand of term.operands) {
|
|
1217
|
+
queryParts.push(queryPartTerm(operand));
|
|
1218
|
+
}
|
|
1219
|
+
return '(' + queryParts.join(' OR ') + ')';
|
|
1220
|
+
}
|
|
1221
|
+
return queryPartToken(term);
|
|
1222
|
+
};
|
|
1223
|
+
// Run through the query and add terms.
|
|
1224
|
+
let termStrings = [];
|
|
1225
|
+
for (let term of parsedFTSQuery) {
|
|
1226
|
+
termStrings.push(queryPartTerm(term));
|
|
1227
|
+
}
|
|
753
1228
|
curQuery +=
|
|
754
|
-
(
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
name +
|
|
760
|
-
' AND position(@' +
|
|
761
|
-
value +
|
|
762
|
-
' IN "value")>0)';
|
|
1229
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1230
|
+
'(' +
|
|
1231
|
+
termStrings.join(' AND ') +
|
|
1232
|
+
')';
|
|
1233
|
+
params[name] = curValue[0];
|
|
763
1234
|
}
|
|
764
|
-
params[name] = curValue[0];
|
|
765
|
-
params[value] = svalue;
|
|
766
1235
|
}
|
|
767
1236
|
break;
|
|
768
1237
|
case 'match':
|
|
769
1238
|
case '!match':
|
|
770
|
-
if (curValue[0] === 'cdate'
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
'
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
')';
|
|
782
|
-
params[cdate] = curValue[1];
|
|
783
|
-
break;
|
|
784
|
-
}
|
|
785
|
-
else if (curValue[0] === 'mdate') {
|
|
1239
|
+
if (curValue[0] === 'cdate' ||
|
|
1240
|
+
curValue[0] === 'mdate' ||
|
|
1241
|
+
(this.nymph.tilmeld != null &&
|
|
1242
|
+
(curValue[0] === 'user' ||
|
|
1243
|
+
curValue[0] === 'group' ||
|
|
1244
|
+
curValue[0] === 'acUser' ||
|
|
1245
|
+
curValue[0] === 'acGroup' ||
|
|
1246
|
+
curValue[0] === 'acOther' ||
|
|
1247
|
+
curValue[0] === 'acRead' ||
|
|
1248
|
+
curValue[0] === 'acWrite' ||
|
|
1249
|
+
curValue[0] === 'acFull'))) {
|
|
786
1250
|
if (curQuery) {
|
|
787
1251
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
788
1252
|
}
|
|
789
|
-
const
|
|
1253
|
+
const value = `param${++count.i}`;
|
|
790
1254
|
curQuery +=
|
|
791
|
-
(
|
|
1255
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
792
1256
|
'(' +
|
|
793
1257
|
ieTable +
|
|
794
|
-
'.
|
|
795
|
-
|
|
1258
|
+
'.' +
|
|
1259
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1260
|
+
' ~ @' +
|
|
1261
|
+
value +
|
|
796
1262
|
')';
|
|
797
|
-
params[
|
|
798
|
-
break;
|
|
1263
|
+
params[value] = curValue[1];
|
|
799
1264
|
}
|
|
800
1265
|
else {
|
|
801
1266
|
if (curQuery) {
|
|
@@ -804,50 +1269,47 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
804
1269
|
const name = `param${++count.i}`;
|
|
805
1270
|
const value = `param${++count.i}`;
|
|
806
1271
|
curQuery +=
|
|
807
|
-
(
|
|
1272
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1273
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1274
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1275
|
+
' WHERE "guid"=' +
|
|
808
1276
|
ieTable +
|
|
809
|
-
'."guid"
|
|
810
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
811
|
-
' WHERE "name"=@' +
|
|
1277
|
+
'."guid" AND "name"=@' +
|
|
812
1278
|
name +
|
|
813
1279
|
' AND "string" ~ @' +
|
|
814
1280
|
value +
|
|
815
1281
|
')';
|
|
816
1282
|
params[name] = curValue[0];
|
|
817
|
-
params[value] = curValue[1];
|
|
1283
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
818
1284
|
}
|
|
819
1285
|
break;
|
|
820
1286
|
case 'imatch':
|
|
821
1287
|
case '!imatch':
|
|
822
|
-
if (curValue[0] === 'cdate'
|
|
1288
|
+
if (curValue[0] === 'cdate' ||
|
|
1289
|
+
curValue[0] === 'mdate' ||
|
|
1290
|
+
(this.nymph.tilmeld != null &&
|
|
1291
|
+
(curValue[0] === 'user' ||
|
|
1292
|
+
curValue[0] === 'group' ||
|
|
1293
|
+
curValue[0] === 'acUser' ||
|
|
1294
|
+
curValue[0] === 'acGroup' ||
|
|
1295
|
+
curValue[0] === 'acOther' ||
|
|
1296
|
+
curValue[0] === 'acRead' ||
|
|
1297
|
+
curValue[0] === 'acWrite' ||
|
|
1298
|
+
curValue[0] === 'acFull'))) {
|
|
823
1299
|
if (curQuery) {
|
|
824
1300
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
825
1301
|
}
|
|
826
|
-
const
|
|
827
|
-
curQuery +=
|
|
828
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
829
|
-
'(' +
|
|
830
|
-
ieTable +
|
|
831
|
-
'."cdate" ~* @' +
|
|
832
|
-
cdate +
|
|
833
|
-
')';
|
|
834
|
-
params[cdate] = curValue[1];
|
|
835
|
-
break;
|
|
836
|
-
}
|
|
837
|
-
else if (curValue[0] === 'mdate') {
|
|
838
|
-
if (curQuery) {
|
|
839
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
840
|
-
}
|
|
841
|
-
const mdate = `param${++count.i}`;
|
|
1302
|
+
const value = `param${++count.i}`;
|
|
842
1303
|
curQuery +=
|
|
843
|
-
(
|
|
1304
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
844
1305
|
'(' +
|
|
845
1306
|
ieTable +
|
|
846
|
-
'.
|
|
847
|
-
|
|
1307
|
+
'.' +
|
|
1308
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1309
|
+
' ~* @' +
|
|
1310
|
+
value +
|
|
848
1311
|
')';
|
|
849
|
-
params[
|
|
850
|
-
break;
|
|
1312
|
+
params[value] = curValue[1];
|
|
851
1313
|
}
|
|
852
1314
|
else {
|
|
853
1315
|
if (curQuery) {
|
|
@@ -856,50 +1318,47 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
856
1318
|
const name = `param${++count.i}`;
|
|
857
1319
|
const value = `param${++count.i}`;
|
|
858
1320
|
curQuery +=
|
|
859
|
-
(
|
|
1321
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1322
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1323
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1324
|
+
' WHERE "guid"=' +
|
|
860
1325
|
ieTable +
|
|
861
|
-
'."guid"
|
|
862
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
863
|
-
' WHERE "name"=@' +
|
|
1326
|
+
'."guid" AND "name"=@' +
|
|
864
1327
|
name +
|
|
865
1328
|
' AND "string" ~* @' +
|
|
866
1329
|
value +
|
|
867
1330
|
')';
|
|
868
1331
|
params[name] = curValue[0];
|
|
869
|
-
params[value] = curValue[1];
|
|
1332
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
870
1333
|
}
|
|
871
1334
|
break;
|
|
872
1335
|
case 'like':
|
|
873
1336
|
case '!like':
|
|
874
|
-
if (curValue[0] === 'cdate'
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
'
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
')';
|
|
886
|
-
params[cdate] = curValue[1];
|
|
887
|
-
break;
|
|
888
|
-
}
|
|
889
|
-
else if (curValue[0] === 'mdate') {
|
|
1337
|
+
if (curValue[0] === 'cdate' ||
|
|
1338
|
+
curValue[0] === 'mdate' ||
|
|
1339
|
+
(this.nymph.tilmeld != null &&
|
|
1340
|
+
(curValue[0] === 'user' ||
|
|
1341
|
+
curValue[0] === 'group' ||
|
|
1342
|
+
curValue[0] === 'acUser' ||
|
|
1343
|
+
curValue[0] === 'acGroup' ||
|
|
1344
|
+
curValue[0] === 'acOther' ||
|
|
1345
|
+
curValue[0] === 'acRead' ||
|
|
1346
|
+
curValue[0] === 'acWrite' ||
|
|
1347
|
+
curValue[0] === 'acFull'))) {
|
|
890
1348
|
if (curQuery) {
|
|
891
1349
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
892
1350
|
}
|
|
893
|
-
const
|
|
1351
|
+
const value = `param${++count.i}`;
|
|
894
1352
|
curQuery +=
|
|
895
|
-
(
|
|
1353
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
896
1354
|
'(' +
|
|
897
1355
|
ieTable +
|
|
898
|
-
'.
|
|
899
|
-
|
|
1356
|
+
'.' +
|
|
1357
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1358
|
+
' LIKE @' +
|
|
1359
|
+
value +
|
|
900
1360
|
')';
|
|
901
|
-
params[
|
|
902
|
-
break;
|
|
1361
|
+
params[value] = curValue[1];
|
|
903
1362
|
}
|
|
904
1363
|
else {
|
|
905
1364
|
if (curQuery) {
|
|
@@ -908,102 +1367,96 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
908
1367
|
const name = `param${++count.i}`;
|
|
909
1368
|
const value = `param${++count.i}`;
|
|
910
1369
|
curQuery +=
|
|
911
|
-
(
|
|
1370
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1371
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1372
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1373
|
+
' WHERE "guid"=' +
|
|
912
1374
|
ieTable +
|
|
913
|
-
'."guid"
|
|
914
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
915
|
-
' WHERE "name"=@' +
|
|
1375
|
+
'."guid" AND "name"=@' +
|
|
916
1376
|
name +
|
|
917
1377
|
' AND "string" LIKE @' +
|
|
918
1378
|
value +
|
|
919
1379
|
')';
|
|
920
1380
|
params[name] = curValue[0];
|
|
921
|
-
params[value] = curValue[1];
|
|
1381
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
922
1382
|
}
|
|
923
1383
|
break;
|
|
924
1384
|
case 'ilike':
|
|
925
1385
|
case '!ilike':
|
|
926
|
-
if (curValue[0] === 'cdate'
|
|
1386
|
+
if (curValue[0] === 'cdate' ||
|
|
1387
|
+
curValue[0] === 'mdate' ||
|
|
1388
|
+
(this.nymph.tilmeld != null &&
|
|
1389
|
+
(curValue[0] === 'user' ||
|
|
1390
|
+
curValue[0] === 'group' ||
|
|
1391
|
+
curValue[0] === 'acUser' ||
|
|
1392
|
+
curValue[0] === 'acGroup' ||
|
|
1393
|
+
curValue[0] === 'acOther' ||
|
|
1394
|
+
curValue[0] === 'acRead' ||
|
|
1395
|
+
curValue[0] === 'acWrite' ||
|
|
1396
|
+
curValue[0] === 'acFull'))) {
|
|
927
1397
|
if (curQuery) {
|
|
928
1398
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
929
1399
|
}
|
|
930
|
-
const
|
|
1400
|
+
const value = `param${++count.i}`;
|
|
931
1401
|
curQuery +=
|
|
932
|
-
(
|
|
1402
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
933
1403
|
'(' +
|
|
934
1404
|
ieTable +
|
|
935
|
-
'.
|
|
936
|
-
|
|
1405
|
+
'.' +
|
|
1406
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1407
|
+
' ILIKE @' +
|
|
1408
|
+
value +
|
|
937
1409
|
')';
|
|
938
|
-
params[
|
|
939
|
-
break;
|
|
1410
|
+
params[value] = curValue[1];
|
|
940
1411
|
}
|
|
941
|
-
else
|
|
1412
|
+
else {
|
|
942
1413
|
if (curQuery) {
|
|
943
1414
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
944
1415
|
}
|
|
945
|
-
const
|
|
1416
|
+
const name = `param${++count.i}`;
|
|
1417
|
+
const value = `param${++count.i}`;
|
|
946
1418
|
curQuery +=
|
|
947
|
-
(
|
|
948
|
-
'(' +
|
|
949
|
-
|
|
950
|
-
'
|
|
951
|
-
mdate +
|
|
952
|
-
')';
|
|
953
|
-
params[mdate] = curValue[1];
|
|
954
|
-
break;
|
|
955
|
-
}
|
|
956
|
-
else {
|
|
957
|
-
if (curQuery) {
|
|
958
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
959
|
-
}
|
|
960
|
-
const name = `param${++count.i}`;
|
|
961
|
-
const value = `param${++count.i}`;
|
|
962
|
-
curQuery +=
|
|
963
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1419
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1420
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1421
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1422
|
+
' WHERE "guid"=' +
|
|
964
1423
|
ieTable +
|
|
965
|
-
'."guid"
|
|
966
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
967
|
-
' WHERE "name"=@' +
|
|
1424
|
+
'."guid" AND "name"=@' +
|
|
968
1425
|
name +
|
|
969
1426
|
' AND "string" ILIKE @' +
|
|
970
1427
|
value +
|
|
971
1428
|
')';
|
|
972
1429
|
params[name] = curValue[0];
|
|
973
|
-
params[value] = curValue[1];
|
|
1430
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
974
1431
|
}
|
|
975
1432
|
break;
|
|
976
1433
|
case 'gt':
|
|
977
1434
|
case '!gt':
|
|
978
|
-
if (curValue[0] === 'cdate'
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
'
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
? null
|
|
990
|
-
: Number(curValue[1]);
|
|
991
|
-
break;
|
|
992
|
-
}
|
|
993
|
-
else if (curValue[0] === 'mdate') {
|
|
1435
|
+
if (curValue[0] === 'cdate' ||
|
|
1436
|
+
curValue[0] === 'mdate' ||
|
|
1437
|
+
(this.nymph.tilmeld != null &&
|
|
1438
|
+
(curValue[0] === 'user' ||
|
|
1439
|
+
curValue[0] === 'group' ||
|
|
1440
|
+
curValue[0] === 'acUser' ||
|
|
1441
|
+
curValue[0] === 'acGroup' ||
|
|
1442
|
+
curValue[0] === 'acOther' ||
|
|
1443
|
+
curValue[0] === 'acRead' ||
|
|
1444
|
+
curValue[0] === 'acWrite' ||
|
|
1445
|
+
curValue[0] === 'acFull'))) {
|
|
994
1446
|
if (curQuery) {
|
|
995
1447
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
996
1448
|
}
|
|
997
|
-
const
|
|
1449
|
+
const value = `param${++count.i}`;
|
|
998
1450
|
curQuery +=
|
|
999
|
-
(
|
|
1451
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1000
1452
|
ieTable +
|
|
1001
|
-
'.
|
|
1002
|
-
|
|
1003
|
-
|
|
1453
|
+
'.' +
|
|
1454
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1455
|
+
'>@' +
|
|
1456
|
+
value;
|
|
1457
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1004
1458
|
? null
|
|
1005
1459
|
: Number(curValue[1]);
|
|
1006
|
-
break;
|
|
1007
1460
|
}
|
|
1008
1461
|
else {
|
|
1009
1462
|
if (curQuery) {
|
|
@@ -1012,11 +1465,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1012
1465
|
const name = `param${++count.i}`;
|
|
1013
1466
|
const value = `param${++count.i}`;
|
|
1014
1467
|
curQuery +=
|
|
1015
|
-
(
|
|
1468
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1469
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1470
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1471
|
+
' WHERE "guid"=' +
|
|
1016
1472
|
ieTable +
|
|
1017
|
-
'."guid"
|
|
1018
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1019
|
-
' WHERE "name"=@' +
|
|
1473
|
+
'."guid" AND "name"=@' +
|
|
1020
1474
|
name +
|
|
1021
1475
|
' AND "number">@' +
|
|
1022
1476
|
value +
|
|
@@ -1029,35 +1483,31 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1029
1483
|
break;
|
|
1030
1484
|
case 'gte':
|
|
1031
1485
|
case '!gte':
|
|
1032
|
-
if (curValue[0] === 'cdate'
|
|
1486
|
+
if (curValue[0] === 'cdate' ||
|
|
1487
|
+
curValue[0] === 'mdate' ||
|
|
1488
|
+
(this.nymph.tilmeld != null &&
|
|
1489
|
+
(curValue[0] === 'user' ||
|
|
1490
|
+
curValue[0] === 'group' ||
|
|
1491
|
+
curValue[0] === 'acUser' ||
|
|
1492
|
+
curValue[0] === 'acGroup' ||
|
|
1493
|
+
curValue[0] === 'acOther' ||
|
|
1494
|
+
curValue[0] === 'acRead' ||
|
|
1495
|
+
curValue[0] === 'acWrite' ||
|
|
1496
|
+
curValue[0] === 'acFull'))) {
|
|
1033
1497
|
if (curQuery) {
|
|
1034
1498
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1035
1499
|
}
|
|
1036
|
-
const
|
|
1037
|
-
curQuery +=
|
|
1038
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1039
|
-
ieTable +
|
|
1040
|
-
'."cdate">=@' +
|
|
1041
|
-
cdate;
|
|
1042
|
-
params[cdate] = isNaN(Number(curValue[1]))
|
|
1043
|
-
? null
|
|
1044
|
-
: Number(curValue[1]);
|
|
1045
|
-
break;
|
|
1046
|
-
}
|
|
1047
|
-
else if (curValue[0] === 'mdate') {
|
|
1048
|
-
if (curQuery) {
|
|
1049
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1050
|
-
}
|
|
1051
|
-
const mdate = `param${++count.i}`;
|
|
1500
|
+
const value = `param${++count.i}`;
|
|
1052
1501
|
curQuery +=
|
|
1053
|
-
(
|
|
1502
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1054
1503
|
ieTable +
|
|
1055
|
-
'.
|
|
1056
|
-
|
|
1057
|
-
|
|
1504
|
+
'.' +
|
|
1505
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1506
|
+
'>=@' +
|
|
1507
|
+
value;
|
|
1508
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1058
1509
|
? null
|
|
1059
1510
|
: Number(curValue[1]);
|
|
1060
|
-
break;
|
|
1061
1511
|
}
|
|
1062
1512
|
else {
|
|
1063
1513
|
if (curQuery) {
|
|
@@ -1066,11 +1516,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1066
1516
|
const name = `param${++count.i}`;
|
|
1067
1517
|
const value = `param${++count.i}`;
|
|
1068
1518
|
curQuery +=
|
|
1069
|
-
(
|
|
1519
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1520
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1521
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1522
|
+
' WHERE "guid"=' +
|
|
1070
1523
|
ieTable +
|
|
1071
|
-
'."guid"
|
|
1072
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1073
|
-
' WHERE "name"=@' +
|
|
1524
|
+
'."guid" AND "name"=@' +
|
|
1074
1525
|
name +
|
|
1075
1526
|
' AND "number">=@' +
|
|
1076
1527
|
value +
|
|
@@ -1083,35 +1534,31 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1083
1534
|
break;
|
|
1084
1535
|
case 'lt':
|
|
1085
1536
|
case '!lt':
|
|
1086
|
-
if (curValue[0] === 'cdate'
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
'
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
? null
|
|
1098
|
-
: Number(curValue[1]);
|
|
1099
|
-
break;
|
|
1100
|
-
}
|
|
1101
|
-
else if (curValue[0] === 'mdate') {
|
|
1537
|
+
if (curValue[0] === 'cdate' ||
|
|
1538
|
+
curValue[0] === 'mdate' ||
|
|
1539
|
+
(this.nymph.tilmeld != null &&
|
|
1540
|
+
(curValue[0] === 'user' ||
|
|
1541
|
+
curValue[0] === 'group' ||
|
|
1542
|
+
curValue[0] === 'acUser' ||
|
|
1543
|
+
curValue[0] === 'acGroup' ||
|
|
1544
|
+
curValue[0] === 'acOther' ||
|
|
1545
|
+
curValue[0] === 'acRead' ||
|
|
1546
|
+
curValue[0] === 'acWrite' ||
|
|
1547
|
+
curValue[0] === 'acFull'))) {
|
|
1102
1548
|
if (curQuery) {
|
|
1103
1549
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1104
1550
|
}
|
|
1105
|
-
const
|
|
1551
|
+
const value = `param${++count.i}`;
|
|
1106
1552
|
curQuery +=
|
|
1107
|
-
(
|
|
1553
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1108
1554
|
ieTable +
|
|
1109
|
-
'.
|
|
1110
|
-
|
|
1111
|
-
|
|
1555
|
+
'.' +
|
|
1556
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1557
|
+
'<@' +
|
|
1558
|
+
value;
|
|
1559
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1112
1560
|
? null
|
|
1113
1561
|
: Number(curValue[1]);
|
|
1114
|
-
break;
|
|
1115
1562
|
}
|
|
1116
1563
|
else {
|
|
1117
1564
|
if (curQuery) {
|
|
@@ -1120,11 +1567,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1120
1567
|
const name = `param${++count.i}`;
|
|
1121
1568
|
const value = `param${++count.i}`;
|
|
1122
1569
|
curQuery +=
|
|
1123
|
-
(
|
|
1570
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1571
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1572
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1573
|
+
' WHERE "guid"=' +
|
|
1124
1574
|
ieTable +
|
|
1125
|
-
'."guid"
|
|
1126
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1127
|
-
' WHERE "name"=@' +
|
|
1575
|
+
'."guid" AND "name"=@' +
|
|
1128
1576
|
name +
|
|
1129
1577
|
' AND "number"<@' +
|
|
1130
1578
|
value +
|
|
@@ -1137,35 +1585,31 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1137
1585
|
break;
|
|
1138
1586
|
case 'lte':
|
|
1139
1587
|
case '!lte':
|
|
1140
|
-
if (curValue[0] === 'cdate'
|
|
1588
|
+
if (curValue[0] === 'cdate' ||
|
|
1589
|
+
curValue[0] === 'mdate' ||
|
|
1590
|
+
(this.nymph.tilmeld != null &&
|
|
1591
|
+
(curValue[0] === 'user' ||
|
|
1592
|
+
curValue[0] === 'group' ||
|
|
1593
|
+
curValue[0] === 'acUser' ||
|
|
1594
|
+
curValue[0] === 'acGroup' ||
|
|
1595
|
+
curValue[0] === 'acOther' ||
|
|
1596
|
+
curValue[0] === 'acRead' ||
|
|
1597
|
+
curValue[0] === 'acWrite' ||
|
|
1598
|
+
curValue[0] === 'acFull'))) {
|
|
1141
1599
|
if (curQuery) {
|
|
1142
1600
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1143
1601
|
}
|
|
1144
|
-
const
|
|
1145
|
-
curQuery +=
|
|
1146
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1147
|
-
ieTable +
|
|
1148
|
-
'."cdate"<=@' +
|
|
1149
|
-
cdate;
|
|
1150
|
-
params[cdate] = isNaN(Number(curValue[1]))
|
|
1151
|
-
? null
|
|
1152
|
-
: Number(curValue[1]);
|
|
1153
|
-
break;
|
|
1154
|
-
}
|
|
1155
|
-
else if (curValue[0] === 'mdate') {
|
|
1156
|
-
if (curQuery) {
|
|
1157
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1158
|
-
}
|
|
1159
|
-
const mdate = `param${++count.i}`;
|
|
1602
|
+
const value = `param${++count.i}`;
|
|
1160
1603
|
curQuery +=
|
|
1161
|
-
(
|
|
1604
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1162
1605
|
ieTable +
|
|
1163
|
-
'.
|
|
1164
|
-
|
|
1165
|
-
|
|
1606
|
+
'.' +
|
|
1607
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1608
|
+
'<=@' +
|
|
1609
|
+
value;
|
|
1610
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1166
1611
|
? null
|
|
1167
1612
|
: Number(curValue[1]);
|
|
1168
|
-
break;
|
|
1169
1613
|
}
|
|
1170
1614
|
else {
|
|
1171
1615
|
if (curQuery) {
|
|
@@ -1174,11 +1618,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1174
1618
|
const name = `param${++count.i}`;
|
|
1175
1619
|
const value = `param${++count.i}`;
|
|
1176
1620
|
curQuery +=
|
|
1177
|
-
(
|
|
1621
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1622
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1623
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1624
|
+
' WHERE "guid"=' +
|
|
1178
1625
|
ieTable +
|
|
1179
|
-
'."guid"
|
|
1180
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1181
|
-
' WHERE "name"=@' +
|
|
1626
|
+
'."guid" AND "name"=@' +
|
|
1182
1627
|
name +
|
|
1183
1628
|
' AND "number"<=@' +
|
|
1184
1629
|
value +
|
|
@@ -1204,71 +1649,190 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1204
1649
|
if (curQuery) {
|
|
1205
1650
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1206
1651
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1652
|
+
if (this.nymph.tilmeld != null &&
|
|
1653
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
1654
|
+
const guid = `param${++count.i}`;
|
|
1655
|
+
curQuery +=
|
|
1656
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1657
|
+
ieTable +
|
|
1658
|
+
'.' +
|
|
1659
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1660
|
+
'=decode(@' +
|
|
1661
|
+
guid +
|
|
1662
|
+
", 'hex')";
|
|
1663
|
+
params[guid] = curQguid;
|
|
1664
|
+
}
|
|
1665
|
+
else if (this.nymph.tilmeld != null &&
|
|
1666
|
+
(curValue[0] === 'acRead' ||
|
|
1667
|
+
curValue[0] === 'acWrite' ||
|
|
1668
|
+
curValue[0] === 'acFull')) {
|
|
1669
|
+
const guid = `param${++count.i}`;
|
|
1670
|
+
curQuery +=
|
|
1671
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1672
|
+
'decode(@' +
|
|
1673
|
+
guid +
|
|
1674
|
+
", 'hex')=ANY(" +
|
|
1675
|
+
ieTable +
|
|
1676
|
+
'.' +
|
|
1677
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1678
|
+
')';
|
|
1679
|
+
params[guid] = curQguid;
|
|
1680
|
+
}
|
|
1681
|
+
else {
|
|
1682
|
+
const name = `param${++count.i}`;
|
|
1683
|
+
const guid = `param${++count.i}`;
|
|
1684
|
+
curQuery +=
|
|
1685
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1686
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1687
|
+
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1688
|
+
' WHERE "guid"=' +
|
|
1689
|
+
ieTable +
|
|
1690
|
+
'."guid" AND "name"=@' +
|
|
1691
|
+
name +
|
|
1692
|
+
' AND "reference"=decode(@' +
|
|
1693
|
+
guid +
|
|
1694
|
+
", 'hex'))";
|
|
1695
|
+
params[name] = curValue[0];
|
|
1696
|
+
params[guid] = curQguid;
|
|
1697
|
+
}
|
|
1221
1698
|
break;
|
|
1222
1699
|
case 'selector':
|
|
1223
1700
|
case '!selector':
|
|
1224
|
-
const
|
|
1701
|
+
const innerquery = this.makeEntityQuery({ ...options, sort: null, limit: undefined }, [curValue], etype, count, params, true, tableSuffix, etypes);
|
|
1225
1702
|
if (curQuery) {
|
|
1226
1703
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1227
1704
|
}
|
|
1228
1705
|
curQuery +=
|
|
1229
|
-
(
|
|
1706
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1230
1707
|
'(' +
|
|
1231
|
-
|
|
1708
|
+
innerquery.query +
|
|
1232
1709
|
')';
|
|
1233
1710
|
break;
|
|
1234
1711
|
case 'qref':
|
|
1235
1712
|
case '!qref':
|
|
1713
|
+
const referenceTableSuffix = makeTableSuffix();
|
|
1236
1714
|
const [qrefOptions, ...qrefSelectors] = curValue[1];
|
|
1237
1715
|
const QrefEntityClass = qrefOptions.class;
|
|
1238
1716
|
etypes.push(QrefEntityClass.ETYPE);
|
|
1239
|
-
const qrefQuery = this.makeEntityQuery({ ...qrefOptions, return: 'guid', class: QrefEntityClass }, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, (0, guid_1.makeTableSuffix)(), etypes);
|
|
1240
1717
|
if (curQuery) {
|
|
1241
1718
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1242
1719
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1720
|
+
if (this.nymph.tilmeld != null &&
|
|
1721
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
1722
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1723
|
+
...qrefOptions,
|
|
1724
|
+
sort: qrefOptions.sort ?? null,
|
|
1725
|
+
return: 'guid',
|
|
1726
|
+
class: QrefEntityClass,
|
|
1727
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' +
|
|
1728
|
+
referenceTableSuffix +
|
|
1729
|
+
'.' +
|
|
1730
|
+
PostgreSQLDriver.escape(curValue[0]));
|
|
1731
|
+
curQuery +=
|
|
1732
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1733
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1734
|
+
PostgreSQLDriver.escape(this.prefix + 'entities_' + etype) +
|
|
1735
|
+
' r' +
|
|
1736
|
+
referenceTableSuffix +
|
|
1737
|
+
' WHERE r' +
|
|
1738
|
+
referenceTableSuffix +
|
|
1739
|
+
'."guid"=' +
|
|
1740
|
+
ieTable +
|
|
1741
|
+
'."guid" AND EXISTS (' +
|
|
1742
|
+
qrefQuery.query +
|
|
1743
|
+
'))';
|
|
1744
|
+
}
|
|
1745
|
+
else if (this.nymph.tilmeld != null &&
|
|
1746
|
+
(curValue[0] === 'acRead' ||
|
|
1747
|
+
curValue[0] === 'acWrite' ||
|
|
1748
|
+
curValue[0] === 'acFull')) {
|
|
1749
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1750
|
+
...qrefOptions,
|
|
1751
|
+
sort: qrefOptions.sort ?? null,
|
|
1752
|
+
return: 'guid',
|
|
1753
|
+
class: QrefEntityClass,
|
|
1754
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, undefined, (guid) => guid +
|
|
1755
|
+
'=ANY(' +
|
|
1246
1756
|
ieTable +
|
|
1247
|
-
'.
|
|
1248
|
-
PostgreSQLDriver.escape(
|
|
1249
|
-
'
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1757
|
+
'.' +
|
|
1758
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1759
|
+
')');
|
|
1760
|
+
curQuery +=
|
|
1761
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1762
|
+
'EXISTS (' +
|
|
1763
|
+
qrefQuery.query +
|
|
1764
|
+
')';
|
|
1765
|
+
}
|
|
1766
|
+
else {
|
|
1767
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1768
|
+
...qrefOptions,
|
|
1769
|
+
sort: qrefOptions.sort ?? null,
|
|
1770
|
+
return: 'guid',
|
|
1771
|
+
class: QrefEntityClass,
|
|
1772
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' + referenceTableSuffix + '."reference"');
|
|
1773
|
+
const qrefName = `param${++count.i}`;
|
|
1774
|
+
curQuery +=
|
|
1775
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1776
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1777
|
+
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1778
|
+
' r' +
|
|
1779
|
+
referenceTableSuffix +
|
|
1780
|
+
' WHERE r' +
|
|
1781
|
+
referenceTableSuffix +
|
|
1782
|
+
'."guid"=' +
|
|
1783
|
+
ieTable +
|
|
1784
|
+
'."guid" AND r' +
|
|
1785
|
+
referenceTableSuffix +
|
|
1786
|
+
'."name"=@' +
|
|
1787
|
+
qrefName +
|
|
1788
|
+
' AND EXISTS (' +
|
|
1789
|
+
qrefQuery.query +
|
|
1790
|
+
'))';
|
|
1791
|
+
params[qrefName] = curValue[0];
|
|
1792
|
+
}
|
|
1255
1793
|
break;
|
|
1256
1794
|
}
|
|
1257
1795
|
}
|
|
1258
1796
|
return curQuery;
|
|
1259
1797
|
});
|
|
1260
1798
|
let sortBy;
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1799
|
+
let sortByInner;
|
|
1800
|
+
let sortJoin = '';
|
|
1801
|
+
let sortJoinInner = '';
|
|
1802
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1803
|
+
if (sort == null) {
|
|
1804
|
+
sortBy = '';
|
|
1805
|
+
sortByInner = '';
|
|
1806
|
+
}
|
|
1807
|
+
else {
|
|
1808
|
+
switch (sort) {
|
|
1809
|
+
case 'mdate':
|
|
1810
|
+
sortBy = `ORDER BY ${eTable}."mdate"${order}`;
|
|
1811
|
+
sortByInner = `ORDER BY ${ieTable}."mdate"${order}`;
|
|
1812
|
+
break;
|
|
1813
|
+
case 'cdate':
|
|
1814
|
+
sortBy = `ORDER BY ${eTable}."cdate"${order}`;
|
|
1815
|
+
sortByInner = `ORDER BY ${ieTable}."cdate"${order}`;
|
|
1816
|
+
break;
|
|
1817
|
+
default:
|
|
1818
|
+
const name = `param${++count.i}`;
|
|
1819
|
+
sortJoin = `LEFT JOIN (
|
|
1820
|
+
SELECT "guid", "string", "number"
|
|
1821
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1822
|
+
WHERE "name"=@${name}
|
|
1823
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1824
|
+
) ${sTable} ON ${eTable}."guid"=${sTable}."guid"`;
|
|
1825
|
+
sortJoinInner = `LEFT JOIN (
|
|
1826
|
+
SELECT "guid", "string", "number"
|
|
1827
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1828
|
+
WHERE "name"=@${name}
|
|
1829
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1830
|
+
) ${sTable} ON ${ieTable}."guid"=${sTable}."guid"`;
|
|
1831
|
+
sortBy = `ORDER BY ${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1832
|
+
sortByInner = sortBy;
|
|
1833
|
+
params[name] = sort;
|
|
1834
|
+
break;
|
|
1835
|
+
}
|
|
1272
1836
|
}
|
|
1273
1837
|
let query;
|
|
1274
1838
|
if (queryParts.length) {
|
|
@@ -1285,18 +1849,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1285
1849
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1286
1850
|
}
|
|
1287
1851
|
const whereClause = queryParts.join(') AND (');
|
|
1852
|
+
const guidClause = guidExplicitSelector
|
|
1853
|
+
? `${guidExplicitSelector(`${ieTable}."guid"`)} AND `
|
|
1854
|
+
: guidSelector
|
|
1855
|
+
? `${ieTable}."guid"=${guidSelector} AND `
|
|
1856
|
+
: '';
|
|
1288
1857
|
if (options.return === 'count') {
|
|
1289
1858
|
if (limit || offset) {
|
|
1290
1859
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1291
|
-
SELECT
|
|
1860
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1292
1861
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1293
|
-
WHERE (${whereClause})${limit}${offset}
|
|
1862
|
+
WHERE ${guidClause}(${whereClause})${limit}${offset}
|
|
1294
1863
|
) ${countTable}`;
|
|
1295
1864
|
}
|
|
1296
1865
|
else {
|
|
1297
1866
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1298
1867
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1299
|
-
WHERE (${whereClause})`;
|
|
1868
|
+
WHERE ${guidClause}(${whereClause})`;
|
|
1300
1869
|
}
|
|
1301
1870
|
}
|
|
1302
1871
|
else if (options.return === 'guid') {
|
|
@@ -1305,8 +1874,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1305
1874
|
: `${ieTable}."guid"`;
|
|
1306
1875
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1307
1876
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1308
|
-
|
|
1309
|
-
|
|
1877
|
+
${sortJoinInner}
|
|
1878
|
+
WHERE ${guidClause}(${whereClause})
|
|
1879
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}${ieTable}."guid"${limit}${offset}`;
|
|
1310
1880
|
}
|
|
1311
1881
|
else {
|
|
1312
1882
|
query = `SELECT
|
|
@@ -1314,20 +1884,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1314
1884
|
${eTable}."tags",
|
|
1315
1885
|
${eTable}."cdate",
|
|
1316
1886
|
${eTable}."mdate",
|
|
1887
|
+
encode(${eTable}."user", 'hex') AS "user",
|
|
1888
|
+
encode(${eTable}."group", 'hex') AS "group",
|
|
1889
|
+
${eTable}."acUser",
|
|
1890
|
+
${eTable}."acGroup",
|
|
1891
|
+
${eTable}."acOther",
|
|
1892
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acRead") AS n) as "acRead",
|
|
1893
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acWrite") AS n) as "acWrite",
|
|
1894
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acFull") AS n) as "acFull",
|
|
1317
1895
|
${dTable}."name",
|
|
1318
1896
|
${dTable}."value",
|
|
1319
|
-
${
|
|
1320
|
-
${
|
|
1897
|
+
${dTable}."json",
|
|
1898
|
+
${dTable}."string",
|
|
1899
|
+
${dTable}."number"
|
|
1321
1900
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1322
1901
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1323
|
-
|
|
1902
|
+
${sortJoin}
|
|
1324
1903
|
INNER JOIN (
|
|
1325
1904
|
SELECT ${ieTable}."guid"
|
|
1326
1905
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1327
|
-
|
|
1328
|
-
|
|
1906
|
+
${sortJoinInner}
|
|
1907
|
+
WHERE ${guidClause}(${whereClause})
|
|
1908
|
+
${sortByInner}${limit}${offset}
|
|
1329
1909
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1330
|
-
ORDER BY ${eTable}
|
|
1910
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1331
1911
|
}
|
|
1332
1912
|
}
|
|
1333
1913
|
}
|
|
@@ -1344,16 +1924,21 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1344
1924
|
if ('offset' in options) {
|
|
1345
1925
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1346
1926
|
}
|
|
1927
|
+
const guidClause = guidExplicitSelector
|
|
1928
|
+
? ` WHERE ${guidExplicitSelector(`${ieTable}."guid"`)}`
|
|
1929
|
+
: guidSelector
|
|
1930
|
+
? ` WHERE ${ieTable}."guid"=${guidSelector}`
|
|
1931
|
+
: '';
|
|
1347
1932
|
if (options.return === 'count') {
|
|
1348
1933
|
if (limit || offset) {
|
|
1349
1934
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1350
|
-
SELECT
|
|
1351
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${limit}${offset}
|
|
1935
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1936
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}${limit}${offset}
|
|
1352
1937
|
) ${countTable}`;
|
|
1353
1938
|
}
|
|
1354
1939
|
else {
|
|
1355
1940
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1356
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}`;
|
|
1941
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}`;
|
|
1357
1942
|
}
|
|
1358
1943
|
}
|
|
1359
1944
|
else if (options.return === 'guid') {
|
|
@@ -1362,7 +1947,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1362
1947
|
: `${ieTable}."guid"`;
|
|
1363
1948
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1364
1949
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1365
|
-
|
|
1950
|
+
${sortJoinInner}
|
|
1951
|
+
${guidClause}
|
|
1952
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}${ieTable}."guid"${limit}${offset}`;
|
|
1366
1953
|
}
|
|
1367
1954
|
else {
|
|
1368
1955
|
if (limit || offset) {
|
|
@@ -1371,19 +1958,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1371
1958
|
${eTable}."tags",
|
|
1372
1959
|
${eTable}."cdate",
|
|
1373
1960
|
${eTable}."mdate",
|
|
1961
|
+
encode(${eTable}."user", 'hex') AS "user",
|
|
1962
|
+
encode(${eTable}."group", 'hex') AS "group",
|
|
1963
|
+
${eTable}."acUser",
|
|
1964
|
+
${eTable}."acGroup",
|
|
1965
|
+
${eTable}."acOther",
|
|
1966
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acRead") AS n) as "acRead",
|
|
1967
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acWrite") AS n) as "acWrite",
|
|
1968
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acFull") AS n) as "acFull",
|
|
1374
1969
|
${dTable}."name",
|
|
1375
1970
|
${dTable}."value",
|
|
1376
|
-
${
|
|
1377
|
-
${
|
|
1971
|
+
${dTable}."json",
|
|
1972
|
+
${dTable}."string",
|
|
1973
|
+
${dTable}."number"
|
|
1378
1974
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1379
1975
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1380
|
-
|
|
1976
|
+
${sortJoin}
|
|
1381
1977
|
INNER JOIN (
|
|
1382
1978
|
SELECT ${ieTable}."guid"
|
|
1383
1979
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1384
|
-
|
|
1980
|
+
${sortJoinInner}
|
|
1981
|
+
${guidClause}
|
|
1982
|
+
${sortByInner}${limit}${offset}
|
|
1385
1983
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1386
|
-
ORDER BY ${eTable}
|
|
1984
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1387
1985
|
}
|
|
1388
1986
|
else {
|
|
1389
1987
|
query = `SELECT
|
|
@@ -1391,14 +1989,24 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1391
1989
|
${eTable}."tags",
|
|
1392
1990
|
${eTable}."cdate",
|
|
1393
1991
|
${eTable}."mdate",
|
|
1992
|
+
encode(${eTable}."user", 'hex') AS "user",
|
|
1993
|
+
encode(${eTable}."group", 'hex') AS "group",
|
|
1994
|
+
${eTable}."acUser",
|
|
1995
|
+
${eTable}."acGroup",
|
|
1996
|
+
${eTable}."acOther",
|
|
1997
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acRead") AS n) as "acRead",
|
|
1998
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acWrite") AS n) as "acWrite",
|
|
1999
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acFull") AS n) as "acFull",
|
|
1394
2000
|
${dTable}."name",
|
|
1395
2001
|
${dTable}."value",
|
|
1396
|
-
${
|
|
1397
|
-
${
|
|
2002
|
+
${dTable}."json",
|
|
2003
|
+
${dTable}."string",
|
|
2004
|
+
${dTable}."number"
|
|
1398
2005
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1399
2006
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1400
|
-
|
|
1401
|
-
|
|
2007
|
+
${sortJoin}
|
|
2008
|
+
${guidExplicitSelector ? `WHERE ${guidExplicitSelector(`${eTable}."guid"`)}` : guidSelector ? `WHERE ${eTable}."guid"=${guidSelector}` : ''}
|
|
2009
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1402
2010
|
}
|
|
1403
2011
|
}
|
|
1404
2012
|
}
|
|
@@ -1414,33 +2022,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1414
2022
|
}
|
|
1415
2023
|
performQuery(options, formattedSelectors, etype) {
|
|
1416
2024
|
const { query, params, etypes } = this.makeEntityQuery(options, formattedSelectors, etype);
|
|
1417
|
-
const result = this.
|
|
1418
|
-
return {
|
|
1419
|
-
result,
|
|
1420
|
-
};
|
|
1421
|
-
}
|
|
1422
|
-
performQuerySync(options, formattedSelectors, etype) {
|
|
1423
|
-
const { query, params, etypes } = this.makeEntityQuery(options, formattedSelectors, etype);
|
|
1424
|
-
const result = (this.queryIterSync(query, { etypes, params }) || [])[Symbol.iterator]();
|
|
2025
|
+
const result = this.queryArray(query, { etypes, params }).then((val) => val[Symbol.iterator]());
|
|
1425
2026
|
return {
|
|
1426
2027
|
result,
|
|
1427
2028
|
};
|
|
1428
2029
|
}
|
|
1429
2030
|
async getEntities(options = {}, ...selectors) {
|
|
1430
|
-
const { result: resultPromise, process } = this.
|
|
2031
|
+
const { result: resultPromise, process } = this.getEntitiesRowLike(
|
|
2032
|
+
// @ts-ignore: options is correct here.
|
|
2033
|
+
options, selectors, ({ options, selectors, etype }) => this.performQuery(options, selectors, etype), () => {
|
|
1431
2034
|
const next = result.next();
|
|
1432
2035
|
return next.done ? null : next.value;
|
|
1433
2036
|
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1434
|
-
tags: row.tags,
|
|
1435
|
-
cdate: isNaN(Number(row.cdate)) ?
|
|
1436
|
-
mdate: isNaN(Number(row.mdate)) ?
|
|
2037
|
+
tags: row.tags.filter((tag) => tag),
|
|
2038
|
+
cdate: isNaN(Number(row.cdate)) ? Date.now() : Number(row.cdate),
|
|
2039
|
+
mdate: isNaN(Number(row.mdate)) ? Date.now() : Number(row.mdate),
|
|
2040
|
+
user: row.user,
|
|
2041
|
+
group: row.group,
|
|
2042
|
+
acUser: row.acUser,
|
|
2043
|
+
acGroup: row.acGroup,
|
|
2044
|
+
acOther: row.acOther,
|
|
2045
|
+
acRead: row.acRead?.filter((guid) => guid) ?? [],
|
|
2046
|
+
acWrite: row.acWrite?.filter((guid) => guid) ?? [],
|
|
2047
|
+
acFull: row.acFull?.filter((guid) => guid) ?? [],
|
|
1437
2048
|
}), (row) => ({
|
|
1438
2049
|
name: row.name,
|
|
1439
2050
|
svalue: row.value === 'N'
|
|
1440
2051
|
? JSON.stringify(Number(row.number))
|
|
1441
2052
|
: row.value === 'S'
|
|
1442
|
-
? JSON.stringify(row.string)
|
|
1443
|
-
: row.value
|
|
2053
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(row.string))
|
|
2054
|
+
: row.value === 'J'
|
|
2055
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(row.json))
|
|
2056
|
+
: row.value,
|
|
1444
2057
|
}));
|
|
1445
2058
|
const result = await resultPromise;
|
|
1446
2059
|
const value = process();
|
|
@@ -1449,31 +2062,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1449
2062
|
}
|
|
1450
2063
|
return value;
|
|
1451
2064
|
}
|
|
1452
|
-
getEntitiesSync(options = {}, ...selectors) {
|
|
1453
|
-
const { result, process } = this.getEntitesRowLike(options, selectors, (options, formattedSelectors, etype) => this.performQuerySync(options, formattedSelectors, etype), () => {
|
|
1454
|
-
const next = result.next();
|
|
1455
|
-
return next.done ? null : next.value;
|
|
1456
|
-
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1457
|
-
tags: row.tags,
|
|
1458
|
-
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
1459
|
-
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
1460
|
-
}), (row) => ({
|
|
1461
|
-
name: row.name,
|
|
1462
|
-
svalue: row.value === 'N'
|
|
1463
|
-
? JSON.stringify(Number(row.number))
|
|
1464
|
-
: row.value === 'S'
|
|
1465
|
-
? JSON.stringify(row.string)
|
|
1466
|
-
: row.value,
|
|
1467
|
-
}));
|
|
1468
|
-
const value = process();
|
|
1469
|
-
if (value instanceof Error) {
|
|
1470
|
-
throw value;
|
|
1471
|
-
}
|
|
1472
|
-
return value;
|
|
1473
|
-
}
|
|
1474
2065
|
async getUID(name) {
|
|
1475
2066
|
if (name == null) {
|
|
1476
|
-
throw new
|
|
2067
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1477
2068
|
}
|
|
1478
2069
|
const result = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
1479
2070
|
params: {
|
|
@@ -1482,50 +2073,75 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1482
2073
|
});
|
|
1483
2074
|
return result?.cur_uid == null ? null : Number(result.cur_uid);
|
|
1484
2075
|
}
|
|
1485
|
-
async
|
|
2076
|
+
async importEntity(entity) {
|
|
2077
|
+
return await this.importEntityInternal(entity);
|
|
2078
|
+
}
|
|
2079
|
+
async importEntityTokens(entity) {
|
|
2080
|
+
return await this.importEntityInternal(entity, { only: 'tokens' });
|
|
2081
|
+
}
|
|
2082
|
+
async importEntityTilmeldAC(entity) {
|
|
2083
|
+
return await this.importEntityInternal(entity, { only: 'tilmeldAC' });
|
|
2084
|
+
}
|
|
2085
|
+
async importEntityInternal({ guid, cdate, mdate, tags, sdata, etype, }, { only = undefined } = {}) {
|
|
1486
2086
|
try {
|
|
1487
|
-
|
|
1488
|
-
|
|
2087
|
+
let promises = [];
|
|
2088
|
+
if (only == null) {
|
|
2089
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1489
2090
|
etypes: [etype],
|
|
1490
2091
|
params: {
|
|
1491
2092
|
guid,
|
|
1492
2093
|
},
|
|
1493
|
-
});
|
|
1494
|
-
|
|
2094
|
+
}));
|
|
2095
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1495
2096
|
etypes: [etype],
|
|
1496
2097
|
params: {
|
|
1497
2098
|
guid,
|
|
1498
|
-
tags,
|
|
1499
|
-
cdate: isNaN(Number(JSON.parse(sdata.cdate)))
|
|
1500
|
-
? null
|
|
1501
|
-
: Number(JSON.parse(sdata.cdate)),
|
|
1502
|
-
mdate: isNaN(Number(JSON.parse(sdata.mdate)))
|
|
1503
|
-
? null
|
|
1504
|
-
: Number(JSON.parse(sdata.mdate)),
|
|
1505
2099
|
},
|
|
1506
|
-
});
|
|
1507
|
-
|
|
1508
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
2100
|
+
}));
|
|
2101
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1509
2102
|
etypes: [etype],
|
|
1510
2103
|
params: {
|
|
1511
2104
|
guid,
|
|
1512
2105
|
},
|
|
1513
2106
|
}));
|
|
1514
|
-
|
|
2107
|
+
}
|
|
2108
|
+
if (only == null || only === 'tokens') {
|
|
2109
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1515
2110
|
etypes: [etype],
|
|
1516
2111
|
params: {
|
|
1517
2112
|
guid,
|
|
1518
2113
|
},
|
|
1519
2114
|
}));
|
|
1520
|
-
|
|
2115
|
+
}
|
|
2116
|
+
if (only == null) {
|
|
2117
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1521
2118
|
etypes: [etype],
|
|
1522
2119
|
params: {
|
|
1523
2120
|
guid,
|
|
1524
2121
|
},
|
|
1525
2122
|
}));
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
2123
|
+
}
|
|
2124
|
+
await Promise.all(promises);
|
|
2125
|
+
promises = [];
|
|
2126
|
+
if (only == null) {
|
|
2127
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } = this.removeAndReturnACValues(etype, {}, sdata);
|
|
2128
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate", "user", "group", "acUser", "acGroup", "acOther", "acRead", "acWrite", "acFull") VALUES (decode(@guid, 'hex'), @tags, @cdate, @mdate, ${user == null ? '@user' : "decode(@user, 'hex')"}, ${group == null ? '@group' : "decode(@group, 'hex')"}, @acUser, @acGroup, @acOther, ${!acRead?.length ? '@acRead' : "array(SELECT decode(n, 'hex') FROM unnest(@acRead::text[]) AS n)"}, ${!acWrite?.length ? '@acWrite' : "array(SELECT decode(n, 'hex') FROM unnest(@acWrite::text[]) AS n)"}, ${!acFull?.length ? '@acFull' : "array(SELECT decode(n, 'hex') FROM unnest(@acFull::text[]) AS n)"});`, {
|
|
2129
|
+
etypes: [etype],
|
|
2130
|
+
params: {
|
|
2131
|
+
guid,
|
|
2132
|
+
tags,
|
|
2133
|
+
cdate: isNaN(cdate) ? null : cdate,
|
|
2134
|
+
mdate: isNaN(mdate) ? null : mdate,
|
|
2135
|
+
user,
|
|
2136
|
+
group,
|
|
2137
|
+
acUser,
|
|
2138
|
+
acGroup,
|
|
2139
|
+
acOther,
|
|
2140
|
+
acRead,
|
|
2141
|
+
acWrite,
|
|
2142
|
+
acFull,
|
|
2143
|
+
},
|
|
2144
|
+
});
|
|
1529
2145
|
for (const name in sdata) {
|
|
1530
2146
|
const value = sdata[name];
|
|
1531
2147
|
const uvalue = JSON.parse(value);
|
|
@@ -1536,24 +2152,22 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1536
2152
|
? 'N'
|
|
1537
2153
|
: typeof uvalue === 'string'
|
|
1538
2154
|
? 'S'
|
|
1539
|
-
:
|
|
1540
|
-
const
|
|
1541
|
-
|
|
2155
|
+
: 'J';
|
|
2156
|
+
const jsonValue = storageValue === 'J'
|
|
2157
|
+
? PostgreSQLDriver.escapeNullSequences(value)
|
|
2158
|
+
: null;
|
|
2159
|
+
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);`, {
|
|
1542
2160
|
etypes: [etype],
|
|
1543
2161
|
params: {
|
|
1544
2162
|
guid,
|
|
1545
2163
|
name,
|
|
1546
2164
|
storageValue,
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
params: {
|
|
1552
|
-
guid,
|
|
1553
|
-
name,
|
|
1554
|
-
truthy: !!uvalue,
|
|
1555
|
-
string: `${uvalue}`,
|
|
2165
|
+
jsonValue,
|
|
2166
|
+
string: storageValue === 'J'
|
|
2167
|
+
? null
|
|
2168
|
+
: PostgreSQLDriver.escapeNulls(`${uvalue}`),
|
|
1556
2169
|
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
2170
|
+
truthy: !!uvalue,
|
|
1557
2171
|
},
|
|
1558
2172
|
}));
|
|
1559
2173
|
const references = this.findReferences(value);
|
|
@@ -1568,43 +2182,130 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1568
2182
|
}));
|
|
1569
2183
|
}
|
|
1570
2184
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
},
|
|
1577
|
-
});
|
|
1578
|
-
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
2185
|
+
}
|
|
2186
|
+
if (only === 'tilmeldAC') {
|
|
2187
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } = this.removeAndReturnACValues(etype, {}, sdata);
|
|
2188
|
+
promises.push(this.queryRun(`UPDATE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} SET "user"=${user == null ? '@user' : "decode(@user, 'hex')"}, "group"=${group == null ? '@group' : "decode(@group, 'hex')"}, "acUser"=@acUser, "acGroup"=@acGroup, "acOther"=@acOther, "acRead"=${!acRead?.length ? '@acRead' : "array(SELECT decode(n, 'hex') FROM unnest(@acRead::text[]) AS n)"}, "acWrite"=${!acWrite?.length ? '@acWrite' : "array(SELECT decode(n, 'hex') FROM unnest(@acWrite::text[]) AS n)"}, "acFull"=${!acFull?.length ? '@acFull' : "array(SELECT decode(n, 'hex') FROM unnest(@acFull::text[]) AS n)"} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
2189
|
+
etypes: [etype],
|
|
1579
2190
|
params: {
|
|
1580
|
-
|
|
1581
|
-
|
|
2191
|
+
user,
|
|
2192
|
+
group,
|
|
2193
|
+
acUser,
|
|
2194
|
+
acGroup,
|
|
2195
|
+
acOther,
|
|
2196
|
+
acRead,
|
|
2197
|
+
acWrite,
|
|
2198
|
+
acFull,
|
|
2199
|
+
guid,
|
|
1582
2200
|
},
|
|
2201
|
+
}));
|
|
2202
|
+
}
|
|
2203
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
2204
|
+
if (only == null || only === 'tokens') {
|
|
2205
|
+
for (let name in sdata) {
|
|
2206
|
+
let tokenString = null;
|
|
2207
|
+
try {
|
|
2208
|
+
tokenString = EntityClass.getFTSText(name, JSON.parse(sdata[name]));
|
|
2209
|
+
}
|
|
2210
|
+
catch (e) {
|
|
2211
|
+
// Ignore error.
|
|
2212
|
+
}
|
|
2213
|
+
if (tokenString != null) {
|
|
2214
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2215
|
+
while (tokens.length) {
|
|
2216
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2217
|
+
const params = {
|
|
2218
|
+
guid,
|
|
2219
|
+
name,
|
|
2220
|
+
};
|
|
2221
|
+
const values = [];
|
|
2222
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2223
|
+
const token = currentTokens[i];
|
|
2224
|
+
params['token' + i] = token.token;
|
|
2225
|
+
params['position' + i] = token.position;
|
|
2226
|
+
params['stem' + i] = token.stem;
|
|
2227
|
+
values.push("(decode(@guid, 'hex'), @name, @token" +
|
|
2228
|
+
i +
|
|
2229
|
+
', @position' +
|
|
2230
|
+
i +
|
|
2231
|
+
', @stem' +
|
|
2232
|
+
i +
|
|
2233
|
+
')');
|
|
2234
|
+
}
|
|
2235
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
2236
|
+
etypes: [etype],
|
|
2237
|
+
params,
|
|
2238
|
+
}));
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
if (only == null) {
|
|
2244
|
+
const uniques = await EntityClass.getUniques({
|
|
2245
|
+
guid,
|
|
2246
|
+
cdate,
|
|
2247
|
+
mdate,
|
|
2248
|
+
tags,
|
|
2249
|
+
data: {},
|
|
2250
|
+
sdata,
|
|
1583
2251
|
});
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
2252
|
+
for (const unique of uniques) {
|
|
2253
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
2254
|
+
etypes: [etype],
|
|
2255
|
+
params: {
|
|
2256
|
+
guid,
|
|
2257
|
+
unique,
|
|
2258
|
+
},
|
|
2259
|
+
}).catch((e) => {
|
|
2260
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2261
|
+
this.nymph.config.debugError('postgresql', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
2262
|
+
}
|
|
2263
|
+
return e;
|
|
2264
|
+
}));
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
await Promise.all(promises);
|
|
2268
|
+
}
|
|
2269
|
+
catch (e) {
|
|
2270
|
+
this.nymph.config.debugError('postgresql', `Import entity error: "${e}"`);
|
|
2271
|
+
throw e;
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
async importUID({ name, value }) {
|
|
2275
|
+
try {
|
|
2276
|
+
await this.internalTransaction(`nymph-import-uid-${name}`);
|
|
2277
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
2278
|
+
params: {
|
|
2279
|
+
name,
|
|
2280
|
+
},
|
|
1588
2281
|
});
|
|
1589
|
-
|
|
2282
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @value);`, {
|
|
2283
|
+
params: {
|
|
2284
|
+
name,
|
|
2285
|
+
value,
|
|
2286
|
+
},
|
|
2287
|
+
});
|
|
2288
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
1590
2289
|
}
|
|
1591
2290
|
catch (e) {
|
|
1592
|
-
|
|
1593
|
-
|
|
2291
|
+
this.nymph.config.debugError('postgresql', `Import UID error: "${e}"`);
|
|
2292
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
2293
|
+
throw e;
|
|
1594
2294
|
}
|
|
1595
2295
|
}
|
|
1596
2296
|
async newUID(name) {
|
|
1597
2297
|
if (name == null) {
|
|
1598
|
-
throw new
|
|
2298
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1599
2299
|
}
|
|
1600
2300
|
await this.internalTransaction('nymph-newuid');
|
|
2301
|
+
let curUid = undefined;
|
|
1601
2302
|
try {
|
|
1602
2303
|
const lock = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name FOR UPDATE;`, {
|
|
1603
2304
|
params: {
|
|
1604
2305
|
name,
|
|
1605
2306
|
},
|
|
1606
2307
|
});
|
|
1607
|
-
|
|
2308
|
+
curUid = lock?.cur_uid == null ? undefined : Number(lock.cur_uid);
|
|
1608
2309
|
if (curUid == null) {
|
|
1609
2310
|
curUid = 1;
|
|
1610
2311
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
@@ -1623,17 +2324,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1623
2324
|
},
|
|
1624
2325
|
});
|
|
1625
2326
|
}
|
|
1626
|
-
await this.commit('nymph-newuid');
|
|
1627
|
-
return curUid;
|
|
1628
2327
|
}
|
|
1629
2328
|
catch (e) {
|
|
2329
|
+
this.nymph.config.debugError('postgresql', `New UID error: "${e}"`);
|
|
1630
2330
|
await this.rollback('nymph-newuid');
|
|
1631
2331
|
throw e;
|
|
1632
2332
|
}
|
|
2333
|
+
await this.commit('nymph-newuid');
|
|
2334
|
+
return curUid;
|
|
1633
2335
|
}
|
|
1634
2336
|
async renameUID(oldName, newName) {
|
|
1635
2337
|
if (oldName == null || newName == null) {
|
|
1636
|
-
throw new
|
|
2338
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1637
2339
|
}
|
|
1638
2340
|
await this.queryRun(`UPDATE ${PostgreSQLDriver.escape(`${this.prefix}uids`)} SET "name"=@newName WHERE "name"=@oldName;`, {
|
|
1639
2341
|
params: {
|
|
@@ -1645,7 +2347,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1645
2347
|
}
|
|
1646
2348
|
async rollback(name) {
|
|
1647
2349
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1648
|
-
throw new
|
|
2350
|
+
throw new InvalidParametersError('Transaction rollback attempted without a name.');
|
|
1649
2351
|
}
|
|
1650
2352
|
if (!this.transaction || this.transaction.count === 0) {
|
|
1651
2353
|
this.transaction = null;
|
|
@@ -1662,7 +2364,8 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1662
2364
|
return true;
|
|
1663
2365
|
}
|
|
1664
2366
|
async saveEntity(entity) {
|
|
1665
|
-
const insertData = async (guid, data, sdata, etype) => {
|
|
2367
|
+
const insertData = async (guid, data, sdata, uniques, etype) => {
|
|
2368
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
1666
2369
|
const runInsertQuery = async (name, value, svalue) => {
|
|
1667
2370
|
if (value === undefined) {
|
|
1668
2371
|
return;
|
|
@@ -1671,24 +2374,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1671
2374
|
? 'N'
|
|
1672
2375
|
: typeof value === 'string'
|
|
1673
2376
|
? 'S'
|
|
1674
|
-
:
|
|
2377
|
+
: 'J';
|
|
2378
|
+
const jsonValue = storageValue === 'J'
|
|
2379
|
+
? PostgreSQLDriver.escapeNullSequences(svalue)
|
|
2380
|
+
: null;
|
|
1675
2381
|
const promises = [];
|
|
1676
|
-
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`, {
|
|
2382
|
+
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);`, {
|
|
1677
2383
|
etypes: [etype],
|
|
1678
2384
|
params: {
|
|
1679
2385
|
guid,
|
|
1680
2386
|
name,
|
|
1681
2387
|
storageValue,
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
params: {
|
|
1687
|
-
guid,
|
|
1688
|
-
name,
|
|
1689
|
-
truthy: !!value,
|
|
1690
|
-
string: `${value}`,
|
|
2388
|
+
jsonValue,
|
|
2389
|
+
string: storageValue === 'J'
|
|
2390
|
+
? null
|
|
2391
|
+
: PostgreSQLDriver.escapeNulls(`${value}`),
|
|
1691
2392
|
number: isNaN(Number(value)) ? null : Number(value),
|
|
2393
|
+
truthy: !!value,
|
|
1692
2394
|
},
|
|
1693
2395
|
}));
|
|
1694
2396
|
const references = this.findReferences(svalue);
|
|
@@ -1702,8 +2404,60 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1702
2404
|
},
|
|
1703
2405
|
}));
|
|
1704
2406
|
}
|
|
2407
|
+
let tokenString = null;
|
|
2408
|
+
try {
|
|
2409
|
+
tokenString = EntityClass.getFTSText(name, value);
|
|
2410
|
+
}
|
|
2411
|
+
catch (e) {
|
|
2412
|
+
// Ignore error.
|
|
2413
|
+
}
|
|
2414
|
+
if (tokenString != null) {
|
|
2415
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2416
|
+
while (tokens.length) {
|
|
2417
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2418
|
+
const params = {
|
|
2419
|
+
guid,
|
|
2420
|
+
name,
|
|
2421
|
+
};
|
|
2422
|
+
const values = [];
|
|
2423
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2424
|
+
const token = currentTokens[i];
|
|
2425
|
+
params['token' + i] = token.token;
|
|
2426
|
+
params['position' + i] = token.position;
|
|
2427
|
+
params['stem' + i] = token.stem;
|
|
2428
|
+
values.push("(decode(@guid, 'hex'), @name, @token" +
|
|
2429
|
+
i +
|
|
2430
|
+
', @position' +
|
|
2431
|
+
i +
|
|
2432
|
+
', @stem' +
|
|
2433
|
+
i +
|
|
2434
|
+
')');
|
|
2435
|
+
}
|
|
2436
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
2437
|
+
etypes: [etype],
|
|
2438
|
+
params,
|
|
2439
|
+
}));
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
1705
2442
|
await Promise.all(promises);
|
|
1706
2443
|
};
|
|
2444
|
+
for (const unique of uniques) {
|
|
2445
|
+
try {
|
|
2446
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
2447
|
+
etypes: [etype],
|
|
2448
|
+
params: {
|
|
2449
|
+
guid,
|
|
2450
|
+
unique,
|
|
2451
|
+
},
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
catch (e) {
|
|
2455
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2456
|
+
this.nymph.config.debugError('postgresql', `Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
2457
|
+
}
|
|
2458
|
+
throw e;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
1707
2461
|
for (const name in data) {
|
|
1708
2462
|
await runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
1709
2463
|
}
|
|
@@ -1711,19 +2465,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1711
2465
|
await runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
1712
2466
|
}
|
|
1713
2467
|
};
|
|
2468
|
+
let inTransaction = false;
|
|
1714
2469
|
try {
|
|
1715
|
-
const result = await this.saveEntityRowLike(entity, async (
|
|
1716
|
-
|
|
2470
|
+
const result = await this.saveEntityRowLike(entity, async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
2471
|
+
if (Object.keys(data).length === 0 &&
|
|
2472
|
+
Object.keys(sdata).length === 0) {
|
|
2473
|
+
return false;
|
|
2474
|
+
}
|
|
2475
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull, } = this.removeAndReturnACValues(etype, data, sdata);
|
|
2476
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate", "user", "group", "acUser", "acGroup", "acOther", "acRead", "acWrite", "acFull") VALUES (decode(@guid, 'hex'), @tags, @cdate, @cdate, ${user == null ? '@user' : "decode(@user, 'hex')"}, ${group == null ? '@group' : "decode(@group, 'hex')"}, @acUser, @acGroup, @acOther, ${!acRead?.length ? '@acRead' : "array(SELECT decode(n, 'hex') FROM unnest(@acRead::text[]) AS n)"}, ${!acWrite?.length ? '@acWrite' : "array(SELECT decode(n, 'hex') FROM unnest(@acWrite::text[]) AS n)"}, ${!acFull?.length ? '@acFull' : "array(SELECT decode(n, 'hex') FROM unnest(@acFull::text[]) AS n)"});`, {
|
|
1717
2477
|
etypes: [etype],
|
|
1718
2478
|
params: {
|
|
1719
2479
|
guid,
|
|
1720
2480
|
tags,
|
|
1721
2481
|
cdate,
|
|
2482
|
+
user,
|
|
2483
|
+
group,
|
|
2484
|
+
acUser,
|
|
2485
|
+
acGroup,
|
|
2486
|
+
acOther,
|
|
2487
|
+
acRead,
|
|
2488
|
+
acWrite,
|
|
2489
|
+
acFull,
|
|
1722
2490
|
},
|
|
1723
2491
|
});
|
|
1724
|
-
await insertData(guid, data, sdata, etype);
|
|
2492
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1725
2493
|
return true;
|
|
1726
|
-
}, async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
2494
|
+
}, async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
2495
|
+
if (Object.keys(data).length === 0 &&
|
|
2496
|
+
Object.keys(sdata).length === 0) {
|
|
2497
|
+
return false;
|
|
2498
|
+
}
|
|
2499
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull, } = this.removeAndReturnACValues(etype, data, sdata);
|
|
1727
2500
|
const promises = [];
|
|
1728
2501
|
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1729
2502
|
etypes: [etype],
|
|
@@ -1737,24 +2510,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1737
2510
|
guid,
|
|
1738
2511
|
},
|
|
1739
2512
|
}));
|
|
1740
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2513
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1741
2514
|
etypes: [etype],
|
|
1742
2515
|
params: {
|
|
1743
2516
|
guid,
|
|
1744
2517
|
},
|
|
1745
2518
|
}));
|
|
1746
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2519
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
2520
|
+
etypes: [etype],
|
|
2521
|
+
params: {
|
|
2522
|
+
guid,
|
|
2523
|
+
},
|
|
2524
|
+
}));
|
|
2525
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1747
2526
|
etypes: [etype],
|
|
1748
2527
|
params: {
|
|
1749
2528
|
guid,
|
|
1750
2529
|
},
|
|
1751
2530
|
}));
|
|
1752
2531
|
await Promise.all(promises);
|
|
1753
|
-
const info = await this.queryRun(`UPDATE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} SET "tags"=@tags, "mdate"=@mdate WHERE "guid"=decode(@guid, 'hex') AND "mdate" <= @emdate;`, {
|
|
2532
|
+
const info = await this.queryRun(`UPDATE ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} SET "tags"=@tags, "mdate"=@mdate, "user"=${user == null ? '@user' : "decode(@user, 'hex')"}, "group"=${group == null ? '@group' : "decode(@group, 'hex')"}, "acUser"=@acUser, "acGroup"=@acGroup, "acOther"=@acOther, "acRead"=${!acRead?.length ? '@acRead' : "array(SELECT decode(n, 'hex') FROM unnest(@acRead::text[]) AS n)"}, "acWrite"=${!acWrite?.length ? '@acWrite' : "array(SELECT decode(n, 'hex') FROM unnest(@acWrite::text[]) AS n)"}, "acFull"=${!acFull?.length ? '@acFull' : "array(SELECT decode(n, 'hex') FROM unnest(@acFull::text[]) AS n)"} WHERE "guid"=decode(@guid, 'hex') AND "mdate" <= @emdate;`, {
|
|
1754
2533
|
etypes: [etype],
|
|
1755
2534
|
params: {
|
|
1756
2535
|
tags,
|
|
1757
2536
|
mdate,
|
|
2537
|
+
user,
|
|
2538
|
+
group,
|
|
2539
|
+
acUser,
|
|
2540
|
+
acGroup,
|
|
2541
|
+
acOther,
|
|
2542
|
+
acRead,
|
|
2543
|
+
acWrite,
|
|
2544
|
+
acFull,
|
|
1758
2545
|
guid,
|
|
1759
2546
|
emdate: isNaN(Number(entity.mdate)) ? 0 : Number(entity.mdate),
|
|
1760
2547
|
},
|
|
@@ -1768,44 +2555,57 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1768
2555
|
guid,
|
|
1769
2556
|
},
|
|
1770
2557
|
}));
|
|
1771
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2558
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1772
2559
|
etypes: [etype],
|
|
1773
2560
|
params: {
|
|
1774
2561
|
guid,
|
|
1775
2562
|
},
|
|
1776
2563
|
}));
|
|
1777
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2564
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
2565
|
+
etypes: [etype],
|
|
2566
|
+
params: {
|
|
2567
|
+
guid,
|
|
2568
|
+
},
|
|
2569
|
+
}));
|
|
2570
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1778
2571
|
etypes: [etype],
|
|
1779
2572
|
params: {
|
|
1780
2573
|
guid,
|
|
1781
2574
|
},
|
|
1782
2575
|
}));
|
|
1783
2576
|
await Promise.all(promises);
|
|
1784
|
-
await insertData(guid, data, sdata, etype);
|
|
2577
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1785
2578
|
success = true;
|
|
1786
2579
|
}
|
|
1787
2580
|
return success;
|
|
1788
2581
|
}, async () => {
|
|
1789
2582
|
await this.internalTransaction('nymph-save');
|
|
2583
|
+
inTransaction = true;
|
|
1790
2584
|
}, async (success) => {
|
|
1791
|
-
if (
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
2585
|
+
if (inTransaction) {
|
|
2586
|
+
inTransaction = false;
|
|
2587
|
+
if (success) {
|
|
2588
|
+
await this.commit('nymph-save');
|
|
2589
|
+
}
|
|
2590
|
+
else {
|
|
2591
|
+
await this.rollback('nymph-save');
|
|
2592
|
+
}
|
|
1796
2593
|
}
|
|
1797
2594
|
return success;
|
|
1798
2595
|
});
|
|
1799
2596
|
return result;
|
|
1800
2597
|
}
|
|
1801
2598
|
catch (e) {
|
|
1802
|
-
|
|
2599
|
+
this.nymph.config.debugError('postgresql', `Save entity error: "${e}"`);
|
|
2600
|
+
if (inTransaction) {
|
|
2601
|
+
await this.rollback('nymph-save');
|
|
2602
|
+
}
|
|
1803
2603
|
throw e;
|
|
1804
2604
|
}
|
|
1805
2605
|
}
|
|
1806
2606
|
async setUID(name, curUid) {
|
|
1807
2607
|
if (name == null) {
|
|
1808
|
-
throw new
|
|
2608
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1809
2609
|
}
|
|
1810
2610
|
await this.internalTransaction('nymph-setuid');
|
|
1811
2611
|
try {
|
|
@@ -1821,23 +2621,25 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1821
2621
|
curUid,
|
|
1822
2622
|
},
|
|
1823
2623
|
});
|
|
1824
|
-
await this.commit('nymph-setuid');
|
|
1825
|
-
return true;
|
|
1826
2624
|
}
|
|
1827
2625
|
catch (e) {
|
|
1828
2626
|
await this.rollback('nymph-setuid');
|
|
1829
2627
|
throw e;
|
|
1830
2628
|
}
|
|
2629
|
+
await this.commit('nymph-setuid');
|
|
2630
|
+
return true;
|
|
1831
2631
|
}
|
|
1832
2632
|
async internalTransaction(name) {
|
|
1833
2633
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1834
|
-
throw new
|
|
2634
|
+
throw new InvalidParametersError('Transaction start attempted without a name.');
|
|
1835
2635
|
}
|
|
1836
2636
|
if (!this.transaction || this.transaction.count === 0) {
|
|
2637
|
+
// Lock to one connection.
|
|
1837
2638
|
this.transaction = {
|
|
1838
2639
|
count: 0,
|
|
1839
2640
|
connection: await this.getConnection(),
|
|
1840
2641
|
};
|
|
2642
|
+
// We're not in a transaction yet, so start one.
|
|
1841
2643
|
await this.queryRun('BEGIN;');
|
|
1842
2644
|
}
|
|
1843
2645
|
await this.queryRun(`SAVEPOINT ${PostgreSQLDriver.escape(name)};`);
|
|
@@ -1845,7 +2647,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1845
2647
|
return this.transaction;
|
|
1846
2648
|
}
|
|
1847
2649
|
async startTransaction(name) {
|
|
1848
|
-
const inTransaction = this.inTransaction();
|
|
2650
|
+
const inTransaction = await this.inTransaction();
|
|
1849
2651
|
const transaction = await this.internalTransaction(name);
|
|
1850
2652
|
if (!inTransaction) {
|
|
1851
2653
|
this.transaction = null;
|
|
@@ -1854,6 +2656,121 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1854
2656
|
nymph.driver.transaction = transaction;
|
|
1855
2657
|
return nymph;
|
|
1856
2658
|
}
|
|
2659
|
+
async removeTilmeldOldRows(etype, connection) {
|
|
2660
|
+
await this.internalTransaction('nymph-remove-tilmeld-rows');
|
|
2661
|
+
try {
|
|
2662
|
+
for (let name of [
|
|
2663
|
+
'user',
|
|
2664
|
+
'group',
|
|
2665
|
+
'acUser',
|
|
2666
|
+
'acGroup',
|
|
2667
|
+
'acOther',
|
|
2668
|
+
'acRead',
|
|
2669
|
+
'acWrite',
|
|
2670
|
+
'acFull',
|
|
2671
|
+
]) {
|
|
2672
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "name"=@name;`, {
|
|
2673
|
+
etypes: [etype],
|
|
2674
|
+
params: {
|
|
2675
|
+
name,
|
|
2676
|
+
},
|
|
2677
|
+
connection,
|
|
2678
|
+
});
|
|
2679
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "name"=@name;`, {
|
|
2680
|
+
etypes: [etype],
|
|
2681
|
+
params: {
|
|
2682
|
+
name,
|
|
2683
|
+
},
|
|
2684
|
+
connection,
|
|
2685
|
+
});
|
|
2686
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "name"=@name;`, {
|
|
2687
|
+
etypes: [etype],
|
|
2688
|
+
params: {
|
|
2689
|
+
name,
|
|
2690
|
+
},
|
|
2691
|
+
connection,
|
|
2692
|
+
});
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
catch (e) {
|
|
2696
|
+
this.nymph.config.debugError('postgresql', `Remove tilmeld rows error: "${e}"`);
|
|
2697
|
+
await this.rollback('nymph-remove-tilmeld-rows');
|
|
2698
|
+
throw e;
|
|
2699
|
+
}
|
|
2700
|
+
await this.commit('nymph-remove-tilmeld-rows');
|
|
2701
|
+
return true;
|
|
2702
|
+
}
|
|
2703
|
+
async needsMigration() {
|
|
2704
|
+
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;', {
|
|
2705
|
+
params: {
|
|
2706
|
+
db: this.config.database,
|
|
2707
|
+
prefix: this.prefix + 'data_' + '%',
|
|
2708
|
+
},
|
|
2709
|
+
});
|
|
2710
|
+
if (table?.table_name) {
|
|
2711
|
+
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\';', {
|
|
2712
|
+
params: {
|
|
2713
|
+
db: this.config.database,
|
|
2714
|
+
table: table.table_name,
|
|
2715
|
+
},
|
|
2716
|
+
});
|
|
2717
|
+
if (!result?.exists) {
|
|
2718
|
+
return 'json';
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
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;', {
|
|
2722
|
+
params: {
|
|
2723
|
+
db: this.config.database,
|
|
2724
|
+
tokenTable: this.prefix + 'tokens_' + '%',
|
|
2725
|
+
},
|
|
2726
|
+
});
|
|
2727
|
+
if (!table2 || !table2.table_name) {
|
|
2728
|
+
return 'tokens';
|
|
2729
|
+
}
|
|
2730
|
+
const table3 = 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;', {
|
|
2731
|
+
params: {
|
|
2732
|
+
db: this.config.database,
|
|
2733
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
2734
|
+
},
|
|
2735
|
+
});
|
|
2736
|
+
if (table3?.table_name) {
|
|
2737
|
+
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"=\'user\';', {
|
|
2738
|
+
params: {
|
|
2739
|
+
db: this.config.database,
|
|
2740
|
+
table: table3.table_name,
|
|
2741
|
+
},
|
|
2742
|
+
});
|
|
2743
|
+
if (!result?.exists) {
|
|
2744
|
+
return 'tilmeldColumns';
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
return false;
|
|
2748
|
+
}
|
|
2749
|
+
async liveMigration(migrationType) {
|
|
2750
|
+
if (migrationType === 'tokenTables') {
|
|
2751
|
+
const etypes = await this.getEtypes();
|
|
2752
|
+
const connection = await this.getConnection(true);
|
|
2753
|
+
for (let etype of etypes) {
|
|
2754
|
+
await this.createTokensTable(etype, connection);
|
|
2755
|
+
}
|
|
2756
|
+
connection.done();
|
|
2757
|
+
}
|
|
2758
|
+
else if (migrationType === 'tilmeldColumns') {
|
|
2759
|
+
const etypes = await this.getEtypes();
|
|
2760
|
+
const connection = await this.getConnection(true);
|
|
2761
|
+
for (let etype of etypes) {
|
|
2762
|
+
await this.addTilmeldColumnsAndIndexes(etype, connection);
|
|
2763
|
+
}
|
|
2764
|
+
connection.done();
|
|
2765
|
+
}
|
|
2766
|
+
else if (migrationType === 'tilmeldRemoveOldRows') {
|
|
2767
|
+
const etypes = await this.getEtypes();
|
|
2768
|
+
const connection = await this.getConnection(true);
|
|
2769
|
+
for (let etype of etypes) {
|
|
2770
|
+
await this.removeTilmeldOldRows(etype, connection);
|
|
2771
|
+
}
|
|
2772
|
+
connection.done();
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
1857
2775
|
}
|
|
1858
|
-
exports.default = PostgreSQLDriver;
|
|
1859
2776
|
//# sourceMappingURL=PostgreSQLDriver.js.map
|