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