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