@nymphjs/driver-mysql 1.0.0-beta.12 → 1.0.0-beta.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +563 -0
- package/README.md +1 -1
- package/dist/MySQLDriver.d.ts +107 -24
- package/dist/MySQLDriver.js +1632 -755
- package/dist/MySQLDriver.js.map +1 -1
- package/dist/MySQLDriver.test.js +13 -12
- package/dist/MySQLDriver.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 +66 -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 +31 -23
- package/src/MySQLDriver.test.ts +16 -5
- package/src/MySQLDriver.ts +2283 -1071
- package/src/conf/defaults.ts +1 -1
- package/src/conf/index.ts +67 -2
- package/src/index.ts +2 -2
- package/tsconfig.json +6 -3
- package/typedoc.json +4 -0
- package/dist/conf/d.d.ts +0 -15
- package/dist/conf/d.js +0 -3
- package/dist/conf/d.js.map +0 -1
- package/dist/runMysqlSync.js +0 -44
- package/src/conf/d.ts +0 -67
- package/src/runMysqlSync.js +0 -44
- package/src/testmysql.js +0 -32
package/src/MySQLDriver.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { Pool, PoolOptions, PoolConnection } from 'mysql2';
|
|
2
|
+
import mysql from 'mysql2';
|
|
3
|
+
import SqlString from 'sqlstring';
|
|
4
|
+
import type {
|
|
5
|
+
SearchTerm,
|
|
6
|
+
SearchOrTerm,
|
|
7
|
+
SearchNotTerm,
|
|
8
|
+
SearchSeriesTerm,
|
|
9
|
+
} from '@sciactive/tokenizer';
|
|
6
10
|
import {
|
|
7
11
|
NymphDriver,
|
|
8
|
-
EntityConstructor,
|
|
9
|
-
EntityData,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
type EntityConstructor,
|
|
13
|
+
type EntityData,
|
|
14
|
+
type EntityObjectType,
|
|
15
|
+
type EntityInterface,
|
|
16
|
+
type EntityInstanceType,
|
|
17
|
+
type SerializedEntityData,
|
|
18
|
+
type FormattedSelector,
|
|
19
|
+
type Options,
|
|
20
|
+
type Selector,
|
|
21
|
+
EntityUniqueConstraintError,
|
|
12
22
|
InvalidParametersError,
|
|
13
23
|
NotConfiguredError,
|
|
14
24
|
QueryFailedError,
|
|
15
25
|
UnableToConnectError,
|
|
16
|
-
FormattedSelector,
|
|
17
|
-
Options,
|
|
18
|
-
Selector,
|
|
19
26
|
xor,
|
|
20
27
|
} from '@nymphjs/nymph';
|
|
21
28
|
import { makeTableSuffix } from '@nymphjs/guid';
|
|
@@ -23,29 +30,28 @@ import { makeTableSuffix } from '@nymphjs/guid';
|
|
|
23
30
|
import {
|
|
24
31
|
MySQLDriverConfig,
|
|
25
32
|
MySQLDriverConfigDefaults as defaults,
|
|
26
|
-
} from './conf';
|
|
33
|
+
} from './conf/index.js';
|
|
27
34
|
|
|
28
35
|
type MySQLDriverTransaction = {
|
|
29
|
-
connection:
|
|
36
|
+
connection: PoolConnection | null;
|
|
30
37
|
count: number;
|
|
38
|
+
latestPromise: Promise<any>;
|
|
31
39
|
};
|
|
32
40
|
|
|
33
|
-
const mysql = vlaskyMysql as typeof MySQLType;
|
|
34
|
-
|
|
35
41
|
/**
|
|
36
42
|
* The MySQL Nymph database driver.
|
|
37
43
|
*/
|
|
38
44
|
export default class MySQLDriver extends NymphDriver {
|
|
39
45
|
public config: MySQLDriverConfig;
|
|
40
|
-
private mysqlConfig:
|
|
46
|
+
private mysqlConfig: PoolOptions;
|
|
41
47
|
protected prefix: string;
|
|
42
48
|
protected connected: boolean = false;
|
|
43
49
|
// @ts-ignore: this is assigned in connect(), which is called by the constructor.
|
|
44
|
-
protected link:
|
|
50
|
+
protected link: Pool;
|
|
45
51
|
protected transaction: MySQLDriverTransaction | null = null;
|
|
46
52
|
|
|
47
53
|
static escape(input: string) {
|
|
48
|
-
return
|
|
54
|
+
return SqlString.escapeId(input, true);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
static escapeValue(input: string) {
|
|
@@ -54,8 +60,8 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
54
60
|
|
|
55
61
|
constructor(
|
|
56
62
|
config: Partial<MySQLDriverConfig>,
|
|
57
|
-
link?:
|
|
58
|
-
transaction?: MySQLDriverTransaction
|
|
63
|
+
link?: Pool,
|
|
64
|
+
transaction?: MySQLDriverTransaction,
|
|
59
65
|
) {
|
|
60
66
|
super();
|
|
61
67
|
this.config = { ...defaults, ...config };
|
|
@@ -90,16 +96,16 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
90
96
|
return new MySQLDriver(
|
|
91
97
|
this.config,
|
|
92
98
|
this.link,
|
|
93
|
-
this.transaction ?? undefined
|
|
99
|
+
this.transaction ?? undefined,
|
|
94
100
|
);
|
|
95
101
|
}
|
|
96
102
|
|
|
97
|
-
private getConnection(): Promise<
|
|
103
|
+
private getConnection(): Promise<PoolConnection> {
|
|
98
104
|
if (this.transaction != null && this.transaction.connection != null) {
|
|
99
105
|
return Promise.resolve(this.transaction.connection);
|
|
100
106
|
}
|
|
101
107
|
return new Promise((resolve, reject) =>
|
|
102
|
-
this.link.getConnection((err, con) => (err ? reject(err) : resolve(con)))
|
|
108
|
+
this.link.getConnection((err, con) => (err ? reject(err) : resolve(con))),
|
|
103
109
|
);
|
|
104
110
|
}
|
|
105
111
|
|
|
@@ -112,16 +118,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
112
118
|
// If we think we're connected, try pinging the server.
|
|
113
119
|
try {
|
|
114
120
|
if (this.connected) {
|
|
115
|
-
const connection:
|
|
121
|
+
const connection: PoolConnection = await new Promise(
|
|
116
122
|
(resolve, reject) =>
|
|
117
123
|
this.link.getConnection((err, con) =>
|
|
118
|
-
err ? reject(err) : resolve(con)
|
|
119
|
-
)
|
|
120
|
-
);
|
|
121
|
-
await new Promise((resolve) =>
|
|
122
|
-
connection.ping(() => {
|
|
123
|
-
resolve(0);
|
|
124
|
-
})
|
|
124
|
+
err ? reject(err) : resolve(con),
|
|
125
|
+
),
|
|
125
126
|
);
|
|
126
127
|
connection.release();
|
|
127
128
|
}
|
|
@@ -132,7 +133,10 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
132
133
|
// Connecting, selecting database
|
|
133
134
|
if (!this.connected) {
|
|
134
135
|
try {
|
|
135
|
-
this.link = mysql.createPool(
|
|
136
|
+
this.link = mysql.createPool({
|
|
137
|
+
...this.mysqlConfig,
|
|
138
|
+
charset: 'utf8mb4_bin',
|
|
139
|
+
});
|
|
136
140
|
this.connected = true;
|
|
137
141
|
} catch (e: any) {
|
|
138
142
|
if (
|
|
@@ -142,7 +146,7 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
142
146
|
this.mysqlConfig.database === 'nymph'
|
|
143
147
|
) {
|
|
144
148
|
throw new NotConfiguredError(
|
|
145
|
-
"It seems the config hasn't been set up correctly."
|
|
149
|
+
"It seems the config hasn't been set up correctly.",
|
|
146
150
|
);
|
|
147
151
|
} else {
|
|
148
152
|
throw new UnableToConnectError('Could not connect: ' + e?.message);
|
|
@@ -166,6 +170,9 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
166
170
|
}
|
|
167
171
|
|
|
168
172
|
public async inTransaction() {
|
|
173
|
+
if (this.transaction && this.transaction.count === 0) {
|
|
174
|
+
this.transaction = null;
|
|
175
|
+
}
|
|
169
176
|
return !!this.transaction;
|
|
170
177
|
}
|
|
171
178
|
|
|
@@ -178,106 +185,283 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
178
185
|
return this.connected;
|
|
179
186
|
}
|
|
180
187
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
private createTables(etype: string | null = null) {
|
|
188
|
-
this.queryRunSync('SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";');
|
|
189
|
-
let foreignKeyDataTableGuid = '';
|
|
190
|
-
let foreignKeyDataComparisonsTableGuid = '';
|
|
191
|
-
let foreignKeyReferencesTableGuid = '';
|
|
192
|
-
if (this.config.foreignKeys) {
|
|
193
|
-
foreignKeyDataTableGuid = ` REFERENCES ${MySQLDriver.escape(
|
|
194
|
-
`${this.prefix}entities_${etype}`
|
|
195
|
-
)}(\`guid\`) ON DELETE CASCADE`;
|
|
196
|
-
foreignKeyDataComparisonsTableGuid = ` REFERENCES ${MySQLDriver.escape(
|
|
197
|
-
`${this.prefix}entities_${etype}`
|
|
198
|
-
)}(\`guid\`) ON DELETE CASCADE`;
|
|
199
|
-
foreignKeyReferencesTableGuid = ` REFERENCES ${MySQLDriver.escape(
|
|
200
|
-
`${this.prefix}entities_${etype}`
|
|
201
|
-
)}(\`guid\`) ON DELETE CASCADE`;
|
|
202
|
-
}
|
|
203
|
-
if (etype != null) {
|
|
204
|
-
// Create the entity table.
|
|
205
|
-
this.queryRunSync(
|
|
206
|
-
`CREATE TABLE IF NOT EXISTS ${MySQLDriver.escape(
|
|
207
|
-
`${this.prefix}entities_${etype}`
|
|
208
|
-
)} (
|
|
188
|
+
private async createEntitiesTable(etype: string) {
|
|
189
|
+
// Create the entity table.
|
|
190
|
+
await this.queryRun(
|
|
191
|
+
`CREATE TABLE IF NOT EXISTS ${MySQLDriver.escape(
|
|
192
|
+
`${this.prefix}entities_${etype}`,
|
|
193
|
+
)} (
|
|
209
194
|
\`guid\` BINARY(12) NOT NULL,
|
|
210
195
|
\`tags\` LONGTEXT,
|
|
211
196
|
\`cdate\` DOUBLE PRECISION NOT NULL,
|
|
212
197
|
\`mdate\` DOUBLE PRECISION NOT NULL,
|
|
198
|
+
\`user\` BINARY(12),
|
|
199
|
+
\`group\` BINARY(12),
|
|
200
|
+
\`acUser\` TINYINT UNSIGNED,
|
|
201
|
+
\`acGroup\` TINYINT UNSIGNED,
|
|
202
|
+
\`acOther\` TINYINT UNSIGNED,
|
|
203
|
+
\`acRead\` LONGTEXT,
|
|
204
|
+
\`acWrite\` LONGTEXT,
|
|
205
|
+
\`acFull\` LONGTEXT,
|
|
213
206
|
PRIMARY KEY (\`guid\`),
|
|
207
|
+
INDEX \`id_guid\` USING HASH (\`guid\`),
|
|
214
208
|
INDEX \`id_cdate\` USING BTREE (\`cdate\`),
|
|
215
209
|
INDEX \`id_mdate\` USING BTREE (\`mdate\`),
|
|
216
|
-
FULLTEXT \`id_tags\` (\`tags\`)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
\`guid\` BINARY(12) NOT NULL${foreignKeyDataTableGuid},
|
|
226
|
-
\`name\` TEXT NOT NULL,
|
|
227
|
-
\`value\` LONGTEXT NOT NULL,
|
|
228
|
-
PRIMARY KEY (\`guid\`,\`name\`(255)),
|
|
229
|
-
INDEX \`id_name\` USING HASH (\`name\`(255)),
|
|
230
|
-
INDEX \`id_name_value\` USING BTREE (\`name\`(255), \`value\`(512))
|
|
210
|
+
FULLTEXT \`id_tags\` (\`tags\`),
|
|
211
|
+
INDEX \`id_user_acUser\` USING BTREE (\`user\`, \`acUser\`),
|
|
212
|
+
INDEX \`id_group_acGroup\` USING BTREE (\`group\`, \`acGroup\`),
|
|
213
|
+
INDEX \`id_acUser\` USING BTREE (\`acUser\`),
|
|
214
|
+
INDEX \`id_acGroup\` USING BTREE (\`acGroup\`),
|
|
215
|
+
INDEX \`id_acOther\` USING BTREE (\`acOther\`),
|
|
216
|
+
FULLTEXT \`id_acRead\` (\`acRead\`),
|
|
217
|
+
FULLTEXT \`id_acWrite\` (\`acWrite\`),
|
|
218
|
+
FULLTEXT \`id_acFull\` (\`acFull\`)
|
|
231
219
|
) ENGINE ${this.config.engine}
|
|
232
|
-
CHARACTER SET
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
220
|
+
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`,
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private async addTilmeldColumnsAndIndexes(etype: string) {
|
|
225
|
+
await this.queryRun(
|
|
226
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
227
|
+
`${this.prefix}entities_${etype}`,
|
|
228
|
+
)} ADD COLUMN \`user\` BINARY(12);`,
|
|
229
|
+
);
|
|
230
|
+
await this.queryRun(
|
|
231
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
232
|
+
`${this.prefix}entities_${etype}`,
|
|
233
|
+
)} ADD COLUMN \`group\` BINARY(12);`,
|
|
234
|
+
);
|
|
235
|
+
await this.queryRun(
|
|
236
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
237
|
+
`${this.prefix}entities_${etype}`,
|
|
238
|
+
)} ADD COLUMN \`acUser\` TINYINT UNSIGNED;`,
|
|
239
|
+
);
|
|
240
|
+
await this.queryRun(
|
|
241
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
242
|
+
`${this.prefix}entities_${etype}`,
|
|
243
|
+
)} ADD COLUMN \`acGroup\` TINYINT UNSIGNED;`,
|
|
244
|
+
);
|
|
245
|
+
await this.queryRun(
|
|
246
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
247
|
+
`${this.prefix}entities_${etype}`,
|
|
248
|
+
)} ADD COLUMN \`acOther\` TINYINT UNSIGNED;`,
|
|
249
|
+
);
|
|
250
|
+
await this.queryRun(
|
|
251
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
252
|
+
`${this.prefix}entities_${etype}`,
|
|
253
|
+
)} ADD COLUMN \`acRead\` LONGTEXT;`,
|
|
254
|
+
);
|
|
255
|
+
await this.queryRun(
|
|
256
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
257
|
+
`${this.prefix}entities_${etype}`,
|
|
258
|
+
)} ADD COLUMN \`acWrite\` LONGTEXT;`,
|
|
259
|
+
);
|
|
260
|
+
await this.queryRun(
|
|
261
|
+
`ALTER TABLE ${MySQLDriver.escape(
|
|
262
|
+
`${this.prefix}entities_${etype}`,
|
|
263
|
+
)} ADD COLUMN \`acFull\` LONGTEXT;`,
|
|
264
|
+
);
|
|
265
|
+
await this.createEntitiesTilmeldIndexes(etype);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private async createEntitiesTilmeldIndexes(etype: string) {
|
|
269
|
+
await this.queryRun(
|
|
270
|
+
`CREATE INDEX \`id_user_acUser\` ON ${MySQLDriver.escape(
|
|
271
|
+
`${this.prefix}entities_${etype}`,
|
|
272
|
+
)} (\`user\`, \`acUser\`) USING BTREE;`,
|
|
273
|
+
);
|
|
274
|
+
await this.queryRun(
|
|
275
|
+
`CREATE INDEX \`id_group_acGroup\` ON ${MySQLDriver.escape(
|
|
276
|
+
`${this.prefix}entities_${etype}`,
|
|
277
|
+
)} (\`group\`, \`acGroup\`) USING BTREE;`,
|
|
278
|
+
);
|
|
279
|
+
await this.queryRun(
|
|
280
|
+
`CREATE INDEX \`id_acUser\` ON ${MySQLDriver.escape(
|
|
281
|
+
`${this.prefix}entities_${etype}`,
|
|
282
|
+
)} (\`acUser\`) USING BTREE;`,
|
|
283
|
+
);
|
|
284
|
+
await this.queryRun(
|
|
285
|
+
`CREATE INDEX \`id_acGroup\` ON ${MySQLDriver.escape(
|
|
286
|
+
`${this.prefix}entities_${etype}`,
|
|
287
|
+
)} (\`acGroup\`) USING BTREE;`,
|
|
288
|
+
);
|
|
289
|
+
await this.queryRun(
|
|
290
|
+
`CREATE INDEX \`id_acOther\` ON ${MySQLDriver.escape(
|
|
291
|
+
`${this.prefix}entities_${etype}`,
|
|
292
|
+
)} (\`acOther\`) USING BTREE;`,
|
|
293
|
+
);
|
|
294
|
+
await this.queryRun(
|
|
295
|
+
`CREATE FULLTEXT INDEX \`id_acRead\` ON ${MySQLDriver.escape(
|
|
296
|
+
`${this.prefix}entities_${etype}`,
|
|
297
|
+
)} (\`acRead\`);`,
|
|
298
|
+
);
|
|
299
|
+
await this.queryRun(
|
|
300
|
+
`CREATE FULLTEXT INDEX \`id_acWrite\` ON ${MySQLDriver.escape(
|
|
301
|
+
`${this.prefix}entities_${etype}`,
|
|
302
|
+
)} (\`acWrite\`);`,
|
|
303
|
+
);
|
|
304
|
+
await this.queryRun(
|
|
305
|
+
`CREATE FULLTEXT INDEX \`id_acFull\` ON ${MySQLDriver.escape(
|
|
306
|
+
`${this.prefix}entities_${etype}`,
|
|
307
|
+
)} (\`acFull\`);`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private async createDataTable(etype: string) {
|
|
312
|
+
let foreignKey = '';
|
|
313
|
+
if (this.config.foreignKeys) {
|
|
314
|
+
foreignKey = ` REFERENCES ${MySQLDriver.escape(
|
|
315
|
+
`${this.prefix}entities_${etype}`,
|
|
316
|
+
)}(\`guid\`) ON DELETE CASCADE`;
|
|
317
|
+
}
|
|
318
|
+
// Create the data table.
|
|
319
|
+
await this.queryRun(
|
|
320
|
+
`CREATE TABLE IF NOT EXISTS ${MySQLDriver.escape(
|
|
321
|
+
`${this.prefix}data_${etype}`,
|
|
322
|
+
)} (
|
|
323
|
+
\`guid\` BINARY(12) NOT NULL${foreignKey},
|
|
240
324
|
\`name\` TEXT NOT NULL,
|
|
241
|
-
\`
|
|
325
|
+
\`value\` CHAR(1) NOT NULL,
|
|
326
|
+
\`json\` JSON,
|
|
242
327
|
\`string\` LONGTEXT,
|
|
243
328
|
\`number\` DOUBLE,
|
|
329
|
+
\`truthy\` BOOLEAN,
|
|
244
330
|
PRIMARY KEY (\`guid\`, \`name\`(255)),
|
|
245
|
-
INDEX \`
|
|
331
|
+
INDEX \`id_guid\` USING HASH (\`guid\`),
|
|
332
|
+
INDEX \`id_guid_name\` USING HASH (\`guid\`, \`name\`(255)),
|
|
333
|
+
INDEX \`id_name\` USING BTREE (\`name\`(255)),
|
|
334
|
+
INDEX \`id_name_json\` USING BTREE (\`name\`(255), (CAST(\`json\` AS CHAR(512)) COLLATE utf8mb4_bin)),
|
|
335
|
+
INDEX \`id_name_string\` USING BTREE (\`name\`(255), \`string\`(512)),
|
|
336
|
+
INDEX \`id_name_number\` USING BTREE (\`name\`(255), \`number\`),
|
|
337
|
+
INDEX \`id_guid_name_number\` USING BTREE (\`guid\`, \`name\`(255), \`number\`),
|
|
338
|
+
INDEX \`id_name_truthy\` USING HASH (\`name\`(255), \`truthy\`),
|
|
339
|
+
INDEX \`id_guid_name_truthy\` USING HASH (\`guid\`, \`name\`(255), \`truthy\`)
|
|
246
340
|
) ENGINE ${this.config.engine}
|
|
247
|
-
CHARACTER SET
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
341
|
+
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`,
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private async createReferencesTable(etype: string) {
|
|
346
|
+
let foreignKey = '';
|
|
347
|
+
if (this.config.foreignKeys) {
|
|
348
|
+
foreignKey = ` REFERENCES ${MySQLDriver.escape(
|
|
349
|
+
`${this.prefix}entities_${etype}`,
|
|
350
|
+
)}(\`guid\`) ON DELETE CASCADE`;
|
|
351
|
+
}
|
|
352
|
+
// Create the references table.
|
|
353
|
+
await this.queryRun(
|
|
354
|
+
`CREATE TABLE IF NOT EXISTS ${MySQLDriver.escape(
|
|
355
|
+
`${this.prefix}references_${etype}`,
|
|
356
|
+
)} (
|
|
357
|
+
\`guid\` BINARY(12) NOT NULL${foreignKey},
|
|
255
358
|
\`name\` TEXT NOT NULL,
|
|
256
359
|
\`reference\` BINARY(12) NOT NULL,
|
|
257
360
|
PRIMARY KEY (\`guid\`, \`name\`(255), \`reference\`),
|
|
258
|
-
INDEX \`
|
|
361
|
+
INDEX \`id_guid\` USING HASH (\`guid\`),
|
|
362
|
+
INDEX \`id_name\` USING HASH (\`name\`(255)),
|
|
363
|
+
INDEX \`id_name_reference\` USING HASH (\`name\`(255), \`reference\`),
|
|
364
|
+
INDEX \`id_reference\` USING HASH (\`reference\`),
|
|
365
|
+
INDEX \`id_guid_name\` USING HASH (\`guid\`, \`name\`(255)),
|
|
366
|
+
INDEX \`id_guid_name_reference\` USING HASH (\`guid\`, \`name\`(255), \`reference\`),
|
|
367
|
+
INDEX \`id_reference_name_guid\` USING BTREE (\`reference\`, \`name\`(255), \`guid\`),
|
|
368
|
+
INDEX \`id_reference_guid_name\` USING BTREE (\`reference\`, \`guid\`, \`name\`(255))
|
|
259
369
|
) ENGINE ${this.config.engine}
|
|
260
|
-
CHARACTER SET
|
|
261
|
-
|
|
370
|
+
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`,
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
private async createTokensTable(etype: string) {
|
|
375
|
+
let foreignKey = '';
|
|
376
|
+
if (this.config.foreignKeys) {
|
|
377
|
+
foreignKey = ` REFERENCES ${MySQLDriver.escape(
|
|
378
|
+
`${this.prefix}entities_${etype}`,
|
|
379
|
+
)}(\`guid\`) ON DELETE CASCADE`;
|
|
380
|
+
}
|
|
381
|
+
// Create the tokens table.
|
|
382
|
+
await this.queryRun(
|
|
383
|
+
`CREATE TABLE IF NOT EXISTS ${MySQLDriver.escape(
|
|
384
|
+
`${this.prefix}tokens_${etype}`,
|
|
385
|
+
)} (
|
|
386
|
+
\`guid\` BINARY(12) NOT NULL${foreignKey},
|
|
387
|
+
\`name\` TEXT NOT NULL,
|
|
388
|
+
\`token\` INT NOT NULL,
|
|
389
|
+
\`position\` INT UNSIGNED NOT NULL,
|
|
390
|
+
\`stem\` BOOLEAN NOT NULL,
|
|
391
|
+
PRIMARY KEY (\`guid\`, \`name\`(255), \`token\`, \`position\`),
|
|
392
|
+
INDEX \`id_guid_name_token_position\` USING HASH (\`guid\`, \`name\`(255), \`token\`, \`position\`),
|
|
393
|
+
INDEX \`id_name_token\` USING btree (\`name\`(255), \`token\`)
|
|
394
|
+
) ENGINE ${this.config.engine}
|
|
395
|
+
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`,
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private async createUniquesTable(etype: string) {
|
|
400
|
+
let foreignKey = '';
|
|
401
|
+
if (this.config.foreignKeys) {
|
|
402
|
+
foreignKey = ` REFERENCES ${MySQLDriver.escape(
|
|
403
|
+
`${this.prefix}entities_${etype}`,
|
|
404
|
+
)}(\`guid\`) ON DELETE CASCADE`;
|
|
405
|
+
}
|
|
406
|
+
// Create the unique strings table.
|
|
407
|
+
// 255 key is max length for charset UTF8.
|
|
408
|
+
await this.queryRun(
|
|
409
|
+
`CREATE TABLE IF NOT EXISTS ${MySQLDriver.escape(
|
|
410
|
+
`${this.prefix}uniques_${etype}`,
|
|
411
|
+
)} (
|
|
412
|
+
\`guid\` BINARY(12) NOT NULL${foreignKey},
|
|
413
|
+
\`unique\` TEXT NOT NULL,
|
|
414
|
+
PRIMARY KEY (\`guid\`, \`unique\`(255)),
|
|
415
|
+
INDEX \`id_guid\` USING HASH (\`guid\`),
|
|
416
|
+
CONSTRAINT \`uc_unique\` UNIQUE (\`unique\`(255))
|
|
417
|
+
) ENGINE ${this.config.engine}
|
|
418
|
+
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`,
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Create entity tables in the database.
|
|
424
|
+
*
|
|
425
|
+
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
426
|
+
* @returns True on success, false on failure.
|
|
427
|
+
*/
|
|
428
|
+
private async createTables(etype: string | null = null) {
|
|
429
|
+
await this.queryRun('SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";');
|
|
430
|
+
if (etype != null) {
|
|
431
|
+
await this.createEntitiesTable(etype);
|
|
432
|
+
await this.createDataTable(etype);
|
|
433
|
+
await this.createReferencesTable(etype);
|
|
434
|
+
await this.createTokensTable(etype);
|
|
435
|
+
await this.createUniquesTable(etype);
|
|
262
436
|
} else {
|
|
263
437
|
// Create the UID table.
|
|
264
|
-
this.
|
|
438
|
+
await this.queryRun(
|
|
265
439
|
`CREATE TABLE IF NOT EXISTS ${MySQLDriver.escape(
|
|
266
|
-
`${this.prefix}uids
|
|
440
|
+
`${this.prefix}uids`,
|
|
267
441
|
)} (
|
|
268
442
|
\`name\` TEXT NOT NULL,
|
|
269
443
|
\`cur_uid\` BIGINT(20) UNSIGNED NOT NULL,
|
|
270
444
|
PRIMARY KEY (\`name\`(100))
|
|
271
445
|
) ENGINE ${this.config.engine}
|
|
272
|
-
CHARACTER SET
|
|
446
|
+
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;`,
|
|
273
447
|
);
|
|
274
448
|
}
|
|
449
|
+
if (
|
|
450
|
+
this.transaction &&
|
|
451
|
+
this.transaction.count > 0 &&
|
|
452
|
+
this.config.transactions
|
|
453
|
+
) {
|
|
454
|
+
this.transaction.count = 0;
|
|
455
|
+
this.transaction.connection?.release();
|
|
456
|
+
this.transaction.connection = null;
|
|
457
|
+
this.transaction = null;
|
|
458
|
+
}
|
|
275
459
|
return true;
|
|
276
460
|
}
|
|
277
461
|
|
|
278
462
|
private translateQuery(
|
|
279
463
|
origQuery: string,
|
|
280
|
-
origParams: { [k: string]: any }
|
|
464
|
+
origParams: { [k: string]: any },
|
|
281
465
|
): { query: string; params: any[] } {
|
|
282
466
|
const params: any[] = [];
|
|
283
467
|
let query = origQuery;
|
|
@@ -296,32 +480,37 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
296
480
|
private async query<T extends () => any>(
|
|
297
481
|
runQuery: T,
|
|
298
482
|
query: string,
|
|
299
|
-
etypes: string[] = []
|
|
483
|
+
etypes: string[] = [],
|
|
300
484
|
// @ts-ignore: The return type of T is a promise.
|
|
301
485
|
): ReturnType<T> {
|
|
302
486
|
try {
|
|
487
|
+
this.nymph.config.debugInfo('mysql:query', query);
|
|
303
488
|
return await runQuery();
|
|
304
489
|
} catch (e: any) {
|
|
305
490
|
const errorCode = e?.errno;
|
|
306
|
-
if (errorCode === 1146 && this.createTables()) {
|
|
491
|
+
if (errorCode === 1146 && (await this.createTables())) {
|
|
307
492
|
// If the tables don't exist yet, create them.
|
|
308
493
|
for (let etype of etypes) {
|
|
309
|
-
this.createTables(etype);
|
|
494
|
+
await this.createTables(etype);
|
|
310
495
|
}
|
|
311
496
|
try {
|
|
312
497
|
return await runQuery();
|
|
313
498
|
} catch (e2: any) {
|
|
314
499
|
throw new QueryFailedError(
|
|
315
500
|
'Query failed: ' + e2?.errno + ' - ' + e2?.message,
|
|
316
|
-
query
|
|
501
|
+
query,
|
|
502
|
+
e2?.errno,
|
|
317
503
|
);
|
|
318
504
|
}
|
|
505
|
+
} else if (errorCode === 1062) {
|
|
506
|
+
throw new EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
319
507
|
} else if (errorCode === 2006) {
|
|
320
508
|
// If the MySQL server disconnected, reconnect to it.
|
|
321
|
-
if (!this.connect()) {
|
|
509
|
+
if (!(await this.connect())) {
|
|
322
510
|
throw new QueryFailedError(
|
|
323
511
|
'Query failed: ' + e?.errno + ' - ' + e?.message,
|
|
324
|
-
query
|
|
512
|
+
query,
|
|
513
|
+
e?.errno,
|
|
325
514
|
);
|
|
326
515
|
}
|
|
327
516
|
try {
|
|
@@ -329,60 +518,21 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
329
518
|
} catch (e2: any) {
|
|
330
519
|
throw new QueryFailedError(
|
|
331
520
|
'Query failed: ' + e2?.errno + ' - ' + e2?.message,
|
|
332
|
-
query
|
|
333
|
-
|
|
334
|
-
}
|
|
335
|
-
} else {
|
|
336
|
-
throw e;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
private querySync<T extends () => any>(
|
|
342
|
-
runQuery: T,
|
|
343
|
-
query: string,
|
|
344
|
-
etypes: string[] = []
|
|
345
|
-
): ReturnType<T> {
|
|
346
|
-
try {
|
|
347
|
-
return runQuery();
|
|
348
|
-
} catch (e: any) {
|
|
349
|
-
const errorCode = e?.errno;
|
|
350
|
-
if (errorCode === 1146 && this.createTables()) {
|
|
351
|
-
// If the tables don't exist yet, create them.
|
|
352
|
-
for (let etype of etypes) {
|
|
353
|
-
this.createTables(etype);
|
|
354
|
-
}
|
|
355
|
-
try {
|
|
356
|
-
return runQuery();
|
|
357
|
-
} catch (e2: any) {
|
|
358
|
-
throw new QueryFailedError(
|
|
359
|
-
'Query failed: ' + e2?.errno + ' - ' + e2?.message,
|
|
360
|
-
query
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
} else if (errorCode === 2006) {
|
|
364
|
-
// If the MySQL server disconnected, reconnect to it.
|
|
365
|
-
if (!this.connect()) {
|
|
366
|
-
throw new QueryFailedError(
|
|
367
|
-
'Query failed: ' + e?.errno + ' - ' + e?.message,
|
|
368
|
-
query
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
try {
|
|
372
|
-
return runQuery();
|
|
373
|
-
} catch (e2: any) {
|
|
374
|
-
throw new QueryFailedError(
|
|
375
|
-
'Query failed: ' + e2?.errno + ' - ' + e2?.message,
|
|
376
|
-
query
|
|
521
|
+
query,
|
|
522
|
+
e2?.errno,
|
|
377
523
|
);
|
|
378
524
|
}
|
|
379
525
|
} else {
|
|
380
|
-
throw
|
|
526
|
+
throw new QueryFailedError(
|
|
527
|
+
'Query failed: ' + e?.errno + ' - ' + e?.message,
|
|
528
|
+
query,
|
|
529
|
+
e?.errno,
|
|
530
|
+
);
|
|
381
531
|
}
|
|
382
532
|
}
|
|
383
533
|
}
|
|
384
534
|
|
|
385
|
-
private
|
|
535
|
+
private queryArray(
|
|
386
536
|
query: string,
|
|
387
537
|
{
|
|
388
538
|
etypes = [],
|
|
@@ -390,82 +540,42 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
390
540
|
}: {
|
|
391
541
|
etypes?: string[];
|
|
392
542
|
params?: { [k: string]: any };
|
|
393
|
-
} = {}
|
|
543
|
+
} = {},
|
|
394
544
|
) {
|
|
395
545
|
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
396
546
|
query,
|
|
397
|
-
params
|
|
547
|
+
params,
|
|
398
548
|
);
|
|
399
549
|
return this.query(
|
|
400
550
|
async () => {
|
|
401
|
-
const results: any = await new Promise((resolve, reject) => {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
551
|
+
const results: any = await new Promise(async (resolve, reject) => {
|
|
552
|
+
// Only do one query at a time in transations.
|
|
553
|
+
let myResolve: (value: void | PromiseLike<void>) => void = () => {};
|
|
554
|
+
const myPromise = new Promise<void>((resolve) => {
|
|
555
|
+
myResolve = resolve;
|
|
556
|
+
});
|
|
557
|
+
if (this.transaction) {
|
|
558
|
+
const latestPromise = this.transaction.latestPromise;
|
|
559
|
+
this.transaction.latestPromise = myPromise;
|
|
560
|
+
await latestPromise;
|
|
561
|
+
}
|
|
562
|
+
(this.transaction?.connection ?? this.link).query(
|
|
563
|
+
newQuery,
|
|
564
|
+
newParams,
|
|
565
|
+
(error, results) => {
|
|
566
|
+
if (error) {
|
|
567
|
+
reject(error);
|
|
568
|
+
} else {
|
|
410
569
|
resolve(results);
|
|
411
570
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
571
|
+
myResolve();
|
|
572
|
+
},
|
|
573
|
+
);
|
|
416
574
|
});
|
|
417
575
|
return results;
|
|
418
576
|
},
|
|
419
577
|
`${query} -- ${JSON.stringify(params)}`,
|
|
420
|
-
etypes
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
private queryIterSync(
|
|
425
|
-
query: string,
|
|
426
|
-
{
|
|
427
|
-
etypes = [],
|
|
428
|
-
params = {},
|
|
429
|
-
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
430
|
-
) {
|
|
431
|
-
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
432
|
-
query,
|
|
433
|
-
params
|
|
434
|
-
);
|
|
435
|
-
return this.querySync(
|
|
436
|
-
() => {
|
|
437
|
-
const output = cp.spawnSync(
|
|
438
|
-
process.argv0,
|
|
439
|
-
[__dirname + '/runMysqlSync.js'],
|
|
440
|
-
{
|
|
441
|
-
input: JSON.stringify({
|
|
442
|
-
mysqlConfig: this.mysqlConfig,
|
|
443
|
-
query: newQuery,
|
|
444
|
-
params: newParams,
|
|
445
|
-
}),
|
|
446
|
-
timeout: 30000,
|
|
447
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
448
|
-
encoding: 'utf8',
|
|
449
|
-
windowsHide: true,
|
|
450
|
-
}
|
|
451
|
-
);
|
|
452
|
-
try {
|
|
453
|
-
const { results } = JSON.parse(output.stdout);
|
|
454
|
-
return results;
|
|
455
|
-
} catch (e) {
|
|
456
|
-
// Do nothing.
|
|
457
|
-
}
|
|
458
|
-
if (output.status === 0) {
|
|
459
|
-
throw new Error('Unknown parse error.');
|
|
460
|
-
}
|
|
461
|
-
const err = JSON.parse(output.stderr);
|
|
462
|
-
const e = new Error(err.name);
|
|
463
|
-
for (const name in err) {
|
|
464
|
-
(e as any)[name] = err[name];
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
`${query} -- ${JSON.stringify(params)}`,
|
|
468
|
-
etypes
|
|
578
|
+
etypes,
|
|
469
579
|
);
|
|
470
580
|
}
|
|
471
581
|
|
|
@@ -477,34 +587,42 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
477
587
|
}: {
|
|
478
588
|
etypes?: string[];
|
|
479
589
|
params?: { [k: string]: any };
|
|
480
|
-
} = {}
|
|
590
|
+
} = {},
|
|
481
591
|
) {
|
|
482
592
|
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
483
593
|
query,
|
|
484
|
-
params
|
|
594
|
+
params,
|
|
485
595
|
);
|
|
486
596
|
return this.query(
|
|
487
597
|
async () => {
|
|
488
|
-
const results: any = await new Promise((resolve, reject) => {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
598
|
+
const results: any = await new Promise(async (resolve, reject) => {
|
|
599
|
+
// Only do one query at a time in transations.
|
|
600
|
+
let myResolve: (value: void | PromiseLike<void>) => void = () => {};
|
|
601
|
+
const myPromise = new Promise<void>((resolve) => {
|
|
602
|
+
myResolve = resolve;
|
|
603
|
+
});
|
|
604
|
+
if (this.transaction) {
|
|
605
|
+
const latestPromise = this.transaction.latestPromise;
|
|
606
|
+
this.transaction.latestPromise = myPromise;
|
|
607
|
+
await latestPromise;
|
|
608
|
+
}
|
|
609
|
+
(this.transaction?.connection ?? this.link).query(
|
|
610
|
+
newQuery,
|
|
611
|
+
newParams,
|
|
612
|
+
(error, results) => {
|
|
613
|
+
if (error) {
|
|
614
|
+
reject(error);
|
|
615
|
+
} else {
|
|
497
616
|
resolve(results);
|
|
498
617
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
618
|
+
myResolve();
|
|
619
|
+
},
|
|
620
|
+
);
|
|
503
621
|
});
|
|
504
622
|
return results[0];
|
|
505
623
|
},
|
|
506
624
|
`${query} -- ${JSON.stringify(params)}`,
|
|
507
|
-
etypes
|
|
625
|
+
etypes,
|
|
508
626
|
);
|
|
509
627
|
}
|
|
510
628
|
|
|
@@ -513,92 +631,55 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
513
631
|
{
|
|
514
632
|
etypes = [],
|
|
515
633
|
params = {},
|
|
634
|
+
transaction,
|
|
516
635
|
}: {
|
|
517
636
|
etypes?: string[];
|
|
518
637
|
params?: { [k: string]: any };
|
|
519
|
-
|
|
638
|
+
transaction?: MySQLDriverTransaction;
|
|
639
|
+
} = {},
|
|
520
640
|
) {
|
|
521
641
|
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
522
642
|
query,
|
|
523
|
-
params
|
|
643
|
+
params,
|
|
524
644
|
);
|
|
525
645
|
return this.query(
|
|
526
646
|
async () => {
|
|
527
|
-
const results: any = await new Promise((resolve, reject) => {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
647
|
+
const results: any = await new Promise(async (resolve, reject) => {
|
|
648
|
+
// Only do one query at a time in transations.
|
|
649
|
+
let myResolve: (value: void | PromiseLike<void>) => void = () => {};
|
|
650
|
+
const myPromise = new Promise<void>((resolve) => {
|
|
651
|
+
myResolve = resolve;
|
|
652
|
+
});
|
|
653
|
+
const trans = transaction ?? this.transaction;
|
|
654
|
+
if (trans) {
|
|
655
|
+
const latestPromise = trans.latestPromise;
|
|
656
|
+
trans.latestPromise = myPromise;
|
|
657
|
+
await latestPromise;
|
|
658
|
+
}
|
|
659
|
+
(trans?.connection ?? this.link).query(
|
|
660
|
+
newQuery,
|
|
661
|
+
newParams,
|
|
662
|
+
(error, results) => {
|
|
663
|
+
if (error) {
|
|
664
|
+
reject(error);
|
|
665
|
+
} else {
|
|
536
666
|
resolve(results);
|
|
537
667
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
668
|
+
myResolve();
|
|
669
|
+
},
|
|
670
|
+
);
|
|
542
671
|
});
|
|
543
672
|
return { changes: results.changedRows ?? 0 };
|
|
544
673
|
},
|
|
545
674
|
`${query} -- ${JSON.stringify(params)}`,
|
|
546
|
-
etypes
|
|
547
|
-
);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
private queryRunSync(
|
|
551
|
-
query: string,
|
|
552
|
-
{
|
|
553
|
-
etypes = [],
|
|
554
|
-
params = {},
|
|
555
|
-
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
556
|
-
) {
|
|
557
|
-
const { query: newQuery, params: newParams } = this.translateQuery(
|
|
558
|
-
query,
|
|
559
|
-
params
|
|
560
|
-
);
|
|
561
|
-
return this.querySync(
|
|
562
|
-
() => {
|
|
563
|
-
const output = cp.spawnSync(
|
|
564
|
-
process.argv0,
|
|
565
|
-
[__dirname + '/runMysqlSync.js'],
|
|
566
|
-
{
|
|
567
|
-
input: JSON.stringify({
|
|
568
|
-
mysqlConfig: this.mysqlConfig,
|
|
569
|
-
query: newQuery,
|
|
570
|
-
params: newParams,
|
|
571
|
-
}),
|
|
572
|
-
timeout: 30000,
|
|
573
|
-
maxBuffer: 100 * 1024 * 1024,
|
|
574
|
-
encoding: 'utf8',
|
|
575
|
-
windowsHide: true,
|
|
576
|
-
}
|
|
577
|
-
);
|
|
578
|
-
try {
|
|
579
|
-
const { results } = JSON.parse(output.stdout);
|
|
580
|
-
return { changes: results.changedRows ?? 0 };
|
|
581
|
-
} catch (e) {
|
|
582
|
-
// Do nothing.
|
|
583
|
-
}
|
|
584
|
-
if (output.status === 0) {
|
|
585
|
-
throw new Error('Unknown parse error.');
|
|
586
|
-
}
|
|
587
|
-
const err = JSON.parse(output.stderr);
|
|
588
|
-
const e = new Error(err.name);
|
|
589
|
-
for (const name in err) {
|
|
590
|
-
(e as any)[name] = err[name];
|
|
591
|
-
}
|
|
592
|
-
},
|
|
593
|
-
`${query} -- ${JSON.stringify(params)}`,
|
|
594
|
-
etypes
|
|
675
|
+
etypes,
|
|
595
676
|
);
|
|
596
677
|
}
|
|
597
678
|
|
|
598
679
|
public async commit(name: string) {
|
|
599
680
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
600
681
|
throw new InvalidParametersError(
|
|
601
|
-
'Transaction commit attempted without a name.'
|
|
682
|
+
'Transaction commit attempted without a name.',
|
|
602
683
|
);
|
|
603
684
|
}
|
|
604
685
|
if (!this.transaction || this.transaction.count === 0) {
|
|
@@ -620,7 +701,7 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
620
701
|
|
|
621
702
|
public async deleteEntityByID(
|
|
622
703
|
guid: string,
|
|
623
|
-
className?: EntityConstructor | string | null
|
|
704
|
+
className?: EntityConstructor | string | null,
|
|
624
705
|
) {
|
|
625
706
|
let EntityClass: EntityConstructor;
|
|
626
707
|
if (typeof className === 'string' || className == null) {
|
|
@@ -634,58 +715,71 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
634
715
|
try {
|
|
635
716
|
await this.queryRun(
|
|
636
717
|
`DELETE FROM ${MySQLDriver.escape(
|
|
637
|
-
`${this.prefix}entities_${etype}
|
|
718
|
+
`${this.prefix}entities_${etype}`,
|
|
638
719
|
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
639
720
|
{
|
|
640
721
|
etypes: [etype],
|
|
641
722
|
params: {
|
|
642
723
|
guid,
|
|
643
724
|
},
|
|
644
|
-
}
|
|
725
|
+
},
|
|
645
726
|
);
|
|
646
727
|
await this.queryRun(
|
|
647
728
|
`DELETE FROM ${MySQLDriver.escape(
|
|
648
|
-
`${this.prefix}data_${etype}
|
|
729
|
+
`${this.prefix}data_${etype}`,
|
|
649
730
|
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
650
731
|
{
|
|
651
732
|
etypes: [etype],
|
|
652
733
|
params: {
|
|
653
734
|
guid,
|
|
654
735
|
},
|
|
655
|
-
}
|
|
736
|
+
},
|
|
656
737
|
);
|
|
657
738
|
await this.queryRun(
|
|
658
739
|
`DELETE FROM ${MySQLDriver.escape(
|
|
659
|
-
`${this.prefix}
|
|
740
|
+
`${this.prefix}references_${etype}`,
|
|
660
741
|
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
661
742
|
{
|
|
662
743
|
etypes: [etype],
|
|
663
744
|
params: {
|
|
664
745
|
guid,
|
|
665
746
|
},
|
|
666
|
-
}
|
|
747
|
+
},
|
|
667
748
|
);
|
|
668
749
|
await this.queryRun(
|
|
669
750
|
`DELETE FROM ${MySQLDriver.escape(
|
|
670
|
-
`${this.prefix}
|
|
751
|
+
`${this.prefix}tokens_${etype}`,
|
|
671
752
|
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
672
753
|
{
|
|
673
754
|
etypes: [etype],
|
|
674
755
|
params: {
|
|
675
756
|
guid,
|
|
676
757
|
},
|
|
677
|
-
}
|
|
758
|
+
},
|
|
759
|
+
);
|
|
760
|
+
await this.queryRun(
|
|
761
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
762
|
+
`${this.prefix}uniques_${etype}`,
|
|
763
|
+
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
764
|
+
{
|
|
765
|
+
etypes: [etype],
|
|
766
|
+
params: {
|
|
767
|
+
guid,
|
|
768
|
+
},
|
|
769
|
+
},
|
|
678
770
|
);
|
|
679
|
-
await this.commit('nymph-delete');
|
|
680
|
-
// Remove any cached versions of this entity.
|
|
681
|
-
if (this.nymph.config.cache) {
|
|
682
|
-
this.cleanCache(guid);
|
|
683
|
-
}
|
|
684
|
-
return true;
|
|
685
771
|
} catch (e: any) {
|
|
772
|
+
this.nymph.config.debugError('mysql', `Delete entity error: "${e}"`);
|
|
686
773
|
await this.rollback('nymph-delete');
|
|
687
774
|
throw e;
|
|
688
775
|
}
|
|
776
|
+
|
|
777
|
+
await this.commit('nymph-delete');
|
|
778
|
+
// Remove any cached versions of this entity.
|
|
779
|
+
if (this.nymph.config.cache) {
|
|
780
|
+
this.cleanCache(guid);
|
|
781
|
+
}
|
|
782
|
+
return true;
|
|
689
783
|
}
|
|
690
784
|
|
|
691
785
|
public async deleteUID(name: string) {
|
|
@@ -694,101 +788,341 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
694
788
|
}
|
|
695
789
|
await this.queryRun(
|
|
696
790
|
`DELETE FROM ${MySQLDriver.escape(
|
|
697
|
-
`${this.prefix}uids
|
|
791
|
+
`${this.prefix}uids`,
|
|
698
792
|
)} WHERE \`name\`=@name;`,
|
|
699
793
|
{
|
|
700
794
|
params: {
|
|
701
795
|
name,
|
|
702
796
|
},
|
|
703
|
-
}
|
|
797
|
+
},
|
|
704
798
|
);
|
|
705
799
|
return true;
|
|
706
800
|
}
|
|
707
801
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
writeLine('');
|
|
715
|
-
|
|
716
|
-
writeLine('#');
|
|
717
|
-
writeLine('# UIDs');
|
|
718
|
-
writeLine('#');
|
|
719
|
-
writeLine('');
|
|
720
|
-
|
|
721
|
-
// Export UIDs.
|
|
722
|
-
let uids = await this.queryIter(
|
|
723
|
-
`SELECT * FROM ${MySQLDriver.escape(
|
|
724
|
-
`${this.prefix}uids`
|
|
725
|
-
)} ORDER BY \`name\`;`
|
|
726
|
-
);
|
|
727
|
-
for (const uid of uids) {
|
|
728
|
-
writeLine(`<${uid.name}>[${uid.cur_uid}]`);
|
|
729
|
-
}
|
|
802
|
+
public async getIndexes(etype: string) {
|
|
803
|
+
const indexes: {
|
|
804
|
+
scope: 'data' | 'references' | 'tokens';
|
|
805
|
+
name: string;
|
|
806
|
+
property: string;
|
|
807
|
+
}[] = [];
|
|
730
808
|
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
809
|
+
for (let [scope, suffix] of [
|
|
810
|
+
['data', '_json'],
|
|
811
|
+
['references', '_reference_guid'],
|
|
812
|
+
['tokens', '_token_position_stem'],
|
|
813
|
+
] as (
|
|
814
|
+
| ['data', '_json']
|
|
815
|
+
| ['references', '_reference_guid']
|
|
816
|
+
| ['tokens', '_token_position_stem']
|
|
817
|
+
)[]) {
|
|
818
|
+
const indexDefinitions = await this.queryArray(
|
|
819
|
+
`SHOW INDEXES FROM ${MySQLDriver.escape(
|
|
820
|
+
`${this.prefix}${scope}_${etype}`,
|
|
821
|
+
)} WHERE \`Key_name\` LIKE @pattern AND \`Seq_in_index\`=1;`,
|
|
822
|
+
{
|
|
823
|
+
params: {
|
|
824
|
+
pattern: `${this.prefix}${scope}_${etype}_id_custom_%${suffix}`,
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
);
|
|
828
|
+
for (const indexDefinition of indexDefinitions) {
|
|
829
|
+
indexes.push({
|
|
830
|
+
scope,
|
|
831
|
+
name: (indexDefinition.Key_name as string).substring(
|
|
832
|
+
`${this.prefix}${scope}_${etype}_id_custom_`.length,
|
|
833
|
+
indexDefinition.Key_name.length - suffix.length,
|
|
834
|
+
),
|
|
835
|
+
property:
|
|
836
|
+
((indexDefinition.Expression as string).match(
|
|
837
|
+
/\s*\(?\s*`?name`?\s*=\s*(?:\w*)\\*'(.*?)\\*'/,
|
|
838
|
+
) ?? [])[1] ?? '',
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
736
842
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
843
|
+
return indexes;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
public async addIndex(
|
|
847
|
+
etype: string,
|
|
848
|
+
definition: {
|
|
849
|
+
scope: 'data' | 'references' | 'tokens';
|
|
850
|
+
name: string;
|
|
851
|
+
property: string;
|
|
852
|
+
},
|
|
853
|
+
) {
|
|
854
|
+
this.checkIndexName(definition.name);
|
|
855
|
+
await this.deleteIndex(etype, definition.scope, definition.name);
|
|
856
|
+
if (definition.scope === 'data') {
|
|
857
|
+
await this.queryRun(
|
|
858
|
+
`CREATE INDEX ${MySQLDriver.escape(
|
|
859
|
+
`${this.prefix}data_${etype}_id_custom_${definition.name}_json`,
|
|
860
|
+
)} USING btree ON ${MySQLDriver.escape(
|
|
861
|
+
`${this.prefix}data_${etype}`,
|
|
862
|
+
)} (((\`name\` = ${MySQLDriver.escapeValue(definition.property)})), (CAST(\`json\` AS CHAR(512)) COLLATE utf8mb4_bin));`,
|
|
863
|
+
);
|
|
864
|
+
await this.queryRun(
|
|
865
|
+
`CREATE INDEX ${MySQLDriver.escape(
|
|
866
|
+
`${this.prefix}data_${etype}_id_custom_${definition.name}_string`,
|
|
867
|
+
)} USING btree ON ${MySQLDriver.escape(
|
|
868
|
+
`${this.prefix}data_${etype}`,
|
|
869
|
+
)} (((\`name\` = ${MySQLDriver.escapeValue(definition.property)})), \`string\`(512));`,
|
|
870
|
+
);
|
|
871
|
+
await this.queryRun(
|
|
872
|
+
`CREATE INDEX ${MySQLDriver.escape(
|
|
873
|
+
`${this.prefix}data_${etype}_id_custom_${definition.name}_number`,
|
|
874
|
+
)} USING btree ON ${MySQLDriver.escape(
|
|
875
|
+
`${this.prefix}data_${etype}`,
|
|
876
|
+
)} (((\`name\` = ${MySQLDriver.escapeValue(definition.property)})), \`number\`);`,
|
|
877
|
+
);
|
|
878
|
+
await this.queryRun(
|
|
879
|
+
`CREATE INDEX ${MySQLDriver.escape(
|
|
880
|
+
`${this.prefix}data_${etype}_id_custom_${definition.name}_truthy`,
|
|
881
|
+
)} USING btree ON ${MySQLDriver.escape(
|
|
882
|
+
`${this.prefix}data_${etype}`,
|
|
883
|
+
)} (((\`name\` = ${MySQLDriver.escapeValue(definition.property)})), \`truthy\`);`,
|
|
884
|
+
);
|
|
885
|
+
} else if (definition.scope === 'references') {
|
|
886
|
+
await this.queryRun(
|
|
887
|
+
`CREATE INDEX ${MySQLDriver.escape(
|
|
888
|
+
`${this.prefix}references_${etype}_id_custom_${definition.name}_reference_guid`,
|
|
889
|
+
)} USING btree ON ${MySQLDriver.escape(
|
|
890
|
+
`${this.prefix}references_${etype}`,
|
|
891
|
+
)} (((\`name\` = ${MySQLDriver.escapeValue(definition.property)})), \`reference\`, \`guid\`);`,
|
|
892
|
+
);
|
|
893
|
+
} else if (definition.scope === 'tokens') {
|
|
894
|
+
await this.queryRun(
|
|
895
|
+
`CREATE INDEX ${MySQLDriver.escape(
|
|
896
|
+
`${this.prefix}tokens_${etype}_id_custom_${definition.name}_token_position_stem`,
|
|
897
|
+
)} USING btree ON ${MySQLDriver.escape(
|
|
898
|
+
`${this.prefix}tokens_${etype}`,
|
|
899
|
+
)} (((\`name\` = ${MySQLDriver.escapeValue(definition.property)})), \`token\`, \`position\`, \`stem\`);`,
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
public async deleteIndex(
|
|
906
|
+
etype: string,
|
|
907
|
+
scope: 'data' | 'references' | 'tokens',
|
|
908
|
+
name: string,
|
|
909
|
+
) {
|
|
910
|
+
this.checkIndexName(name);
|
|
911
|
+
try {
|
|
912
|
+
if (scope === 'data') {
|
|
913
|
+
await this.queryRun(
|
|
914
|
+
`DROP INDEX ${MySQLDriver.escape(
|
|
915
|
+
`${this.prefix}data_${etype}_id_custom_${name}_json`,
|
|
916
|
+
)} ON ${MySQLDriver.escape(`${this.prefix}data_${etype}`)};`,
|
|
917
|
+
);
|
|
918
|
+
await this.queryRun(
|
|
919
|
+
`DROP INDEX ${MySQLDriver.escape(
|
|
920
|
+
`${this.prefix}data_${etype}_id_custom_${name}_string`,
|
|
921
|
+
)} ON ${MySQLDriver.escape(`${this.prefix}data_${etype}`)};`,
|
|
922
|
+
);
|
|
923
|
+
await this.queryRun(
|
|
924
|
+
`DROP INDEX ${MySQLDriver.escape(
|
|
925
|
+
`${this.prefix}data_${etype}_id_custom_${name}_number`,
|
|
926
|
+
)} ON ${MySQLDriver.escape(`${this.prefix}data_${etype}`)};`,
|
|
927
|
+
);
|
|
928
|
+
await this.queryRun(
|
|
929
|
+
`DROP INDEX ${MySQLDriver.escape(
|
|
930
|
+
`${this.prefix}data_${etype}_id_custom_${name}_truthy`,
|
|
931
|
+
)} ON ${MySQLDriver.escape(`${this.prefix}data_${etype}`)};`,
|
|
932
|
+
);
|
|
933
|
+
} else if (scope === 'references') {
|
|
934
|
+
await this.queryRun(
|
|
935
|
+
`DROP INDEX ${MySQLDriver.escape(
|
|
936
|
+
`${this.prefix}references_${etype}_id_custom_${name}_reference_guid`,
|
|
937
|
+
)} ON ${MySQLDriver.escape(`${this.prefix}references_${etype}`)};`,
|
|
938
|
+
);
|
|
939
|
+
} else if (scope === 'tokens') {
|
|
940
|
+
await this.queryRun(
|
|
941
|
+
`DROP INDEX ${MySQLDriver.escape(
|
|
942
|
+
`${this.prefix}tokens_${etype}_id_custom_${name}_token_position_stem`,
|
|
943
|
+
)} ON ${MySQLDriver.escape(`${this.prefix}tokens_${etype}`)};`,
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
} catch (e: any) {
|
|
947
|
+
if (e?.errorCode !== 1091) {
|
|
948
|
+
throw e;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
return true;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
public async getEtypes() {
|
|
955
|
+
const tables = await this.queryArray(
|
|
956
|
+
'SELECT `table_name` AS `table_name` FROM `information_schema`.`tables` WHERE `table_schema`=@db AND `table_name` LIKE @prefix;',
|
|
957
|
+
{
|
|
958
|
+
params: {
|
|
959
|
+
db: this.config.database,
|
|
960
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
);
|
|
964
|
+
const etypes: string[] = [];
|
|
965
|
+
for (const table of tables) {
|
|
966
|
+
etypes.push(table.table_name.substr((this.prefix + 'entities_').length));
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
return etypes;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
public async *exportDataIterator(): AsyncGenerator<
|
|
973
|
+
{ type: 'comment' | 'uid' | 'entity'; content: string },
|
|
974
|
+
void,
|
|
975
|
+
false | undefined
|
|
976
|
+
> {
|
|
977
|
+
if (
|
|
978
|
+
yield {
|
|
979
|
+
type: 'comment',
|
|
980
|
+
content: `#nex2
|
|
981
|
+
# Nymph Entity Exchange v2
|
|
982
|
+
# http://nymph.io
|
|
983
|
+
#
|
|
984
|
+
# Generation Time: ${new Date().toLocaleString()}
|
|
985
|
+
`,
|
|
986
|
+
}
|
|
987
|
+
) {
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
if (
|
|
992
|
+
yield {
|
|
993
|
+
type: 'comment',
|
|
994
|
+
content: `
|
|
995
|
+
|
|
996
|
+
#
|
|
997
|
+
# UIDs
|
|
998
|
+
#
|
|
999
|
+
|
|
1000
|
+
`,
|
|
1001
|
+
}
|
|
1002
|
+
) {
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// Export UIDs.
|
|
1007
|
+
let uids = await this.queryArray(
|
|
1008
|
+
`SELECT * FROM ${MySQLDriver.escape(
|
|
1009
|
+
`${this.prefix}uids`,
|
|
1010
|
+
)} ORDER BY \`name\`;`,
|
|
1011
|
+
);
|
|
1012
|
+
for (const uid of uids) {
|
|
1013
|
+
if (yield { type: 'uid', content: `<${uid.name}>[${uid.cur_uid}]\n` }) {
|
|
1014
|
+
return;
|
|
744
1015
|
}
|
|
745
1016
|
}
|
|
746
1017
|
|
|
1018
|
+
if (
|
|
1019
|
+
yield {
|
|
1020
|
+
type: 'comment',
|
|
1021
|
+
content: `
|
|
1022
|
+
|
|
1023
|
+
#
|
|
1024
|
+
# Entities
|
|
1025
|
+
#
|
|
1026
|
+
|
|
1027
|
+
`,
|
|
1028
|
+
}
|
|
1029
|
+
) {
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Get the etypes.
|
|
1034
|
+
const etypes = await this.getEtypes();
|
|
1035
|
+
|
|
747
1036
|
for (const etype of etypes) {
|
|
748
1037
|
// Export entities.
|
|
749
1038
|
const dataIterator = (
|
|
750
|
-
await this.
|
|
751
|
-
`SELECT LOWER(HEX(e.\`guid\`)) AS \`guid\`, e.\`tags\`, e.\`cdate\`, e.\`mdate\`,
|
|
1039
|
+
await this.queryArray(
|
|
1040
|
+
`SELECT LOWER(HEX(e.\`guid\`)) AS \`guid\`, e.\`tags\`, e.\`cdate\`, e.\`mdate\`, LOWER(HEX(e.\`user\`)) AS \`user\`, LOWER(HEX(e.\`group\`)) AS \`group\`, e.\`acUser\`, e.\`acGroup\`, e.\`acOther\`, e.\`acRead\`, e.\`acWrite\`, e.\`acFull\`, d.\`name\`, d.\`value\`, d.\`json\`, d.\`string\`, d.\`number\`
|
|
752
1041
|
FROM ${MySQLDriver.escape(`${this.prefix}entities_${etype}`)} e
|
|
753
1042
|
LEFT JOIN ${MySQLDriver.escape(
|
|
754
|
-
`${this.prefix}data_${etype}
|
|
1043
|
+
`${this.prefix}data_${etype}`,
|
|
755
1044
|
)} d ON e.\`guid\`=d.\`guid\`
|
|
756
|
-
|
|
757
|
-
`${this.prefix}comparisons_${etype}`
|
|
758
|
-
)} c ON d.\`guid\`=c.\`guid\` AND d.\`name\`=c.\`name\`
|
|
759
|
-
ORDER BY e.\`guid\`;`
|
|
1045
|
+
ORDER BY e.\`guid\`;`,
|
|
760
1046
|
)
|
|
761
1047
|
)[Symbol.iterator]();
|
|
762
1048
|
let datum = dataIterator.next();
|
|
763
1049
|
while (!datum.done) {
|
|
764
1050
|
const guid = datum.value.guid;
|
|
765
|
-
const tags = datum.value.tags
|
|
1051
|
+
const tags = datum.value.tags
|
|
1052
|
+
.slice(1, -1)
|
|
1053
|
+
.split(' ')
|
|
1054
|
+
.filter((tag: string) => tag)
|
|
1055
|
+
.join(',');
|
|
766
1056
|
const cdate = datum.value.cdate;
|
|
767
1057
|
const mdate = datum.value.mdate;
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1058
|
+
const user = datum.value.user;
|
|
1059
|
+
const group = datum.value.group;
|
|
1060
|
+
const acUser = datum.value.acUser;
|
|
1061
|
+
const acGroup = datum.value.acGroup;
|
|
1062
|
+
const acOther = datum.value.acOther;
|
|
1063
|
+
const acRead = datum.value.acRead?.slice(1, -1).split(' ');
|
|
1064
|
+
const acWrite = datum.value.acWrite?.slice(1, -1).split(' ');
|
|
1065
|
+
const acFull = datum.value.acFull?.slice(1, -1).split(' ');
|
|
1066
|
+
let currentEntityExport: string[] = [];
|
|
1067
|
+
currentEntityExport.push(`{${guid}}<${etype}>[${tags}]`);
|
|
1068
|
+
currentEntityExport.push(`\tcdate=${JSON.stringify(cdate)}`);
|
|
1069
|
+
currentEntityExport.push(`\tmdate=${JSON.stringify(mdate)}`);
|
|
1070
|
+
if (this.nymph.tilmeld != null) {
|
|
1071
|
+
if (user != null) {
|
|
1072
|
+
currentEntityExport.push(
|
|
1073
|
+
`\tuser=${JSON.stringify(['nymph_entity_reference', user, 'User'])}`,
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
if (group != null) {
|
|
1077
|
+
currentEntityExport.push(
|
|
1078
|
+
`\tgroup=${JSON.stringify(['nymph_entity_reference', group, 'Group'])}`,
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
if (acUser != null) {
|
|
1082
|
+
currentEntityExport.push(`\tacUser=${JSON.stringify(acUser)}`);
|
|
1083
|
+
}
|
|
1084
|
+
if (acGroup != null) {
|
|
1085
|
+
currentEntityExport.push(`\tacGroup=${JSON.stringify(acGroup)}`);
|
|
1086
|
+
}
|
|
1087
|
+
if (acOther != null) {
|
|
1088
|
+
currentEntityExport.push(`\tacOther=${JSON.stringify(acOther)}`);
|
|
1089
|
+
}
|
|
1090
|
+
if (acRead != null) {
|
|
1091
|
+
currentEntityExport.push(`\tacRead=${JSON.stringify(acRead)}`);
|
|
1092
|
+
}
|
|
1093
|
+
if (acWrite != null) {
|
|
1094
|
+
currentEntityExport.push(`\tacWrite=${JSON.stringify(acWrite)}`);
|
|
1095
|
+
}
|
|
1096
|
+
if (acFull != null) {
|
|
1097
|
+
currentEntityExport.push(`\tacFull=${JSON.stringify(acFull)}`);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
if (datum.value.name != null) {
|
|
772
1101
|
// This do will keep going and adding the data until the
|
|
773
1102
|
// next entity is reached. $row will end on the next entity.
|
|
774
1103
|
do {
|
|
775
1104
|
const value =
|
|
776
|
-
datum.value.
|
|
1105
|
+
datum.value.value === 'N'
|
|
777
1106
|
? JSON.stringify(datum.value.number)
|
|
778
|
-
: datum.value.
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1107
|
+
: datum.value.value === 'S'
|
|
1108
|
+
? JSON.stringify(datum.value.string)
|
|
1109
|
+
: datum.value.value === 'J'
|
|
1110
|
+
? JSON.stringify(datum.value.json)
|
|
1111
|
+
: datum.value.value;
|
|
1112
|
+
currentEntityExport.push(`\t${datum.value.name}=${value}`);
|
|
782
1113
|
datum = dataIterator.next();
|
|
783
1114
|
} while (!datum.done && datum.value.guid === guid);
|
|
784
1115
|
} else {
|
|
785
1116
|
// Make sure that datum is incremented :)
|
|
786
1117
|
datum = dataIterator.next();
|
|
787
1118
|
}
|
|
1119
|
+
currentEntityExport.push('');
|
|
1120
|
+
|
|
1121
|
+
if (yield { type: 'entity', content: currentEntityExport.join('\n') }) {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
788
1124
|
}
|
|
789
1125
|
}
|
|
790
|
-
|
|
791
|
-
return;
|
|
792
1126
|
}
|
|
793
1127
|
|
|
794
1128
|
/**
|
|
@@ -809,21 +1143,23 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
809
1143
|
params: { [k: string]: any } = {},
|
|
810
1144
|
subquery = false,
|
|
811
1145
|
tableSuffix = '',
|
|
812
|
-
etypes: string[] = []
|
|
1146
|
+
etypes: string[] = [],
|
|
1147
|
+
guidSelector: string | undefined = undefined,
|
|
1148
|
+
guidExplicitSelector: ((guid: string) => string) | undefined = undefined,
|
|
813
1149
|
) {
|
|
814
1150
|
if (typeof options.class?.alterOptions === 'function') {
|
|
815
1151
|
options = options.class.alterOptions(options);
|
|
816
1152
|
}
|
|
817
1153
|
const eTable = `e${tableSuffix}`;
|
|
818
1154
|
const dTable = `d${tableSuffix}`;
|
|
819
|
-
const cTable = `c${tableSuffix}`;
|
|
820
1155
|
const fTable = `f${tableSuffix}`;
|
|
821
1156
|
const ieTable = `ie${tableSuffix}`;
|
|
822
1157
|
const countTable = `count${tableSuffix}`;
|
|
823
|
-
const
|
|
1158
|
+
const sTable = `s${tableSuffix}`;
|
|
1159
|
+
const sort = options.sort === undefined ? 'cdate' : options.sort;
|
|
824
1160
|
const queryParts = this.iterateSelectorsForQuery(
|
|
825
1161
|
formattedSelectors,
|
|
826
|
-
(key, value, typeIsOr, typeIsNot) => {
|
|
1162
|
+
({ key, value, typeIsOr, typeIsNot }) => {
|
|
827
1163
|
const clauseNot = key.startsWith('!');
|
|
828
1164
|
let curQuery = '';
|
|
829
1165
|
for (const curValue of value) {
|
|
@@ -885,17 +1221,39 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
885
1221
|
if (curQuery) {
|
|
886
1222
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
887
1223
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1224
|
+
if (
|
|
1225
|
+
curVar === 'cdate' ||
|
|
1226
|
+
curVar === 'mdate' ||
|
|
1227
|
+
(this.nymph.tilmeld != null &&
|
|
1228
|
+
(curVar === 'user' ||
|
|
1229
|
+
curVar === 'group' ||
|
|
1230
|
+
curVar === 'acUser' ||
|
|
1231
|
+
curVar === 'acGroup' ||
|
|
1232
|
+
curVar === 'acOther' ||
|
|
1233
|
+
curVar === 'acRead' ||
|
|
1234
|
+
curVar === 'acWrite' ||
|
|
1235
|
+
curVar === 'acFull'))
|
|
1236
|
+
) {
|
|
1237
|
+
curQuery +=
|
|
1238
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1239
|
+
'(' +
|
|
1240
|
+
ieTable +
|
|
1241
|
+
'.' +
|
|
1242
|
+
MySQLDriver.escape(curVar) +
|
|
1243
|
+
' IS NOT NULL)';
|
|
1244
|
+
} else {
|
|
1245
|
+
const name = `param${++count.i}`;
|
|
1246
|
+
curQuery +=
|
|
1247
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1248
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1249
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1250
|
+
' WHERE `guid`=' +
|
|
1251
|
+
ieTable +
|
|
1252
|
+
'.`guid` AND `name`=@' +
|
|
1253
|
+
name +
|
|
1254
|
+
')';
|
|
1255
|
+
params[name] = curVar;
|
|
1256
|
+
}
|
|
899
1257
|
}
|
|
900
1258
|
break;
|
|
901
1259
|
case 'truthy':
|
|
@@ -904,28 +1262,35 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
904
1262
|
if (curQuery) {
|
|
905
1263
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
906
1264
|
}
|
|
907
|
-
if (
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1265
|
+
if (
|
|
1266
|
+
curVar === 'cdate' ||
|
|
1267
|
+
curVar === 'mdate' ||
|
|
1268
|
+
(this.nymph.tilmeld != null &&
|
|
1269
|
+
(curVar === 'user' ||
|
|
1270
|
+
curVar === 'group' ||
|
|
1271
|
+
curVar === 'acUser' ||
|
|
1272
|
+
curVar === 'acGroup' ||
|
|
1273
|
+
curVar === 'acOther' ||
|
|
1274
|
+
curVar === 'acRead' ||
|
|
1275
|
+
curVar === 'acWrite' ||
|
|
1276
|
+
curVar === 'acFull'))
|
|
1277
|
+
) {
|
|
915
1278
|
curQuery +=
|
|
916
1279
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
917
1280
|
'(' +
|
|
918
1281
|
ieTable +
|
|
919
|
-
'
|
|
920
|
-
|
|
1282
|
+
'.' +
|
|
1283
|
+
MySQLDriver.escape(curVar) +
|
|
1284
|
+
' IS NOT NULL)';
|
|
921
1285
|
} else {
|
|
922
1286
|
const name = `param${++count.i}`;
|
|
923
1287
|
curQuery +=
|
|
924
1288
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1289
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1290
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1291
|
+
' WHERE `guid`=' +
|
|
925
1292
|
ieTable +
|
|
926
|
-
'.`guid`
|
|
927
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
928
|
-
' WHERE `name`=@' +
|
|
1293
|
+
'.`guid` AND `name`=@' +
|
|
929
1294
|
name +
|
|
930
1295
|
' AND `truthy`=TRUE)';
|
|
931
1296
|
params[name] = curVar;
|
|
@@ -934,34 +1299,65 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
934
1299
|
break;
|
|
935
1300
|
case 'equal':
|
|
936
1301
|
case '!equal':
|
|
937
|
-
if (
|
|
1302
|
+
if (
|
|
1303
|
+
curValue[0] === 'cdate' ||
|
|
1304
|
+
curValue[0] === 'mdate' ||
|
|
1305
|
+
(this.nymph.tilmeld != null &&
|
|
1306
|
+
(curValue[0] === 'acUser' ||
|
|
1307
|
+
curValue[0] === 'acGroup' ||
|
|
1308
|
+
curValue[0] === 'acOther'))
|
|
1309
|
+
) {
|
|
938
1310
|
if (curQuery) {
|
|
939
1311
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
940
1312
|
}
|
|
941
|
-
const
|
|
1313
|
+
const value = `param${++count.i}`;
|
|
942
1314
|
curQuery +=
|
|
943
1315
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
944
1316
|
ieTable +
|
|
945
|
-
'
|
|
946
|
-
|
|
947
|
-
|
|
1317
|
+
'.' +
|
|
1318
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1319
|
+
'=@' +
|
|
1320
|
+
value;
|
|
1321
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
948
1322
|
? null
|
|
949
1323
|
: Number(curValue[1]);
|
|
950
|
-
|
|
951
|
-
|
|
1324
|
+
} else if (
|
|
1325
|
+
this.nymph.tilmeld != null &&
|
|
1326
|
+
(curValue[0] === 'user' || curValue[0] === 'group')
|
|
1327
|
+
) {
|
|
952
1328
|
if (curQuery) {
|
|
953
1329
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
954
1330
|
}
|
|
955
|
-
const
|
|
1331
|
+
const value = `param${++count.i}`;
|
|
956
1332
|
curQuery +=
|
|
957
1333
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
958
1334
|
ieTable +
|
|
959
|
-
'
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1335
|
+
'.' +
|
|
1336
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1337
|
+
'=UNHEX(@' +
|
|
1338
|
+
value +
|
|
1339
|
+
')';
|
|
1340
|
+
params[value] = `${curValue[1]}`;
|
|
1341
|
+
} else if (
|
|
1342
|
+
this.nymph.tilmeld != null &&
|
|
1343
|
+
(curValue[0] === 'acRead' ||
|
|
1344
|
+
curValue[0] === 'acWrite' ||
|
|
1345
|
+
curValue[0] === 'acFull')
|
|
1346
|
+
) {
|
|
1347
|
+
if (curQuery) {
|
|
1348
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1349
|
+
}
|
|
1350
|
+
const value = `param${++count.i}`;
|
|
1351
|
+
curQuery +=
|
|
1352
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1353
|
+
ieTable +
|
|
1354
|
+
'.' +
|
|
1355
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1356
|
+
'=@' +
|
|
1357
|
+
value;
|
|
1358
|
+
params[value] = Array.isArray(curValue[1])
|
|
1359
|
+
? ' ' + curValue[1].join(' ') + ' '
|
|
1360
|
+
: '';
|
|
965
1361
|
} else if (typeof curValue[1] === 'number') {
|
|
966
1362
|
if (curQuery) {
|
|
967
1363
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -970,10 +1366,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
970
1366
|
const value = `param${++count.i}`;
|
|
971
1367
|
curQuery +=
|
|
972
1368
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1369
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1370
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1371
|
+
' WHERE `guid`=' +
|
|
973
1372
|
ieTable +
|
|
974
|
-
'.`guid`
|
|
975
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
976
|
-
' WHERE `name`=@' +
|
|
1373
|
+
'.`guid` AND `name`=@' +
|
|
977
1374
|
name +
|
|
978
1375
|
' AND `number`=@' +
|
|
979
1376
|
value +
|
|
@@ -988,10 +1385,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
988
1385
|
const value = `param${++count.i}`;
|
|
989
1386
|
curQuery +=
|
|
990
1387
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1388
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1389
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1390
|
+
' WHERE `guid`=' +
|
|
991
1391
|
ieTable +
|
|
992
|
-
'.`guid`
|
|
993
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
994
|
-
' WHERE `name`=@' +
|
|
1392
|
+
'.`guid` AND `name`=@' +
|
|
995
1393
|
name +
|
|
996
1394
|
' AND `string`=@' +
|
|
997
1395
|
value +
|
|
@@ -1013,136 +1411,313 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1013
1411
|
}
|
|
1014
1412
|
const name = `param${++count.i}`;
|
|
1015
1413
|
const value = `param${++count.i}`;
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1414
|
+
if (svalue.length < 512) {
|
|
1415
|
+
curQuery +=
|
|
1416
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1417
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1418
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1419
|
+
' WHERE `guid`=' +
|
|
1420
|
+
ieTable +
|
|
1421
|
+
'.`guid` AND `name`=@' +
|
|
1422
|
+
name +
|
|
1423
|
+
' AND (CAST(`json` AS CHAR(512)) COLLATE utf8mb4_bin)=CAST(CAST(@' +
|
|
1424
|
+
value +
|
|
1425
|
+
' AS JSON) AS CHAR(512)))';
|
|
1426
|
+
} else {
|
|
1427
|
+
curQuery +=
|
|
1428
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1429
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1430
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1431
|
+
' WHERE `guid`=' +
|
|
1432
|
+
ieTable +
|
|
1433
|
+
'.`guid` AND `name`=@' +
|
|
1434
|
+
name +
|
|
1435
|
+
' AND `json`=CAST(@' +
|
|
1436
|
+
value +
|
|
1437
|
+
' AS JSON))';
|
|
1438
|
+
}
|
|
1026
1439
|
params[name] = curValue[0];
|
|
1027
1440
|
params[value] = svalue;
|
|
1028
1441
|
}
|
|
1029
1442
|
break;
|
|
1030
1443
|
case 'contain':
|
|
1031
1444
|
case '!contain':
|
|
1032
|
-
if (
|
|
1445
|
+
if (
|
|
1446
|
+
curValue[0] === 'cdate' ||
|
|
1447
|
+
curValue[0] === 'mdate' ||
|
|
1448
|
+
(this.nymph.tilmeld != null &&
|
|
1449
|
+
(curValue[0] === 'acUser' ||
|
|
1450
|
+
curValue[0] === 'acGroup' ||
|
|
1451
|
+
curValue[0] === 'acOther'))
|
|
1452
|
+
) {
|
|
1033
1453
|
if (curQuery) {
|
|
1034
1454
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1035
1455
|
}
|
|
1036
|
-
const
|
|
1456
|
+
const value = `param${++count.i}`;
|
|
1037
1457
|
curQuery +=
|
|
1038
1458
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1039
1459
|
ieTable +
|
|
1040
|
-
'
|
|
1041
|
-
|
|
1042
|
-
|
|
1460
|
+
'.' +
|
|
1461
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1462
|
+
'=@' +
|
|
1463
|
+
value;
|
|
1464
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1043
1465
|
? null
|
|
1044
1466
|
: Number(curValue[1]);
|
|
1045
|
-
|
|
1046
|
-
|
|
1467
|
+
} else if (
|
|
1468
|
+
this.nymph.tilmeld != null &&
|
|
1469
|
+
(curValue[0] === 'user' || curValue[0] === 'group')
|
|
1470
|
+
) {
|
|
1047
1471
|
if (curQuery) {
|
|
1048
1472
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1049
1473
|
}
|
|
1050
|
-
const
|
|
1474
|
+
const value = `param${++count.i}`;
|
|
1051
1475
|
curQuery +=
|
|
1052
1476
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1053
1477
|
ieTable +
|
|
1054
|
-
'
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1478
|
+
'.' +
|
|
1479
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1480
|
+
'=UNHEX(@' +
|
|
1481
|
+
value +
|
|
1482
|
+
')';
|
|
1483
|
+
params[value] = `${curValue[1]}`;
|
|
1484
|
+
} else if (
|
|
1485
|
+
this.nymph.tilmeld != null &&
|
|
1486
|
+
(curValue[0] === 'acRead' ||
|
|
1487
|
+
curValue[0] === 'acWrite' ||
|
|
1488
|
+
curValue[0] === 'acFull')
|
|
1489
|
+
) {
|
|
1490
|
+
if (curQuery) {
|
|
1491
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1492
|
+
}
|
|
1493
|
+
const queryParam = `param${++count.i}`;
|
|
1494
|
+
curQuery +=
|
|
1495
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1496
|
+
'MATCH (' +
|
|
1497
|
+
ieTable +
|
|
1498
|
+
'.' +
|
|
1499
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1500
|
+
') AGAINST (@' +
|
|
1501
|
+
queryParam +
|
|
1502
|
+
' IN BOOLEAN MODE)';
|
|
1503
|
+
params[queryParam] = '+' + curValue[1];
|
|
1060
1504
|
} else {
|
|
1061
1505
|
if (curQuery) {
|
|
1062
1506
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1063
1507
|
}
|
|
1064
1508
|
let svalue: string;
|
|
1065
|
-
let stringValue: string;
|
|
1066
1509
|
if (
|
|
1067
1510
|
curValue[1] instanceof Object &&
|
|
1068
1511
|
typeof curValue[1].toReference === 'function'
|
|
1069
1512
|
) {
|
|
1070
1513
|
svalue = JSON.stringify(curValue[1].toReference());
|
|
1071
|
-
stringValue = `${curValue[1].toReference()}`;
|
|
1072
1514
|
} else {
|
|
1073
1515
|
svalue = JSON.stringify(curValue[1]);
|
|
1074
|
-
stringValue = `${curValue[1]}`;
|
|
1075
1516
|
}
|
|
1076
1517
|
const name = `param${++count.i}`;
|
|
1077
1518
|
const value = `param${++count.i}`;
|
|
1078
|
-
|
|
1079
|
-
|
|
1519
|
+
curQuery +=
|
|
1520
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1521
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1522
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1523
|
+
' WHERE `guid`=' +
|
|
1524
|
+
ieTable +
|
|
1525
|
+
'.`guid` AND `name`=@' +
|
|
1526
|
+
name +
|
|
1527
|
+
' AND JSON_CONTAINS(`json`, @' +
|
|
1528
|
+
value +
|
|
1529
|
+
'))';
|
|
1530
|
+
params[name] = curValue[0];
|
|
1531
|
+
params[value] = svalue;
|
|
1532
|
+
}
|
|
1533
|
+
break;
|
|
1534
|
+
case 'search':
|
|
1535
|
+
case '!search':
|
|
1536
|
+
if (
|
|
1537
|
+
curValue[0] === 'cdate' ||
|
|
1538
|
+
curValue[0] === 'mdate' ||
|
|
1539
|
+
(this.nymph.tilmeld != null &&
|
|
1540
|
+
(curValue[0] === 'user' ||
|
|
1541
|
+
curValue[0] === 'group' ||
|
|
1542
|
+
curValue[0] === 'acUser' ||
|
|
1543
|
+
curValue[0] === 'acGroup' ||
|
|
1544
|
+
curValue[0] === 'acOther' ||
|
|
1545
|
+
curValue[0] === 'acRead' ||
|
|
1546
|
+
curValue[0] === 'acWrite' ||
|
|
1547
|
+
curValue[0] === 'acFull'))
|
|
1548
|
+
) {
|
|
1549
|
+
if (curQuery) {
|
|
1550
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1551
|
+
}
|
|
1552
|
+
curQuery +=
|
|
1553
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
|
|
1554
|
+
} else {
|
|
1555
|
+
if (curQuery) {
|
|
1556
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
const parsedFTSQuery = this.tokenizer.parseSearchQuery(
|
|
1560
|
+
curValue[1],
|
|
1561
|
+
);
|
|
1562
|
+
|
|
1563
|
+
if (!parsedFTSQuery.length) {
|
|
1080
1564
|
curQuery +=
|
|
1081
|
-
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1082
|
-
'(' +
|
|
1083
|
-
ieTable +
|
|
1084
|
-
'.`guid` IN (SELECT `guid` FROM ' +
|
|
1085
|
-
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1086
|
-
' WHERE `name`=@' +
|
|
1087
|
-
name +
|
|
1088
|
-
' AND INSTR(`value`, @' +
|
|
1089
|
-
value +
|
|
1090
|
-
')) OR ' +
|
|
1091
|
-
ieTable +
|
|
1092
|
-
'.`guid` IN (SELECT `guid` FROM ' +
|
|
1093
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1094
|
-
' WHERE `name`=@' +
|
|
1095
|
-
name +
|
|
1096
|
-
' AND `string`=@' +
|
|
1097
|
-
stringParam +
|
|
1098
|
-
'))';
|
|
1099
|
-
params[stringParam] = stringValue;
|
|
1565
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(FALSE)';
|
|
1100
1566
|
} else {
|
|
1567
|
+
const name = `param${++count.i}`;
|
|
1568
|
+
|
|
1569
|
+
const queryPartToken = (term: SearchTerm) => {
|
|
1570
|
+
const value = `param${++count.i}`;
|
|
1571
|
+
params[value] = term.token;
|
|
1572
|
+
return (
|
|
1573
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1574
|
+
MySQLDriver.escape(this.prefix + 'tokens_' + etype) +
|
|
1575
|
+
' WHERE `guid`=' +
|
|
1576
|
+
ieTable +
|
|
1577
|
+
'.`guid` AND `name`=@' +
|
|
1578
|
+
name +
|
|
1579
|
+
' AND `token`=@' +
|
|
1580
|
+
value +
|
|
1581
|
+
(term.nostemmed ? ' AND `stem`=FALSE' : '') +
|
|
1582
|
+
')'
|
|
1583
|
+
);
|
|
1584
|
+
};
|
|
1585
|
+
|
|
1586
|
+
const queryPartSeries = (series: SearchSeriesTerm) => {
|
|
1587
|
+
const tokenTableSuffix = makeTableSuffix();
|
|
1588
|
+
const tokenParts = series.tokens.map((token, i) => {
|
|
1589
|
+
const value = `param${++count.i}`;
|
|
1590
|
+
params[value] = token.token;
|
|
1591
|
+
return {
|
|
1592
|
+
fromClause:
|
|
1593
|
+
i === 0
|
|
1594
|
+
? 'FROM ' +
|
|
1595
|
+
MySQLDriver.escape(
|
|
1596
|
+
this.prefix + 'tokens_' + etype,
|
|
1597
|
+
) +
|
|
1598
|
+
' t' +
|
|
1599
|
+
tokenTableSuffix +
|
|
1600
|
+
'0'
|
|
1601
|
+
: 'JOIN ' +
|
|
1602
|
+
MySQLDriver.escape(
|
|
1603
|
+
this.prefix + 'tokens_' + etype,
|
|
1604
|
+
) +
|
|
1605
|
+
' t' +
|
|
1606
|
+
tokenTableSuffix +
|
|
1607
|
+
i +
|
|
1608
|
+
' ON t' +
|
|
1609
|
+
tokenTableSuffix +
|
|
1610
|
+
i +
|
|
1611
|
+
'.`guid` = t' +
|
|
1612
|
+
tokenTableSuffix +
|
|
1613
|
+
'0.`guid` AND t' +
|
|
1614
|
+
tokenTableSuffix +
|
|
1615
|
+
i +
|
|
1616
|
+
'.`name` = t' +
|
|
1617
|
+
tokenTableSuffix +
|
|
1618
|
+
'0.`name` AND t' +
|
|
1619
|
+
tokenTableSuffix +
|
|
1620
|
+
i +
|
|
1621
|
+
'.`position` = t' +
|
|
1622
|
+
tokenTableSuffix +
|
|
1623
|
+
'0.`position` + ' +
|
|
1624
|
+
i,
|
|
1625
|
+
whereClause:
|
|
1626
|
+
't' +
|
|
1627
|
+
tokenTableSuffix +
|
|
1628
|
+
i +
|
|
1629
|
+
'.`token`=@' +
|
|
1630
|
+
value +
|
|
1631
|
+
(token.nostemmed
|
|
1632
|
+
? ' AND t' + tokenTableSuffix + i + '.`stem`=FALSE'
|
|
1633
|
+
: ''),
|
|
1634
|
+
};
|
|
1635
|
+
});
|
|
1636
|
+
return (
|
|
1637
|
+
'EXISTS (SELECT t' +
|
|
1638
|
+
tokenTableSuffix +
|
|
1639
|
+
'0.`guid` ' +
|
|
1640
|
+
tokenParts.map((part) => part.fromClause).join(' ') +
|
|
1641
|
+
' WHERE t' +
|
|
1642
|
+
tokenTableSuffix +
|
|
1643
|
+
'0.`guid`=' +
|
|
1644
|
+
ieTable +
|
|
1645
|
+
'.`guid` AND t' +
|
|
1646
|
+
tokenTableSuffix +
|
|
1647
|
+
'0.`name`=@' +
|
|
1648
|
+
name +
|
|
1649
|
+
' AND ' +
|
|
1650
|
+
tokenParts.map((part) => part.whereClause).join(' AND ') +
|
|
1651
|
+
')'
|
|
1652
|
+
);
|
|
1653
|
+
};
|
|
1654
|
+
|
|
1655
|
+
const queryPartTerm = (
|
|
1656
|
+
term:
|
|
1657
|
+
| SearchTerm
|
|
1658
|
+
| SearchOrTerm
|
|
1659
|
+
| SearchNotTerm
|
|
1660
|
+
| SearchSeriesTerm,
|
|
1661
|
+
): string => {
|
|
1662
|
+
if (term.type === 'series') {
|
|
1663
|
+
return queryPartSeries(term);
|
|
1664
|
+
} else if (term.type === 'not') {
|
|
1665
|
+
return 'NOT ' + queryPartTerm(term.operand);
|
|
1666
|
+
} else if (term.type === 'or') {
|
|
1667
|
+
let queryParts: string[] = [];
|
|
1668
|
+
for (let operand of term.operands) {
|
|
1669
|
+
queryParts.push(queryPartTerm(operand));
|
|
1670
|
+
}
|
|
1671
|
+
return '(' + queryParts.join(' OR ') + ')';
|
|
1672
|
+
}
|
|
1673
|
+
return queryPartToken(term);
|
|
1674
|
+
};
|
|
1675
|
+
|
|
1676
|
+
// Run through the query and add terms.
|
|
1677
|
+
let termStrings: string[] = [];
|
|
1678
|
+
for (let term of parsedFTSQuery) {
|
|
1679
|
+
termStrings.push(queryPartTerm(term));
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1101
1682
|
curQuery +=
|
|
1102
1683
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1103
|
-
|
|
1104
|
-
'
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
' AND INSTR(`value`, @' +
|
|
1109
|
-
value +
|
|
1110
|
-
'))';
|
|
1684
|
+
'(' +
|
|
1685
|
+
termStrings.join(' AND ') +
|
|
1686
|
+
')';
|
|
1687
|
+
|
|
1688
|
+
params[name] = curValue[0];
|
|
1111
1689
|
}
|
|
1112
|
-
params[name] = curValue[0];
|
|
1113
|
-
params[value] = svalue;
|
|
1114
1690
|
}
|
|
1115
1691
|
break;
|
|
1116
1692
|
case 'match':
|
|
1117
1693
|
case '!match':
|
|
1118
|
-
if (
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
break;
|
|
1132
|
-
} else if (curValue[0] === 'mdate') {
|
|
1694
|
+
if (
|
|
1695
|
+
curValue[0] === 'cdate' ||
|
|
1696
|
+
curValue[0] === 'mdate' ||
|
|
1697
|
+
(this.nymph.tilmeld != null &&
|
|
1698
|
+
(curValue[0] === 'user' ||
|
|
1699
|
+
curValue[0] === 'group' ||
|
|
1700
|
+
curValue[0] === 'acUser' ||
|
|
1701
|
+
curValue[0] === 'acGroup' ||
|
|
1702
|
+
curValue[0] === 'acOther' ||
|
|
1703
|
+
curValue[0] === 'acRead' ||
|
|
1704
|
+
curValue[0] === 'acWrite' ||
|
|
1705
|
+
curValue[0] === 'acFull'))
|
|
1706
|
+
) {
|
|
1133
1707
|
if (curQuery) {
|
|
1134
1708
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1135
1709
|
}
|
|
1136
|
-
const
|
|
1710
|
+
const value = `param${++count.i}`;
|
|
1137
1711
|
curQuery +=
|
|
1138
1712
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1139
1713
|
'(' +
|
|
1140
1714
|
ieTable +
|
|
1141
|
-
'
|
|
1142
|
-
|
|
1715
|
+
'.' +
|
|
1716
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1717
|
+
' REGEXP @' +
|
|
1718
|
+
value +
|
|
1143
1719
|
')';
|
|
1144
|
-
params[
|
|
1145
|
-
break;
|
|
1720
|
+
params[value] = curValue[1];
|
|
1146
1721
|
} else {
|
|
1147
1722
|
if (curQuery) {
|
|
1148
1723
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1151,10 +1726,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1151
1726
|
const value = `param${++count.i}`;
|
|
1152
1727
|
curQuery +=
|
|
1153
1728
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1729
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1730
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1731
|
+
' WHERE `guid`=' +
|
|
1154
1732
|
ieTable +
|
|
1155
|
-
'.`guid`
|
|
1156
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1157
|
-
' WHERE `name`=@' +
|
|
1733
|
+
'.`guid` AND `name`=@' +
|
|
1158
1734
|
name +
|
|
1159
1735
|
' AND `string` REGEXP @' +
|
|
1160
1736
|
value +
|
|
@@ -1165,34 +1741,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1165
1741
|
break;
|
|
1166
1742
|
case 'imatch':
|
|
1167
1743
|
case '!imatch':
|
|
1168
|
-
if (
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
break;
|
|
1182
|
-
} else if (curValue[0] === 'mdate') {
|
|
1744
|
+
if (
|
|
1745
|
+
curValue[0] === 'cdate' ||
|
|
1746
|
+
curValue[0] === 'mdate' ||
|
|
1747
|
+
(this.nymph.tilmeld != null &&
|
|
1748
|
+
(curValue[0] === 'user' ||
|
|
1749
|
+
curValue[0] === 'group' ||
|
|
1750
|
+
curValue[0] === 'acUser' ||
|
|
1751
|
+
curValue[0] === 'acGroup' ||
|
|
1752
|
+
curValue[0] === 'acOther' ||
|
|
1753
|
+
curValue[0] === 'acRead' ||
|
|
1754
|
+
curValue[0] === 'acWrite' ||
|
|
1755
|
+
curValue[0] === 'acFull'))
|
|
1756
|
+
) {
|
|
1183
1757
|
if (curQuery) {
|
|
1184
1758
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1185
1759
|
}
|
|
1186
|
-
const
|
|
1760
|
+
const value = `param${++count.i}`;
|
|
1187
1761
|
curQuery +=
|
|
1188
1762
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1189
1763
|
'(' +
|
|
1190
1764
|
ieTable +
|
|
1191
|
-
'
|
|
1192
|
-
|
|
1765
|
+
'.' +
|
|
1766
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1767
|
+
' REGEXP @' +
|
|
1768
|
+
value +
|
|
1193
1769
|
')';
|
|
1194
|
-
params[
|
|
1195
|
-
break;
|
|
1770
|
+
params[value] = curValue[1];
|
|
1196
1771
|
} else {
|
|
1197
1772
|
if (curQuery) {
|
|
1198
1773
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1201,10 +1776,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1201
1776
|
const value = `param${++count.i}`;
|
|
1202
1777
|
curQuery +=
|
|
1203
1778
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1779
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1780
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1781
|
+
' WHERE `guid`=' +
|
|
1204
1782
|
ieTable +
|
|
1205
|
-
'.`guid`
|
|
1206
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1207
|
-
' WHERE `name`=@' +
|
|
1783
|
+
'.`guid` AND `name`=@' +
|
|
1208
1784
|
name +
|
|
1209
1785
|
' AND LOWER(`string`) REGEXP LOWER(@' +
|
|
1210
1786
|
value +
|
|
@@ -1215,34 +1791,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1215
1791
|
break;
|
|
1216
1792
|
case 'like':
|
|
1217
1793
|
case '!like':
|
|
1218
|
-
if (
|
|
1794
|
+
if (
|
|
1795
|
+
curValue[0] === 'cdate' ||
|
|
1796
|
+
curValue[0] === 'mdate' ||
|
|
1797
|
+
(this.nymph.tilmeld != null &&
|
|
1798
|
+
(curValue[0] === 'user' ||
|
|
1799
|
+
curValue[0] === 'group' ||
|
|
1800
|
+
curValue[0] === 'acUser' ||
|
|
1801
|
+
curValue[0] === 'acGroup' ||
|
|
1802
|
+
curValue[0] === 'acOther' ||
|
|
1803
|
+
curValue[0] === 'acRead' ||
|
|
1804
|
+
curValue[0] === 'acWrite' ||
|
|
1805
|
+
curValue[0] === 'acFull'))
|
|
1806
|
+
) {
|
|
1219
1807
|
if (curQuery) {
|
|
1220
1808
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1221
1809
|
}
|
|
1222
|
-
const
|
|
1223
|
-
curQuery +=
|
|
1224
|
-
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1225
|
-
'(' +
|
|
1226
|
-
ieTable +
|
|
1227
|
-
'.`cdate` LIKE @' +
|
|
1228
|
-
cdate +
|
|
1229
|
-
')';
|
|
1230
|
-
params[cdate] = curValue[1];
|
|
1231
|
-
break;
|
|
1232
|
-
} else if (curValue[0] === 'mdate') {
|
|
1233
|
-
if (curQuery) {
|
|
1234
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1235
|
-
}
|
|
1236
|
-
const mdate = `param${++count.i}`;
|
|
1810
|
+
const value = `param${++count.i}`;
|
|
1237
1811
|
curQuery +=
|
|
1238
1812
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1239
1813
|
'(' +
|
|
1240
1814
|
ieTable +
|
|
1241
|
-
'
|
|
1242
|
-
|
|
1815
|
+
'.' +
|
|
1816
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1817
|
+
' LIKE @' +
|
|
1818
|
+
value +
|
|
1243
1819
|
')';
|
|
1244
|
-
params[
|
|
1245
|
-
break;
|
|
1820
|
+
params[value] = curValue[1];
|
|
1246
1821
|
} else {
|
|
1247
1822
|
if (curQuery) {
|
|
1248
1823
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1251,10 +1826,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1251
1826
|
const value = `param${++count.i}`;
|
|
1252
1827
|
curQuery +=
|
|
1253
1828
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1829
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1830
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1831
|
+
' WHERE `guid`=' +
|
|
1254
1832
|
ieTable +
|
|
1255
|
-
'.`guid`
|
|
1256
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1257
|
-
' WHERE `name`=@' +
|
|
1833
|
+
'.`guid` AND `name`=@' +
|
|
1258
1834
|
name +
|
|
1259
1835
|
' AND `string` LIKE @' +
|
|
1260
1836
|
value +
|
|
@@ -1265,34 +1841,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1265
1841
|
break;
|
|
1266
1842
|
case 'ilike':
|
|
1267
1843
|
case '!ilike':
|
|
1268
|
-
if (
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
break;
|
|
1282
|
-
} else if (curValue[0] === 'mdate') {
|
|
1844
|
+
if (
|
|
1845
|
+
curValue[0] === 'cdate' ||
|
|
1846
|
+
curValue[0] === 'mdate' ||
|
|
1847
|
+
(this.nymph.tilmeld != null &&
|
|
1848
|
+
(curValue[0] === 'user' ||
|
|
1849
|
+
curValue[0] === 'group' ||
|
|
1850
|
+
curValue[0] === 'acUser' ||
|
|
1851
|
+
curValue[0] === 'acGroup' ||
|
|
1852
|
+
curValue[0] === 'acOther' ||
|
|
1853
|
+
curValue[0] === 'acRead' ||
|
|
1854
|
+
curValue[0] === 'acWrite' ||
|
|
1855
|
+
curValue[0] === 'acFull'))
|
|
1856
|
+
) {
|
|
1283
1857
|
if (curQuery) {
|
|
1284
1858
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1285
1859
|
}
|
|
1286
|
-
const
|
|
1860
|
+
const value = `param${++count.i}`;
|
|
1287
1861
|
curQuery +=
|
|
1288
1862
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1289
1863
|
'(' +
|
|
1290
1864
|
ieTable +
|
|
1291
|
-
'
|
|
1292
|
-
|
|
1865
|
+
'.' +
|
|
1866
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1867
|
+
' LIKE @' +
|
|
1868
|
+
value +
|
|
1293
1869
|
')';
|
|
1294
|
-
params[
|
|
1295
|
-
break;
|
|
1870
|
+
params[value] = curValue[1];
|
|
1296
1871
|
} else {
|
|
1297
1872
|
if (curQuery) {
|
|
1298
1873
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1301,10 +1876,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1301
1876
|
const value = `param${++count.i}`;
|
|
1302
1877
|
curQuery +=
|
|
1303
1878
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1879
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1880
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1881
|
+
' WHERE `guid`=' +
|
|
1304
1882
|
ieTable +
|
|
1305
|
-
'.`guid`
|
|
1306
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1307
|
-
' WHERE `name`=@' +
|
|
1883
|
+
'.`guid` AND `name`=@' +
|
|
1308
1884
|
name +
|
|
1309
1885
|
' AND LOWER(`string`) LIKE LOWER(@' +
|
|
1310
1886
|
value +
|
|
@@ -1315,34 +1891,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1315
1891
|
break;
|
|
1316
1892
|
case 'gt':
|
|
1317
1893
|
case '!gt':
|
|
1318
|
-
if (
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
break;
|
|
1332
|
-
} else if (curValue[0] === 'mdate') {
|
|
1894
|
+
if (
|
|
1895
|
+
curValue[0] === 'cdate' ||
|
|
1896
|
+
curValue[0] === 'mdate' ||
|
|
1897
|
+
(this.nymph.tilmeld != null &&
|
|
1898
|
+
(curValue[0] === 'user' ||
|
|
1899
|
+
curValue[0] === 'group' ||
|
|
1900
|
+
curValue[0] === 'acUser' ||
|
|
1901
|
+
curValue[0] === 'acGroup' ||
|
|
1902
|
+
curValue[0] === 'acOther' ||
|
|
1903
|
+
curValue[0] === 'acRead' ||
|
|
1904
|
+
curValue[0] === 'acWrite' ||
|
|
1905
|
+
curValue[0] === 'acFull'))
|
|
1906
|
+
) {
|
|
1333
1907
|
if (curQuery) {
|
|
1334
1908
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1335
1909
|
}
|
|
1336
|
-
const
|
|
1910
|
+
const value = `param${++count.i}`;
|
|
1337
1911
|
curQuery +=
|
|
1338
1912
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1339
1913
|
ieTable +
|
|
1340
|
-
'
|
|
1341
|
-
|
|
1342
|
-
|
|
1914
|
+
'.' +
|
|
1915
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1916
|
+
'>@' +
|
|
1917
|
+
value;
|
|
1918
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1343
1919
|
? null
|
|
1344
1920
|
: Number(curValue[1]);
|
|
1345
|
-
break;
|
|
1346
1921
|
} else {
|
|
1347
1922
|
if (curQuery) {
|
|
1348
1923
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1351,10 +1926,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1351
1926
|
const value = `param${++count.i}`;
|
|
1352
1927
|
curQuery +=
|
|
1353
1928
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1929
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1930
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1931
|
+
' WHERE `guid`=' +
|
|
1354
1932
|
ieTable +
|
|
1355
|
-
'.`guid`
|
|
1356
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1357
|
-
' WHERE `name`=@' +
|
|
1933
|
+
'.`guid` AND `name`=@' +
|
|
1358
1934
|
name +
|
|
1359
1935
|
' AND `number`>@' +
|
|
1360
1936
|
value +
|
|
@@ -1367,34 +1943,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1367
1943
|
break;
|
|
1368
1944
|
case 'gte':
|
|
1369
1945
|
case '!gte':
|
|
1370
|
-
if (
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
break;
|
|
1384
|
-
} else if (curValue[0] === 'mdate') {
|
|
1946
|
+
if (
|
|
1947
|
+
curValue[0] === 'cdate' ||
|
|
1948
|
+
curValue[0] === 'mdate' ||
|
|
1949
|
+
(this.nymph.tilmeld != null &&
|
|
1950
|
+
(curValue[0] === 'user' ||
|
|
1951
|
+
curValue[0] === 'group' ||
|
|
1952
|
+
curValue[0] === 'acUser' ||
|
|
1953
|
+
curValue[0] === 'acGroup' ||
|
|
1954
|
+
curValue[0] === 'acOther' ||
|
|
1955
|
+
curValue[0] === 'acRead' ||
|
|
1956
|
+
curValue[0] === 'acWrite' ||
|
|
1957
|
+
curValue[0] === 'acFull'))
|
|
1958
|
+
) {
|
|
1385
1959
|
if (curQuery) {
|
|
1386
1960
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1387
1961
|
}
|
|
1388
|
-
const
|
|
1962
|
+
const value = `param${++count.i}`;
|
|
1389
1963
|
curQuery +=
|
|
1390
1964
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1391
1965
|
ieTable +
|
|
1392
|
-
'
|
|
1393
|
-
|
|
1394
|
-
|
|
1966
|
+
'.' +
|
|
1967
|
+
MySQLDriver.escape(curValue[0]) +
|
|
1968
|
+
'>=@' +
|
|
1969
|
+
value;
|
|
1970
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1395
1971
|
? null
|
|
1396
1972
|
: Number(curValue[1]);
|
|
1397
|
-
break;
|
|
1398
1973
|
} else {
|
|
1399
1974
|
if (curQuery) {
|
|
1400
1975
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1403,10 +1978,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1403
1978
|
const value = `param${++count.i}`;
|
|
1404
1979
|
curQuery +=
|
|
1405
1980
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1981
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
1982
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
1983
|
+
' WHERE `guid`=' +
|
|
1406
1984
|
ieTable +
|
|
1407
|
-
'.`guid`
|
|
1408
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1409
|
-
' WHERE `name`=@' +
|
|
1985
|
+
'.`guid` AND `name`=@' +
|
|
1410
1986
|
name +
|
|
1411
1987
|
' AND `number`>=@' +
|
|
1412
1988
|
value +
|
|
@@ -1419,34 +1995,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1419
1995
|
break;
|
|
1420
1996
|
case 'lt':
|
|
1421
1997
|
case '!lt':
|
|
1422
|
-
if (
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
break;
|
|
1436
|
-
} else if (curValue[0] === 'mdate') {
|
|
1998
|
+
if (
|
|
1999
|
+
curValue[0] === 'cdate' ||
|
|
2000
|
+
curValue[0] === 'mdate' ||
|
|
2001
|
+
(this.nymph.tilmeld != null &&
|
|
2002
|
+
(curValue[0] === 'user' ||
|
|
2003
|
+
curValue[0] === 'group' ||
|
|
2004
|
+
curValue[0] === 'acUser' ||
|
|
2005
|
+
curValue[0] === 'acGroup' ||
|
|
2006
|
+
curValue[0] === 'acOther' ||
|
|
2007
|
+
curValue[0] === 'acRead' ||
|
|
2008
|
+
curValue[0] === 'acWrite' ||
|
|
2009
|
+
curValue[0] === 'acFull'))
|
|
2010
|
+
) {
|
|
1437
2011
|
if (curQuery) {
|
|
1438
2012
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1439
2013
|
}
|
|
1440
|
-
const
|
|
2014
|
+
const value = `param${++count.i}`;
|
|
1441
2015
|
curQuery +=
|
|
1442
2016
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1443
2017
|
ieTable +
|
|
1444
|
-
'
|
|
1445
|
-
|
|
1446
|
-
|
|
2018
|
+
'.' +
|
|
2019
|
+
MySQLDriver.escape(curValue[0]) +
|
|
2020
|
+
'<@' +
|
|
2021
|
+
value;
|
|
2022
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1447
2023
|
? null
|
|
1448
2024
|
: Number(curValue[1]);
|
|
1449
|
-
break;
|
|
1450
2025
|
} else {
|
|
1451
2026
|
if (curQuery) {
|
|
1452
2027
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1455,10 +2030,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1455
2030
|
const value = `param${++count.i}`;
|
|
1456
2031
|
curQuery +=
|
|
1457
2032
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2033
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
2034
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
2035
|
+
' WHERE `guid`=' +
|
|
1458
2036
|
ieTable +
|
|
1459
|
-
'.`guid`
|
|
1460
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1461
|
-
' WHERE `name`=@' +
|
|
2037
|
+
'.`guid` AND `name`=@' +
|
|
1462
2038
|
name +
|
|
1463
2039
|
' AND `number`<@' +
|
|
1464
2040
|
value +
|
|
@@ -1471,34 +2047,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1471
2047
|
break;
|
|
1472
2048
|
case 'lte':
|
|
1473
2049
|
case '!lte':
|
|
1474
|
-
if (
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
break;
|
|
1488
|
-
} else if (curValue[0] === 'mdate') {
|
|
2050
|
+
if (
|
|
2051
|
+
curValue[0] === 'cdate' ||
|
|
2052
|
+
curValue[0] === 'mdate' ||
|
|
2053
|
+
(this.nymph.tilmeld != null &&
|
|
2054
|
+
(curValue[0] === 'user' ||
|
|
2055
|
+
curValue[0] === 'group' ||
|
|
2056
|
+
curValue[0] === 'acUser' ||
|
|
2057
|
+
curValue[0] === 'acGroup' ||
|
|
2058
|
+
curValue[0] === 'acOther' ||
|
|
2059
|
+
curValue[0] === 'acRead' ||
|
|
2060
|
+
curValue[0] === 'acWrite' ||
|
|
2061
|
+
curValue[0] === 'acFull'))
|
|
2062
|
+
) {
|
|
1489
2063
|
if (curQuery) {
|
|
1490
2064
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1491
2065
|
}
|
|
1492
|
-
const
|
|
2066
|
+
const value = `param${++count.i}`;
|
|
1493
2067
|
curQuery +=
|
|
1494
2068
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1495
2069
|
ieTable +
|
|
1496
|
-
'
|
|
1497
|
-
|
|
1498
|
-
|
|
2070
|
+
'.' +
|
|
2071
|
+
MySQLDriver.escape(curValue[0]) +
|
|
2072
|
+
'<=@' +
|
|
2073
|
+
value;
|
|
2074
|
+
params[value] = isNaN(Number(curValue[1]))
|
|
1499
2075
|
? null
|
|
1500
2076
|
: Number(curValue[1]);
|
|
1501
|
-
break;
|
|
1502
2077
|
} else {
|
|
1503
2078
|
if (curQuery) {
|
|
1504
2079
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1507,10 +2082,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1507
2082
|
const value = `param${++count.i}`;
|
|
1508
2083
|
curQuery +=
|
|
1509
2084
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2085
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
2086
|
+
MySQLDriver.escape(this.prefix + 'data_' + etype) +
|
|
2087
|
+
' WHERE `guid`=' +
|
|
1510
2088
|
ieTable +
|
|
1511
|
-
'.`guid`
|
|
1512
|
-
MySQLDriver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1513
|
-
' WHERE `name`=@' +
|
|
2089
|
+
'.`guid` AND `name`=@' +
|
|
1514
2090
|
name +
|
|
1515
2091
|
' AND `number`<=@' +
|
|
1516
2092
|
value +
|
|
@@ -1534,32 +2110,66 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1534
2110
|
if (curQuery) {
|
|
1535
2111
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1536
2112
|
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
2113
|
+
if (
|
|
2114
|
+
this.nymph.tilmeld != null &&
|
|
2115
|
+
(curValue[0] === 'user' || curValue[0] === 'group')
|
|
2116
|
+
) {
|
|
2117
|
+
const guid = `param${++count.i}`;
|
|
2118
|
+
curQuery +=
|
|
2119
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2120
|
+
ieTable +
|
|
2121
|
+
'.' +
|
|
2122
|
+
MySQLDriver.escape(curValue[0]) +
|
|
2123
|
+
'=UNHEX(@' +
|
|
2124
|
+
guid +
|
|
2125
|
+
')';
|
|
2126
|
+
params[guid] = curQguid;
|
|
2127
|
+
} else if (
|
|
2128
|
+
this.nymph.tilmeld != null &&
|
|
2129
|
+
(curValue[0] === 'acRead' ||
|
|
2130
|
+
curValue[0] === 'acWrite' ||
|
|
2131
|
+
curValue[0] === 'acFull')
|
|
2132
|
+
) {
|
|
2133
|
+
const queryParam = `param${++count.i}`;
|
|
2134
|
+
curQuery +=
|
|
2135
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2136
|
+
'MATCH (' +
|
|
2137
|
+
ieTable +
|
|
2138
|
+
'.' +
|
|
2139
|
+
MySQLDriver.escape(curValue[0]) +
|
|
2140
|
+
') AGAINST (@' +
|
|
2141
|
+
queryParam +
|
|
2142
|
+
' IN BOOLEAN MODE)';
|
|
2143
|
+
params[queryParam] = '+' + curQguid;
|
|
2144
|
+
} else {
|
|
2145
|
+
const name = `param${++count.i}`;
|
|
2146
|
+
const guid = `param${++count.i}`;
|
|
2147
|
+
curQuery +=
|
|
2148
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2149
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
2150
|
+
MySQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
2151
|
+
' WHERE `guid`=' +
|
|
2152
|
+
ieTable +
|
|
2153
|
+
'.`guid` AND `name`=@' +
|
|
2154
|
+
name +
|
|
2155
|
+
' AND `reference`=UNHEX(@' +
|
|
2156
|
+
guid +
|
|
2157
|
+
'))';
|
|
2158
|
+
params[name] = curValue[0];
|
|
2159
|
+
params[guid] = curQguid;
|
|
2160
|
+
}
|
|
1551
2161
|
break;
|
|
1552
2162
|
case 'selector':
|
|
1553
2163
|
case '!selector':
|
|
1554
|
-
const
|
|
1555
|
-
options,
|
|
2164
|
+
const innerquery = this.makeEntityQuery(
|
|
2165
|
+
{ ...options, sort: null, limit: undefined },
|
|
1556
2166
|
[curValue],
|
|
1557
2167
|
etype,
|
|
1558
2168
|
count,
|
|
1559
2169
|
params,
|
|
1560
2170
|
true,
|
|
1561
2171
|
tableSuffix,
|
|
1562
|
-
etypes
|
|
2172
|
+
etypes,
|
|
1563
2173
|
);
|
|
1564
2174
|
if (curQuery) {
|
|
1565
2175
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1567,61 +2177,174 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1567
2177
|
curQuery +=
|
|
1568
2178
|
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1569
2179
|
'(' +
|
|
1570
|
-
|
|
2180
|
+
innerquery.query +
|
|
1571
2181
|
')';
|
|
1572
2182
|
break;
|
|
1573
2183
|
case 'qref':
|
|
1574
2184
|
case '!qref':
|
|
2185
|
+
const referenceTableSuffix = makeTableSuffix();
|
|
1575
2186
|
const [qrefOptions, ...qrefSelectors] = curValue[1] as [
|
|
1576
2187
|
Options,
|
|
1577
|
-
...FormattedSelector[]
|
|
2188
|
+
...FormattedSelector[],
|
|
1578
2189
|
];
|
|
1579
2190
|
const QrefEntityClass = qrefOptions.class as EntityConstructor;
|
|
1580
2191
|
etypes.push(QrefEntityClass.ETYPE);
|
|
1581
|
-
const qrefQuery = this.makeEntityQuery(
|
|
1582
|
-
{ ...qrefOptions, return: 'guid', class: QrefEntityClass },
|
|
1583
|
-
qrefSelectors,
|
|
1584
|
-
QrefEntityClass.ETYPE,
|
|
1585
|
-
count,
|
|
1586
|
-
params,
|
|
1587
|
-
false,
|
|
1588
|
-
makeTableSuffix(),
|
|
1589
|
-
etypes
|
|
1590
|
-
);
|
|
1591
2192
|
if (curQuery) {
|
|
1592
2193
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1593
2194
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
(
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
2195
|
+
if (
|
|
2196
|
+
this.nymph.tilmeld != null &&
|
|
2197
|
+
(curValue[0] === 'user' || curValue[0] === 'group')
|
|
2198
|
+
) {
|
|
2199
|
+
const qrefQuery = this.makeEntityQuery(
|
|
2200
|
+
{
|
|
2201
|
+
...qrefOptions,
|
|
2202
|
+
sort: qrefOptions.sort ?? null,
|
|
2203
|
+
return: 'guid',
|
|
2204
|
+
class: QrefEntityClass,
|
|
2205
|
+
},
|
|
2206
|
+
qrefSelectors,
|
|
2207
|
+
QrefEntityClass.ETYPE,
|
|
2208
|
+
count,
|
|
2209
|
+
params,
|
|
2210
|
+
false,
|
|
2211
|
+
makeTableSuffix(),
|
|
2212
|
+
etypes,
|
|
2213
|
+
'r' +
|
|
2214
|
+
referenceTableSuffix +
|
|
2215
|
+
'.' +
|
|
2216
|
+
MySQLDriver.escape(curValue[0]),
|
|
2217
|
+
);
|
|
2218
|
+
|
|
2219
|
+
curQuery +=
|
|
2220
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2221
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
2222
|
+
MySQLDriver.escape(this.prefix + 'entities_' + etype) +
|
|
2223
|
+
' r' +
|
|
2224
|
+
referenceTableSuffix +
|
|
2225
|
+
' WHERE r' +
|
|
2226
|
+
referenceTableSuffix +
|
|
2227
|
+
'.`guid`=' +
|
|
2228
|
+
ieTable +
|
|
2229
|
+
'.`guid` AND EXISTS (' +
|
|
2230
|
+
qrefQuery.query +
|
|
2231
|
+
'))';
|
|
2232
|
+
} else if (
|
|
2233
|
+
this.nymph.tilmeld != null &&
|
|
2234
|
+
(curValue[0] === 'acRead' ||
|
|
2235
|
+
curValue[0] === 'acWrite' ||
|
|
2236
|
+
curValue[0] === 'acFull')
|
|
2237
|
+
) {
|
|
2238
|
+
const qrefQuery = this.makeEntityQuery(
|
|
2239
|
+
{
|
|
2240
|
+
...qrefOptions,
|
|
2241
|
+
sort: qrefOptions.sort ?? null,
|
|
2242
|
+
return: 'guid',
|
|
2243
|
+
class: QrefEntityClass,
|
|
2244
|
+
},
|
|
2245
|
+
qrefSelectors,
|
|
2246
|
+
QrefEntityClass.ETYPE,
|
|
2247
|
+
count,
|
|
2248
|
+
params,
|
|
2249
|
+
false,
|
|
2250
|
+
makeTableSuffix(),
|
|
2251
|
+
etypes,
|
|
2252
|
+
undefined,
|
|
2253
|
+
(guid) =>
|
|
2254
|
+
ieTable +
|
|
2255
|
+
'.' +
|
|
2256
|
+
MySQLDriver.escape(curValue[0]) +
|
|
2257
|
+
" LIKE CONCAT('% ', LOWER(HEX(" +
|
|
2258
|
+
guid +
|
|
2259
|
+
")), ' %')",
|
|
2260
|
+
);
|
|
2261
|
+
|
|
2262
|
+
curQuery +=
|
|
2263
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2264
|
+
'EXISTS (' +
|
|
2265
|
+
qrefQuery.query +
|
|
2266
|
+
')';
|
|
2267
|
+
} else {
|
|
2268
|
+
const qrefQuery = this.makeEntityQuery(
|
|
2269
|
+
{
|
|
2270
|
+
...qrefOptions,
|
|
2271
|
+
sort: qrefOptions.sort ?? null,
|
|
2272
|
+
return: 'guid',
|
|
2273
|
+
class: QrefEntityClass,
|
|
2274
|
+
},
|
|
2275
|
+
qrefSelectors,
|
|
2276
|
+
QrefEntityClass.ETYPE,
|
|
2277
|
+
count,
|
|
2278
|
+
params,
|
|
2279
|
+
false,
|
|
2280
|
+
makeTableSuffix(),
|
|
2281
|
+
etypes,
|
|
2282
|
+
'r' + referenceTableSuffix + '.`reference`',
|
|
2283
|
+
);
|
|
2284
|
+
const qrefName = `param${++count.i}`;
|
|
2285
|
+
curQuery +=
|
|
2286
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
2287
|
+
'EXISTS (SELECT `guid` FROM ' +
|
|
2288
|
+
MySQLDriver.escape(this.prefix + 'references_' + etype) +
|
|
2289
|
+
' r' +
|
|
2290
|
+
referenceTableSuffix +
|
|
2291
|
+
' WHERE r' +
|
|
2292
|
+
referenceTableSuffix +
|
|
2293
|
+
'.`guid`=' +
|
|
2294
|
+
ieTable +
|
|
2295
|
+
'.`guid` AND r' +
|
|
2296
|
+
referenceTableSuffix +
|
|
2297
|
+
'.`name`=@' +
|
|
2298
|
+
qrefName +
|
|
2299
|
+
' AND EXISTS (' +
|
|
2300
|
+
qrefQuery.query +
|
|
2301
|
+
'))';
|
|
2302
|
+
params[qrefName] = curValue[0];
|
|
2303
|
+
}
|
|
1606
2304
|
break;
|
|
1607
2305
|
}
|
|
1608
2306
|
}
|
|
1609
2307
|
return curQuery;
|
|
1610
|
-
}
|
|
2308
|
+
},
|
|
1611
2309
|
);
|
|
1612
2310
|
|
|
1613
2311
|
let sortBy: string;
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
2312
|
+
let sortByInner: string;
|
|
2313
|
+
let sortJoin = '';
|
|
2314
|
+
let sortJoinInner = '';
|
|
2315
|
+
const order = options.reverse ? ' DESC' : '';
|
|
2316
|
+
if (sort == null) {
|
|
2317
|
+
sortBy = '';
|
|
2318
|
+
sortByInner = '';
|
|
2319
|
+
} else {
|
|
2320
|
+
switch (sort) {
|
|
2321
|
+
case 'mdate':
|
|
2322
|
+
sortBy = `ORDER BY ${eTable}.\`mdate\`${order}`;
|
|
2323
|
+
sortByInner = `ORDER BY ${ieTable}.\`mdate\`${order}`;
|
|
2324
|
+
break;
|
|
2325
|
+
case 'cdate':
|
|
2326
|
+
sortBy = `ORDER BY ${eTable}.\`cdate\`${order}`;
|
|
2327
|
+
sortByInner = `ORDER BY ${ieTable}.\`cdate\`${order}`;
|
|
2328
|
+
break;
|
|
2329
|
+
default:
|
|
2330
|
+
const name = `param${++count.i}`;
|
|
2331
|
+
sortJoin = `LEFT JOIN (
|
|
2332
|
+
SELECT \`guid\`, \`string\`, \`number\`
|
|
2333
|
+
FROM ${MySQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
2334
|
+
WHERE \`name\`=@${name}
|
|
2335
|
+
ORDER BY \`number\`${order}, \`string\`${order}
|
|
2336
|
+
) ${sTable} ON ${eTable}.\`guid\`=${sTable}.\`guid\``;
|
|
2337
|
+
sortJoinInner = `LEFT JOIN (
|
|
2338
|
+
SELECT \`guid\`, \`string\`, \`number\`
|
|
2339
|
+
FROM ${MySQLDriver.escape(this.prefix + 'data_' + etype)}
|
|
2340
|
+
WHERE \`name\`=@${name}
|
|
2341
|
+
ORDER BY \`number\`${order}, \`string\`${order}
|
|
2342
|
+
) ${sTable} ON ${ieTable}.\`guid\`=${sTable}.\`guid\``;
|
|
2343
|
+
sortBy = `ORDER BY ${sTable}.\`number\`${order}, ${sTable}.\`string\`${order}`;
|
|
2344
|
+
sortByInner = sortBy;
|
|
2345
|
+
params[name] = sort;
|
|
2346
|
+
break;
|
|
2347
|
+
}
|
|
1625
2348
|
}
|
|
1626
2349
|
|
|
1627
2350
|
let query: string;
|
|
@@ -1632,31 +2355,36 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1632
2355
|
let limit = '';
|
|
1633
2356
|
if ('limit' in options) {
|
|
1634
2357
|
limit = ` LIMIT ${Math.floor(
|
|
1635
|
-
isNaN(Number(options.limit)) ? 0 : Number(options.limit)
|
|
2358
|
+
isNaN(Number(options.limit)) ? 0 : Number(options.limit),
|
|
1636
2359
|
)}`;
|
|
1637
2360
|
}
|
|
1638
2361
|
let offset = '';
|
|
1639
2362
|
if ('offset' in options) {
|
|
1640
2363
|
offset = ` OFFSET ${Math.floor(
|
|
1641
|
-
isNaN(Number(options.offset)) ? 0 : Number(options.offset)
|
|
2364
|
+
isNaN(Number(options.offset)) ? 0 : Number(options.offset),
|
|
1642
2365
|
)}`;
|
|
1643
2366
|
}
|
|
1644
2367
|
const whereClause = queryParts.join(') AND (');
|
|
2368
|
+
const guidClause = guidExplicitSelector
|
|
2369
|
+
? `${guidExplicitSelector(`${ieTable}.\`guid\``)} AND `
|
|
2370
|
+
: guidSelector
|
|
2371
|
+
? `${ieTable}.\`guid\`=${guidSelector} AND `
|
|
2372
|
+
: '';
|
|
1645
2373
|
if (options.return === 'count') {
|
|
1646
2374
|
if (limit || offset) {
|
|
1647
2375
|
query = `SELECT COUNT(${countTable}.\`guid\`) AS \`count\` FROM (
|
|
1648
2376
|
SELECT ${ieTable}.\`guid\` AS \`guid\`
|
|
1649
2377
|
FROM ${MySQLDriver.escape(
|
|
1650
|
-
`${this.prefix}entities_${etype}
|
|
2378
|
+
`${this.prefix}entities_${etype}`,
|
|
1651
2379
|
)} ${ieTable}
|
|
1652
|
-
WHERE (${whereClause})${limit}${offset}
|
|
2380
|
+
WHERE ${guidClause}(${whereClause})${limit}${offset}
|
|
1653
2381
|
) ${countTable}`;
|
|
1654
2382
|
} else {
|
|
1655
2383
|
query = `SELECT COUNT(${ieTable}.\`guid\`) AS \`count\`
|
|
1656
2384
|
FROM ${MySQLDriver.escape(
|
|
1657
|
-
`${this.prefix}entities_${etype}
|
|
2385
|
+
`${this.prefix}entities_${etype}`,
|
|
1658
2386
|
)} ${ieTable}
|
|
1659
|
-
WHERE (${whereClause})`;
|
|
2387
|
+
WHERE ${guidClause}(${whereClause})`;
|
|
1660
2388
|
}
|
|
1661
2389
|
} else if (options.return === 'guid') {
|
|
1662
2390
|
const guidColumn =
|
|
@@ -1665,38 +2393,47 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1665
2393
|
: `${ieTable}.\`guid\``;
|
|
1666
2394
|
query = `SELECT ${guidColumn} AS \`guid\`
|
|
1667
2395
|
FROM ${MySQLDriver.escape(
|
|
1668
|
-
`${this.prefix}entities_${etype}
|
|
2396
|
+
`${this.prefix}entities_${etype}`,
|
|
1669
2397
|
)} ${ieTable}
|
|
1670
|
-
|
|
1671
|
-
|
|
2398
|
+
${sortJoinInner}
|
|
2399
|
+
WHERE ${guidClause}(${whereClause})
|
|
2400
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}${ieTable}.\`guid\`${limit}${offset}`;
|
|
1672
2401
|
} else {
|
|
1673
2402
|
query = `SELECT
|
|
1674
2403
|
LOWER(HEX(${eTable}.\`guid\`)) AS \`guid\`,
|
|
1675
2404
|
${eTable}.\`tags\`,
|
|
1676
2405
|
${eTable}.\`cdate\`,
|
|
1677
2406
|
${eTable}.\`mdate\`,
|
|
2407
|
+
LOWER(HEX(${eTable}.\`user\`)) AS \`user\`,
|
|
2408
|
+
LOWER(HEX(${eTable}.\`group\`)) AS \`group\`,
|
|
2409
|
+
${eTable}.\`acUser\`,
|
|
2410
|
+
${eTable}.\`acGroup\`,
|
|
2411
|
+
${eTable}.\`acOther\`,
|
|
2412
|
+
${eTable}.\`acRead\`,
|
|
2413
|
+
${eTable}.\`acWrite\`,
|
|
2414
|
+
${eTable}.\`acFull\`,
|
|
1678
2415
|
${dTable}.\`name\`,
|
|
1679
2416
|
${dTable}.\`value\`,
|
|
1680
|
-
${
|
|
1681
|
-
${
|
|
2417
|
+
${dTable}.\`json\`,
|
|
2418
|
+
${dTable}.\`string\`,
|
|
2419
|
+
${dTable}.\`number\`
|
|
1682
2420
|
FROM ${MySQLDriver.escape(
|
|
1683
|
-
`${this.prefix}entities_${etype}
|
|
2421
|
+
`${this.prefix}entities_${etype}`,
|
|
1684
2422
|
)} ${eTable}
|
|
1685
2423
|
LEFT JOIN ${MySQLDriver.escape(
|
|
1686
|
-
`${this.prefix}data_${etype}
|
|
2424
|
+
`${this.prefix}data_${etype}`,
|
|
1687
2425
|
)} ${dTable} ON ${eTable}.\`guid\`=${dTable}.\`guid\`
|
|
1688
|
-
|
|
1689
|
-
`${this.prefix}comparisons_${etype}`
|
|
1690
|
-
)} ${cTable} ON ${dTable}.\`guid\`=${cTable}.\`guid\` AND ${dTable}.\`name\`=${cTable}.\`name\`
|
|
2426
|
+
${sortJoin}
|
|
1691
2427
|
INNER JOIN (
|
|
1692
2428
|
SELECT ${ieTable}.\`guid\`
|
|
1693
2429
|
FROM ${MySQLDriver.escape(
|
|
1694
|
-
`${this.prefix}entities_${etype}
|
|
2430
|
+
`${this.prefix}entities_${etype}`,
|
|
1695
2431
|
)} ${ieTable}
|
|
1696
|
-
|
|
1697
|
-
|
|
2432
|
+
${sortJoinInner}
|
|
2433
|
+
WHERE ${guidClause}(${whereClause})
|
|
2434
|
+
${sortByInner}${limit}${offset}
|
|
1698
2435
|
) ${fTable} ON ${eTable}.\`guid\`=${fTable}.\`guid\`
|
|
1699
|
-
ORDER BY ${eTable}
|
|
2436
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}.\`guid\``;
|
|
1700
2437
|
}
|
|
1701
2438
|
}
|
|
1702
2439
|
} else {
|
|
@@ -1706,28 +2443,33 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1706
2443
|
let limit = '';
|
|
1707
2444
|
if ('limit' in options) {
|
|
1708
2445
|
limit = ` LIMIT ${Math.floor(
|
|
1709
|
-
isNaN(Number(options.limit)) ? 0 : Number(options.limit)
|
|
2446
|
+
isNaN(Number(options.limit)) ? 0 : Number(options.limit),
|
|
1710
2447
|
)}`;
|
|
1711
2448
|
}
|
|
1712
2449
|
let offset = '';
|
|
1713
2450
|
if ('offset' in options) {
|
|
1714
2451
|
offset = ` OFFSET ${Math.floor(
|
|
1715
|
-
isNaN(Number(options.offset)) ? 0 : Number(options.offset)
|
|
2452
|
+
isNaN(Number(options.offset)) ? 0 : Number(options.offset),
|
|
1716
2453
|
)}`;
|
|
1717
2454
|
}
|
|
2455
|
+
const guidClause = guidExplicitSelector
|
|
2456
|
+
? ` WHERE ${guidExplicitSelector(`${ieTable}.\`guid\``)}`
|
|
2457
|
+
: guidSelector
|
|
2458
|
+
? ` WHERE ${ieTable}.\`guid\`=${guidSelector}`
|
|
2459
|
+
: '';
|
|
1718
2460
|
if (options.return === 'count') {
|
|
1719
2461
|
if (limit || offset) {
|
|
1720
2462
|
query = `SELECT COUNT(${countTable}.\`guid\`) AS \`count\` FROM (
|
|
1721
2463
|
SELECT ${ieTable}.\`guid\` AS \`guid\`
|
|
1722
2464
|
FROM ${MySQLDriver.escape(
|
|
1723
|
-
`${this.prefix}entities_${etype}
|
|
1724
|
-
)} ${ieTable}${limit}${offset}
|
|
2465
|
+
`${this.prefix}entities_${etype}`,
|
|
2466
|
+
)} ${ieTable}${guidClause}${limit}${offset}
|
|
1725
2467
|
) ${countTable}`;
|
|
1726
2468
|
} else {
|
|
1727
2469
|
query = `SELECT COUNT(${ieTable}.\`guid\`) AS \`count\`
|
|
1728
2470
|
FROM ${MySQLDriver.escape(
|
|
1729
|
-
`${this.prefix}entities_${etype}
|
|
1730
|
-
)} ${ieTable}`;
|
|
2471
|
+
`${this.prefix}entities_${etype}`,
|
|
2472
|
+
)} ${ieTable}${guidClause}`;
|
|
1731
2473
|
}
|
|
1732
2474
|
} else if (options.return === 'guid') {
|
|
1733
2475
|
const guidColumn =
|
|
@@ -1736,9 +2478,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1736
2478
|
: `${ieTable}.\`guid\``;
|
|
1737
2479
|
query = `SELECT ${guidColumn} AS \`guid\`
|
|
1738
2480
|
FROM ${MySQLDriver.escape(
|
|
1739
|
-
`${this.prefix}entities_${etype}
|
|
2481
|
+
`${this.prefix}entities_${etype}`,
|
|
1740
2482
|
)} ${ieTable}
|
|
1741
|
-
|
|
2483
|
+
${sortJoinInner}
|
|
2484
|
+
${guidClause}
|
|
2485
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}${ieTable}.\`guid\`${limit}${offset}`;
|
|
1742
2486
|
} else {
|
|
1743
2487
|
if (limit || offset) {
|
|
1744
2488
|
query = `SELECT
|
|
@@ -1746,47 +2490,64 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1746
2490
|
${eTable}.\`tags\`,
|
|
1747
2491
|
${eTable}.\`cdate\`,
|
|
1748
2492
|
${eTable}.\`mdate\`,
|
|
2493
|
+
LOWER(HEX(${eTable}.\`user\`)) AS \`user\`,
|
|
2494
|
+
LOWER(HEX(${eTable}.\`group\`)) AS \`group\`,
|
|
2495
|
+
${eTable}.\`acUser\`,
|
|
2496
|
+
${eTable}.\`acGroup\`,
|
|
2497
|
+
${eTable}.\`acOther\`,
|
|
2498
|
+
${eTable}.\`acRead\`,
|
|
2499
|
+
${eTable}.\`acWrite\`,
|
|
2500
|
+
${eTable}.\`acFull\`,
|
|
1749
2501
|
${dTable}.\`name\`,
|
|
1750
2502
|
${dTable}.\`value\`,
|
|
1751
|
-
${
|
|
1752
|
-
${
|
|
2503
|
+
${dTable}.\`json\`,
|
|
2504
|
+
${dTable}.\`string\`,
|
|
2505
|
+
${dTable}.\`number\`
|
|
1753
2506
|
FROM ${MySQLDriver.escape(
|
|
1754
|
-
`${this.prefix}entities_${etype}
|
|
2507
|
+
`${this.prefix}entities_${etype}`,
|
|
1755
2508
|
)} ${eTable}
|
|
1756
2509
|
LEFT JOIN ${MySQLDriver.escape(
|
|
1757
|
-
`${this.prefix}data_${etype}
|
|
2510
|
+
`${this.prefix}data_${etype}`,
|
|
1758
2511
|
)} ${dTable} ON ${eTable}.\`guid\`=${dTable}.\`guid\`
|
|
1759
|
-
|
|
1760
|
-
`${this.prefix}comparisons_${etype}`
|
|
1761
|
-
)} ${cTable} ON ${dTable}.\`guid\`=${cTable}.\`guid\` AND ${dTable}.\`name\`=${cTable}.\`name\`
|
|
2512
|
+
${sortJoin}
|
|
1762
2513
|
INNER JOIN (
|
|
1763
2514
|
SELECT ${ieTable}.\`guid\`
|
|
1764
2515
|
FROM ${MySQLDriver.escape(
|
|
1765
|
-
`${this.prefix}entities_${etype}
|
|
2516
|
+
`${this.prefix}entities_${etype}`,
|
|
1766
2517
|
)} ${ieTable}
|
|
1767
|
-
|
|
2518
|
+
${sortJoinInner}
|
|
2519
|
+
${guidClause}
|
|
2520
|
+
${sortByInner}${limit}${offset}
|
|
1768
2521
|
) ${fTable} ON ${eTable}.\`guid\`=${fTable}.\`guid\`
|
|
1769
|
-
ORDER BY ${eTable}
|
|
2522
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}.\`guid\``;
|
|
1770
2523
|
} else {
|
|
1771
2524
|
query = `SELECT
|
|
1772
2525
|
LOWER(HEX(${eTable}.\`guid\`)) AS \`guid\`,
|
|
1773
2526
|
${eTable}.\`tags\`,
|
|
1774
2527
|
${eTable}.\`cdate\`,
|
|
1775
2528
|
${eTable}.\`mdate\`,
|
|
2529
|
+
LOWER(HEX(${eTable}.\`user\`)) AS \`user\`,
|
|
2530
|
+
LOWER(HEX(${eTable}.\`group\`)) AS \`group\`,
|
|
2531
|
+
${eTable}.\`acUser\`,
|
|
2532
|
+
${eTable}.\`acGroup\`,
|
|
2533
|
+
${eTable}.\`acOther\`,
|
|
2534
|
+
${eTable}.\`acRead\`,
|
|
2535
|
+
${eTable}.\`acWrite\`,
|
|
2536
|
+
${eTable}.\`acFull\`,
|
|
1776
2537
|
${dTable}.\`name\`,
|
|
1777
2538
|
${dTable}.\`value\`,
|
|
1778
|
-
${
|
|
1779
|
-
${
|
|
2539
|
+
${dTable}.\`json\`,
|
|
2540
|
+
${dTable}.\`string\`,
|
|
2541
|
+
${dTable}.\`number\`
|
|
1780
2542
|
FROM ${MySQLDriver.escape(
|
|
1781
|
-
`${this.prefix}entities_${etype}
|
|
2543
|
+
`${this.prefix}entities_${etype}`,
|
|
1782
2544
|
)} ${eTable}
|
|
1783
2545
|
LEFT JOIN ${MySQLDriver.escape(
|
|
1784
|
-
`${this.prefix}data_${etype}
|
|
2546
|
+
`${this.prefix}data_${etype}`,
|
|
1785
2547
|
)} ${dTable} ON ${eTable}.\`guid\`=${dTable}.\`guid\`
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
ORDER BY ${eTable}.${sortBy}`;
|
|
2548
|
+
${sortJoin}
|
|
2549
|
+
${guidExplicitSelector ? `WHERE ${guidExplicitSelector(`${eTable}.\`guid\``)}` : guidSelector ? `WHERE ${eTable}.\`guid\`=${guidSelector}` : ''}
|
|
2550
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}.\`guid\``;
|
|
1790
2551
|
}
|
|
1791
2552
|
}
|
|
1792
2553
|
}
|
|
@@ -1806,38 +2567,18 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1806
2567
|
protected performQuery(
|
|
1807
2568
|
options: Options,
|
|
1808
2569
|
formattedSelectors: FormattedSelector[],
|
|
1809
|
-
etype: string
|
|
2570
|
+
etype: string,
|
|
1810
2571
|
): {
|
|
1811
2572
|
result: any;
|
|
1812
2573
|
} {
|
|
1813
2574
|
const { query, params, etypes } = this.makeEntityQuery(
|
|
1814
2575
|
options,
|
|
1815
2576
|
formattedSelectors,
|
|
1816
|
-
etype
|
|
1817
|
-
);
|
|
1818
|
-
const result = this.queryIter(query, { etypes, params }).then((val) =>
|
|
1819
|
-
val[Symbol.iterator]()
|
|
2577
|
+
etype,
|
|
1820
2578
|
);
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
};
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
protected performQuerySync(
|
|
1827
|
-
options: Options,
|
|
1828
|
-
formattedSelectors: FormattedSelector[],
|
|
1829
|
-
etype: string
|
|
1830
|
-
): {
|
|
1831
|
-
result: any;
|
|
1832
|
-
} {
|
|
1833
|
-
const { query, params, etypes } = this.makeEntityQuery(
|
|
1834
|
-
options,
|
|
1835
|
-
formattedSelectors,
|
|
1836
|
-
etype
|
|
2579
|
+
const result = this.queryArray(query, { etypes, params }).then((val) =>
|
|
2580
|
+
val[Symbol.iterator](),
|
|
1837
2581
|
);
|
|
1838
|
-
const result = (this.queryIterSync(query, { etypes, params }) || [])[
|
|
1839
|
-
Symbol.iterator
|
|
1840
|
-
]();
|
|
1841
2582
|
return {
|
|
1842
2583
|
result,
|
|
1843
2584
|
};
|
|
@@ -1851,20 +2592,26 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1851
2592
|
options: Options<T> & { return: 'guid' },
|
|
1852
2593
|
...selectors: Selector[]
|
|
1853
2594
|
): Promise<string[]>;
|
|
2595
|
+
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
2596
|
+
options: Options<T> & { return: 'object' },
|
|
2597
|
+
...selectors: Selector[]
|
|
2598
|
+
): Promise<EntityObjectType<T>[]>;
|
|
1854
2599
|
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
1855
2600
|
options?: Options<T>,
|
|
1856
2601
|
...selectors: Selector[]
|
|
1857
|
-
): Promise<
|
|
2602
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
1858
2603
|
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
1859
2604
|
options: Options<T> = {},
|
|
1860
2605
|
...selectors: Selector[]
|
|
1861
|
-
): Promise<
|
|
1862
|
-
|
|
2606
|
+
): Promise<
|
|
2607
|
+
EntityInstanceType<T>[] | EntityObjectType<T>[] | string[] | number
|
|
2608
|
+
> {
|
|
2609
|
+
const { result: resultPromise, process } = this.getEntitiesRowLike<T>(
|
|
1863
2610
|
// @ts-ignore: options is correct here.
|
|
1864
2611
|
options,
|
|
1865
2612
|
selectors,
|
|
1866
|
-
(options,
|
|
1867
|
-
this.performQuery(options,
|
|
2613
|
+
({ options, selectors, etype }) =>
|
|
2614
|
+
this.performQuery(options, selectors, etype),
|
|
1868
2615
|
() => {
|
|
1869
2616
|
const next: any = result.next();
|
|
1870
2617
|
return next.done ? null : next.value;
|
|
@@ -1873,9 +2620,23 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1873
2620
|
(row) => Number(row.count),
|
|
1874
2621
|
(row) => row.guid,
|
|
1875
2622
|
(row) => ({
|
|
1876
|
-
tags:
|
|
1877
|
-
|
|
1878
|
-
|
|
2623
|
+
tags:
|
|
2624
|
+
row.tags.length > 2
|
|
2625
|
+
? row.tags
|
|
2626
|
+
.slice(1, -1)
|
|
2627
|
+
.split(' ')
|
|
2628
|
+
.filter((tag: string) => tag)
|
|
2629
|
+
: [],
|
|
2630
|
+
cdate: isNaN(Number(row.cdate)) ? Date.now() : Number(row.cdate),
|
|
2631
|
+
mdate: isNaN(Number(row.mdate)) ? Date.now() : Number(row.mdate),
|
|
2632
|
+
user: row.user,
|
|
2633
|
+
group: row.group,
|
|
2634
|
+
acUser: row.acUser,
|
|
2635
|
+
acGroup: row.acGroup,
|
|
2636
|
+
acOther: row.acOther,
|
|
2637
|
+
acRead: row.acRead?.slice(1, -1).split(' ') ?? [],
|
|
2638
|
+
acWrite: row.acWrite?.slice(1, -1).split(' ') ?? [],
|
|
2639
|
+
acFull: row.acFull?.slice(1, -1).split(' ') ?? [],
|
|
1879
2640
|
}),
|
|
1880
2641
|
(row) => ({
|
|
1881
2642
|
name: row.name,
|
|
@@ -1883,9 +2644,11 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1883
2644
|
row.value === 'N'
|
|
1884
2645
|
? JSON.stringify(row.number)
|
|
1885
2646
|
: row.value === 'S'
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
2647
|
+
? JSON.stringify(row.string)
|
|
2648
|
+
: row.value === 'J'
|
|
2649
|
+
? JSON.stringify(row.json)
|
|
2650
|
+
: row.value,
|
|
2651
|
+
}),
|
|
1889
2652
|
);
|
|
1890
2653
|
|
|
1891
2654
|
const result = await resultPromise;
|
|
@@ -1896,231 +2659,374 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
1896
2659
|
return value;
|
|
1897
2660
|
}
|
|
1898
2661
|
|
|
1899
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1900
|
-
options: Options<T> & { return: 'count' },
|
|
1901
|
-
...selectors: Selector[]
|
|
1902
|
-
): number;
|
|
1903
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1904
|
-
options: Options<T> & { return: 'guid' },
|
|
1905
|
-
...selectors: Selector[]
|
|
1906
|
-
): string[];
|
|
1907
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1908
|
-
options?: Options<T>,
|
|
1909
|
-
...selectors: Selector[]
|
|
1910
|
-
): ReturnType<T['factorySync']>[];
|
|
1911
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1912
|
-
options: Options<T> = {},
|
|
1913
|
-
...selectors: Selector[]
|
|
1914
|
-
): ReturnType<T['factorySync']>[] | string[] | number {
|
|
1915
|
-
const { result, process } = this.getEntitesRowLike<T>(
|
|
1916
|
-
// @ts-ignore: options is correct here.
|
|
1917
|
-
options,
|
|
1918
|
-
selectors,
|
|
1919
|
-
(options, formattedSelectors, etype) =>
|
|
1920
|
-
this.performQuerySync(options, formattedSelectors, etype),
|
|
1921
|
-
() => {
|
|
1922
|
-
const next: any = result.next();
|
|
1923
|
-
return next.done ? null : next.value;
|
|
1924
|
-
},
|
|
1925
|
-
() => undefined,
|
|
1926
|
-
(row) => Number(row.count),
|
|
1927
|
-
(row) => row.guid,
|
|
1928
|
-
(row) => ({
|
|
1929
|
-
tags: row.tags.length > 2 ? row.tags.slice(1, -1).split(',') : [],
|
|
1930
|
-
cdate: isNaN(Number(row.cdate)) ? null : Number(row.cdate),
|
|
1931
|
-
mdate: isNaN(Number(row.mdate)) ? null : Number(row.mdate),
|
|
1932
|
-
}),
|
|
1933
|
-
(row) => ({
|
|
1934
|
-
name: row.name,
|
|
1935
|
-
svalue:
|
|
1936
|
-
row.value === 'N'
|
|
1937
|
-
? JSON.stringify(row.number)
|
|
1938
|
-
: row.value === 'S'
|
|
1939
|
-
? JSON.stringify(row.string)
|
|
1940
|
-
: row.value,
|
|
1941
|
-
})
|
|
1942
|
-
);
|
|
1943
|
-
const value = process();
|
|
1944
|
-
if (value instanceof Error) {
|
|
1945
|
-
throw value;
|
|
1946
|
-
}
|
|
1947
|
-
return value;
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
2662
|
public async getUID(name: string) {
|
|
1951
2663
|
if (name == null) {
|
|
1952
2664
|
throw new InvalidParametersError('Name not given for UID.');
|
|
1953
2665
|
}
|
|
1954
2666
|
const result = await this.queryGet(
|
|
1955
2667
|
`SELECT \`cur_uid\` FROM ${MySQLDriver.escape(
|
|
1956
|
-
`${this.prefix}uids
|
|
2668
|
+
`${this.prefix}uids`,
|
|
1957
2669
|
)} WHERE \`name\`=@name;`,
|
|
1958
2670
|
{
|
|
1959
2671
|
params: {
|
|
1960
2672
|
name: name,
|
|
1961
2673
|
},
|
|
1962
|
-
}
|
|
2674
|
+
},
|
|
1963
2675
|
);
|
|
1964
2676
|
return (result?.cur_uid as number | null) ?? null;
|
|
1965
2677
|
}
|
|
1966
2678
|
|
|
1967
|
-
public async
|
|
2679
|
+
public async importEntity(entity: {
|
|
2680
|
+
guid: string;
|
|
2681
|
+
cdate: number;
|
|
2682
|
+
mdate: number;
|
|
2683
|
+
tags: string[];
|
|
2684
|
+
sdata: SerializedEntityData;
|
|
2685
|
+
etype: string;
|
|
2686
|
+
}) {
|
|
2687
|
+
return await this.importEntityInternal(entity);
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
public async importEntityTokens(entity: {
|
|
2691
|
+
guid: string;
|
|
2692
|
+
cdate: number;
|
|
2693
|
+
mdate: number;
|
|
2694
|
+
tags: string[];
|
|
2695
|
+
sdata: SerializedEntityData;
|
|
2696
|
+
etype: string;
|
|
2697
|
+
}) {
|
|
2698
|
+
return await this.importEntityInternal(entity, { only: 'tokens' });
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
public async importEntityTilmeldAC(entity: {
|
|
2702
|
+
guid: string;
|
|
2703
|
+
cdate: number;
|
|
2704
|
+
mdate: number;
|
|
2705
|
+
tags: string[];
|
|
2706
|
+
sdata: SerializedEntityData;
|
|
2707
|
+
etype: string;
|
|
2708
|
+
}) {
|
|
2709
|
+
return await this.importEntityInternal(entity, { only: 'tilmeldAC' });
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
private async importEntityInternal(
|
|
2713
|
+
{
|
|
2714
|
+
guid,
|
|
2715
|
+
cdate,
|
|
2716
|
+
mdate,
|
|
2717
|
+
tags,
|
|
2718
|
+
sdata,
|
|
2719
|
+
etype,
|
|
2720
|
+
}: {
|
|
2721
|
+
guid: string;
|
|
2722
|
+
cdate: number;
|
|
2723
|
+
mdate: number;
|
|
2724
|
+
tags: string[];
|
|
2725
|
+
sdata: SerializedEntityData;
|
|
2726
|
+
etype: string;
|
|
2727
|
+
},
|
|
2728
|
+
{ only = undefined }: { only?: 'tokens' | 'tilmeldAC' } = {},
|
|
2729
|
+
) {
|
|
1968
2730
|
try {
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
`
|
|
1974
|
-
`${this.prefix}entities_${etype}
|
|
1975
|
-
)}
|
|
2731
|
+
let promises = [];
|
|
2732
|
+
if (only == null) {
|
|
2733
|
+
promises.push(
|
|
2734
|
+
this.queryRun(
|
|
2735
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
2736
|
+
`${this.prefix}entities_${etype}`,
|
|
2737
|
+
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
1976
2738
|
{
|
|
1977
2739
|
etypes: [etype],
|
|
1978
2740
|
params: {
|
|
1979
2741
|
guid,
|
|
1980
|
-
tags: ' ' + tags.join(' ') + ' ',
|
|
1981
|
-
cdate: isNaN(Number(JSON.parse(sdata.cdate)))
|
|
1982
|
-
? null
|
|
1983
|
-
: Number(JSON.parse(sdata.cdate)),
|
|
1984
|
-
mdate: isNaN(Number(JSON.parse(sdata.mdate)))
|
|
1985
|
-
? null
|
|
1986
|
-
: Number(JSON.parse(sdata.mdate)),
|
|
1987
2742
|
},
|
|
1988
|
-
}
|
|
1989
|
-
)
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2743
|
+
},
|
|
2744
|
+
),
|
|
2745
|
+
);
|
|
2746
|
+
promises.push(
|
|
2747
|
+
this.queryRun(
|
|
2748
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
2749
|
+
`${this.prefix}data_${etype}`,
|
|
2750
|
+
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
2751
|
+
{
|
|
2752
|
+
etypes: [etype],
|
|
2753
|
+
params: {
|
|
2754
|
+
guid,
|
|
2755
|
+
},
|
|
2756
|
+
},
|
|
2757
|
+
),
|
|
2758
|
+
);
|
|
2759
|
+
promises.push(
|
|
2760
|
+
this.queryRun(
|
|
2761
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
2762
|
+
`${this.prefix}references_${etype}`,
|
|
2763
|
+
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
2764
|
+
{
|
|
2765
|
+
etypes: [etype],
|
|
2766
|
+
params: {
|
|
2767
|
+
guid,
|
|
2768
|
+
},
|
|
2769
|
+
},
|
|
2770
|
+
),
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
if (only == null || only === 'tokens') {
|
|
2774
|
+
promises.push(
|
|
2775
|
+
this.queryRun(
|
|
2776
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
2777
|
+
`${this.prefix}tokens_${etype}`,
|
|
2778
|
+
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
2779
|
+
{
|
|
2780
|
+
etypes: [etype],
|
|
2781
|
+
params: {
|
|
2782
|
+
guid,
|
|
2783
|
+
},
|
|
2784
|
+
},
|
|
2785
|
+
),
|
|
2786
|
+
);
|
|
2787
|
+
}
|
|
2788
|
+
if (only == null) {
|
|
2789
|
+
promises.push(
|
|
2790
|
+
this.queryRun(
|
|
2791
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
2792
|
+
`${this.prefix}uniques_${etype}`,
|
|
2793
|
+
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
2794
|
+
{
|
|
2795
|
+
etypes: [etype],
|
|
2796
|
+
params: {
|
|
2797
|
+
guid,
|
|
2798
|
+
},
|
|
2799
|
+
},
|
|
2800
|
+
),
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
await Promise.all(promises);
|
|
2805
|
+
promises = [];
|
|
2806
|
+
|
|
2807
|
+
if (only == null) {
|
|
2808
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } =
|
|
2809
|
+
this.removeAndReturnACValues(etype, {}, sdata);
|
|
2810
|
+
|
|
2811
|
+
await this.queryRun(
|
|
2812
|
+
`INSERT INTO ${MySQLDriver.escape(
|
|
2813
|
+
`${this.prefix}entities_${etype}`,
|
|
2814
|
+
)} (\`guid\`, \`tags\`, \`cdate\`, \`mdate\`, \`user\`, \`group\`, \`acUser\`, \`acGroup\`, \`acOther\`, \`acRead\`, \`acWrite\`, \`acFull\`) VALUES (UNHEX(@guid), @tags, @cdate, @mdate, ${user == null ? '@user' : 'UNHEX(@user)'}, ${group == null ? '@group' : 'UNHEX(@group)'}, @acUser, @acGroup, @acOther, @acRead, @acWrite, @acFull);`,
|
|
2815
|
+
{
|
|
2816
|
+
etypes: [etype],
|
|
2817
|
+
params: {
|
|
2818
|
+
guid,
|
|
2819
|
+
tags: ' ' + tags.join(' ') + ' ',
|
|
2820
|
+
cdate: isNaN(cdate) ? null : cdate,
|
|
2821
|
+
mdate: isNaN(mdate) ? null : mdate,
|
|
2822
|
+
user,
|
|
2823
|
+
group,
|
|
2824
|
+
acUser,
|
|
2825
|
+
acGroup,
|
|
2826
|
+
acOther,
|
|
2827
|
+
acRead: acRead && ' ' + acRead.join(' ') + ' ',
|
|
2828
|
+
acWrite: acWrite && ' ' + acWrite.join(' ') + ' ',
|
|
2829
|
+
acFull: acFull && ' ' + acFull.join(' ') + ' ',
|
|
2830
|
+
},
|
|
2831
|
+
},
|
|
2832
|
+
);
|
|
2833
|
+
|
|
2834
|
+
for (const name in sdata) {
|
|
2835
|
+
const value = sdata[name];
|
|
2836
|
+
const uvalue = JSON.parse(value);
|
|
2837
|
+
if (value === undefined) {
|
|
2838
|
+
continue;
|
|
2839
|
+
}
|
|
2840
|
+
const storageValue =
|
|
2841
|
+
typeof uvalue === 'number'
|
|
2842
|
+
? 'N'
|
|
2843
|
+
: typeof uvalue === 'string'
|
|
2844
|
+
? 'S'
|
|
2845
|
+
: 'J';
|
|
2846
|
+
const jsonValue = storageValue === 'J' ? value : null;
|
|
2847
|
+
|
|
2017
2848
|
promises.push(
|
|
2018
2849
|
this.queryRun(
|
|
2019
|
-
`
|
|
2020
|
-
`${this.prefix}
|
|
2021
|
-
)}
|
|
2850
|
+
`INSERT INTO ${MySQLDriver.escape(
|
|
2851
|
+
`${this.prefix}data_${etype}`,
|
|
2852
|
+
)} (\`guid\`, \`name\`, \`value\`, \`json\`, \`string\`, \`number\`, \`truthy\`) VALUES (UNHEX(@guid), @name, @storageValue, @jsonValue, @string, @number, @truthy);`,
|
|
2022
2853
|
{
|
|
2023
2854
|
etypes: [etype],
|
|
2024
2855
|
params: {
|
|
2025
2856
|
guid,
|
|
2857
|
+
name,
|
|
2858
|
+
storageValue,
|
|
2859
|
+
jsonValue,
|
|
2860
|
+
string: storageValue === 'J' ? null : `${uvalue}`,
|
|
2861
|
+
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
2862
|
+
truthy: !!uvalue,
|
|
2026
2863
|
},
|
|
2027
|
-
}
|
|
2028
|
-
)
|
|
2029
|
-
);
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
for (const name in sdata) {
|
|
2034
|
-
const value = sdata[name];
|
|
2035
|
-
const uvalue = JSON.parse(value);
|
|
2036
|
-
if (value === undefined) {
|
|
2037
|
-
continue;
|
|
2038
|
-
}
|
|
2039
|
-
const storageValue =
|
|
2040
|
-
typeof uvalue === 'number'
|
|
2041
|
-
? 'N'
|
|
2042
|
-
: typeof uvalue === 'string'
|
|
2043
|
-
? 'S'
|
|
2044
|
-
: value;
|
|
2045
|
-
const promises = [];
|
|
2046
|
-
promises.push(
|
|
2047
|
-
this.queryRun(
|
|
2048
|
-
`INSERT INTO ${MySQLDriver.escape(
|
|
2049
|
-
`${this.prefix}data_${etype}`
|
|
2050
|
-
)} (\`guid\`, \`name\`, \`value\`) VALUES (UNHEX(@guid), @name, @storageValue);`,
|
|
2051
|
-
{
|
|
2052
|
-
etypes: [etype],
|
|
2053
|
-
params: {
|
|
2054
|
-
guid,
|
|
2055
|
-
name,
|
|
2056
|
-
storageValue,
|
|
2057
|
-
},
|
|
2058
|
-
}
|
|
2059
|
-
)
|
|
2060
|
-
);
|
|
2864
|
+
},
|
|
2865
|
+
),
|
|
2866
|
+
);
|
|
2867
|
+
|
|
2868
|
+
const references = this.findReferences(value);
|
|
2869
|
+
for (const reference of references) {
|
|
2061
2870
|
promises.push(
|
|
2062
2871
|
this.queryRun(
|
|
2063
2872
|
`INSERT INTO ${MySQLDriver.escape(
|
|
2064
|
-
`${this.prefix}
|
|
2065
|
-
)} (\`guid\`, \`name\`, \`
|
|
2873
|
+
`${this.prefix}references_${etype}`,
|
|
2874
|
+
)} (\`guid\`, \`name\`, \`reference\`) VALUES (UNHEX(@guid), @name, UNHEX(@reference));`,
|
|
2066
2875
|
{
|
|
2067
2876
|
etypes: [etype],
|
|
2068
2877
|
params: {
|
|
2069
2878
|
guid,
|
|
2070
2879
|
name,
|
|
2071
|
-
|
|
2072
|
-
string: `${uvalue}`,
|
|
2073
|
-
number: isNaN(Number(uvalue)) ? null : Number(uvalue),
|
|
2880
|
+
reference,
|
|
2074
2881
|
},
|
|
2075
|
-
}
|
|
2076
|
-
)
|
|
2882
|
+
},
|
|
2883
|
+
),
|
|
2077
2884
|
);
|
|
2078
|
-
|
|
2079
|
-
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
if (only === 'tilmeldAC') {
|
|
2890
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } =
|
|
2891
|
+
this.removeAndReturnACValues(etype, {}, sdata);
|
|
2892
|
+
|
|
2893
|
+
promises.push(
|
|
2894
|
+
this.queryRun(
|
|
2895
|
+
`UPDATE ${MySQLDriver.escape(
|
|
2896
|
+
`${this.prefix}entities_${etype}`,
|
|
2897
|
+
)} SET \`user\`=${user == null ? '@user' : 'UNHEX(@user)'}, \`group\`=${group == null ? '@group' : 'UNHEX(@group)'}, \`acUser\`=@acUser, \`acGroup\`=@acGroup, \`acOther\`=@acOther, \`acRead\`=@acRead, \`acWrite\`=@acWrite, \`acFull\`=@acFull WHERE \`guid\`=UNHEX(@guid);`,
|
|
2898
|
+
{
|
|
2899
|
+
etypes: [etype],
|
|
2900
|
+
params: {
|
|
2901
|
+
user,
|
|
2902
|
+
group,
|
|
2903
|
+
acUser,
|
|
2904
|
+
acGroup,
|
|
2905
|
+
acOther,
|
|
2906
|
+
acRead: acRead && ' ' + acRead.join(' ') + ' ',
|
|
2907
|
+
acWrite: acWrite && ' ' + acWrite.join(' ') + ' ',
|
|
2908
|
+
acFull: acFull && ' ' + acFull.join(' ') + ' ',
|
|
2909
|
+
guid,
|
|
2910
|
+
},
|
|
2911
|
+
},
|
|
2912
|
+
),
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
2917
|
+
|
|
2918
|
+
if (only == null || only === 'tokens') {
|
|
2919
|
+
for (let name in sdata) {
|
|
2920
|
+
let tokenString: string | null = null;
|
|
2921
|
+
try {
|
|
2922
|
+
tokenString = EntityClass.getFTSText(name, JSON.parse(sdata[name]));
|
|
2923
|
+
} catch (e: any) {
|
|
2924
|
+
// Ignore error.
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
if (tokenString != null) {
|
|
2928
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2929
|
+
while (tokens.length) {
|
|
2930
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2931
|
+
const params: { [k: string]: any } = {
|
|
2932
|
+
guid,
|
|
2933
|
+
name,
|
|
2934
|
+
};
|
|
2935
|
+
const values: string[] = [];
|
|
2936
|
+
|
|
2937
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2938
|
+
const token = currentTokens[i];
|
|
2939
|
+
params['token' + i] = token.token;
|
|
2940
|
+
params['position' + i] = token.position;
|
|
2941
|
+
params['stem' + i] = token.stem;
|
|
2942
|
+
values.push(
|
|
2943
|
+
'(UNHEX(@guid), @name, @token' +
|
|
2944
|
+
i +
|
|
2945
|
+
', @position' +
|
|
2946
|
+
i +
|
|
2947
|
+
', @stem' +
|
|
2948
|
+
i +
|
|
2949
|
+
')',
|
|
2950
|
+
);
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2080
2953
|
promises.push(
|
|
2081
2954
|
this.queryRun(
|
|
2082
2955
|
`INSERT INTO ${MySQLDriver.escape(
|
|
2083
|
-
`${this.prefix}
|
|
2084
|
-
)} (\`guid\`, \`name\`, \`
|
|
2956
|
+
`${this.prefix}tokens_${etype}`,
|
|
2957
|
+
)} (\`guid\`, \`name\`, \`token\`, \`position\`, \`stem\`) VALUES ${values.join(', ')};`,
|
|
2085
2958
|
{
|
|
2086
2959
|
etypes: [etype],
|
|
2087
|
-
params
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
reference,
|
|
2091
|
-
},
|
|
2092
|
-
}
|
|
2093
|
-
)
|
|
2960
|
+
params,
|
|
2961
|
+
},
|
|
2962
|
+
),
|
|
2094
2963
|
);
|
|
2095
2964
|
}
|
|
2096
2965
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
if (only == null) {
|
|
2970
|
+
const uniques = await EntityClass.getUniques({
|
|
2971
|
+
guid,
|
|
2972
|
+
cdate,
|
|
2973
|
+
mdate,
|
|
2974
|
+
tags,
|
|
2975
|
+
data: {},
|
|
2976
|
+
sdata,
|
|
2977
|
+
});
|
|
2978
|
+
for (const unique of uniques) {
|
|
2979
|
+
promises.push(
|
|
2980
|
+
this.queryRun(
|
|
2981
|
+
`INSERT INTO ${MySQLDriver.escape(
|
|
2982
|
+
`${this.prefix}uniques_${etype}`,
|
|
2983
|
+
)} (\`guid\`, \`unique\`) VALUES (UNHEX(@guid), @unique);`,
|
|
2984
|
+
{
|
|
2985
|
+
etypes: [etype],
|
|
2986
|
+
params: {
|
|
2987
|
+
guid,
|
|
2988
|
+
unique,
|
|
2989
|
+
},
|
|
2108
2990
|
},
|
|
2109
|
-
|
|
2991
|
+
).catch((e: any) => {
|
|
2992
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2993
|
+
this.nymph.config.debugError(
|
|
2994
|
+
'mysql',
|
|
2995
|
+
`Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2998
|
+
return e;
|
|
2999
|
+
}),
|
|
2110
3000
|
);
|
|
2111
|
-
},
|
|
2112
|
-
async () => {
|
|
2113
|
-
await this.internalTransaction('nymph-import');
|
|
2114
|
-
},
|
|
2115
|
-
async () => {
|
|
2116
|
-
await this.commit('nymph-import');
|
|
2117
3001
|
}
|
|
2118
|
-
|
|
3002
|
+
}
|
|
2119
3003
|
|
|
2120
|
-
|
|
3004
|
+
await Promise.all(promises);
|
|
3005
|
+
} catch (e: any) {
|
|
3006
|
+
this.nymph.config.debugError('mysql', `Import entity error: "${e}"`);
|
|
3007
|
+
throw e;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
public async importUID({ name, value }: { name: string; value: number }) {
|
|
3012
|
+
try {
|
|
3013
|
+
await this.internalTransaction(`nymph-import-uid-${name}`);
|
|
3014
|
+
await this.queryRun(
|
|
3015
|
+
`INSERT INTO ${MySQLDriver.escape(
|
|
3016
|
+
`${this.prefix}uids`,
|
|
3017
|
+
)} (\`name\`, \`cur_uid\`) VALUES (@name, @value) ON DUPLICATE KEY UPDATE \`cur_uid\`=@value;`,
|
|
3018
|
+
{
|
|
3019
|
+
params: {
|
|
3020
|
+
name,
|
|
3021
|
+
value,
|
|
3022
|
+
},
|
|
3023
|
+
},
|
|
3024
|
+
);
|
|
3025
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
2121
3026
|
} catch (e: any) {
|
|
2122
|
-
|
|
2123
|
-
|
|
3027
|
+
this.nymph.config.debugError('mysql', `Import UID error: "${e}"`);
|
|
3028
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
3029
|
+
throw e;
|
|
2124
3030
|
}
|
|
2125
3031
|
}
|
|
2126
3032
|
|
|
@@ -2129,55 +3035,57 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2129
3035
|
throw new InvalidParametersError('Name not given for UID.');
|
|
2130
3036
|
}
|
|
2131
3037
|
await this.internalTransaction('nymph-newuid');
|
|
3038
|
+
let curUid: number | null = null;
|
|
2132
3039
|
try {
|
|
2133
3040
|
const lock = await this.queryGet(
|
|
2134
3041
|
`SELECT GET_LOCK(${MySQLDriver.escapeValue(
|
|
2135
|
-
`${this.prefix}uids_${name}
|
|
2136
|
-
)}, 10) AS \`lock
|
|
3042
|
+
`${this.prefix}uids_${name}`,
|
|
3043
|
+
)}, 10) AS \`lock\`;`,
|
|
2137
3044
|
);
|
|
2138
3045
|
if (lock.lock !== 1) {
|
|
2139
3046
|
throw new QueryFailedError("Couldn't get lock for UID: " + name);
|
|
2140
3047
|
}
|
|
2141
|
-
let curUid: number | null = null;
|
|
2142
3048
|
await this.queryRun(
|
|
2143
3049
|
`INSERT INTO ${MySQLDriver.escape(
|
|
2144
|
-
`${this.prefix}uids
|
|
3050
|
+
`${this.prefix}uids`,
|
|
2145
3051
|
)} (\`name\`, \`cur_uid\`) VALUES (@name, 1) ON DUPLICATE KEY UPDATE \`cur_uid\`=\`cur_uid\`+1;`,
|
|
2146
3052
|
{
|
|
2147
3053
|
params: {
|
|
2148
3054
|
name,
|
|
2149
3055
|
},
|
|
2150
|
-
}
|
|
3056
|
+
},
|
|
2151
3057
|
);
|
|
2152
3058
|
const result = await this.queryGet(
|
|
2153
3059
|
`SELECT \`cur_uid\` FROM ${MySQLDriver.escape(
|
|
2154
|
-
`${this.prefix}uids
|
|
3060
|
+
`${this.prefix}uids`,
|
|
2155
3061
|
)} WHERE \`name\`=@name;`,
|
|
2156
3062
|
{
|
|
2157
3063
|
params: {
|
|
2158
3064
|
name,
|
|
2159
3065
|
},
|
|
2160
|
-
}
|
|
3066
|
+
},
|
|
2161
3067
|
);
|
|
2162
3068
|
await this.queryRun(
|
|
2163
3069
|
`SELECT RELEASE_LOCK(${MySQLDriver.escapeValue(
|
|
2164
|
-
`${this.prefix}uids_${name}
|
|
2165
|
-
)})
|
|
3070
|
+
`${this.prefix}uids_${name}`,
|
|
3071
|
+
)});`,
|
|
2166
3072
|
);
|
|
2167
|
-
await this.commit('nymph-newuid');
|
|
2168
3073
|
curUid = result.cur_uid ?? null;
|
|
2169
|
-
return curUid;
|
|
2170
3074
|
} catch (e: any) {
|
|
3075
|
+
this.nymph.config.debugError('mysql', `New UID error: "${e}"`);
|
|
2171
3076
|
if (e?.message !== "Couldn't get lock for UID: " + name) {
|
|
2172
3077
|
await this.queryRun(
|
|
2173
3078
|
`SELECT RELEASE_LOCK(${MySQLDriver.escapeValue(
|
|
2174
|
-
`${this.prefix}uids_${name}
|
|
2175
|
-
)})
|
|
3079
|
+
`${this.prefix}uids_${name}`,
|
|
3080
|
+
)});`,
|
|
2176
3081
|
);
|
|
2177
3082
|
}
|
|
2178
3083
|
await this.rollback('nymph-newuid');
|
|
2179
3084
|
throw e;
|
|
2180
3085
|
}
|
|
3086
|
+
|
|
3087
|
+
await this.commit('nymph-newuid');
|
|
3088
|
+
return curUid;
|
|
2181
3089
|
}
|
|
2182
3090
|
|
|
2183
3091
|
public async renameUID(oldName: string, newName: string) {
|
|
@@ -2186,14 +3094,14 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2186
3094
|
}
|
|
2187
3095
|
await this.queryRun(
|
|
2188
3096
|
`UPDATE ${MySQLDriver.escape(
|
|
2189
|
-
`${this.prefix}uids
|
|
3097
|
+
`${this.prefix}uids`,
|
|
2190
3098
|
)} SET \`name\`=@newName WHERE \`name\`=@oldName;`,
|
|
2191
3099
|
{
|
|
2192
3100
|
params: {
|
|
2193
3101
|
newName,
|
|
2194
3102
|
oldName,
|
|
2195
3103
|
},
|
|
2196
|
-
}
|
|
3104
|
+
},
|
|
2197
3105
|
);
|
|
2198
3106
|
return true;
|
|
2199
3107
|
}
|
|
@@ -2201,7 +3109,7 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2201
3109
|
public async rollback(name: string) {
|
|
2202
3110
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
2203
3111
|
throw new InvalidParametersError(
|
|
2204
|
-
'Transaction rollback attempted without a name.'
|
|
3112
|
+
'Transaction rollback attempted without a name.',
|
|
2205
3113
|
);
|
|
2206
3114
|
}
|
|
2207
3115
|
if (!this.transaction || this.transaction.count === 0) {
|
|
@@ -2226,12 +3134,15 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2226
3134
|
guid: string,
|
|
2227
3135
|
data: EntityData,
|
|
2228
3136
|
sdata: SerializedEntityData,
|
|
2229
|
-
|
|
3137
|
+
uniques: string[],
|
|
3138
|
+
etype: string,
|
|
2230
3139
|
) => {
|
|
3140
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
3141
|
+
|
|
2231
3142
|
const runInsertQuery = async (
|
|
2232
3143
|
name: string,
|
|
2233
3144
|
value: any,
|
|
2234
|
-
svalue: string
|
|
3145
|
+
svalue: string,
|
|
2235
3146
|
) => {
|
|
2236
3147
|
if (value === undefined) {
|
|
2237
3148
|
return;
|
|
@@ -2240,47 +3151,38 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2240
3151
|
typeof value === 'number'
|
|
2241
3152
|
? 'N'
|
|
2242
3153
|
: typeof value === 'string'
|
|
2243
|
-
|
|
2244
|
-
|
|
3154
|
+
? 'S'
|
|
3155
|
+
: 'J';
|
|
3156
|
+
const jsonValue = storageValue === 'J' ? svalue : null;
|
|
3157
|
+
|
|
2245
3158
|
const promises = [];
|
|
3159
|
+
|
|
2246
3160
|
promises.push(
|
|
2247
3161
|
this.queryRun(
|
|
2248
3162
|
`INSERT INTO ${MySQLDriver.escape(
|
|
2249
|
-
`${this.prefix}data_${etype}
|
|
2250
|
-
)} (\`guid\`, \`name\`, \`value\`) VALUES (UNHEX(@guid), @name, @storageValue);`,
|
|
3163
|
+
`${this.prefix}data_${etype}`,
|
|
3164
|
+
)} (\`guid\`, \`name\`, \`value\`, \`json\`, \`string\`, \`number\`, \`truthy\`) VALUES (UNHEX(@guid), @name, @storageValue, @jsonValue, @string, @number, @truthy);`,
|
|
2251
3165
|
{
|
|
2252
3166
|
etypes: [etype],
|
|
2253
3167
|
params: {
|
|
2254
3168
|
guid,
|
|
2255
3169
|
name,
|
|
2256
3170
|
storageValue,
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
)
|
|
2260
|
-
);
|
|
2261
|
-
promises.push(
|
|
2262
|
-
this.queryRun(
|
|
2263
|
-
`INSERT INTO ${MySQLDriver.escape(
|
|
2264
|
-
`${this.prefix}comparisons_${etype}`
|
|
2265
|
-
)} (\`guid\`, \`name\`, \`truthy\`, \`string\`, \`number\`) VALUES (UNHEX(@guid), @name, @truthy, @string, @number);`,
|
|
2266
|
-
{
|
|
2267
|
-
etypes: [etype],
|
|
2268
|
-
params: {
|
|
2269
|
-
guid,
|
|
2270
|
-
name,
|
|
2271
|
-
truthy: !!value,
|
|
2272
|
-
string: `${value}`,
|
|
3171
|
+
jsonValue,
|
|
3172
|
+
string: storageValue === 'J' ? null : `${value}`,
|
|
2273
3173
|
number: isNaN(Number(value)) ? null : Number(value),
|
|
3174
|
+
truthy: !!value,
|
|
2274
3175
|
},
|
|
2275
|
-
}
|
|
2276
|
-
)
|
|
3176
|
+
},
|
|
3177
|
+
),
|
|
2277
3178
|
);
|
|
3179
|
+
|
|
2278
3180
|
const references = this.findReferences(svalue);
|
|
2279
3181
|
for (const reference of references) {
|
|
2280
3182
|
promises.push(
|
|
2281
3183
|
this.queryRun(
|
|
2282
3184
|
`INSERT INTO ${MySQLDriver.escape(
|
|
2283
|
-
`${this.prefix}references_${etype}
|
|
3185
|
+
`${this.prefix}references_${etype}`,
|
|
2284
3186
|
)} (\`guid\`, \`name\`, \`reference\`) VALUES (UNHEX(@guid), @name, UNHEX(@reference));`,
|
|
2285
3187
|
{
|
|
2286
3188
|
etypes: [etype],
|
|
@@ -2289,12 +3191,85 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2289
3191
|
name,
|
|
2290
3192
|
reference,
|
|
2291
3193
|
},
|
|
2292
|
-
}
|
|
2293
|
-
)
|
|
3194
|
+
},
|
|
3195
|
+
),
|
|
2294
3196
|
);
|
|
2295
3197
|
}
|
|
3198
|
+
|
|
3199
|
+
let tokenString: string | null = null;
|
|
3200
|
+
try {
|
|
3201
|
+
tokenString = EntityClass.getFTSText(name, value);
|
|
3202
|
+
} catch (e: any) {
|
|
3203
|
+
// Ignore error.
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
if (tokenString != null) {
|
|
3207
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
3208
|
+
while (tokens.length) {
|
|
3209
|
+
const currentTokens = tokens.splice(0, 100);
|
|
3210
|
+
const params: { [k: string]: any } = {
|
|
3211
|
+
guid,
|
|
3212
|
+
name,
|
|
3213
|
+
};
|
|
3214
|
+
const values: string[] = [];
|
|
3215
|
+
|
|
3216
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
3217
|
+
const token = currentTokens[i];
|
|
3218
|
+
params['token' + i] = token.token;
|
|
3219
|
+
params['position' + i] = token.position;
|
|
3220
|
+
params['stem' + i] = token.stem;
|
|
3221
|
+
values.push(
|
|
3222
|
+
'(UNHEX(@guid), @name, @token' +
|
|
3223
|
+
i +
|
|
3224
|
+
', @position' +
|
|
3225
|
+
i +
|
|
3226
|
+
', @stem' +
|
|
3227
|
+
i +
|
|
3228
|
+
')',
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
promises.push(
|
|
3233
|
+
this.queryRun(
|
|
3234
|
+
`INSERT INTO ${MySQLDriver.escape(
|
|
3235
|
+
`${this.prefix}tokens_${etype}`,
|
|
3236
|
+
)} (\`guid\`, \`name\`, \`token\`, \`position\`, \`stem\`) VALUES ${values.join(', ')};`,
|
|
3237
|
+
{
|
|
3238
|
+
etypes: [etype],
|
|
3239
|
+
params,
|
|
3240
|
+
},
|
|
3241
|
+
),
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
|
|
2296
3246
|
await Promise.all(promises);
|
|
2297
3247
|
};
|
|
3248
|
+
|
|
3249
|
+
for (const unique of uniques) {
|
|
3250
|
+
try {
|
|
3251
|
+
await this.queryRun(
|
|
3252
|
+
`INSERT INTO ${MySQLDriver.escape(
|
|
3253
|
+
`${this.prefix}uniques_${etype}`,
|
|
3254
|
+
)} (\`guid\`, \`unique\`) VALUES (UNHEX(@guid), @unique);`,
|
|
3255
|
+
{
|
|
3256
|
+
etypes: [etype],
|
|
3257
|
+
params: {
|
|
3258
|
+
guid,
|
|
3259
|
+
unique,
|
|
3260
|
+
},
|
|
3261
|
+
},
|
|
3262
|
+
);
|
|
3263
|
+
} catch (e: any) {
|
|
3264
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
3265
|
+
this.nymph.config.debugError(
|
|
3266
|
+
'mysql',
|
|
3267
|
+
`Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
|
|
3268
|
+
);
|
|
3269
|
+
}
|
|
3270
|
+
throw e;
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
2298
3273
|
for (const name in data) {
|
|
2299
3274
|
await runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
2300
3275
|
}
|
|
@@ -2302,109 +3277,173 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2302
3277
|
await runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
2303
3278
|
}
|
|
2304
3279
|
};
|
|
3280
|
+
let inTransaction = false;
|
|
2305
3281
|
try {
|
|
2306
3282
|
const result = await this.saveEntityRowLike(
|
|
2307
3283
|
entity,
|
|
2308
|
-
async (
|
|
3284
|
+
async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
3285
|
+
if (
|
|
3286
|
+
Object.keys(data).length === 0 &&
|
|
3287
|
+
Object.keys(sdata).length === 0
|
|
3288
|
+
) {
|
|
3289
|
+
return false;
|
|
3290
|
+
}
|
|
3291
|
+
let {
|
|
3292
|
+
user,
|
|
3293
|
+
group,
|
|
3294
|
+
acUser,
|
|
3295
|
+
acGroup,
|
|
3296
|
+
acOther,
|
|
3297
|
+
acRead,
|
|
3298
|
+
acWrite,
|
|
3299
|
+
acFull,
|
|
3300
|
+
} = this.removeAndReturnACValues(etype, data, sdata);
|
|
2309
3301
|
await this.queryRun(
|
|
2310
3302
|
`INSERT INTO ${MySQLDriver.escape(
|
|
2311
|
-
`${this.prefix}entities_${etype}
|
|
2312
|
-
)} (\`guid\`, \`tags\`, \`cdate\`, \`mdate\`) VALUES (UNHEX(@guid), @tags, @cdate, @cdate);`,
|
|
3303
|
+
`${this.prefix}entities_${etype}`,
|
|
3304
|
+
)} (\`guid\`, \`tags\`, \`cdate\`, \`mdate\`, \`user\`, \`group\`, \`acUser\`, \`acGroup\`, \`acOther\`, \`acRead\`, \`acWrite\`, \`acFull\`) VALUES (UNHEX(@guid), @tags, @cdate, @cdate, ${user == null ? '@user' : 'UNHEX(@user)'}, ${group == null ? '@group' : 'UNHEX(@group)'}, @acUser, @acGroup, @acOther, @acRead, @acWrite, @acFull);`,
|
|
2313
3305
|
{
|
|
2314
3306
|
etypes: [etype],
|
|
2315
3307
|
params: {
|
|
2316
3308
|
guid,
|
|
2317
3309
|
tags: ' ' + tags.join(' ') + ' ',
|
|
2318
3310
|
cdate,
|
|
3311
|
+
user,
|
|
3312
|
+
group,
|
|
3313
|
+
acUser,
|
|
3314
|
+
acGroup,
|
|
3315
|
+
acOther,
|
|
3316
|
+
acRead: acRead && ' ' + acRead.join(' ') + ' ',
|
|
3317
|
+
acWrite: acWrite && ' ' + acWrite.join(' ') + ' ',
|
|
3318
|
+
acFull: acFull && ' ' + acFull.join(' ') + ' ',
|
|
2319
3319
|
},
|
|
2320
|
-
}
|
|
3320
|
+
},
|
|
2321
3321
|
);
|
|
2322
|
-
await insertData(guid, data, sdata, etype);
|
|
3322
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
2323
3323
|
return true;
|
|
2324
3324
|
},
|
|
2325
|
-
async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
3325
|
+
async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
3326
|
+
if (
|
|
3327
|
+
Object.keys(data).length === 0 &&
|
|
3328
|
+
Object.keys(sdata).length === 0
|
|
3329
|
+
) {
|
|
3330
|
+
return false;
|
|
3331
|
+
}
|
|
3332
|
+
let {
|
|
3333
|
+
user,
|
|
3334
|
+
group,
|
|
3335
|
+
acUser,
|
|
3336
|
+
acGroup,
|
|
3337
|
+
acOther,
|
|
3338
|
+
acRead,
|
|
3339
|
+
acWrite,
|
|
3340
|
+
acFull,
|
|
3341
|
+
} = this.removeAndReturnACValues(etype, data, sdata);
|
|
2326
3342
|
if (this.config.rowLocking) {
|
|
2327
3343
|
const promises = [];
|
|
2328
3344
|
promises.push(
|
|
2329
3345
|
this.queryRun(
|
|
2330
3346
|
`SELECT 1 FROM ${MySQLDriver.escape(
|
|
2331
|
-
`${this.prefix}entities_${etype}
|
|
3347
|
+
`${this.prefix}entities_${etype}`,
|
|
2332
3348
|
)} WHERE \`guid\`=UNHEX(@guid) GROUP BY 1 FOR UPDATE;`,
|
|
2333
3349
|
{
|
|
2334
3350
|
etypes: [etype],
|
|
2335
3351
|
params: {
|
|
2336
3352
|
guid,
|
|
2337
3353
|
},
|
|
2338
|
-
}
|
|
2339
|
-
)
|
|
3354
|
+
},
|
|
3355
|
+
),
|
|
2340
3356
|
);
|
|
2341
3357
|
promises.push(
|
|
2342
3358
|
this.queryRun(
|
|
2343
3359
|
`SELECT 1 FROM ${MySQLDriver.escape(
|
|
2344
|
-
`${this.prefix}data_${etype}
|
|
3360
|
+
`${this.prefix}data_${etype}`,
|
|
2345
3361
|
)} WHERE \`guid\`=UNHEX(@guid) GROUP BY 1 FOR UPDATE;`,
|
|
2346
3362
|
{
|
|
2347
3363
|
etypes: [etype],
|
|
2348
3364
|
params: {
|
|
2349
3365
|
guid,
|
|
2350
3366
|
},
|
|
2351
|
-
}
|
|
2352
|
-
)
|
|
3367
|
+
},
|
|
3368
|
+
),
|
|
2353
3369
|
);
|
|
2354
3370
|
promises.push(
|
|
2355
3371
|
this.queryRun(
|
|
2356
3372
|
`SELECT 1 FROM ${MySQLDriver.escape(
|
|
2357
|
-
`${this.prefix}
|
|
3373
|
+
`${this.prefix}references_${etype}`,
|
|
2358
3374
|
)} WHERE \`guid\`=UNHEX(@guid) GROUP BY 1 FOR UPDATE;`,
|
|
2359
3375
|
{
|
|
2360
3376
|
etypes: [etype],
|
|
2361
3377
|
params: {
|
|
2362
3378
|
guid,
|
|
2363
3379
|
},
|
|
2364
|
-
}
|
|
2365
|
-
)
|
|
3380
|
+
},
|
|
3381
|
+
),
|
|
2366
3382
|
);
|
|
2367
3383
|
promises.push(
|
|
2368
3384
|
this.queryRun(
|
|
2369
3385
|
`SELECT 1 FROM ${MySQLDriver.escape(
|
|
2370
|
-
`${this.prefix}
|
|
3386
|
+
`${this.prefix}tokens_${etype}`,
|
|
2371
3387
|
)} WHERE \`guid\`=UNHEX(@guid) GROUP BY 1 FOR UPDATE;`,
|
|
2372
3388
|
{
|
|
2373
3389
|
etypes: [etype],
|
|
2374
3390
|
params: {
|
|
2375
3391
|
guid,
|
|
2376
3392
|
},
|
|
2377
|
-
}
|
|
2378
|
-
)
|
|
3393
|
+
},
|
|
3394
|
+
),
|
|
3395
|
+
);
|
|
3396
|
+
promises.push(
|
|
3397
|
+
this.queryRun(
|
|
3398
|
+
`SELECT 1 FROM ${MySQLDriver.escape(
|
|
3399
|
+
`${this.prefix}uniques_${etype}`,
|
|
3400
|
+
)} WHERE \`guid\`=UNHEX(@guid) GROUP BY 1 FOR UPDATE;`,
|
|
3401
|
+
{
|
|
3402
|
+
etypes: [etype],
|
|
3403
|
+
params: {
|
|
3404
|
+
guid,
|
|
3405
|
+
},
|
|
3406
|
+
},
|
|
3407
|
+
),
|
|
2379
3408
|
);
|
|
2380
3409
|
await Promise.all(promises);
|
|
2381
3410
|
}
|
|
2382
3411
|
if (this.config.tableLocking) {
|
|
2383
3412
|
await this.queryRun(
|
|
2384
3413
|
`LOCK TABLES ${MySQLDriver.escape(
|
|
2385
|
-
`${this.prefix}entities_${etype}
|
|
3414
|
+
`${this.prefix}entities_${etype}`,
|
|
2386
3415
|
)} WRITE, ${MySQLDriver.escape(
|
|
2387
|
-
`${this.prefix}data_${etype}
|
|
3416
|
+
`${this.prefix}data_${etype}`,
|
|
2388
3417
|
)} WRITE, ${MySQLDriver.escape(
|
|
2389
|
-
`${this.prefix}
|
|
3418
|
+
`${this.prefix}references_${etype}`,
|
|
2390
3419
|
)} WRITE, ${MySQLDriver.escape(
|
|
2391
|
-
`${this.prefix}
|
|
2392
|
-
)} WRITE
|
|
3420
|
+
`${this.prefix}tokens_${etype}`,
|
|
3421
|
+
)} WRITE, ${MySQLDriver.escape(
|
|
3422
|
+
`${this.prefix}uniques_${etype}`,
|
|
3423
|
+
)} WRITE;`,
|
|
2393
3424
|
);
|
|
2394
3425
|
}
|
|
2395
3426
|
const info = await this.queryRun(
|
|
2396
3427
|
`UPDATE ${MySQLDriver.escape(
|
|
2397
|
-
`${this.prefix}entities_${etype}
|
|
2398
|
-
)} SET \`tags\`=@tags, \`mdate\`=@mdate WHERE \`guid\`=UNHEX(@guid) AND \`mdate\` <= @emdate;`,
|
|
3428
|
+
`${this.prefix}entities_${etype}`,
|
|
3429
|
+
)} SET \`tags\`=@tags, \`mdate\`=@mdate, \`user\`=${user == null ? '@user' : 'UNHEX(@user)'}, \`group\`=${group == null ? '@group' : 'UNHEX(@group)'}, \`acUser\`=@acUser, \`acGroup\`=@acGroup, \`acOther\`=@acOther, \`acRead\`=@acRead, \`acWrite\`=@acWrite, \`acFull\`=@acFull WHERE \`guid\`=UNHEX(@guid) AND \`mdate\` <= @emdate;`,
|
|
2399
3430
|
{
|
|
2400
3431
|
etypes: [etype],
|
|
2401
3432
|
params: {
|
|
2402
3433
|
tags: ' ' + tags.join(' ') + ' ',
|
|
2403
3434
|
mdate,
|
|
3435
|
+
user,
|
|
3436
|
+
group,
|
|
3437
|
+
acUser,
|
|
3438
|
+
acGroup,
|
|
3439
|
+
acOther,
|
|
3440
|
+
acRead: acRead && ' ' + acRead.join(' ') + ' ',
|
|
3441
|
+
acWrite: acWrite && ' ' + acWrite.join(' ') + ' ',
|
|
3442
|
+
acFull: acFull && ' ' + acFull.join(' ') + ' ',
|
|
2404
3443
|
guid,
|
|
2405
3444
|
emdate: isNaN(Number(entity.mdate)) ? 0 : Number(entity.mdate),
|
|
2406
3445
|
},
|
|
2407
|
-
}
|
|
3446
|
+
},
|
|
2408
3447
|
);
|
|
2409
3448
|
let success = false;
|
|
2410
3449
|
if (info.changes === 1) {
|
|
@@ -2412,44 +3451,57 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2412
3451
|
promises.push(
|
|
2413
3452
|
this.queryRun(
|
|
2414
3453
|
`DELETE FROM ${MySQLDriver.escape(
|
|
2415
|
-
`${this.prefix}data_${etype}
|
|
3454
|
+
`${this.prefix}data_${etype}`,
|
|
2416
3455
|
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
2417
3456
|
{
|
|
2418
3457
|
etypes: [etype],
|
|
2419
3458
|
params: {
|
|
2420
3459
|
guid,
|
|
2421
3460
|
},
|
|
2422
|
-
}
|
|
2423
|
-
)
|
|
3461
|
+
},
|
|
3462
|
+
),
|
|
2424
3463
|
);
|
|
2425
3464
|
promises.push(
|
|
2426
3465
|
this.queryRun(
|
|
2427
3466
|
`DELETE FROM ${MySQLDriver.escape(
|
|
2428
|
-
`${this.prefix}
|
|
3467
|
+
`${this.prefix}references_${etype}`,
|
|
2429
3468
|
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
2430
3469
|
{
|
|
2431
3470
|
etypes: [etype],
|
|
2432
3471
|
params: {
|
|
2433
3472
|
guid,
|
|
2434
3473
|
},
|
|
2435
|
-
}
|
|
2436
|
-
)
|
|
3474
|
+
},
|
|
3475
|
+
),
|
|
2437
3476
|
);
|
|
2438
3477
|
promises.push(
|
|
2439
3478
|
this.queryRun(
|
|
2440
3479
|
`DELETE FROM ${MySQLDriver.escape(
|
|
2441
|
-
`${this.prefix}
|
|
3480
|
+
`${this.prefix}tokens_${etype}`,
|
|
2442
3481
|
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
2443
3482
|
{
|
|
2444
3483
|
etypes: [etype],
|
|
2445
3484
|
params: {
|
|
2446
3485
|
guid,
|
|
2447
3486
|
},
|
|
2448
|
-
}
|
|
2449
|
-
)
|
|
3487
|
+
},
|
|
3488
|
+
),
|
|
3489
|
+
);
|
|
3490
|
+
promises.push(
|
|
3491
|
+
this.queryRun(
|
|
3492
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
3493
|
+
`${this.prefix}uniques_${etype}`,
|
|
3494
|
+
)} WHERE \`guid\`=UNHEX(@guid);`,
|
|
3495
|
+
{
|
|
3496
|
+
etypes: [etype],
|
|
3497
|
+
params: {
|
|
3498
|
+
guid,
|
|
3499
|
+
},
|
|
3500
|
+
},
|
|
3501
|
+
),
|
|
2450
3502
|
);
|
|
2451
3503
|
await Promise.all(promises);
|
|
2452
|
-
await insertData(guid, data, sdata, etype);
|
|
3504
|
+
await insertData(guid, data, sdata, uniques, etype);
|
|
2453
3505
|
success = true;
|
|
2454
3506
|
}
|
|
2455
3507
|
if (this.config.tableLocking) {
|
|
@@ -2459,20 +3511,27 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2459
3511
|
},
|
|
2460
3512
|
async () => {
|
|
2461
3513
|
await this.internalTransaction('nymph-save');
|
|
3514
|
+
inTransaction = true;
|
|
2462
3515
|
},
|
|
2463
3516
|
async (success) => {
|
|
2464
|
-
if (
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
3517
|
+
if (inTransaction) {
|
|
3518
|
+
inTransaction = false;
|
|
3519
|
+
if (success) {
|
|
3520
|
+
await this.commit('nymph-save');
|
|
3521
|
+
} else {
|
|
3522
|
+
await this.rollback('nymph-save');
|
|
3523
|
+
}
|
|
2468
3524
|
}
|
|
2469
3525
|
return success;
|
|
2470
|
-
}
|
|
3526
|
+
},
|
|
2471
3527
|
);
|
|
2472
3528
|
|
|
2473
3529
|
return result;
|
|
2474
3530
|
} catch (e: any) {
|
|
2475
|
-
|
|
3531
|
+
this.nymph.config.debugError('mysql', `Save entity error: "${e}"`);
|
|
3532
|
+
if (inTransaction) {
|
|
3533
|
+
await this.rollback('nymph-save');
|
|
3534
|
+
}
|
|
2476
3535
|
throw e;
|
|
2477
3536
|
}
|
|
2478
3537
|
}
|
|
@@ -2483,56 +3542,209 @@ export default class MySQLDriver extends NymphDriver {
|
|
|
2483
3542
|
}
|
|
2484
3543
|
await this.queryRun(
|
|
2485
3544
|
`INSERT INTO ${MySQLDriver.escape(
|
|
2486
|
-
`${this.prefix}uids
|
|
3545
|
+
`${this.prefix}uids`,
|
|
2487
3546
|
)} (\`name\`, \`cur_uid\`) VALUES (@name, @curUid) ON DUPLICATE KEY UPDATE \`cur_uid\`=@curUid;`,
|
|
2488
3547
|
{
|
|
2489
3548
|
params: {
|
|
2490
3549
|
name,
|
|
2491
3550
|
curUid,
|
|
2492
3551
|
},
|
|
2493
|
-
}
|
|
3552
|
+
},
|
|
2494
3553
|
);
|
|
2495
3554
|
return true;
|
|
2496
3555
|
}
|
|
2497
3556
|
|
|
2498
|
-
|
|
3557
|
+
protected async internalTransaction(name: string, setOnSelf = true) {
|
|
2499
3558
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
2500
3559
|
throw new InvalidParametersError(
|
|
2501
|
-
'Transaction start attempted without a name.'
|
|
3560
|
+
'Transaction start attempted without a name.',
|
|
2502
3561
|
);
|
|
2503
3562
|
}
|
|
2504
3563
|
|
|
2505
|
-
|
|
3564
|
+
let transaction = this.transaction;
|
|
3565
|
+
|
|
3566
|
+
if (!transaction) {
|
|
2506
3567
|
// Lock to one connection.
|
|
2507
|
-
|
|
3568
|
+
transaction = {
|
|
2508
3569
|
count: 0,
|
|
2509
3570
|
connection: await this.getConnection(),
|
|
3571
|
+
latestPromise: Promise.resolve(),
|
|
2510
3572
|
};
|
|
2511
|
-
if (
|
|
2512
|
-
|
|
2513
|
-
await this.queryRun('START TRANSACTION;');
|
|
3573
|
+
if (setOnSelf) {
|
|
3574
|
+
this.transaction = transaction;
|
|
2514
3575
|
}
|
|
2515
3576
|
}
|
|
2516
3577
|
|
|
2517
|
-
if (this.config.transactions) {
|
|
2518
|
-
|
|
3578
|
+
if (this.config.transactions && transaction.count === 0) {
|
|
3579
|
+
// We're not in a transaction yet, so start one.
|
|
3580
|
+
await this.queryRun('START TRANSACTION;', { transaction });
|
|
2519
3581
|
}
|
|
2520
3582
|
|
|
2521
|
-
|
|
3583
|
+
transaction.count++;
|
|
3584
|
+
|
|
3585
|
+
if (this.config.transactions) {
|
|
3586
|
+
await this.queryRun(`SAVEPOINT ${MySQLDriver.escape(name)};`, {
|
|
3587
|
+
transaction,
|
|
3588
|
+
});
|
|
3589
|
+
}
|
|
2522
3590
|
|
|
2523
|
-
return
|
|
3591
|
+
return transaction;
|
|
2524
3592
|
}
|
|
2525
3593
|
|
|
2526
3594
|
public async startTransaction(name: string) {
|
|
2527
|
-
const
|
|
2528
|
-
const transaction = await this.internalTransaction(name);
|
|
2529
|
-
if (!inTransaction) {
|
|
2530
|
-
this.transaction = null;
|
|
2531
|
-
}
|
|
3595
|
+
const transaction = await this.internalTransaction(name, false);
|
|
2532
3596
|
|
|
2533
3597
|
const nymph = this.nymph.clone();
|
|
2534
3598
|
(nymph.driver as MySQLDriver).transaction = transaction;
|
|
2535
3599
|
|
|
2536
3600
|
return nymph;
|
|
2537
3601
|
}
|
|
3602
|
+
|
|
3603
|
+
private async removeTilmeldOldRows(etype: string) {
|
|
3604
|
+
await this.internalTransaction('nymph-remove-tilmeld-rows');
|
|
3605
|
+
try {
|
|
3606
|
+
for (let name of [
|
|
3607
|
+
'user',
|
|
3608
|
+
'group',
|
|
3609
|
+
'acUser',
|
|
3610
|
+
'acGroup',
|
|
3611
|
+
'acOther',
|
|
3612
|
+
'acRead',
|
|
3613
|
+
'acWrite',
|
|
3614
|
+
'acFull',
|
|
3615
|
+
]) {
|
|
3616
|
+
await this.queryRun(
|
|
3617
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
3618
|
+
`${this.prefix}data_${etype}`,
|
|
3619
|
+
)} WHERE \`name\`=@name;`,
|
|
3620
|
+
{
|
|
3621
|
+
etypes: [etype],
|
|
3622
|
+
params: {
|
|
3623
|
+
name,
|
|
3624
|
+
},
|
|
3625
|
+
},
|
|
3626
|
+
);
|
|
3627
|
+
await this.queryRun(
|
|
3628
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
3629
|
+
`${this.prefix}references_${etype}`,
|
|
3630
|
+
)} WHERE \`name\`=@name;`,
|
|
3631
|
+
{
|
|
3632
|
+
etypes: [etype],
|
|
3633
|
+
params: {
|
|
3634
|
+
name,
|
|
3635
|
+
},
|
|
3636
|
+
},
|
|
3637
|
+
);
|
|
3638
|
+
await this.queryRun(
|
|
3639
|
+
`DELETE FROM ${MySQLDriver.escape(
|
|
3640
|
+
`${this.prefix}tokens_${etype}`,
|
|
3641
|
+
)} WHERE \`name\`=@name;`,
|
|
3642
|
+
{
|
|
3643
|
+
etypes: [etype],
|
|
3644
|
+
params: {
|
|
3645
|
+
name,
|
|
3646
|
+
},
|
|
3647
|
+
},
|
|
3648
|
+
);
|
|
3649
|
+
}
|
|
3650
|
+
} catch (e: any) {
|
|
3651
|
+
this.nymph.config.debugError(
|
|
3652
|
+
'mysql',
|
|
3653
|
+
`Remove tilmeld rows error: "${e}"`,
|
|
3654
|
+
);
|
|
3655
|
+
await this.rollback('nymph-remove-tilmeld-rows');
|
|
3656
|
+
throw e;
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
await this.commit('nymph-remove-tilmeld-rows');
|
|
3660
|
+
return true;
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
public async needsMigration(): Promise<
|
|
3664
|
+
'json' | 'tokens' | 'tilmeldColumns' | false
|
|
3665
|
+
> {
|
|
3666
|
+
const table = await this.queryGet(
|
|
3667
|
+
'SELECT `table_name` AS `table_name` FROM `information_schema`.`tables` WHERE `table_schema`=@db AND `table_name` LIKE @prefix LIMIT 1;',
|
|
3668
|
+
{
|
|
3669
|
+
params: {
|
|
3670
|
+
db: this.config.database,
|
|
3671
|
+
prefix: this.prefix + 'data_' + '%',
|
|
3672
|
+
},
|
|
3673
|
+
},
|
|
3674
|
+
);
|
|
3675
|
+
if (table?.table_name) {
|
|
3676
|
+
const result = await this.queryGet(
|
|
3677
|
+
"SELECT 1 AS `exists` FROM `information_schema`.`columns` WHERE `table_schema`=@db AND `table_name`=@table AND `column_name`='json';",
|
|
3678
|
+
{
|
|
3679
|
+
params: {
|
|
3680
|
+
db: this.config.database,
|
|
3681
|
+
table: table.table_name,
|
|
3682
|
+
},
|
|
3683
|
+
},
|
|
3684
|
+
);
|
|
3685
|
+
if (!result?.exists) {
|
|
3686
|
+
return 'json';
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
const table2 = await this.queryGet(
|
|
3690
|
+
'SELECT `table_name` AS `table_name` FROM `information_schema`.`tables` WHERE `table_schema`=@db AND `table_name` LIKE @tokenTable LIMIT 1;',
|
|
3691
|
+
{
|
|
3692
|
+
params: {
|
|
3693
|
+
db: this.config.database,
|
|
3694
|
+
tokenTable: this.prefix + 'tokens_' + '%',
|
|
3695
|
+
},
|
|
3696
|
+
},
|
|
3697
|
+
);
|
|
3698
|
+
if (!table2 || !table2.table_name) {
|
|
3699
|
+
return 'tokens';
|
|
3700
|
+
}
|
|
3701
|
+
const table3 = await this.queryGet(
|
|
3702
|
+
'SELECT `table_name` AS `table_name` FROM `information_schema`.`tables` WHERE `table_schema`=@db AND `table_name` LIKE @prefix LIMIT 1;',
|
|
3703
|
+
{
|
|
3704
|
+
params: {
|
|
3705
|
+
db: this.config.database,
|
|
3706
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
3707
|
+
},
|
|
3708
|
+
},
|
|
3709
|
+
);
|
|
3710
|
+
if (table3?.table_name) {
|
|
3711
|
+
const result = await this.queryGet(
|
|
3712
|
+
"SELECT 1 AS `exists` FROM `information_schema`.`columns` WHERE `table_schema`=@db AND `table_name`=@table AND `column_name`='user';",
|
|
3713
|
+
{
|
|
3714
|
+
params: {
|
|
3715
|
+
db: this.config.database,
|
|
3716
|
+
table: table3.table_name,
|
|
3717
|
+
},
|
|
3718
|
+
},
|
|
3719
|
+
);
|
|
3720
|
+
if (!result?.exists) {
|
|
3721
|
+
return 'tilmeldColumns';
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
return false;
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
public async liveMigration(
|
|
3728
|
+
migrationType: 'tokenTables' | 'tilmeldColumns' | 'tilmeldRemoveOldRows',
|
|
3729
|
+
) {
|
|
3730
|
+
if (migrationType === 'tokenTables') {
|
|
3731
|
+
const etypes = await this.getEtypes();
|
|
3732
|
+
|
|
3733
|
+
for (let etype of etypes) {
|
|
3734
|
+
await this.createTokensTable(etype);
|
|
3735
|
+
}
|
|
3736
|
+
} else if (migrationType === 'tilmeldColumns') {
|
|
3737
|
+
const etypes = await this.getEtypes();
|
|
3738
|
+
|
|
3739
|
+
for (let etype of etypes) {
|
|
3740
|
+
await this.addTilmeldColumnsAndIndexes(etype);
|
|
3741
|
+
}
|
|
3742
|
+
} else if (migrationType === 'tilmeldRemoveOldRows') {
|
|
3743
|
+
const etypes = await this.getEtypes();
|
|
3744
|
+
|
|
3745
|
+
for (let etype of etypes) {
|
|
3746
|
+
await this.removeTilmeldOldRows(etype);
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
2538
3750
|
}
|