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