@nymphjs/driver-postgresql 1.0.0-beta.12 → 1.0.0-beta.121
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 +552 -0
- package/README.md +1 -1
- package/dist/PostgreSQLDriver.d.ts +108 -20
- package/dist/PostgreSQLDriver.js +1718 -747
- package/dist/PostgreSQLDriver.js.map +1 -1
- package/dist/PostgreSQLDriver.test.js +13 -12
- package/dist/PostgreSQLDriver.test.js.map +1 -1
- 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 +37 -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 +31 -6
- package/package.json +33 -24
- package/src/PostgreSQLDriver.test.ts +16 -5
- package/src/PostgreSQLDriver.ts +2775 -1225
- package/src/conf/defaults.ts +1 -1
- package/src/conf/index.ts +38 -2
- package/src/index.ts +2 -2
- package/tsconfig.json +6 -3
- package/typedoc.json +4 -0
- package/dist/conf/d.d.ts +0 -10
- package/dist/conf/d.js +0 -3
- package/dist/conf/d.js.map +0 -1
- package/dist/runPostgresqlSync.js +0 -35
- package/src/conf/d.ts +0 -37
- 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,158 +361,159 @@ 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
|
-
const results = await new Promise((resolve, reject) => {
|
|
392
|
+
const results = await new Promise(async (resolve, reject) => {
|
|
393
|
+
// Only do one query at a time in transations. The Postgres
|
|
394
|
+
// package will only support one query at a time on a given
|
|
395
|
+
// connection in a future release.
|
|
396
|
+
let myResolve = () => { };
|
|
397
|
+
const myPromise = new Promise((resolve) => {
|
|
398
|
+
myResolve = resolve;
|
|
399
|
+
});
|
|
400
|
+
if (this.transaction) {
|
|
401
|
+
const latestPromise = this.transaction.latestPromise;
|
|
402
|
+
this.transaction.latestPromise = myPromise;
|
|
403
|
+
await latestPromise;
|
|
404
|
+
}
|
|
240
405
|
try {
|
|
241
|
-
(this.transaction?.connection?.client ?? this.link)
|
|
242
|
-
|
|
243
|
-
.then((results) => resolve(results), (error) => reject(error));
|
|
406
|
+
const results = await (this.transaction?.connection?.client ?? this.link).query(newQuery, newParams);
|
|
407
|
+
resolve(results);
|
|
244
408
|
}
|
|
245
409
|
catch (e) {
|
|
246
410
|
reject(e);
|
|
247
411
|
}
|
|
412
|
+
myResolve();
|
|
248
413
|
});
|
|
249
414
|
return results.rows;
|
|
250
415
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
251
416
|
}
|
|
252
|
-
|
|
417
|
+
async queryIter(query, { etypes = [], params = {}, } = {}) {
|
|
253
418
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
263
|
-
encoding: 'utf8',
|
|
264
|
-
windowsHide: true,
|
|
419
|
+
const that = this;
|
|
420
|
+
return this.query(async function* () {
|
|
421
|
+
// Only do one query at a time in transations. The Postgres
|
|
422
|
+
// package will only support one query at a time on a given
|
|
423
|
+
// connection in a future release.
|
|
424
|
+
let myResolve = () => { };
|
|
425
|
+
const myPromise = new Promise((resolve) => {
|
|
426
|
+
myResolve = resolve;
|
|
265
427
|
});
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
428
|
+
if (that.transaction) {
|
|
429
|
+
const latestPromise = that.transaction.latestPromise;
|
|
430
|
+
that.transaction.latestPromise = myPromise;
|
|
431
|
+
await latestPromise;
|
|
270
432
|
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
433
|
+
const transaction = !!that.transaction?.connection;
|
|
434
|
+
const connection = await that.getConnection();
|
|
435
|
+
const cursor = new Cursor(newQuery, newParams);
|
|
436
|
+
const iter = connection.client.query(cursor);
|
|
437
|
+
while (true) {
|
|
438
|
+
const rows = await iter.read(100);
|
|
439
|
+
myResolve();
|
|
440
|
+
if (!rows.length) {
|
|
441
|
+
await new Promise((resolve) => {
|
|
442
|
+
iter.close(() => {
|
|
443
|
+
if (!transaction) {
|
|
444
|
+
connection.done();
|
|
445
|
+
}
|
|
446
|
+
resolve();
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
for (let row of rows) {
|
|
452
|
+
yield row;
|
|
453
|
+
}
|
|
278
454
|
}
|
|
279
455
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
280
456
|
}
|
|
281
457
|
queryGet(query, { etypes = [], params = {}, } = {}) {
|
|
282
458
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
283
459
|
return this.query(async () => {
|
|
284
|
-
const results = await new Promise((resolve, reject) => {
|
|
460
|
+
const results = await new Promise(async (resolve, reject) => {
|
|
461
|
+
// Only do one query at a time in transations. The Postgres
|
|
462
|
+
// package will only support one query at a time on a given
|
|
463
|
+
// connection in a future release.
|
|
464
|
+
let myResolve = () => { };
|
|
465
|
+
const myPromise = new Promise((resolve) => {
|
|
466
|
+
myResolve = resolve;
|
|
467
|
+
});
|
|
468
|
+
if (this.transaction) {
|
|
469
|
+
const latestPromise = this.transaction.latestPromise;
|
|
470
|
+
this.transaction.latestPromise = myPromise;
|
|
471
|
+
await latestPromise;
|
|
472
|
+
}
|
|
285
473
|
try {
|
|
286
|
-
(this.transaction?.connection?.client ?? this.link)
|
|
287
|
-
|
|
288
|
-
.then((results) => resolve(results), (error) => reject(error));
|
|
474
|
+
const results = await (this.transaction?.connection?.client ?? this.link).query(newQuery, newParams);
|
|
475
|
+
resolve(results);
|
|
289
476
|
}
|
|
290
477
|
catch (e) {
|
|
291
478
|
reject(e);
|
|
292
479
|
}
|
|
480
|
+
myResolve();
|
|
293
481
|
});
|
|
294
482
|
return results.rows[0];
|
|
295
483
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
296
484
|
}
|
|
297
|
-
queryRun(query, { etypes = [], params = {}, } = {}) {
|
|
485
|
+
queryRun(query, { etypes = [], params = {}, transaction, connection, } = {}) {
|
|
298
486
|
const { query: newQuery, params: newParams } = this.translateQuery(query, params);
|
|
299
487
|
return this.query(async () => {
|
|
300
|
-
const results = await new Promise((resolve, reject) => {
|
|
488
|
+
const results = await new Promise(async (resolve, reject) => {
|
|
489
|
+
// Only do one query at a time in transations. The Postgres
|
|
490
|
+
// package will only support one query at a time on a given
|
|
491
|
+
// connection in a future release.
|
|
492
|
+
let myResolve = () => { };
|
|
493
|
+
const myPromise = new Promise((resolve) => {
|
|
494
|
+
myResolve = resolve;
|
|
495
|
+
});
|
|
496
|
+
const trans = transaction ?? this.transaction;
|
|
497
|
+
if (trans) {
|
|
498
|
+
const latestPromise = trans.latestPromise;
|
|
499
|
+
trans.latestPromise = myPromise;
|
|
500
|
+
await latestPromise;
|
|
501
|
+
}
|
|
301
502
|
try {
|
|
302
|
-
(
|
|
303
|
-
|
|
304
|
-
.then((results) => resolve(results), (error) => reject(error));
|
|
503
|
+
const results = await ((connection ?? trans?.connection)?.client ?? this.link).query(newQuery, newParams);
|
|
504
|
+
resolve(results);
|
|
305
505
|
}
|
|
306
506
|
catch (e) {
|
|
307
507
|
reject(e);
|
|
308
508
|
}
|
|
509
|
+
myResolve();
|
|
309
510
|
});
|
|
310
511
|
return { rowCount: results.rowCount ?? 0 };
|
|
311
512
|
}, `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
312
513
|
}
|
|
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
514
|
async commit(name) {
|
|
344
515
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
345
|
-
throw new
|
|
516
|
+
throw new InvalidParametersError('Transaction commit attempted without a name.');
|
|
346
517
|
}
|
|
347
518
|
if (!this.transaction || this.transaction.count === 0) {
|
|
348
519
|
this.transaction = null;
|
|
@@ -382,32 +553,40 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
382
553
|
guid,
|
|
383
554
|
},
|
|
384
555
|
});
|
|
385
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
556
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
557
|
+
etypes: [etype],
|
|
558
|
+
params: {
|
|
559
|
+
guid,
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
386
563
|
etypes: [etype],
|
|
387
564
|
params: {
|
|
388
565
|
guid,
|
|
389
566
|
},
|
|
390
567
|
});
|
|
391
|
-
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
568
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
392
569
|
etypes: [etype],
|
|
393
570
|
params: {
|
|
394
571
|
guid,
|
|
395
572
|
},
|
|
396
573
|
});
|
|
397
|
-
await this.commit('nymph-delete');
|
|
398
|
-
if (this.nymph.config.cache) {
|
|
399
|
-
this.cleanCache(guid);
|
|
400
|
-
}
|
|
401
|
-
return true;
|
|
402
574
|
}
|
|
403
575
|
catch (e) {
|
|
576
|
+
this.nymph.config.debugError('postgresql', `Delete entity error: "${e}"`);
|
|
404
577
|
await this.rollback('nymph-delete');
|
|
405
578
|
throw e;
|
|
406
579
|
}
|
|
580
|
+
await this.commit('nymph-delete');
|
|
581
|
+
// Remove any cached versions of this entity.
|
|
582
|
+
if (this.nymph.config.cache) {
|
|
583
|
+
this.cleanCache(guid);
|
|
584
|
+
}
|
|
585
|
+
return true;
|
|
407
586
|
}
|
|
408
587
|
async deleteUID(name) {
|
|
409
588
|
if (!name) {
|
|
410
|
-
throw new
|
|
589
|
+
throw new InvalidParametersError('Name not given for UID');
|
|
411
590
|
}
|
|
412
591
|
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
413
592
|
params: {
|
|
@@ -416,79 +595,223 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
416
595
|
});
|
|
417
596
|
return true;
|
|
418
597
|
}
|
|
419
|
-
async
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
598
|
+
async getIndexes(etype) {
|
|
599
|
+
const indexes = [];
|
|
600
|
+
for (let [scope, suffix] of [
|
|
601
|
+
['data', '_json'],
|
|
602
|
+
['references', '_reference_guid'],
|
|
603
|
+
['tokens', '_token_position_stem'],
|
|
604
|
+
]) {
|
|
605
|
+
const indexDefinitions = await this.queryArray(`SELECT * FROM "pg_indexes" WHERE "indexname" LIKE @pattern;`, {
|
|
606
|
+
params: {
|
|
607
|
+
pattern: `${this.prefix}${scope}_${etype}_id_custom_%${suffix}`,
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
for (const indexDefinition of indexDefinitions) {
|
|
611
|
+
indexes.push({
|
|
612
|
+
scope,
|
|
613
|
+
name: indexDefinition.indexname.substring(`${this.prefix}${scope}_${etype}_id_custom_`.length, indexDefinition.indexname.length - suffix.length),
|
|
614
|
+
property: (indexDefinition.indexdef.match(/WHERE\s+\(?\s*name\s*=\s*'(.*)'/) ?? [])[1] ?? '',
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return indexes;
|
|
619
|
+
}
|
|
620
|
+
async addIndex(etype, definition) {
|
|
621
|
+
this.checkIndexName(definition.name);
|
|
622
|
+
await this.deleteIndex(etype, definition.scope, definition.name);
|
|
623
|
+
const connection = await this.getConnection(true);
|
|
624
|
+
if (definition.scope === 'data') {
|
|
625
|
+
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 });
|
|
626
|
+
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 });
|
|
627
|
+
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 });
|
|
628
|
+
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 });
|
|
629
|
+
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 });
|
|
630
|
+
}
|
|
631
|
+
else if (definition.scope === 'references') {
|
|
632
|
+
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 });
|
|
633
|
+
}
|
|
634
|
+
else if (definition.scope === 'tokens') {
|
|
635
|
+
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 });
|
|
636
|
+
}
|
|
637
|
+
connection.done();
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
async deleteIndex(etype, scope, name) {
|
|
641
|
+
this.checkIndexName(name);
|
|
642
|
+
const connection = await this.getConnection(true);
|
|
643
|
+
if (scope === 'data') {
|
|
644
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_json`)};`, { connection });
|
|
645
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_string_gin`)};`, { connection });
|
|
646
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_string_btree`)};`, { connection });
|
|
647
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_number`)};`, { connection });
|
|
648
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}_id_custom_${name}_truthy`)};`, { connection });
|
|
649
|
+
}
|
|
650
|
+
else if (scope === 'references') {
|
|
651
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}_id_custom_${name}_reference_guid`)};`, { connection });
|
|
652
|
+
}
|
|
653
|
+
else if (scope === 'tokens') {
|
|
654
|
+
await this.queryRun(`DROP INDEX IF EXISTS ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}_id_custom_${name}_token_position_stem`)};`, { connection });
|
|
655
|
+
}
|
|
656
|
+
connection.done();
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
async getEtypes() {
|
|
660
|
+
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;', {
|
|
661
|
+
params: {
|
|
662
|
+
db: this.config.database,
|
|
663
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
664
|
+
},
|
|
665
|
+
});
|
|
440
666
|
const etypes = [];
|
|
441
|
-
for (const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
667
|
+
for (const table of tables) {
|
|
668
|
+
etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
|
|
669
|
+
}
|
|
670
|
+
return etypes;
|
|
671
|
+
}
|
|
672
|
+
async *exportDataIterator() {
|
|
673
|
+
if (yield {
|
|
674
|
+
type: 'comment',
|
|
675
|
+
content: `#nex2
|
|
676
|
+
# Nymph Entity Exchange v2
|
|
677
|
+
# http://nymph.io
|
|
678
|
+
#
|
|
679
|
+
# Generation Time: ${new Date().toLocaleString()}
|
|
680
|
+
`,
|
|
681
|
+
}) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
if (yield {
|
|
685
|
+
type: 'comment',
|
|
686
|
+
content: `
|
|
687
|
+
|
|
688
|
+
#
|
|
689
|
+
# UIDs
|
|
690
|
+
#
|
|
691
|
+
|
|
692
|
+
`,
|
|
693
|
+
}) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
// Export UIDs.
|
|
697
|
+
let uids = await this.queryIter(`SELECT * FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ORDER BY "name";`);
|
|
698
|
+
for await (const uid of uids) {
|
|
699
|
+
if (yield { type: 'uid', content: `<${uid.name}>[${uid.cur_uid}]\n` }) {
|
|
700
|
+
return;
|
|
445
701
|
}
|
|
446
702
|
}
|
|
703
|
+
if (yield {
|
|
704
|
+
type: 'comment',
|
|
705
|
+
content: `
|
|
706
|
+
|
|
707
|
+
#
|
|
708
|
+
# Entities
|
|
709
|
+
#
|
|
710
|
+
|
|
711
|
+
`,
|
|
712
|
+
}) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
// Get the etypes.
|
|
716
|
+
const etypes = await this.getEtypes();
|
|
447
717
|
for (const etype of etypes) {
|
|
448
|
-
|
|
718
|
+
// Export entities.
|
|
719
|
+
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
720
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} e
|
|
450
721
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} d ON e."guid"=d."guid"
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
let datum = dataIterator.next();
|
|
722
|
+
ORDER BY e."guid";`);
|
|
723
|
+
let datum = await dataIterator.next();
|
|
454
724
|
while (!datum.done) {
|
|
455
725
|
const guid = datum.value.guid;
|
|
456
|
-
const tags = datum.value.tags.join(',');
|
|
726
|
+
const tags = datum.value.tags.filter((tag) => tag).join(',');
|
|
457
727
|
const cdate = datum.value.cdate;
|
|
458
728
|
const mdate = datum.value.mdate;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
729
|
+
const user = datum.value.user;
|
|
730
|
+
const group = datum.value.group;
|
|
731
|
+
const acUser = datum.value.acUser;
|
|
732
|
+
const acGroup = datum.value.acGroup;
|
|
733
|
+
const acOther = datum.value.acOther;
|
|
734
|
+
const acRead = datum.value.acRead?.filter((guid) => guid);
|
|
735
|
+
const acWrite = datum.value.acWrite?.filter((guid) => guid);
|
|
736
|
+
const acFull = datum.value.acFull?.filter((guid) => guid);
|
|
737
|
+
let currentEntityExport = [];
|
|
738
|
+
currentEntityExport.push(`{${guid}}<${etype}>[${tags}]`);
|
|
739
|
+
currentEntityExport.push(`\tcdate=${JSON.stringify(cdate)}`);
|
|
740
|
+
currentEntityExport.push(`\tmdate=${JSON.stringify(mdate)}`);
|
|
741
|
+
if (this.nymph.tilmeld != null) {
|
|
742
|
+
if (user != null) {
|
|
743
|
+
currentEntityExport.push(`\tuser=${JSON.stringify(['nymph_entity_reference', user, 'User'])}`);
|
|
744
|
+
}
|
|
745
|
+
if (group != null) {
|
|
746
|
+
currentEntityExport.push(`\tgroup=${JSON.stringify(['nymph_entity_reference', group, 'Group'])}`);
|
|
747
|
+
}
|
|
748
|
+
if (acUser != null) {
|
|
749
|
+
currentEntityExport.push(`\tacUser=${JSON.stringify(acUser)}`);
|
|
750
|
+
}
|
|
751
|
+
if (acGroup != null) {
|
|
752
|
+
currentEntityExport.push(`\tacGroup=${JSON.stringify(acGroup)}`);
|
|
753
|
+
}
|
|
754
|
+
if (acOther != null) {
|
|
755
|
+
currentEntityExport.push(`\tacOther=${JSON.stringify(acOther)}`);
|
|
756
|
+
}
|
|
757
|
+
if (acRead != null) {
|
|
758
|
+
currentEntityExport.push(`\tacRead=${JSON.stringify(acRead)}`);
|
|
759
|
+
}
|
|
760
|
+
if (acWrite != null) {
|
|
761
|
+
currentEntityExport.push(`\tacWrite=${JSON.stringify(acWrite)}`);
|
|
762
|
+
}
|
|
763
|
+
if (acFull != null) {
|
|
764
|
+
currentEntityExport.push(`\tacFull=${JSON.stringify(acFull)}`);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (datum.value.name != null) {
|
|
768
|
+
// This do will keep going and adding the data until the
|
|
769
|
+
// next entity is reached. $row will end on the next entity.
|
|
463
770
|
do {
|
|
464
|
-
const value = datum.value.
|
|
771
|
+
const value = datum.value.value === 'N'
|
|
465
772
|
? JSON.stringify(Number(datum.value.number))
|
|
466
|
-
: datum.value.
|
|
467
|
-
? JSON.stringify(datum.value.string)
|
|
468
|
-
: datum.value.
|
|
469
|
-
|
|
470
|
-
|
|
773
|
+
: datum.value.value === 'S'
|
|
774
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(datum.value.string))
|
|
775
|
+
: datum.value.value === 'J'
|
|
776
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(datum.value.json))
|
|
777
|
+
: datum.value.value;
|
|
778
|
+
currentEntityExport.push(`\t${datum.value.name}=${value}`);
|
|
779
|
+
datum = await dataIterator.next();
|
|
471
780
|
} while (!datum.done && datum.value.guid === guid);
|
|
472
781
|
}
|
|
473
782
|
else {
|
|
474
|
-
datum
|
|
783
|
+
// Make sure that datum is incremented :)
|
|
784
|
+
datum = await dataIterator.next();
|
|
785
|
+
}
|
|
786
|
+
currentEntityExport.push('');
|
|
787
|
+
if (yield { type: 'entity', content: currentEntityExport.join('\n') }) {
|
|
788
|
+
return;
|
|
475
789
|
}
|
|
476
790
|
}
|
|
477
791
|
}
|
|
478
|
-
return;
|
|
479
792
|
}
|
|
480
|
-
|
|
793
|
+
/**
|
|
794
|
+
* Generate the PostgreSQL query.
|
|
795
|
+
* @param options The options array.
|
|
796
|
+
* @param formattedSelectors The formatted selector array.
|
|
797
|
+
* @param etype
|
|
798
|
+
* @param count Used to track internal params.
|
|
799
|
+
* @param params Used to store internal params.
|
|
800
|
+
* @param subquery Whether only a subquery should be returned.
|
|
801
|
+
* @returns The SQL query.
|
|
802
|
+
*/
|
|
803
|
+
makeEntityQuery(options, formattedSelectors, etype, count = { i: 0 }, params = {}, subquery = false, tableSuffix = '', etypes = [], guidSelector = undefined, guidExplicitSelector = undefined) {
|
|
481
804
|
if (typeof options.class?.alterOptions === 'function') {
|
|
482
805
|
options = options.class.alterOptions(options);
|
|
483
806
|
}
|
|
484
807
|
const eTable = `e${tableSuffix}`;
|
|
485
808
|
const dTable = `d${tableSuffix}`;
|
|
486
|
-
const cTable = `c${tableSuffix}`;
|
|
487
809
|
const fTable = `f${tableSuffix}`;
|
|
488
810
|
const ieTable = `ie${tableSuffix}`;
|
|
489
811
|
const countTable = `count${tableSuffix}`;
|
|
490
|
-
const
|
|
491
|
-
const
|
|
812
|
+
const sTable = `s${tableSuffix}`;
|
|
813
|
+
const sort = options.sort === undefined ? 'cdate' : options.sort;
|
|
814
|
+
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, ({ key, value, typeIsOr, typeIsNot }) => {
|
|
492
815
|
const clauseNot = key.startsWith('!');
|
|
493
816
|
let curQuery = '';
|
|
494
817
|
for (const curValue of value) {
|
|
@@ -501,7 +824,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
501
824
|
}
|
|
502
825
|
const guid = `param${++count.i}`;
|
|
503
826
|
curQuery +=
|
|
504
|
-
(
|
|
827
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
505
828
|
ieTable +
|
|
506
829
|
'."guid"=decode(@' +
|
|
507
830
|
guid +
|
|
@@ -517,7 +840,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
517
840
|
}
|
|
518
841
|
const tag = `param${++count.i}`;
|
|
519
842
|
curQuery +=
|
|
520
|
-
(
|
|
843
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
521
844
|
'@' +
|
|
522
845
|
tag +
|
|
523
846
|
' <@ ie."tags"';
|
|
@@ -530,17 +853,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
530
853
|
if (curQuery) {
|
|
531
854
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
532
855
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
856
|
+
if (curVar === 'cdate' ||
|
|
857
|
+
curVar === 'mdate' ||
|
|
858
|
+
(this.nymph.tilmeld != null &&
|
|
859
|
+
(curVar === 'user' ||
|
|
860
|
+
curVar === 'group' ||
|
|
861
|
+
curVar === 'acUser' ||
|
|
862
|
+
curVar === 'acGroup' ||
|
|
863
|
+
curVar === 'acOther' ||
|
|
864
|
+
curVar === 'acRead' ||
|
|
865
|
+
curVar === 'acWrite' ||
|
|
866
|
+
curVar === 'acFull'))) {
|
|
867
|
+
curQuery +=
|
|
868
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
869
|
+
'(' +
|
|
870
|
+
ieTable +
|
|
871
|
+
'.' +
|
|
872
|
+
PostgreSQLDriver.escape(curVar) +
|
|
873
|
+
' IS NOT NULL)';
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
const name = `param${++count.i}`;
|
|
877
|
+
curQuery +=
|
|
878
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
879
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
880
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
881
|
+
' WHERE "guid"=' +
|
|
882
|
+
ieTable +
|
|
883
|
+
'."guid" AND "name"=@' +
|
|
884
|
+
name +
|
|
885
|
+
')';
|
|
886
|
+
params[name] = curVar;
|
|
887
|
+
}
|
|
544
888
|
}
|
|
545
889
|
break;
|
|
546
890
|
case 'truthy':
|
|
@@ -549,30 +893,34 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
549
893
|
if (curQuery) {
|
|
550
894
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
551
895
|
}
|
|
552
|
-
if (curVar === 'cdate'
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
896
|
+
if (curVar === 'cdate' ||
|
|
897
|
+
curVar === 'mdate' ||
|
|
898
|
+
(this.nymph.tilmeld != null &&
|
|
899
|
+
(curVar === 'user' ||
|
|
900
|
+
curVar === 'group' ||
|
|
901
|
+
curVar === 'acUser' ||
|
|
902
|
+
curVar === 'acGroup' ||
|
|
903
|
+
curVar === 'acOther' ||
|
|
904
|
+
curVar === 'acRead' ||
|
|
905
|
+
curVar === 'acWrite' ||
|
|
906
|
+
curVar === 'acFull'))) {
|
|
561
907
|
curQuery +=
|
|
562
|
-
(
|
|
908
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
563
909
|
'(' +
|
|
564
910
|
ieTable +
|
|
565
|
-
'.
|
|
566
|
-
|
|
911
|
+
'.' +
|
|
912
|
+
PostgreSQLDriver.escape(curVar) +
|
|
913
|
+
' IS NOT NULL)';
|
|
567
914
|
}
|
|
568
915
|
else {
|
|
569
916
|
const name = `param${++count.i}`;
|
|
570
917
|
curQuery +=
|
|
571
|
-
(
|
|
918
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
919
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
920
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
921
|
+
' WHERE "guid"=' +
|
|
572
922
|
ieTable +
|
|
573
|
-
'."guid"
|
|
574
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
575
|
-
' WHERE "name"=@' +
|
|
923
|
+
'."guid" AND "name"=@' +
|
|
576
924
|
name +
|
|
577
925
|
' AND "truthy"=TRUE)';
|
|
578
926
|
params[name] = curVar;
|
|
@@ -581,35 +929,63 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
581
929
|
break;
|
|
582
930
|
case 'equal':
|
|
583
931
|
case '!equal':
|
|
584
|
-
if (curValue[0] === 'cdate'
|
|
932
|
+
if (curValue[0] === 'cdate' ||
|
|
933
|
+
curValue[0] === 'mdate' ||
|
|
934
|
+
(this.nymph.tilmeld != null &&
|
|
935
|
+
(curValue[0] === 'acUser' ||
|
|
936
|
+
curValue[0] === 'acGroup' ||
|
|
937
|
+
curValue[0] === 'acOther'))) {
|
|
585
938
|
if (curQuery) {
|
|
586
939
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
587
940
|
}
|
|
588
|
-
const
|
|
941
|
+
const value = `param${++count.i}`;
|
|
589
942
|
curQuery +=
|
|
590
|
-
(
|
|
943
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
591
944
|
ieTable +
|
|
592
|
-
'.
|
|
593
|
-
|
|
594
|
-
|
|
945
|
+
'.' +
|
|
946
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
947
|
+
'=@' +
|
|
948
|
+
value;
|
|
949
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
595
950
|
? null
|
|
596
951
|
: Number(curValue[1]);
|
|
597
|
-
break;
|
|
598
952
|
}
|
|
599
|
-
else if (
|
|
953
|
+
else if (this.nymph.tilmeld != null &&
|
|
954
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
600
955
|
if (curQuery) {
|
|
601
956
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
602
957
|
}
|
|
603
|
-
const
|
|
958
|
+
const value = `param${++count.i}`;
|
|
604
959
|
curQuery +=
|
|
605
|
-
(
|
|
960
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
606
961
|
ieTable +
|
|
607
|
-
'.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
962
|
+
'.' +
|
|
963
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
964
|
+
'=decode(@' +
|
|
965
|
+
value +
|
|
966
|
+
", 'hex')";
|
|
967
|
+
params[value] = `${curValue[1]}`;
|
|
968
|
+
}
|
|
969
|
+
else if (this.nymph.tilmeld != null &&
|
|
970
|
+
(curValue[0] === 'acRead' ||
|
|
971
|
+
curValue[0] === 'acWrite' ||
|
|
972
|
+
curValue[0] === 'acFull')) {
|
|
973
|
+
if (curQuery) {
|
|
974
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
975
|
+
}
|
|
976
|
+
const value = `param${++count.i}`;
|
|
977
|
+
curQuery +=
|
|
978
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
979
|
+
ieTable +
|
|
980
|
+
'.' +
|
|
981
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
982
|
+
'=' +
|
|
983
|
+
!curValue[1]?.length
|
|
984
|
+
? '@' + value
|
|
985
|
+
: "array(SELECT decode(n, 'hex') FROM unnest(@" +
|
|
986
|
+
value +
|
|
987
|
+
'::text[]) AS n)';
|
|
988
|
+
params[value] = Array.isArray(curValue[1]) ? curValue[1] : '';
|
|
613
989
|
}
|
|
614
990
|
else if (typeof curValue[1] === 'number') {
|
|
615
991
|
if (curQuery) {
|
|
@@ -618,11 +994,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
618
994
|
const name = `param${++count.i}`;
|
|
619
995
|
const value = `param${++count.i}`;
|
|
620
996
|
curQuery +=
|
|
621
|
-
(
|
|
997
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
998
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
999
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1000
|
+
' WHERE "guid"=' +
|
|
622
1001
|
ieTable +
|
|
623
|
-
'."guid"
|
|
624
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
625
|
-
' WHERE "name"=@' +
|
|
1002
|
+
'."guid" AND "name"=@' +
|
|
626
1003
|
name +
|
|
627
1004
|
' AND "number"=@' +
|
|
628
1005
|
value +
|
|
@@ -637,17 +1014,20 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
637
1014
|
const name = `param${++count.i}`;
|
|
638
1015
|
const value = `param${++count.i}`;
|
|
639
1016
|
curQuery +=
|
|
640
|
-
(
|
|
1017
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1018
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1019
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1020
|
+
' WHERE "guid"=' +
|
|
641
1021
|
ieTable +
|
|
642
|
-
'."guid"
|
|
643
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
644
|
-
' WHERE "name"=@' +
|
|
1022
|
+
'."guid" AND "name"=@' +
|
|
645
1023
|
name +
|
|
646
|
-
' AND "string"
|
|
647
|
-
|
|
1024
|
+
' AND "string"=' +
|
|
1025
|
+
(curValue[1].length < 512
|
|
1026
|
+
? 'LEFT(@' + value + ', 512)'
|
|
1027
|
+
: '@' + value) +
|
|
648
1028
|
')';
|
|
649
1029
|
params[name] = curValue[0];
|
|
650
|
-
params[value] = curValue[1];
|
|
1030
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
651
1031
|
}
|
|
652
1032
|
else {
|
|
653
1033
|
if (curQuery) {
|
|
@@ -664,138 +1044,272 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
664
1044
|
const name = `param${++count.i}`;
|
|
665
1045
|
const value = `param${++count.i}`;
|
|
666
1046
|
curQuery +=
|
|
667
|
-
(
|
|
668
|
-
|
|
669
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
1047
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1048
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
670
1049
|
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
671
|
-
' WHERE "
|
|
1050
|
+
' WHERE "guid"=' +
|
|
1051
|
+
ieTable +
|
|
1052
|
+
'."guid" AND "name"=@' +
|
|
672
1053
|
name +
|
|
673
|
-
' AND "
|
|
1054
|
+
' AND "json"=@' +
|
|
674
1055
|
value +
|
|
675
1056
|
')';
|
|
676
1057
|
params[name] = curValue[0];
|
|
677
|
-
params[value] = svalue;
|
|
1058
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
678
1059
|
}
|
|
679
1060
|
break;
|
|
680
1061
|
case 'contain':
|
|
681
1062
|
case '!contain':
|
|
682
|
-
if (curValue[0] === 'cdate'
|
|
1063
|
+
if (curValue[0] === 'cdate' ||
|
|
1064
|
+
curValue[0] === 'mdate' ||
|
|
1065
|
+
(this.nymph.tilmeld != null &&
|
|
1066
|
+
(curValue[0] === 'acUser' ||
|
|
1067
|
+
curValue[0] === 'acGroup' ||
|
|
1068
|
+
curValue[0] === 'acOther'))) {
|
|
683
1069
|
if (curQuery) {
|
|
684
1070
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
685
1071
|
}
|
|
686
|
-
const
|
|
1072
|
+
const value = `param${++count.i}`;
|
|
687
1073
|
curQuery +=
|
|
688
|
-
(
|
|
1074
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
689
1075
|
ieTable +
|
|
690
|
-
'.
|
|
691
|
-
|
|
692
|
-
|
|
1076
|
+
'.' +
|
|
1077
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1078
|
+
'=@' +
|
|
1079
|
+
value;
|
|
1080
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
693
1081
|
? null
|
|
694
1082
|
: Number(curValue[1]);
|
|
695
|
-
break;
|
|
696
1083
|
}
|
|
697
|
-
else if (
|
|
1084
|
+
else if (this.nymph.tilmeld != null &&
|
|
1085
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
698
1086
|
if (curQuery) {
|
|
699
1087
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
700
1088
|
}
|
|
701
|
-
const
|
|
1089
|
+
const value = `param${++count.i}`;
|
|
702
1090
|
curQuery +=
|
|
703
|
-
(
|
|
1091
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
704
1092
|
ieTable +
|
|
705
|
-
'.
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1093
|
+
'.' +
|
|
1094
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1095
|
+
'=decode(@' +
|
|
1096
|
+
value +
|
|
1097
|
+
", 'hex')";
|
|
1098
|
+
params[value] = `${curValue[1]}`;
|
|
1099
|
+
}
|
|
1100
|
+
else if (this.nymph.tilmeld != null &&
|
|
1101
|
+
(curValue[0] === 'acRead' ||
|
|
1102
|
+
curValue[0] === 'acWrite' ||
|
|
1103
|
+
curValue[0] === 'acFull')) {
|
|
1104
|
+
if (curQuery) {
|
|
1105
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1106
|
+
}
|
|
1107
|
+
const value = `param${++count.i}`;
|
|
1108
|
+
curQuery +=
|
|
1109
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1110
|
+
'decode(@' +
|
|
1111
|
+
value +
|
|
1112
|
+
", 'hex')=ANY(" +
|
|
1113
|
+
ieTable +
|
|
1114
|
+
'.' +
|
|
1115
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1116
|
+
')';
|
|
1117
|
+
params[value] = `${curValue[1]}`;
|
|
711
1118
|
}
|
|
712
1119
|
else {
|
|
713
1120
|
if (curQuery) {
|
|
714
1121
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
715
1122
|
}
|
|
716
1123
|
let svalue;
|
|
717
|
-
let stringValue;
|
|
718
1124
|
if (curValue[1] instanceof Object &&
|
|
719
1125
|
typeof curValue[1].toReference === 'function') {
|
|
720
1126
|
svalue = JSON.stringify(curValue[1].toReference());
|
|
721
|
-
stringValue = `${curValue[1].toReference()}`;
|
|
722
1127
|
}
|
|
723
|
-
else
|
|
1128
|
+
else if (typeof curValue[1] === 'string' ||
|
|
1129
|
+
typeof curValue[1] === 'number') {
|
|
724
1130
|
svalue = JSON.stringify(curValue[1]);
|
|
725
|
-
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
svalue = JSON.stringify([curValue[1]]);
|
|
726
1134
|
}
|
|
727
1135
|
const name = `param${++count.i}`;
|
|
728
1136
|
const value = `param${++count.i}`;
|
|
729
|
-
|
|
730
|
-
|
|
1137
|
+
curQuery +=
|
|
1138
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1139
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1140
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1141
|
+
' WHERE "guid"=' +
|
|
1142
|
+
ieTable +
|
|
1143
|
+
'."guid" AND "name"=@' +
|
|
1144
|
+
name +
|
|
1145
|
+
' AND "json" @> @' +
|
|
1146
|
+
value +
|
|
1147
|
+
')';
|
|
1148
|
+
params[name] = curValue[0];
|
|
1149
|
+
params[value] = PostgreSQLDriver.escapeNullSequences(svalue);
|
|
1150
|
+
}
|
|
1151
|
+
break;
|
|
1152
|
+
case 'search':
|
|
1153
|
+
case '!search':
|
|
1154
|
+
if (curValue[0] === 'cdate' ||
|
|
1155
|
+
curValue[0] === 'mdate' ||
|
|
1156
|
+
(this.nymph.tilmeld != null &&
|
|
1157
|
+
(curValue[0] === 'user' ||
|
|
1158
|
+
curValue[0] === 'group' ||
|
|
1159
|
+
curValue[0] === 'acUser' ||
|
|
1160
|
+
curValue[0] === 'acGroup' ||
|
|
1161
|
+
curValue[0] === 'acOther' ||
|
|
1162
|
+
curValue[0] === 'acRead' ||
|
|
1163
|
+
curValue[0] === 'acWrite' ||
|
|
1164
|
+
curValue[0] === 'acFull'))) {
|
|
1165
|
+
if (curQuery) {
|
|
1166
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1167
|
+
}
|
|
1168
|
+
curQuery +=
|
|
1169
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
if (curQuery) {
|
|
1173
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1174
|
+
}
|
|
1175
|
+
const parsedFTSQuery = this.tokenizer.parseSearchQuery(curValue[1]);
|
|
1176
|
+
if (!parsedFTSQuery.length) {
|
|
731
1177
|
curQuery +=
|
|
732
|
-
(
|
|
733
|
-
|
|
1178
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
|
|
1179
|
+
}
|
|
1180
|
+
else {
|
|
1181
|
+
const name = `param${++count.i}`;
|
|
1182
|
+
const queryPartToken = (term) => {
|
|
1183
|
+
const value = `param${++count.i}`;
|
|
1184
|
+
params[value] = term.token;
|
|
1185
|
+
return ('EXISTS (SELECT "guid" FROM ' +
|
|
1186
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
1187
|
+
' WHERE "guid"=' +
|
|
734
1188
|
ieTable +
|
|
735
|
-
'."guid"
|
|
736
|
-
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
737
|
-
' WHERE "name"=@' +
|
|
1189
|
+
'."guid" AND "name"=@' +
|
|
738
1190
|
name +
|
|
739
|
-
' AND
|
|
1191
|
+
' AND "token"=@' +
|
|
740
1192
|
value +
|
|
741
|
-
'
|
|
1193
|
+
(term.nostemmed ? ' AND "stem"=FALSE' : '') +
|
|
1194
|
+
')');
|
|
1195
|
+
};
|
|
1196
|
+
const queryPartSeries = (series) => {
|
|
1197
|
+
const tokenTableSuffix = makeTableSuffix();
|
|
1198
|
+
const tokenParts = series.tokens.map((token, i) => {
|
|
1199
|
+
const value = `param${++count.i}`;
|
|
1200
|
+
params[value] = token.token;
|
|
1201
|
+
return {
|
|
1202
|
+
fromClause: i === 0
|
|
1203
|
+
? 'FROM ' +
|
|
1204
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
1205
|
+
' t' +
|
|
1206
|
+
tokenTableSuffix +
|
|
1207
|
+
'0'
|
|
1208
|
+
: 'JOIN ' +
|
|
1209
|
+
PostgreSQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
1210
|
+
' t' +
|
|
1211
|
+
tokenTableSuffix +
|
|
1212
|
+
i +
|
|
1213
|
+
' ON t' +
|
|
1214
|
+
tokenTableSuffix +
|
|
1215
|
+
i +
|
|
1216
|
+
'."guid" = t' +
|
|
1217
|
+
tokenTableSuffix +
|
|
1218
|
+
'0."guid" AND t' +
|
|
1219
|
+
tokenTableSuffix +
|
|
1220
|
+
i +
|
|
1221
|
+
'."name" = t' +
|
|
1222
|
+
tokenTableSuffix +
|
|
1223
|
+
'0."name" AND t' +
|
|
1224
|
+
tokenTableSuffix +
|
|
1225
|
+
i +
|
|
1226
|
+
'."position" = t' +
|
|
1227
|
+
tokenTableSuffix +
|
|
1228
|
+
'0."position" + ' +
|
|
1229
|
+
i,
|
|
1230
|
+
whereClause: 't' +
|
|
1231
|
+
tokenTableSuffix +
|
|
1232
|
+
i +
|
|
1233
|
+
'."token"=@' +
|
|
1234
|
+
value +
|
|
1235
|
+
(token.nostemmed
|
|
1236
|
+
? ' AND t' + tokenTableSuffix + i + '."stem"=FALSE'
|
|
1237
|
+
: ''),
|
|
1238
|
+
};
|
|
1239
|
+
});
|
|
1240
|
+
return ('EXISTS (SELECT t' +
|
|
1241
|
+
tokenTableSuffix +
|
|
1242
|
+
'0."guid" ' +
|
|
1243
|
+
tokenParts.map((part) => part.fromClause).join(' ') +
|
|
1244
|
+
' WHERE t' +
|
|
1245
|
+
tokenTableSuffix +
|
|
1246
|
+
'0."guid"=' +
|
|
742
1247
|
ieTable +
|
|
743
|
-
'."guid"
|
|
744
|
-
|
|
745
|
-
'
|
|
1248
|
+
'."guid" AND t' +
|
|
1249
|
+
tokenTableSuffix +
|
|
1250
|
+
'0."name"=@' +
|
|
746
1251
|
name +
|
|
747
|
-
' AND
|
|
748
|
-
|
|
749
|
-
'))
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1252
|
+
' AND ' +
|
|
1253
|
+
tokenParts.map((part) => part.whereClause).join(' AND ') +
|
|
1254
|
+
')');
|
|
1255
|
+
};
|
|
1256
|
+
const queryPartTerm = (term) => {
|
|
1257
|
+
if (term.type === 'series') {
|
|
1258
|
+
return queryPartSeries(term);
|
|
1259
|
+
}
|
|
1260
|
+
else if (term.type === 'not') {
|
|
1261
|
+
return 'NOT ' + queryPartTerm(term.operand);
|
|
1262
|
+
}
|
|
1263
|
+
else if (term.type === 'or') {
|
|
1264
|
+
let queryParts = [];
|
|
1265
|
+
for (let operand of term.operands) {
|
|
1266
|
+
queryParts.push(queryPartTerm(operand));
|
|
1267
|
+
}
|
|
1268
|
+
return '(' + queryParts.join(' OR ') + ')';
|
|
1269
|
+
}
|
|
1270
|
+
return queryPartToken(term);
|
|
1271
|
+
};
|
|
1272
|
+
// Run through the query and add terms.
|
|
1273
|
+
let termStrings = [];
|
|
1274
|
+
for (let term of parsedFTSQuery) {
|
|
1275
|
+
termStrings.push(queryPartTerm(term));
|
|
1276
|
+
}
|
|
753
1277
|
curQuery +=
|
|
754
|
-
(
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
name +
|
|
760
|
-
' AND position(@' +
|
|
761
|
-
value +
|
|
762
|
-
' IN "value")>0)';
|
|
1278
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1279
|
+
'(' +
|
|
1280
|
+
termStrings.join(' AND ') +
|
|
1281
|
+
')';
|
|
1282
|
+
params[name] = curValue[0];
|
|
763
1283
|
}
|
|
764
|
-
params[name] = curValue[0];
|
|
765
|
-
params[value] = svalue;
|
|
766
1284
|
}
|
|
767
1285
|
break;
|
|
768
1286
|
case 'match':
|
|
769
1287
|
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') {
|
|
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'))) {
|
|
786
1299
|
if (curQuery) {
|
|
787
1300
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
788
1301
|
}
|
|
789
|
-
const
|
|
1302
|
+
const value = `param${++count.i}`;
|
|
790
1303
|
curQuery +=
|
|
791
|
-
(
|
|
1304
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
792
1305
|
'(' +
|
|
793
1306
|
ieTable +
|
|
794
|
-
'.
|
|
795
|
-
|
|
1307
|
+
'.' +
|
|
1308
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1309
|
+
' ~ @' +
|
|
1310
|
+
value +
|
|
796
1311
|
')';
|
|
797
|
-
params[
|
|
798
|
-
break;
|
|
1312
|
+
params[value] = curValue[1];
|
|
799
1313
|
}
|
|
800
1314
|
else {
|
|
801
1315
|
if (curQuery) {
|
|
@@ -804,50 +1318,47 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
804
1318
|
const name = `param${++count.i}`;
|
|
805
1319
|
const value = `param${++count.i}`;
|
|
806
1320
|
curQuery +=
|
|
807
|
-
(
|
|
1321
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1322
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1323
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1324
|
+
' WHERE "guid"=' +
|
|
808
1325
|
ieTable +
|
|
809
|
-
'."guid"
|
|
810
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
811
|
-
' WHERE "name"=@' +
|
|
1326
|
+
'."guid" AND "name"=@' +
|
|
812
1327
|
name +
|
|
813
1328
|
' AND "string" ~ @' +
|
|
814
1329
|
value +
|
|
815
1330
|
')';
|
|
816
1331
|
params[name] = curValue[0];
|
|
817
|
-
params[value] = curValue[1];
|
|
1332
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
818
1333
|
}
|
|
819
1334
|
break;
|
|
820
1335
|
case 'imatch':
|
|
821
1336
|
case '!imatch':
|
|
822
|
-
if (curValue[0] === 'cdate'
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
'
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
')';
|
|
834
|
-
params[cdate] = curValue[1];
|
|
835
|
-
break;
|
|
836
|
-
}
|
|
837
|
-
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'))) {
|
|
838
1348
|
if (curQuery) {
|
|
839
1349
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
840
1350
|
}
|
|
841
|
-
const
|
|
1351
|
+
const value = `param${++count.i}`;
|
|
842
1352
|
curQuery +=
|
|
843
|
-
(
|
|
1353
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
844
1354
|
'(' +
|
|
845
1355
|
ieTable +
|
|
846
|
-
'.
|
|
847
|
-
|
|
1356
|
+
'.' +
|
|
1357
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1358
|
+
' ~* @' +
|
|
1359
|
+
value +
|
|
848
1360
|
')';
|
|
849
|
-
params[
|
|
850
|
-
break;
|
|
1361
|
+
params[value] = curValue[1];
|
|
851
1362
|
}
|
|
852
1363
|
else {
|
|
853
1364
|
if (curQuery) {
|
|
@@ -856,50 +1367,47 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
856
1367
|
const name = `param${++count.i}`;
|
|
857
1368
|
const value = `param${++count.i}`;
|
|
858
1369
|
curQuery +=
|
|
859
|
-
(
|
|
1370
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1371
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1372
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1373
|
+
' WHERE "guid"=' +
|
|
860
1374
|
ieTable +
|
|
861
|
-
'."guid"
|
|
862
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
863
|
-
' WHERE "name"=@' +
|
|
1375
|
+
'."guid" AND "name"=@' +
|
|
864
1376
|
name +
|
|
865
1377
|
' AND "string" ~* @' +
|
|
866
1378
|
value +
|
|
867
1379
|
')';
|
|
868
1380
|
params[name] = curValue[0];
|
|
869
|
-
params[value] = curValue[1];
|
|
1381
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
870
1382
|
}
|
|
871
1383
|
break;
|
|
872
1384
|
case 'like':
|
|
873
1385
|
case '!like':
|
|
874
|
-
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'))) {
|
|
875
1397
|
if (curQuery) {
|
|
876
1398
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
877
1399
|
}
|
|
878
|
-
const
|
|
879
|
-
curQuery +=
|
|
880
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
881
|
-
'(' +
|
|
882
|
-
ieTable +
|
|
883
|
-
'."cdate" LIKE @' +
|
|
884
|
-
cdate +
|
|
885
|
-
')';
|
|
886
|
-
params[cdate] = curValue[1];
|
|
887
|
-
break;
|
|
888
|
-
}
|
|
889
|
-
else if (curValue[0] === 'mdate') {
|
|
890
|
-
if (curQuery) {
|
|
891
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
892
|
-
}
|
|
893
|
-
const mdate = `param${++count.i}`;
|
|
1400
|
+
const value = `param${++count.i}`;
|
|
894
1401
|
curQuery +=
|
|
895
|
-
(
|
|
1402
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
896
1403
|
'(' +
|
|
897
1404
|
ieTable +
|
|
898
|
-
'.
|
|
899
|
-
|
|
1405
|
+
'.' +
|
|
1406
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1407
|
+
' LIKE @' +
|
|
1408
|
+
value +
|
|
900
1409
|
')';
|
|
901
|
-
params[
|
|
902
|
-
break;
|
|
1410
|
+
params[value] = curValue[1];
|
|
903
1411
|
}
|
|
904
1412
|
else {
|
|
905
1413
|
if (curQuery) {
|
|
@@ -908,50 +1416,47 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
908
1416
|
const name = `param${++count.i}`;
|
|
909
1417
|
const value = `param${++count.i}`;
|
|
910
1418
|
curQuery +=
|
|
911
|
-
(
|
|
1419
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1420
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1421
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1422
|
+
' WHERE "guid"=' +
|
|
912
1423
|
ieTable +
|
|
913
|
-
'."guid"
|
|
914
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
915
|
-
' WHERE "name"=@' +
|
|
1424
|
+
'."guid" AND "name"=@' +
|
|
916
1425
|
name +
|
|
917
1426
|
' AND "string" LIKE @' +
|
|
918
1427
|
value +
|
|
919
1428
|
')';
|
|
920
1429
|
params[name] = curValue[0];
|
|
921
|
-
params[value] = curValue[1];
|
|
1430
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
922
1431
|
}
|
|
923
1432
|
break;
|
|
924
1433
|
case 'ilike':
|
|
925
1434
|
case '!ilike':
|
|
926
|
-
if (curValue[0] === 'cdate'
|
|
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'))) {
|
|
927
1446
|
if (curQuery) {
|
|
928
1447
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
929
1448
|
}
|
|
930
|
-
const
|
|
1449
|
+
const value = `param${++count.i}`;
|
|
931
1450
|
curQuery +=
|
|
932
|
-
(
|
|
1451
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
933
1452
|
'(' +
|
|
934
1453
|
ieTable +
|
|
935
|
-
'.
|
|
936
|
-
|
|
1454
|
+
'.' +
|
|
1455
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1456
|
+
' ILIKE @' +
|
|
1457
|
+
value +
|
|
937
1458
|
')';
|
|
938
|
-
params[
|
|
939
|
-
break;
|
|
940
|
-
}
|
|
941
|
-
else if (curValue[0] === 'mdate') {
|
|
942
|
-
if (curQuery) {
|
|
943
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
944
|
-
}
|
|
945
|
-
const mdate = `param${++count.i}`;
|
|
946
|
-
curQuery +=
|
|
947
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
948
|
-
'(' +
|
|
949
|
-
ieTable +
|
|
950
|
-
'."mdate" ILIKE @' +
|
|
951
|
-
mdate +
|
|
952
|
-
')';
|
|
953
|
-
params[mdate] = curValue[1];
|
|
954
|
-
break;
|
|
1459
|
+
params[value] = curValue[1];
|
|
955
1460
|
}
|
|
956
1461
|
else {
|
|
957
1462
|
if (curQuery) {
|
|
@@ -960,50 +1465,47 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
960
1465
|
const name = `param${++count.i}`;
|
|
961
1466
|
const value = `param${++count.i}`;
|
|
962
1467
|
curQuery +=
|
|
963
|
-
(
|
|
1468
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1469
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1470
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1471
|
+
' WHERE "guid"=' +
|
|
964
1472
|
ieTable +
|
|
965
|
-
'."guid"
|
|
966
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
967
|
-
' WHERE "name"=@' +
|
|
1473
|
+
'."guid" AND "name"=@' +
|
|
968
1474
|
name +
|
|
969
1475
|
' AND "string" ILIKE @' +
|
|
970
1476
|
value +
|
|
971
1477
|
')';
|
|
972
1478
|
params[name] = curValue[0];
|
|
973
|
-
params[value] = curValue[1];
|
|
1479
|
+
params[value] = PostgreSQLDriver.escapeNulls(curValue[1]);
|
|
974
1480
|
}
|
|
975
1481
|
break;
|
|
976
1482
|
case 'gt':
|
|
977
1483
|
case '!gt':
|
|
978
|
-
if (curValue[0] === 'cdate'
|
|
1484
|
+
if (curValue[0] === 'cdate' ||
|
|
1485
|
+
curValue[0] === 'mdate' ||
|
|
1486
|
+
(this.nymph.tilmeld != null &&
|
|
1487
|
+
(curValue[0] === 'user' ||
|
|
1488
|
+
curValue[0] === 'group' ||
|
|
1489
|
+
curValue[0] === 'acUser' ||
|
|
1490
|
+
curValue[0] === 'acGroup' ||
|
|
1491
|
+
curValue[0] === 'acOther' ||
|
|
1492
|
+
curValue[0] === 'acRead' ||
|
|
1493
|
+
curValue[0] === 'acWrite' ||
|
|
1494
|
+
curValue[0] === 'acFull'))) {
|
|
979
1495
|
if (curQuery) {
|
|
980
1496
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
981
1497
|
}
|
|
982
|
-
const
|
|
983
|
-
curQuery +=
|
|
984
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
985
|
-
ieTable +
|
|
986
|
-
'."cdate">@' +
|
|
987
|
-
cdate;
|
|
988
|
-
params[cdate] = isNaN(Number(curValue[1]))
|
|
989
|
-
? null
|
|
990
|
-
: Number(curValue[1]);
|
|
991
|
-
break;
|
|
992
|
-
}
|
|
993
|
-
else if (curValue[0] === 'mdate') {
|
|
994
|
-
if (curQuery) {
|
|
995
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
996
|
-
}
|
|
997
|
-
const mdate = `param${++count.i}`;
|
|
1498
|
+
const value = `param${++count.i}`;
|
|
998
1499
|
curQuery +=
|
|
999
|
-
(
|
|
1500
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1000
1501
|
ieTable +
|
|
1001
|
-
'.
|
|
1002
|
-
|
|
1003
|
-
|
|
1502
|
+
'.' +
|
|
1503
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1504
|
+
'>@' +
|
|
1505
|
+
value;
|
|
1506
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1004
1507
|
? null
|
|
1005
1508
|
: Number(curValue[1]);
|
|
1006
|
-
break;
|
|
1007
1509
|
}
|
|
1008
1510
|
else {
|
|
1009
1511
|
if (curQuery) {
|
|
@@ -1012,11 +1514,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1012
1514
|
const name = `param${++count.i}`;
|
|
1013
1515
|
const value = `param${++count.i}`;
|
|
1014
1516
|
curQuery +=
|
|
1015
|
-
(
|
|
1517
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1518
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1519
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1520
|
+
' WHERE "guid"=' +
|
|
1016
1521
|
ieTable +
|
|
1017
|
-
'."guid"
|
|
1018
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1019
|
-
' WHERE "name"=@' +
|
|
1522
|
+
'."guid" AND "name"=@' +
|
|
1020
1523
|
name +
|
|
1021
1524
|
' AND "number">@' +
|
|
1022
1525
|
value +
|
|
@@ -1029,35 +1532,31 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1029
1532
|
break;
|
|
1030
1533
|
case 'gte':
|
|
1031
1534
|
case '!gte':
|
|
1032
|
-
if (curValue[0] === 'cdate'
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
'
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
? null
|
|
1044
|
-
: Number(curValue[1]);
|
|
1045
|
-
break;
|
|
1046
|
-
}
|
|
1047
|
-
else if (curValue[0] === 'mdate') {
|
|
1535
|
+
if (curValue[0] === 'cdate' ||
|
|
1536
|
+
curValue[0] === 'mdate' ||
|
|
1537
|
+
(this.nymph.tilmeld != null &&
|
|
1538
|
+
(curValue[0] === 'user' ||
|
|
1539
|
+
curValue[0] === 'group' ||
|
|
1540
|
+
curValue[0] === 'acUser' ||
|
|
1541
|
+
curValue[0] === 'acGroup' ||
|
|
1542
|
+
curValue[0] === 'acOther' ||
|
|
1543
|
+
curValue[0] === 'acRead' ||
|
|
1544
|
+
curValue[0] === 'acWrite' ||
|
|
1545
|
+
curValue[0] === 'acFull'))) {
|
|
1048
1546
|
if (curQuery) {
|
|
1049
1547
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1050
1548
|
}
|
|
1051
|
-
const
|
|
1549
|
+
const value = `param${++count.i}`;
|
|
1052
1550
|
curQuery +=
|
|
1053
|
-
(
|
|
1551
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1054
1552
|
ieTable +
|
|
1055
|
-
'.
|
|
1056
|
-
|
|
1057
|
-
|
|
1553
|
+
'.' +
|
|
1554
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1555
|
+
'>=@' +
|
|
1556
|
+
value;
|
|
1557
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1058
1558
|
? null
|
|
1059
1559
|
: Number(curValue[1]);
|
|
1060
|
-
break;
|
|
1061
1560
|
}
|
|
1062
1561
|
else {
|
|
1063
1562
|
if (curQuery) {
|
|
@@ -1066,11 +1565,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1066
1565
|
const name = `param${++count.i}`;
|
|
1067
1566
|
const value = `param${++count.i}`;
|
|
1068
1567
|
curQuery +=
|
|
1069
|
-
(
|
|
1568
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1569
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1570
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1571
|
+
' WHERE "guid"=' +
|
|
1070
1572
|
ieTable +
|
|
1071
|
-
'."guid"
|
|
1072
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1073
|
-
' WHERE "name"=@' +
|
|
1573
|
+
'."guid" AND "name"=@' +
|
|
1074
1574
|
name +
|
|
1075
1575
|
' AND "number">=@' +
|
|
1076
1576
|
value +
|
|
@@ -1083,35 +1583,31 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1083
1583
|
break;
|
|
1084
1584
|
case 'lt':
|
|
1085
1585
|
case '!lt':
|
|
1086
|
-
if (curValue[0] === 'cdate'
|
|
1586
|
+
if (curValue[0] === 'cdate' ||
|
|
1587
|
+
curValue[0] === 'mdate' ||
|
|
1588
|
+
(this.nymph.tilmeld != null &&
|
|
1589
|
+
(curValue[0] === 'user' ||
|
|
1590
|
+
curValue[0] === 'group' ||
|
|
1591
|
+
curValue[0] === 'acUser' ||
|
|
1592
|
+
curValue[0] === 'acGroup' ||
|
|
1593
|
+
curValue[0] === 'acOther' ||
|
|
1594
|
+
curValue[0] === 'acRead' ||
|
|
1595
|
+
curValue[0] === 'acWrite' ||
|
|
1596
|
+
curValue[0] === 'acFull'))) {
|
|
1087
1597
|
if (curQuery) {
|
|
1088
1598
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1089
1599
|
}
|
|
1090
|
-
const
|
|
1091
|
-
curQuery +=
|
|
1092
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1093
|
-
ieTable +
|
|
1094
|
-
'."cdate"<@' +
|
|
1095
|
-
cdate;
|
|
1096
|
-
params[cdate] = isNaN(Number(curValue[1]))
|
|
1097
|
-
? null
|
|
1098
|
-
: Number(curValue[1]);
|
|
1099
|
-
break;
|
|
1100
|
-
}
|
|
1101
|
-
else if (curValue[0] === 'mdate') {
|
|
1102
|
-
if (curQuery) {
|
|
1103
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1104
|
-
}
|
|
1105
|
-
const mdate = `param${++count.i}`;
|
|
1600
|
+
const value = `param${++count.i}`;
|
|
1106
1601
|
curQuery +=
|
|
1107
|
-
(
|
|
1602
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1108
1603
|
ieTable +
|
|
1109
|
-
'.
|
|
1110
|
-
|
|
1111
|
-
|
|
1604
|
+
'.' +
|
|
1605
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1606
|
+
'<@' +
|
|
1607
|
+
value;
|
|
1608
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1112
1609
|
? null
|
|
1113
1610
|
: Number(curValue[1]);
|
|
1114
|
-
break;
|
|
1115
1611
|
}
|
|
1116
1612
|
else {
|
|
1117
1613
|
if (curQuery) {
|
|
@@ -1120,11 +1616,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1120
1616
|
const name = `param${++count.i}`;
|
|
1121
1617
|
const value = `param${++count.i}`;
|
|
1122
1618
|
curQuery +=
|
|
1123
|
-
(
|
|
1619
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1620
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1621
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1622
|
+
' WHERE "guid"=' +
|
|
1124
1623
|
ieTable +
|
|
1125
|
-
'."guid"
|
|
1126
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1127
|
-
' WHERE "name"=@' +
|
|
1624
|
+
'."guid" AND "name"=@' +
|
|
1128
1625
|
name +
|
|
1129
1626
|
' AND "number"<@' +
|
|
1130
1627
|
value +
|
|
@@ -1137,35 +1634,31 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1137
1634
|
break;
|
|
1138
1635
|
case 'lte':
|
|
1139
1636
|
case '!lte':
|
|
1140
|
-
if (curValue[0] === 'cdate'
|
|
1637
|
+
if (curValue[0] === 'cdate' ||
|
|
1638
|
+
curValue[0] === 'mdate' ||
|
|
1639
|
+
(this.nymph.tilmeld != null &&
|
|
1640
|
+
(curValue[0] === 'user' ||
|
|
1641
|
+
curValue[0] === 'group' ||
|
|
1642
|
+
curValue[0] === 'acUser' ||
|
|
1643
|
+
curValue[0] === 'acGroup' ||
|
|
1644
|
+
curValue[0] === 'acOther' ||
|
|
1645
|
+
curValue[0] === 'acRead' ||
|
|
1646
|
+
curValue[0] === 'acWrite' ||
|
|
1647
|
+
curValue[0] === 'acFull'))) {
|
|
1141
1648
|
if (curQuery) {
|
|
1142
1649
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1143
1650
|
}
|
|
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}`;
|
|
1651
|
+
const value = `param${++count.i}`;
|
|
1160
1652
|
curQuery +=
|
|
1161
|
-
(
|
|
1653
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1162
1654
|
ieTable +
|
|
1163
|
-
'.
|
|
1164
|
-
|
|
1165
|
-
|
|
1655
|
+
'.' +
|
|
1656
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1657
|
+
'<=@' +
|
|
1658
|
+
value;
|
|
1659
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1166
1660
|
? null
|
|
1167
1661
|
: Number(curValue[1]);
|
|
1168
|
-
break;
|
|
1169
1662
|
}
|
|
1170
1663
|
else {
|
|
1171
1664
|
if (curQuery) {
|
|
@@ -1174,11 +1667,12 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1174
1667
|
const name = `param${++count.i}`;
|
|
1175
1668
|
const value = `param${++count.i}`;
|
|
1176
1669
|
curQuery +=
|
|
1177
|
-
(
|
|
1670
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1671
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1672
|
+
PostgreSQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1673
|
+
' WHERE "guid"=' +
|
|
1178
1674
|
ieTable +
|
|
1179
|
-
'."guid"
|
|
1180
|
-
PostgreSQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1181
|
-
' WHERE "name"=@' +
|
|
1675
|
+
'."guid" AND "name"=@' +
|
|
1182
1676
|
name +
|
|
1183
1677
|
' AND "number"<=@' +
|
|
1184
1678
|
value +
|
|
@@ -1204,71 +1698,190 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1204
1698
|
if (curQuery) {
|
|
1205
1699
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1206
1700
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1701
|
+
if (this.nymph.tilmeld != null &&
|
|
1702
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
1703
|
+
const guid = `param${++count.i}`;
|
|
1704
|
+
curQuery +=
|
|
1705
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1706
|
+
ieTable +
|
|
1707
|
+
'.' +
|
|
1708
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1709
|
+
'=decode(@' +
|
|
1710
|
+
guid +
|
|
1711
|
+
", 'hex')";
|
|
1712
|
+
params[guid] = curQguid;
|
|
1713
|
+
}
|
|
1714
|
+
else if (this.nymph.tilmeld != null &&
|
|
1715
|
+
(curValue[0] === 'acRead' ||
|
|
1716
|
+
curValue[0] === 'acWrite' ||
|
|
1717
|
+
curValue[0] === 'acFull')) {
|
|
1718
|
+
const guid = `param${++count.i}`;
|
|
1719
|
+
curQuery +=
|
|
1720
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1721
|
+
'decode(@' +
|
|
1722
|
+
guid +
|
|
1723
|
+
", 'hex')=ANY(" +
|
|
1724
|
+
ieTable +
|
|
1725
|
+
'.' +
|
|
1726
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1727
|
+
')';
|
|
1728
|
+
params[guid] = curQguid;
|
|
1729
|
+
}
|
|
1730
|
+
else {
|
|
1731
|
+
const name = `param${++count.i}`;
|
|
1732
|
+
const guid = `param${++count.i}`;
|
|
1733
|
+
curQuery +=
|
|
1734
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1735
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1736
|
+
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1737
|
+
' WHERE "guid"=' +
|
|
1738
|
+
ieTable +
|
|
1739
|
+
'."guid" AND "name"=@' +
|
|
1740
|
+
name +
|
|
1741
|
+
' AND "reference"=decode(@' +
|
|
1742
|
+
guid +
|
|
1743
|
+
", 'hex'))";
|
|
1744
|
+
params[name] = curValue[0];
|
|
1745
|
+
params[guid] = curQguid;
|
|
1746
|
+
}
|
|
1221
1747
|
break;
|
|
1222
1748
|
case 'selector':
|
|
1223
1749
|
case '!selector':
|
|
1224
|
-
const
|
|
1750
|
+
const innerquery = this.makeEntityQuery({ ...options, sort: null, limit: undefined }, [curValue], etype, count, params, true, tableSuffix, etypes);
|
|
1225
1751
|
if (curQuery) {
|
|
1226
1752
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1227
1753
|
}
|
|
1228
1754
|
curQuery +=
|
|
1229
|
-
(
|
|
1755
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1230
1756
|
'(' +
|
|
1231
|
-
|
|
1757
|
+
innerquery.query +
|
|
1232
1758
|
')';
|
|
1233
1759
|
break;
|
|
1234
1760
|
case 'qref':
|
|
1235
1761
|
case '!qref':
|
|
1762
|
+
const referenceTableSuffix = makeTableSuffix();
|
|
1236
1763
|
const [qrefOptions, ...qrefSelectors] = curValue[1];
|
|
1237
1764
|
const QrefEntityClass = qrefOptions.class;
|
|
1238
1765
|
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
1766
|
if (curQuery) {
|
|
1241
1767
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1242
1768
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1769
|
+
if (this.nymph.tilmeld != null &&
|
|
1770
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
1771
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1772
|
+
...qrefOptions,
|
|
1773
|
+
sort: qrefOptions.sort ?? null,
|
|
1774
|
+
return: 'guid',
|
|
1775
|
+
class: QrefEntityClass,
|
|
1776
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' +
|
|
1777
|
+
referenceTableSuffix +
|
|
1778
|
+
'.' +
|
|
1779
|
+
PostgreSQLDriver.escape(curValue[0]));
|
|
1780
|
+
curQuery +=
|
|
1781
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1782
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1783
|
+
PostgreSQLDriver.escape(this.prefix + 'entities_' + etype) +
|
|
1784
|
+
' r' +
|
|
1785
|
+
referenceTableSuffix +
|
|
1786
|
+
' WHERE r' +
|
|
1787
|
+
referenceTableSuffix +
|
|
1788
|
+
'."guid"=' +
|
|
1789
|
+
ieTable +
|
|
1790
|
+
'."guid" AND EXISTS (' +
|
|
1791
|
+
qrefQuery.query +
|
|
1792
|
+
'))';
|
|
1793
|
+
}
|
|
1794
|
+
else if (this.nymph.tilmeld != null &&
|
|
1795
|
+
(curValue[0] === 'acRead' ||
|
|
1796
|
+
curValue[0] === 'acWrite' ||
|
|
1797
|
+
curValue[0] === 'acFull')) {
|
|
1798
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1799
|
+
...qrefOptions,
|
|
1800
|
+
sort: qrefOptions.sort ?? null,
|
|
1801
|
+
return: 'guid',
|
|
1802
|
+
class: QrefEntityClass,
|
|
1803
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, undefined, (guid) => guid +
|
|
1804
|
+
'=ANY(' +
|
|
1246
1805
|
ieTable +
|
|
1247
|
-
'.
|
|
1248
|
-
PostgreSQLDriver.escape(
|
|
1249
|
-
'
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1806
|
+
'.' +
|
|
1807
|
+
PostgreSQLDriver.escape(curValue[0]) +
|
|
1808
|
+
')');
|
|
1809
|
+
curQuery +=
|
|
1810
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1811
|
+
'EXISTS (' +
|
|
1812
|
+
qrefQuery.query +
|
|
1813
|
+
')';
|
|
1814
|
+
}
|
|
1815
|
+
else {
|
|
1816
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1817
|
+
...qrefOptions,
|
|
1818
|
+
sort: qrefOptions.sort ?? null,
|
|
1819
|
+
return: 'guid',
|
|
1820
|
+
class: QrefEntityClass,
|
|
1821
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' + referenceTableSuffix + '."reference"');
|
|
1822
|
+
const qrefName = `param${++count.i}`;
|
|
1823
|
+
curQuery +=
|
|
1824
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1825
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1826
|
+
PostgreSQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
1827
|
+
' r' +
|
|
1828
|
+
referenceTableSuffix +
|
|
1829
|
+
' WHERE r' +
|
|
1830
|
+
referenceTableSuffix +
|
|
1831
|
+
'."guid"=' +
|
|
1832
|
+
ieTable +
|
|
1833
|
+
'."guid" AND r' +
|
|
1834
|
+
referenceTableSuffix +
|
|
1835
|
+
'."name"=@' +
|
|
1836
|
+
qrefName +
|
|
1837
|
+
' AND EXISTS (' +
|
|
1838
|
+
qrefQuery.query +
|
|
1839
|
+
'))';
|
|
1840
|
+
params[qrefName] = curValue[0];
|
|
1841
|
+
}
|
|
1255
1842
|
break;
|
|
1256
1843
|
}
|
|
1257
1844
|
}
|
|
1258
1845
|
return curQuery;
|
|
1259
1846
|
});
|
|
1260
1847
|
let sortBy;
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1848
|
+
let sortByInner;
|
|
1849
|
+
let sortJoin = '';
|
|
1850
|
+
let sortJoinInner = '';
|
|
1851
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1852
|
+
if (sort == null) {
|
|
1853
|
+
sortBy = '';
|
|
1854
|
+
sortByInner = '';
|
|
1855
|
+
}
|
|
1856
|
+
else {
|
|
1857
|
+
switch (sort) {
|
|
1858
|
+
case 'mdate':
|
|
1859
|
+
sortBy = `ORDER BY ${eTable}."mdate"${order}`;
|
|
1860
|
+
sortByInner = `ORDER BY ${ieTable}."mdate"${order}`;
|
|
1861
|
+
break;
|
|
1862
|
+
case 'cdate':
|
|
1863
|
+
sortBy = `ORDER BY ${eTable}."cdate"${order}`;
|
|
1864
|
+
sortByInner = `ORDER BY ${ieTable}."cdate"${order}`;
|
|
1865
|
+
break;
|
|
1866
|
+
default:
|
|
1867
|
+
const name = `param${++count.i}`;
|
|
1868
|
+
sortJoin = `LEFT JOIN (
|
|
1869
|
+
SELECT "guid", "string", "number"
|
|
1870
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1871
|
+
WHERE "name"=@${name}
|
|
1872
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1873
|
+
) ${sTable} ON ${eTable}."guid"=${sTable}."guid"`;
|
|
1874
|
+
sortJoinInner = `LEFT JOIN (
|
|
1875
|
+
SELECT "guid", "string", "number"
|
|
1876
|
+
FROM ${PostgreSQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
1877
|
+
WHERE "name"=@${name}
|
|
1878
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1879
|
+
) ${sTable} ON ${ieTable}."guid"=${sTable}."guid"`;
|
|
1880
|
+
sortBy = `ORDER BY ${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1881
|
+
sortByInner = sortBy;
|
|
1882
|
+
params[name] = sort;
|
|
1883
|
+
break;
|
|
1884
|
+
}
|
|
1272
1885
|
}
|
|
1273
1886
|
let query;
|
|
1274
1887
|
if (queryParts.length) {
|
|
@@ -1285,18 +1898,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1285
1898
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1286
1899
|
}
|
|
1287
1900
|
const whereClause = queryParts.join(') AND (');
|
|
1901
|
+
const guidClause = guidExplicitSelector
|
|
1902
|
+
? `${guidExplicitSelector(`${ieTable}."guid"`)} AND `
|
|
1903
|
+
: guidSelector
|
|
1904
|
+
? `${ieTable}."guid"=${guidSelector} AND `
|
|
1905
|
+
: '';
|
|
1288
1906
|
if (options.return === 'count') {
|
|
1289
1907
|
if (limit || offset) {
|
|
1290
1908
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1291
|
-
SELECT
|
|
1909
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1292
1910
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1293
|
-
WHERE (${whereClause})${limit}${offset}
|
|
1911
|
+
WHERE ${guidClause}(${whereClause})${limit}${offset}
|
|
1294
1912
|
) ${countTable}`;
|
|
1295
1913
|
}
|
|
1296
1914
|
else {
|
|
1297
1915
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1298
1916
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1299
|
-
WHERE (${whereClause})`;
|
|
1917
|
+
WHERE ${guidClause}(${whereClause})`;
|
|
1300
1918
|
}
|
|
1301
1919
|
}
|
|
1302
1920
|
else if (options.return === 'guid') {
|
|
@@ -1305,8 +1923,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1305
1923
|
: `${ieTable}."guid"`;
|
|
1306
1924
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1307
1925
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1308
|
-
|
|
1309
|
-
|
|
1926
|
+
${sortJoinInner}
|
|
1927
|
+
WHERE ${guidClause}(${whereClause})
|
|
1928
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}${ieTable}."guid"${limit}${offset}`;
|
|
1310
1929
|
}
|
|
1311
1930
|
else {
|
|
1312
1931
|
query = `SELECT
|
|
@@ -1314,20 +1933,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1314
1933
|
${eTable}."tags",
|
|
1315
1934
|
${eTable}."cdate",
|
|
1316
1935
|
${eTable}."mdate",
|
|
1936
|
+
encode(${eTable}."user", 'hex') AS "user",
|
|
1937
|
+
encode(${eTable}."group", 'hex') AS "group",
|
|
1938
|
+
${eTable}."acUser",
|
|
1939
|
+
${eTable}."acGroup",
|
|
1940
|
+
${eTable}."acOther",
|
|
1941
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acRead") AS n) as "acRead",
|
|
1942
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acWrite") AS n) as "acWrite",
|
|
1943
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acFull") AS n) as "acFull",
|
|
1317
1944
|
${dTable}."name",
|
|
1318
1945
|
${dTable}."value",
|
|
1319
|
-
${
|
|
1320
|
-
${
|
|
1946
|
+
${dTable}."json",
|
|
1947
|
+
${dTable}."string",
|
|
1948
|
+
${dTable}."number"
|
|
1321
1949
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1322
1950
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1323
|
-
|
|
1951
|
+
${sortJoin}
|
|
1324
1952
|
INNER JOIN (
|
|
1325
1953
|
SELECT ${ieTable}."guid"
|
|
1326
1954
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1327
|
-
|
|
1328
|
-
|
|
1955
|
+
${sortJoinInner}
|
|
1956
|
+
WHERE ${guidClause}(${whereClause})
|
|
1957
|
+
${sortByInner}${limit}${offset}
|
|
1329
1958
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1330
|
-
ORDER BY ${eTable}
|
|
1959
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1331
1960
|
}
|
|
1332
1961
|
}
|
|
1333
1962
|
}
|
|
@@ -1344,16 +1973,21 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1344
1973
|
if ('offset' in options) {
|
|
1345
1974
|
offset = ` OFFSET ${Math.floor(isNaN(Number(options.offset)) ? 0 : Number(options.offset))}`;
|
|
1346
1975
|
}
|
|
1976
|
+
const guidClause = guidExplicitSelector
|
|
1977
|
+
? ` WHERE ${guidExplicitSelector(`${ieTable}."guid"`)}`
|
|
1978
|
+
: guidSelector
|
|
1979
|
+
? ` WHERE ${ieTable}."guid"=${guidSelector}`
|
|
1980
|
+
: '';
|
|
1347
1981
|
if (options.return === 'count') {
|
|
1348
1982
|
if (limit || offset) {
|
|
1349
1983
|
query = `SELECT COUNT(${countTable}."guid") AS "count" FROM (
|
|
1350
|
-
SELECT
|
|
1351
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${limit}${offset}
|
|
1984
|
+
SELECT ${ieTable}."guid" AS "guid"
|
|
1985
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}${limit}${offset}
|
|
1352
1986
|
) ${countTable}`;
|
|
1353
1987
|
}
|
|
1354
1988
|
else {
|
|
1355
1989
|
query = `SELECT COUNT(${ieTable}."guid") AS "count"
|
|
1356
|
-
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}`;
|
|
1990
|
+
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}${guidClause}`;
|
|
1357
1991
|
}
|
|
1358
1992
|
}
|
|
1359
1993
|
else if (options.return === 'guid') {
|
|
@@ -1362,7 +1996,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1362
1996
|
: `${ieTable}."guid"`;
|
|
1363
1997
|
query = `SELECT ${guidColumn} AS "guid"
|
|
1364
1998
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1365
|
-
|
|
1999
|
+
${sortJoinInner}
|
|
2000
|
+
${guidClause}
|
|
2001
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}${ieTable}."guid"${limit}${offset}`;
|
|
1366
2002
|
}
|
|
1367
2003
|
else {
|
|
1368
2004
|
if (limit || offset) {
|
|
@@ -1371,19 +2007,30 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1371
2007
|
${eTable}."tags",
|
|
1372
2008
|
${eTable}."cdate",
|
|
1373
2009
|
${eTable}."mdate",
|
|
2010
|
+
encode(${eTable}."user", 'hex') AS "user",
|
|
2011
|
+
encode(${eTable}."group", 'hex') AS "group",
|
|
2012
|
+
${eTable}."acUser",
|
|
2013
|
+
${eTable}."acGroup",
|
|
2014
|
+
${eTable}."acOther",
|
|
2015
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acRead") AS n) as "acRead",
|
|
2016
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acWrite") AS n) as "acWrite",
|
|
2017
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acFull") AS n) as "acFull",
|
|
1374
2018
|
${dTable}."name",
|
|
1375
2019
|
${dTable}."value",
|
|
1376
|
-
${
|
|
1377
|
-
${
|
|
2020
|
+
${dTable}."json",
|
|
2021
|
+
${dTable}."string",
|
|
2022
|
+
${dTable}."number"
|
|
1378
2023
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1379
2024
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1380
|
-
|
|
2025
|
+
${sortJoin}
|
|
1381
2026
|
INNER JOIN (
|
|
1382
2027
|
SELECT ${ieTable}."guid"
|
|
1383
2028
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${ieTable}
|
|
1384
|
-
|
|
2029
|
+
${sortJoinInner}
|
|
2030
|
+
${guidClause}
|
|
2031
|
+
${sortByInner}${limit}${offset}
|
|
1385
2032
|
) ${fTable} ON ${eTable}."guid"=${fTable}."guid"
|
|
1386
|
-
ORDER BY ${eTable}
|
|
2033
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1387
2034
|
}
|
|
1388
2035
|
else {
|
|
1389
2036
|
query = `SELECT
|
|
@@ -1391,14 +2038,24 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1391
2038
|
${eTable}."tags",
|
|
1392
2039
|
${eTable}."cdate",
|
|
1393
2040
|
${eTable}."mdate",
|
|
2041
|
+
encode(${eTable}."user", 'hex') AS "user",
|
|
2042
|
+
encode(${eTable}."group", 'hex') AS "group",
|
|
2043
|
+
${eTable}."acUser",
|
|
2044
|
+
${eTable}."acGroup",
|
|
2045
|
+
${eTable}."acOther",
|
|
2046
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acRead") AS n) as "acRead",
|
|
2047
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acWrite") AS n) as "acWrite",
|
|
2048
|
+
array(SELECT encode(n, 'hex') FROM unnest(${eTable}."acFull") AS n) as "acFull",
|
|
1394
2049
|
${dTable}."name",
|
|
1395
2050
|
${dTable}."value",
|
|
1396
|
-
${
|
|
1397
|
-
${
|
|
2051
|
+
${dTable}."json",
|
|
2052
|
+
${dTable}."string",
|
|
2053
|
+
${dTable}."number"
|
|
1398
2054
|
FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} ${eTable}
|
|
1399
2055
|
LEFT JOIN ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ${dTable} ON ${eTable}."guid"=${dTable}."guid"
|
|
1400
|
-
|
|
1401
|
-
|
|
2056
|
+
${sortJoin}
|
|
2057
|
+
${guidExplicitSelector ? `WHERE ${guidExplicitSelector(`${eTable}."guid"`)}` : guidSelector ? `WHERE ${eTable}."guid"=${guidSelector}` : ''}
|
|
2058
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1402
2059
|
}
|
|
1403
2060
|
}
|
|
1404
2061
|
}
|
|
@@ -1414,33 +2071,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1414
2071
|
}
|
|
1415
2072
|
performQuery(options, formattedSelectors, etype) {
|
|
1416
2073
|
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]();
|
|
2074
|
+
const result = this.queryArray(query, { etypes, params }).then((val) => val[Symbol.iterator]());
|
|
1425
2075
|
return {
|
|
1426
2076
|
result,
|
|
1427
2077
|
};
|
|
1428
2078
|
}
|
|
1429
2079
|
async getEntities(options = {}, ...selectors) {
|
|
1430
|
-
const { result: resultPromise, process } = this.
|
|
2080
|
+
const { result: resultPromise, process } = this.getEntitiesRowLike(
|
|
2081
|
+
// @ts-ignore: options is correct here.
|
|
2082
|
+
options, selectors, ({ options, selectors, etype }) => this.performQuery(options, selectors, etype), () => {
|
|
1431
2083
|
const next = result.next();
|
|
1432
2084
|
return next.done ? null : next.value;
|
|
1433
2085
|
}, () => 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)) ?
|
|
2086
|
+
tags: row.tags.filter((tag) => tag),
|
|
2087
|
+
cdate: isNaN(Number(row.cdate)) ? Date.now() : Number(row.cdate),
|
|
2088
|
+
mdate: isNaN(Number(row.mdate)) ? Date.now() : Number(row.mdate),
|
|
2089
|
+
user: row.user,
|
|
2090
|
+
group: row.group,
|
|
2091
|
+
acUser: row.acUser,
|
|
2092
|
+
acGroup: row.acGroup,
|
|
2093
|
+
acOther: row.acOther,
|
|
2094
|
+
acRead: row.acRead?.filter((guid) => guid) ?? [],
|
|
2095
|
+
acWrite: row.acWrite?.filter((guid) => guid) ?? [],
|
|
2096
|
+
acFull: row.acFull?.filter((guid) => guid) ?? [],
|
|
1437
2097
|
}), (row) => ({
|
|
1438
2098
|
name: row.name,
|
|
1439
2099
|
svalue: row.value === 'N'
|
|
1440
2100
|
? JSON.stringify(Number(row.number))
|
|
1441
2101
|
: row.value === 'S'
|
|
1442
|
-
? JSON.stringify(row.string)
|
|
1443
|
-
: row.value
|
|
2102
|
+
? JSON.stringify(PostgreSQLDriver.unescapeNulls(row.string))
|
|
2103
|
+
: row.value === 'J'
|
|
2104
|
+
? PostgreSQLDriver.unescapeNullSequences(JSON.stringify(row.json))
|
|
2105
|
+
: row.value,
|
|
1444
2106
|
}));
|
|
1445
2107
|
const result = await resultPromise;
|
|
1446
2108
|
const value = process();
|
|
@@ -1449,31 +2111,9 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1449
2111
|
}
|
|
1450
2112
|
return value;
|
|
1451
2113
|
}
|
|
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
2114
|
async getUID(name) {
|
|
1475
2115
|
if (name == null) {
|
|
1476
|
-
throw new
|
|
2116
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1477
2117
|
}
|
|
1478
2118
|
const result = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
1479
2119
|
params: {
|
|
@@ -1482,50 +2122,75 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1482
2122
|
});
|
|
1483
2123
|
return result?.cur_uid == null ? null : Number(result.cur_uid);
|
|
1484
2124
|
}
|
|
1485
|
-
async
|
|
2125
|
+
async importEntity(entity) {
|
|
2126
|
+
return await this.importEntityInternal(entity);
|
|
2127
|
+
}
|
|
2128
|
+
async importEntityTokens(entity) {
|
|
2129
|
+
return await this.importEntityInternal(entity, { only: 'tokens' });
|
|
2130
|
+
}
|
|
2131
|
+
async importEntityTilmeldAC(entity) {
|
|
2132
|
+
return await this.importEntityInternal(entity, { only: 'tilmeldAC' });
|
|
2133
|
+
}
|
|
2134
|
+
async importEntityInternal({ guid, cdate, mdate, tags, sdata, etype, }, { only = undefined } = {}) {
|
|
1486
2135
|
try {
|
|
1487
|
-
|
|
1488
|
-
|
|
2136
|
+
let promises = [];
|
|
2137
|
+
if (only == null) {
|
|
2138
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1489
2139
|
etypes: [etype],
|
|
1490
2140
|
params: {
|
|
1491
2141
|
guid,
|
|
1492
2142
|
},
|
|
1493
|
-
});
|
|
1494
|
-
|
|
2143
|
+
}));
|
|
2144
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1495
2145
|
etypes: [etype],
|
|
1496
2146
|
params: {
|
|
1497
2147
|
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
2148
|
},
|
|
1506
|
-
});
|
|
1507
|
-
|
|
1508
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
2149
|
+
}));
|
|
2150
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1509
2151
|
etypes: [etype],
|
|
1510
2152
|
params: {
|
|
1511
2153
|
guid,
|
|
1512
2154
|
},
|
|
1513
2155
|
}));
|
|
1514
|
-
|
|
2156
|
+
}
|
|
2157
|
+
if (only == null || only === 'tokens') {
|
|
2158
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1515
2159
|
etypes: [etype],
|
|
1516
2160
|
params: {
|
|
1517
2161
|
guid,
|
|
1518
2162
|
},
|
|
1519
2163
|
}));
|
|
1520
|
-
|
|
2164
|
+
}
|
|
2165
|
+
if (only == null) {
|
|
2166
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1521
2167
|
etypes: [etype],
|
|
1522
2168
|
params: {
|
|
1523
2169
|
guid,
|
|
1524
2170
|
},
|
|
1525
2171
|
}));
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
2172
|
+
}
|
|
2173
|
+
await Promise.all(promises);
|
|
2174
|
+
promises = [];
|
|
2175
|
+
if (only == null) {
|
|
2176
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } = this.removeAndReturnACValues(etype, {}, sdata);
|
|
2177
|
+
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)"});`, {
|
|
2178
|
+
etypes: [etype],
|
|
2179
|
+
params: {
|
|
2180
|
+
guid,
|
|
2181
|
+
tags,
|
|
2182
|
+
cdate: isNaN(cdate) ? null : cdate,
|
|
2183
|
+
mdate: isNaN(mdate) ? null : mdate,
|
|
2184
|
+
user,
|
|
2185
|
+
group,
|
|
2186
|
+
acUser,
|
|
2187
|
+
acGroup,
|
|
2188
|
+
acOther,
|
|
2189
|
+
acRead,
|
|
2190
|
+
acWrite,
|
|
2191
|
+
acFull,
|
|
2192
|
+
},
|
|
2193
|
+
});
|
|
1529
2194
|
for (const name in sdata) {
|
|
1530
2195
|
const value = sdata[name];
|
|
1531
2196
|
const uvalue = JSON.parse(value);
|
|
@@ -1536,24 +2201,22 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1536
2201
|
? 'N'
|
|
1537
2202
|
: typeof uvalue === 'string'
|
|
1538
2203
|
? 'S'
|
|
1539
|
-
:
|
|
1540
|
-
const
|
|
1541
|
-
|
|
2204
|
+
: 'J';
|
|
2205
|
+
const jsonValue = storageValue === 'J'
|
|
2206
|
+
? PostgreSQLDriver.escapeNullSequences(value)
|
|
2207
|
+
: null;
|
|
2208
|
+
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
2209
|
etypes: [etype],
|
|
1543
2210
|
params: {
|
|
1544
2211
|
guid,
|
|
1545
2212
|
name,
|
|
1546
2213
|
storageValue,
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
params: {
|
|
1552
|
-
guid,
|
|
1553
|
-
name,
|
|
1554
|
-
truthy: !!uvalue,
|
|
1555
|
-
string: `${uvalue}`,
|
|
2214
|
+
jsonValue,
|
|
2215
|
+
string: storageValue === 'J'
|
|
2216
|
+
? null
|
|
2217
|
+
: PostgreSQLDriver.escapeNulls(`${uvalue}`),
|
|
1556
2218
|
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
2219
|
+
truthy: !!uvalue,
|
|
1557
2220
|
},
|
|
1558
2221
|
}));
|
|
1559
2222
|
const references = this.findReferences(value);
|
|
@@ -1568,43 +2231,130 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1568
2231
|
}));
|
|
1569
2232
|
}
|
|
1570
2233
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
},
|
|
1577
|
-
});
|
|
1578
|
-
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
2234
|
+
}
|
|
2235
|
+
if (only === 'tilmeldAC') {
|
|
2236
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } = this.removeAndReturnACValues(etype, {}, sdata);
|
|
2237
|
+
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');`, {
|
|
2238
|
+
etypes: [etype],
|
|
1579
2239
|
params: {
|
|
1580
|
-
|
|
1581
|
-
|
|
2240
|
+
user,
|
|
2241
|
+
group,
|
|
2242
|
+
acUser,
|
|
2243
|
+
acGroup,
|
|
2244
|
+
acOther,
|
|
2245
|
+
acRead,
|
|
2246
|
+
acWrite,
|
|
2247
|
+
acFull,
|
|
2248
|
+
guid,
|
|
1582
2249
|
},
|
|
2250
|
+
}));
|
|
2251
|
+
}
|
|
2252
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
2253
|
+
if (only == null || only === 'tokens') {
|
|
2254
|
+
for (let name in sdata) {
|
|
2255
|
+
let tokenString = null;
|
|
2256
|
+
try {
|
|
2257
|
+
tokenString = EntityClass.getFTSText(name, JSON.parse(sdata[name]));
|
|
2258
|
+
}
|
|
2259
|
+
catch (e) {
|
|
2260
|
+
// Ignore error.
|
|
2261
|
+
}
|
|
2262
|
+
if (tokenString != null) {
|
|
2263
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2264
|
+
while (tokens.length) {
|
|
2265
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2266
|
+
const params = {
|
|
2267
|
+
guid,
|
|
2268
|
+
name,
|
|
2269
|
+
};
|
|
2270
|
+
const values = [];
|
|
2271
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2272
|
+
const token = currentTokens[i];
|
|
2273
|
+
params['token' + i] = token.token;
|
|
2274
|
+
params['position' + i] = token.position;
|
|
2275
|
+
params['stem' + i] = token.stem;
|
|
2276
|
+
values.push("(decode(@guid, 'hex'), @name, @token" +
|
|
2277
|
+
i +
|
|
2278
|
+
', @position' +
|
|
2279
|
+
i +
|
|
2280
|
+
', @stem' +
|
|
2281
|
+
i +
|
|
2282
|
+
')');
|
|
2283
|
+
}
|
|
2284
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
2285
|
+
etypes: [etype],
|
|
2286
|
+
params,
|
|
2287
|
+
}));
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
if (only == null) {
|
|
2293
|
+
const uniques = await EntityClass.getUniques({
|
|
2294
|
+
guid,
|
|
2295
|
+
cdate,
|
|
2296
|
+
mdate,
|
|
2297
|
+
tags,
|
|
2298
|
+
data: {},
|
|
2299
|
+
sdata,
|
|
1583
2300
|
});
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
2301
|
+
for (const unique of uniques) {
|
|
2302
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
2303
|
+
etypes: [etype],
|
|
2304
|
+
params: {
|
|
2305
|
+
guid,
|
|
2306
|
+
unique,
|
|
2307
|
+
},
|
|
2308
|
+
}).catch((e) => {
|
|
2309
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2310
|
+
this.nymph.config.debugError('postgresql', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
2311
|
+
}
|
|
2312
|
+
return e;
|
|
2313
|
+
}));
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
await Promise.all(promises);
|
|
2317
|
+
}
|
|
2318
|
+
catch (e) {
|
|
2319
|
+
this.nymph.config.debugError('postgresql', `Import entity error: "${e}"`);
|
|
2320
|
+
throw e;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
async importUID({ name, value }) {
|
|
2324
|
+
try {
|
|
2325
|
+
await this.internalTransaction(`nymph-import-uid-${name}`);
|
|
2326
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
2327
|
+
params: {
|
|
2328
|
+
name,
|
|
2329
|
+
},
|
|
1588
2330
|
});
|
|
1589
|
-
|
|
2331
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @value);`, {
|
|
2332
|
+
params: {
|
|
2333
|
+
name,
|
|
2334
|
+
value,
|
|
2335
|
+
},
|
|
2336
|
+
});
|
|
2337
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
1590
2338
|
}
|
|
1591
2339
|
catch (e) {
|
|
1592
|
-
|
|
1593
|
-
|
|
2340
|
+
this.nymph.config.debugError('postgresql', `Import UID error: "${e}"`);
|
|
2341
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
2342
|
+
throw e;
|
|
1594
2343
|
}
|
|
1595
2344
|
}
|
|
1596
2345
|
async newUID(name) {
|
|
1597
2346
|
if (name == null) {
|
|
1598
|
-
throw new
|
|
2347
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1599
2348
|
}
|
|
1600
2349
|
await this.internalTransaction('nymph-newuid');
|
|
2350
|
+
let curUid = undefined;
|
|
1601
2351
|
try {
|
|
1602
2352
|
const lock = await this.queryGet(`SELECT "cur_uid" FROM ${PostgreSQLDriver.escape(`${this.prefix}uids`)} WHERE "name"=@name FOR UPDATE;`, {
|
|
1603
2353
|
params: {
|
|
1604
2354
|
name,
|
|
1605
2355
|
},
|
|
1606
2356
|
});
|
|
1607
|
-
|
|
2357
|
+
curUid = lock?.cur_uid == null ? undefined : Number(lock.cur_uid);
|
|
1608
2358
|
if (curUid == null) {
|
|
1609
2359
|
curUid = 1;
|
|
1610
2360
|
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
@@ -1623,17 +2373,18 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1623
2373
|
},
|
|
1624
2374
|
});
|
|
1625
2375
|
}
|
|
1626
|
-
await this.commit('nymph-newuid');
|
|
1627
|
-
return curUid;
|
|
1628
2376
|
}
|
|
1629
2377
|
catch (e) {
|
|
2378
|
+
this.nymph.config.debugError('postgresql', `New UID error: "${e}"`);
|
|
1630
2379
|
await this.rollback('nymph-newuid');
|
|
1631
2380
|
throw e;
|
|
1632
2381
|
}
|
|
2382
|
+
await this.commit('nymph-newuid');
|
|
2383
|
+
return curUid;
|
|
1633
2384
|
}
|
|
1634
2385
|
async renameUID(oldName, newName) {
|
|
1635
2386
|
if (oldName == null || newName == null) {
|
|
1636
|
-
throw new
|
|
2387
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1637
2388
|
}
|
|
1638
2389
|
await this.queryRun(`UPDATE ${PostgreSQLDriver.escape(`${this.prefix}uids`)} SET "name"=@newName WHERE "name"=@oldName;`, {
|
|
1639
2390
|
params: {
|
|
@@ -1645,7 +2396,7 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1645
2396
|
}
|
|
1646
2397
|
async rollback(name) {
|
|
1647
2398
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1648
|
-
throw new
|
|
2399
|
+
throw new InvalidParametersError('Transaction rollback attempted without a name.');
|
|
1649
2400
|
}
|
|
1650
2401
|
if (!this.transaction || this.transaction.count === 0) {
|
|
1651
2402
|
this.transaction = null;
|
|
@@ -1662,7 +2413,8 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1662
2413
|
return true;
|
|
1663
2414
|
}
|
|
1664
2415
|
async saveEntity(entity) {
|
|
1665
|
-
const insertData = async (guid, data, sdata, etype) => {
|
|
2416
|
+
const insertData = async (guid, data, sdata, uniques, etype) => {
|
|
2417
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
1666
2418
|
const runInsertQuery = async (name, value, svalue) => {
|
|
1667
2419
|
if (value === undefined) {
|
|
1668
2420
|
return;
|
|
@@ -1671,24 +2423,23 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1671
2423
|
? 'N'
|
|
1672
2424
|
: typeof value === 'string'
|
|
1673
2425
|
? 'S'
|
|
1674
|
-
:
|
|
2426
|
+
: 'J';
|
|
2427
|
+
const jsonValue = storageValue === 'J'
|
|
2428
|
+
? PostgreSQLDriver.escapeNullSequences(svalue)
|
|
2429
|
+
: null;
|
|
1675
2430
|
const promises = [];
|
|
1676
|
-
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value") VALUES (decode(@guid, 'hex'), @name, @storageValue);`, {
|
|
2431
|
+
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
2432
|
etypes: [etype],
|
|
1678
2433
|
params: {
|
|
1679
2434
|
guid,
|
|
1680
2435
|
name,
|
|
1681
2436
|
storageValue,
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
params: {
|
|
1687
|
-
guid,
|
|
1688
|
-
name,
|
|
1689
|
-
truthy: !!value,
|
|
1690
|
-
string: `${value}`,
|
|
2437
|
+
jsonValue,
|
|
2438
|
+
string: storageValue === 'J'
|
|
2439
|
+
? null
|
|
2440
|
+
: PostgreSQLDriver.escapeNulls(`${value}`),
|
|
1691
2441
|
number: isNaN(Number(value)) ? null : Number(value),
|
|
2442
|
+
truthy: !!value,
|
|
1692
2443
|
},
|
|
1693
2444
|
}));
|
|
1694
2445
|
const references = this.findReferences(svalue);
|
|
@@ -1702,8 +2453,60 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1702
2453
|
},
|
|
1703
2454
|
}));
|
|
1704
2455
|
}
|
|
2456
|
+
let tokenString = null;
|
|
2457
|
+
try {
|
|
2458
|
+
tokenString = EntityClass.getFTSText(name, value);
|
|
2459
|
+
}
|
|
2460
|
+
catch (e) {
|
|
2461
|
+
// Ignore error.
|
|
2462
|
+
}
|
|
2463
|
+
if (tokenString != null) {
|
|
2464
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2465
|
+
while (tokens.length) {
|
|
2466
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2467
|
+
const params = {
|
|
2468
|
+
guid,
|
|
2469
|
+
name,
|
|
2470
|
+
};
|
|
2471
|
+
const values = [];
|
|
2472
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2473
|
+
const token = currentTokens[i];
|
|
2474
|
+
params['token' + i] = token.token;
|
|
2475
|
+
params['position' + i] = token.position;
|
|
2476
|
+
params['stem' + i] = token.stem;
|
|
2477
|
+
values.push("(decode(@guid, 'hex'), @name, @token" +
|
|
2478
|
+
i +
|
|
2479
|
+
', @position' +
|
|
2480
|
+
i +
|
|
2481
|
+
', @stem' +
|
|
2482
|
+
i +
|
|
2483
|
+
')');
|
|
2484
|
+
}
|
|
2485
|
+
promises.push(this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
2486
|
+
etypes: [etype],
|
|
2487
|
+
params,
|
|
2488
|
+
}));
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
1705
2491
|
await Promise.all(promises);
|
|
1706
2492
|
};
|
|
2493
|
+
for (const unique of uniques) {
|
|
2494
|
+
try {
|
|
2495
|
+
await this.queryRun(`INSERT INTO ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (decode(@guid, 'hex'), @unique);`, {
|
|
2496
|
+
etypes: [etype],
|
|
2497
|
+
params: {
|
|
2498
|
+
guid,
|
|
2499
|
+
unique,
|
|
2500
|
+
},
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2503
|
+
catch (e) {
|
|
2504
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2505
|
+
this.nymph.config.debugError('postgresql', `Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
2506
|
+
}
|
|
2507
|
+
throw e;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
1707
2510
|
for (const name in data) {
|
|
1708
2511
|
await runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
1709
2512
|
}
|
|
@@ -1711,19 +2514,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1711
2514
|
await runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
1712
2515
|
}
|
|
1713
2516
|
};
|
|
2517
|
+
let inTransaction = false;
|
|
1714
2518
|
try {
|
|
1715
|
-
const result = await this.saveEntityRowLike(entity, async (
|
|
1716
|
-
|
|
2519
|
+
const result = await this.saveEntityRowLike(entity, async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
2520
|
+
if (Object.keys(data).length === 0 &&
|
|
2521
|
+
Object.keys(sdata).length === 0) {
|
|
2522
|
+
return false;
|
|
2523
|
+
}
|
|
2524
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull, } = this.removeAndReturnACValues(etype, data, sdata);
|
|
2525
|
+
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
2526
|
etypes: [etype],
|
|
1718
2527
|
params: {
|
|
1719
2528
|
guid,
|
|
1720
2529
|
tags,
|
|
1721
2530
|
cdate,
|
|
2531
|
+
user,
|
|
2532
|
+
group,
|
|
2533
|
+
acUser,
|
|
2534
|
+
acGroup,
|
|
2535
|
+
acOther,
|
|
2536
|
+
acRead,
|
|
2537
|
+
acWrite,
|
|
2538
|
+
acFull,
|
|
1722
2539
|
},
|
|
1723
2540
|
});
|
|
1724
|
-
await insertData(guid, data, sdata, etype);
|
|
2541
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1725
2542
|
return true;
|
|
1726
|
-
}, async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
2543
|
+
}, async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
2544
|
+
if (Object.keys(data).length === 0 &&
|
|
2545
|
+
Object.keys(sdata).length === 0) {
|
|
2546
|
+
return false;
|
|
2547
|
+
}
|
|
2548
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull, } = this.removeAndReturnACValues(etype, data, sdata);
|
|
1727
2549
|
const promises = [];
|
|
1728
2550
|
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1729
2551
|
etypes: [etype],
|
|
@@ -1737,24 +2559,38 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1737
2559
|
guid,
|
|
1738
2560
|
},
|
|
1739
2561
|
}));
|
|
1740
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2562
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1741
2563
|
etypes: [etype],
|
|
1742
2564
|
params: {
|
|
1743
2565
|
guid,
|
|
1744
2566
|
},
|
|
1745
2567
|
}));
|
|
1746
|
-
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2568
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
2569
|
+
etypes: [etype],
|
|
2570
|
+
params: {
|
|
2571
|
+
guid,
|
|
2572
|
+
},
|
|
2573
|
+
}));
|
|
2574
|
+
promises.push(this.queryRun(`SELECT 1 FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex') FOR UPDATE;`, {
|
|
1747
2575
|
etypes: [etype],
|
|
1748
2576
|
params: {
|
|
1749
2577
|
guid,
|
|
1750
2578
|
},
|
|
1751
2579
|
}));
|
|
1752
2580
|
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;`, {
|
|
2581
|
+
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
2582
|
etypes: [etype],
|
|
1755
2583
|
params: {
|
|
1756
2584
|
tags,
|
|
1757
2585
|
mdate,
|
|
2586
|
+
user,
|
|
2587
|
+
group,
|
|
2588
|
+
acUser,
|
|
2589
|
+
acGroup,
|
|
2590
|
+
acOther,
|
|
2591
|
+
acRead,
|
|
2592
|
+
acWrite,
|
|
2593
|
+
acFull,
|
|
1758
2594
|
guid,
|
|
1759
2595
|
emdate: isNaN(Number(entity.mdate)) ? 0 : Number(entity.mdate),
|
|
1760
2596
|
},
|
|
@@ -1768,44 +2604,57 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1768
2604
|
guid,
|
|
1769
2605
|
},
|
|
1770
2606
|
}));
|
|
1771
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2607
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1772
2608
|
etypes: [etype],
|
|
1773
2609
|
params: {
|
|
1774
2610
|
guid,
|
|
1775
2611
|
},
|
|
1776
2612
|
}));
|
|
1777
|
-
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}
|
|
2613
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
2614
|
+
etypes: [etype],
|
|
2615
|
+
params: {
|
|
2616
|
+
guid,
|
|
2617
|
+
},
|
|
2618
|
+
}));
|
|
2619
|
+
promises.push(this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=decode(@guid, 'hex');`, {
|
|
1778
2620
|
etypes: [etype],
|
|
1779
2621
|
params: {
|
|
1780
2622
|
guid,
|
|
1781
2623
|
},
|
|
1782
2624
|
}));
|
|
1783
2625
|
await Promise.all(promises);
|
|
1784
|
-
await insertData(guid, data, sdata, etype);
|
|
2626
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
1785
2627
|
success = true;
|
|
1786
2628
|
}
|
|
1787
2629
|
return success;
|
|
1788
2630
|
}, async () => {
|
|
1789
2631
|
await this.internalTransaction('nymph-save');
|
|
2632
|
+
inTransaction = true;
|
|
1790
2633
|
}, async (success) => {
|
|
1791
|
-
if (
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
2634
|
+
if (inTransaction) {
|
|
2635
|
+
inTransaction = false;
|
|
2636
|
+
if (success) {
|
|
2637
|
+
await this.commit('nymph-save');
|
|
2638
|
+
}
|
|
2639
|
+
else {
|
|
2640
|
+
await this.rollback('nymph-save');
|
|
2641
|
+
}
|
|
1796
2642
|
}
|
|
1797
2643
|
return success;
|
|
1798
2644
|
});
|
|
1799
2645
|
return result;
|
|
1800
2646
|
}
|
|
1801
2647
|
catch (e) {
|
|
1802
|
-
|
|
2648
|
+
this.nymph.config.debugError('postgresql', `Save entity error: "${e}"`);
|
|
2649
|
+
if (inTransaction) {
|
|
2650
|
+
await this.rollback('nymph-save');
|
|
2651
|
+
}
|
|
1803
2652
|
throw e;
|
|
1804
2653
|
}
|
|
1805
2654
|
}
|
|
1806
2655
|
async setUID(name, curUid) {
|
|
1807
2656
|
if (name == null) {
|
|
1808
|
-
throw new
|
|
2657
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1809
2658
|
}
|
|
1810
2659
|
await this.internalTransaction('nymph-setuid');
|
|
1811
2660
|
try {
|
|
@@ -1821,39 +2670,161 @@ class PostgreSQLDriver extends nymph_1.NymphDriver {
|
|
|
1821
2670
|
curUid,
|
|
1822
2671
|
},
|
|
1823
2672
|
});
|
|
1824
|
-
await this.commit('nymph-setuid');
|
|
1825
|
-
return true;
|
|
1826
2673
|
}
|
|
1827
2674
|
catch (e) {
|
|
1828
2675
|
await this.rollback('nymph-setuid');
|
|
1829
2676
|
throw e;
|
|
1830
2677
|
}
|
|
2678
|
+
await this.commit('nymph-setuid');
|
|
2679
|
+
return true;
|
|
1831
2680
|
}
|
|
1832
|
-
async internalTransaction(name) {
|
|
2681
|
+
async internalTransaction(name, setOnSelf = true) {
|
|
1833
2682
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1834
|
-
throw new
|
|
2683
|
+
throw new InvalidParametersError('Transaction start attempted without a name.');
|
|
1835
2684
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
2685
|
+
let transaction = this.transaction;
|
|
2686
|
+
if (!transaction) {
|
|
2687
|
+
// Lock to one connection.
|
|
2688
|
+
transaction = {
|
|
1838
2689
|
count: 0,
|
|
1839
2690
|
connection: await this.getConnection(),
|
|
2691
|
+
latestPromise: Promise.resolve(),
|
|
1840
2692
|
};
|
|
1841
|
-
|
|
2693
|
+
if (setOnSelf) {
|
|
2694
|
+
this.transaction = transaction;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
if (transaction.count === 0) {
|
|
2698
|
+
// We're not in a transaction yet, so start one.
|
|
2699
|
+
await this.queryRun('BEGIN;', { transaction });
|
|
1842
2700
|
}
|
|
1843
|
-
|
|
1844
|
-
this.
|
|
1845
|
-
|
|
2701
|
+
transaction.count++;
|
|
2702
|
+
await this.queryRun(`SAVEPOINT ${PostgreSQLDriver.escape(name)};`, {
|
|
2703
|
+
transaction,
|
|
2704
|
+
});
|
|
2705
|
+
return transaction;
|
|
1846
2706
|
}
|
|
1847
2707
|
async startTransaction(name) {
|
|
1848
|
-
const
|
|
1849
|
-
const transaction = await this.internalTransaction(name);
|
|
1850
|
-
if (!inTransaction) {
|
|
1851
|
-
this.transaction = null;
|
|
1852
|
-
}
|
|
2708
|
+
const transaction = await this.internalTransaction(name, false);
|
|
1853
2709
|
const nymph = this.nymph.clone();
|
|
1854
2710
|
nymph.driver.transaction = transaction;
|
|
1855
2711
|
return nymph;
|
|
1856
2712
|
}
|
|
2713
|
+
async removeTilmeldOldRows(etype, connection) {
|
|
2714
|
+
await this.internalTransaction('nymph-remove-tilmeld-rows');
|
|
2715
|
+
try {
|
|
2716
|
+
for (let name of [
|
|
2717
|
+
'user',
|
|
2718
|
+
'group',
|
|
2719
|
+
'acUser',
|
|
2720
|
+
'acGroup',
|
|
2721
|
+
'acOther',
|
|
2722
|
+
'acRead',
|
|
2723
|
+
'acWrite',
|
|
2724
|
+
'acFull',
|
|
2725
|
+
]) {
|
|
2726
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}data_${etype}`)} WHERE "name"=@name;`, {
|
|
2727
|
+
etypes: [etype],
|
|
2728
|
+
params: {
|
|
2729
|
+
name,
|
|
2730
|
+
},
|
|
2731
|
+
connection,
|
|
2732
|
+
});
|
|
2733
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}references_${etype}`)} WHERE "name"=@name;`, {
|
|
2734
|
+
etypes: [etype],
|
|
2735
|
+
params: {
|
|
2736
|
+
name,
|
|
2737
|
+
},
|
|
2738
|
+
connection,
|
|
2739
|
+
});
|
|
2740
|
+
await this.queryRun(`DELETE FROM ${PostgreSQLDriver.escape(`${this.prefix}tokens_${etype}`)} WHERE "name"=@name;`, {
|
|
2741
|
+
etypes: [etype],
|
|
2742
|
+
params: {
|
|
2743
|
+
name,
|
|
2744
|
+
},
|
|
2745
|
+
connection,
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
catch (e) {
|
|
2750
|
+
this.nymph.config.debugError('postgresql', `Remove tilmeld rows error: "${e}"`);
|
|
2751
|
+
await this.rollback('nymph-remove-tilmeld-rows');
|
|
2752
|
+
throw e;
|
|
2753
|
+
}
|
|
2754
|
+
await this.commit('nymph-remove-tilmeld-rows');
|
|
2755
|
+
return true;
|
|
2756
|
+
}
|
|
2757
|
+
async needsMigration() {
|
|
2758
|
+
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;', {
|
|
2759
|
+
params: {
|
|
2760
|
+
db: this.config.database,
|
|
2761
|
+
prefix: this.prefix + 'data_' + '%',
|
|
2762
|
+
},
|
|
2763
|
+
});
|
|
2764
|
+
if (table?.table_name) {
|
|
2765
|
+
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\';', {
|
|
2766
|
+
params: {
|
|
2767
|
+
db: this.config.database,
|
|
2768
|
+
table: table.table_name,
|
|
2769
|
+
},
|
|
2770
|
+
});
|
|
2771
|
+
if (!result?.exists) {
|
|
2772
|
+
return 'json';
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
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;', {
|
|
2776
|
+
params: {
|
|
2777
|
+
db: this.config.database,
|
|
2778
|
+
tokenTable: this.prefix + 'tokens_' + '%',
|
|
2779
|
+
},
|
|
2780
|
+
});
|
|
2781
|
+
if (!table2 || !table2.table_name) {
|
|
2782
|
+
return 'tokens';
|
|
2783
|
+
}
|
|
2784
|
+
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;', {
|
|
2785
|
+
params: {
|
|
2786
|
+
db: this.config.database,
|
|
2787
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
2788
|
+
},
|
|
2789
|
+
});
|
|
2790
|
+
if (table3?.table_name) {
|
|
2791
|
+
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\';', {
|
|
2792
|
+
params: {
|
|
2793
|
+
db: this.config.database,
|
|
2794
|
+
table: table3.table_name,
|
|
2795
|
+
},
|
|
2796
|
+
});
|
|
2797
|
+
if (!result?.exists) {
|
|
2798
|
+
return 'tilmeldColumns';
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
return false;
|
|
2802
|
+
}
|
|
2803
|
+
async liveMigration(migrationType) {
|
|
2804
|
+
if (migrationType === 'tokenTables') {
|
|
2805
|
+
const etypes = await this.getEtypes();
|
|
2806
|
+
const connection = await this.getConnection(true);
|
|
2807
|
+
for (let etype of etypes) {
|
|
2808
|
+
await this.createTokensTable(etype, connection);
|
|
2809
|
+
}
|
|
2810
|
+
connection.done();
|
|
2811
|
+
}
|
|
2812
|
+
else if (migrationType === 'tilmeldColumns') {
|
|
2813
|
+
const etypes = await this.getEtypes();
|
|
2814
|
+
const connection = await this.getConnection(true);
|
|
2815
|
+
for (let etype of etypes) {
|
|
2816
|
+
await this.addTilmeldColumnsAndIndexes(etype, connection);
|
|
2817
|
+
}
|
|
2818
|
+
connection.done();
|
|
2819
|
+
}
|
|
2820
|
+
else if (migrationType === 'tilmeldRemoveOldRows') {
|
|
2821
|
+
const etypes = await this.getEtypes();
|
|
2822
|
+
const connection = await this.getConnection(true);
|
|
2823
|
+
for (let etype of etypes) {
|
|
2824
|
+
await this.removeTilmeldOldRows(etype, connection);
|
|
2825
|
+
}
|
|
2826
|
+
connection.done();
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
1857
2829
|
}
|
|
1858
|
-
exports.default = PostgreSQLDriver;
|
|
1859
2830
|
//# sourceMappingURL=PostgreSQLDriver.js.map
|