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