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