@nymphjs/driver-sqlite3 1.0.0-beta.4 → 1.0.0-beta.41
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 +164 -0
- package/dist/SQLite3Driver.d.ts +13 -13
- package/dist/SQLite3Driver.js +176 -62
- package/dist/SQLite3Driver.js.map +1 -1
- package/dist/SQLite3Driver.test.js +39 -5
- package/dist/SQLite3Driver.test.js.map +1 -1
- package/dist/conf/d.d.ts +2 -1
- package/dist/conf/defaults.js +2 -1
- package/dist/conf/defaults.js.map +1 -1
- package/package.json +13 -13
- package/src/SQLite3Driver.test.ts +41 -6
- package/src/SQLite3Driver.ts +236 -107
- package/src/conf/d.ts +21 -2
- package/src/conf/defaults.ts +2 -1
- package/typedoc.json +4 -0
package/src/SQLite3Driver.ts
CHANGED
|
@@ -21,16 +21,25 @@ import {
|
|
|
21
21
|
SQLite3DriverConfigDefaults as defaults,
|
|
22
22
|
} from './conf';
|
|
23
23
|
|
|
24
|
+
class InternalStore {
|
|
25
|
+
public link: SQLite3.Database;
|
|
26
|
+
public linkWrite?: SQLite3.Database;
|
|
27
|
+
public connected: boolean = false;
|
|
28
|
+
public transactionsStarted = 0;
|
|
29
|
+
|
|
30
|
+
constructor(link: SQLite3.Database) {
|
|
31
|
+
this.link = link;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
24
35
|
/**
|
|
25
36
|
* The SQLite3 Nymph database driver.
|
|
26
37
|
*/
|
|
27
38
|
export default class SQLite3Driver extends NymphDriver {
|
|
28
39
|
public config: SQLite3DriverConfig;
|
|
29
40
|
protected prefix: string;
|
|
30
|
-
protected connected: boolean = false;
|
|
31
41
|
// @ts-ignore: this is assigned in connect(), which is called by the constructor.
|
|
32
|
-
protected
|
|
33
|
-
protected transactionsStarted = 0;
|
|
42
|
+
protected store: InternalStore;
|
|
34
43
|
|
|
35
44
|
static escape(input: string) {
|
|
36
45
|
if (input.indexOf('\x00') !== -1) {
|
|
@@ -42,11 +51,27 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
42
51
|
return '"' + input.replace(/"/g, () => '""') + '"';
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
constructor(config: Partial<SQLite3DriverConfig
|
|
54
|
+
constructor(config: Partial<SQLite3DriverConfig>, store?: InternalStore) {
|
|
46
55
|
super();
|
|
47
56
|
this.config = { ...defaults, ...config };
|
|
57
|
+
if (this.config.filename === ':memory:') {
|
|
58
|
+
this.config.explicitWrite = true;
|
|
59
|
+
}
|
|
48
60
|
this.prefix = this.config.prefix;
|
|
49
|
-
|
|
61
|
+
if (store) {
|
|
62
|
+
this.store = store;
|
|
63
|
+
} else {
|
|
64
|
+
this.connect();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
70
|
+
*
|
|
71
|
+
* @returns A clone of this instance.
|
|
72
|
+
*/
|
|
73
|
+
public clone() {
|
|
74
|
+
return new SQLite3Driver(this.config, this.store);
|
|
50
75
|
}
|
|
51
76
|
|
|
52
77
|
/**
|
|
@@ -54,41 +79,104 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
54
79
|
*
|
|
55
80
|
* @returns Whether this instance is connected to a SQLite3 database.
|
|
56
81
|
*/
|
|
57
|
-
public
|
|
58
|
-
|
|
82
|
+
public connect() {
|
|
83
|
+
if (this.store && this.store.connected) {
|
|
84
|
+
return Promise.resolve(true);
|
|
85
|
+
}
|
|
86
|
+
|
|
59
87
|
// Connecting
|
|
60
|
-
|
|
88
|
+
this._connect(false);
|
|
89
|
+
|
|
90
|
+
return Promise.resolve(this.store.connected);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private _connect(write: boolean) {
|
|
94
|
+
const { filename, fileMustExist, timeout, explicitWrite, wal, verbose } =
|
|
95
|
+
this.config;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const setOptions = (link: SQLite3.Database) => {
|
|
99
|
+
// Set database and connection options.
|
|
100
|
+
if (wal) {
|
|
101
|
+
link.pragma('journal_mode = WAL;');
|
|
102
|
+
}
|
|
103
|
+
link.pragma('encoding = "UTF-8";');
|
|
104
|
+
link.pragma('foreign_keys = 1;');
|
|
105
|
+
link.pragma('case_sensitive_like = 1;');
|
|
106
|
+
// Create the preg_match and regexp functions.
|
|
107
|
+
link.function('regexp', { deterministic: true }, ((
|
|
108
|
+
pattern: string,
|
|
109
|
+
subject: string
|
|
110
|
+
) => (this.posixRegexMatch(pattern, subject) ? 1 : 0)) as (
|
|
111
|
+
...params: any[]
|
|
112
|
+
) => any);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
let link: SQLite3.Database;
|
|
61
116
|
try {
|
|
62
|
-
|
|
63
|
-
readonly,
|
|
117
|
+
link = new SQLite3(filename, {
|
|
118
|
+
readonly: !explicitWrite && !write,
|
|
64
119
|
fileMustExist,
|
|
65
120
|
timeout,
|
|
66
121
|
verbose,
|
|
67
122
|
});
|
|
68
|
-
this.connected = true;
|
|
69
|
-
// Set database and connection options.
|
|
70
|
-
this.link.pragma('encoding = "UTF-8";');
|
|
71
|
-
this.link.pragma('foreign_keys = 1;');
|
|
72
|
-
this.link.pragma('case_sensitive_like = 1;');
|
|
73
|
-
// Create the preg_match and regexp functions.
|
|
74
|
-
this.link.function(
|
|
75
|
-
'regexp',
|
|
76
|
-
{ deterministic: true },
|
|
77
|
-
(pattern: string, subject: string) =>
|
|
78
|
-
this.posixRegexMatch(pattern, subject) ? 1 : 0
|
|
79
|
-
);
|
|
80
123
|
} catch (e: any) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
124
|
+
if (
|
|
125
|
+
e.code === 'SQLITE_CANTOPEN' &&
|
|
126
|
+
!explicitWrite &&
|
|
127
|
+
!write &&
|
|
128
|
+
!this.config.fileMustExist
|
|
129
|
+
) {
|
|
130
|
+
// This happens when the file doesn't exist and we attempt to open it
|
|
131
|
+
// readonly.
|
|
132
|
+
// First open it in write mode.
|
|
133
|
+
const writeLink = new SQLite3(filename, {
|
|
134
|
+
readonly: false,
|
|
135
|
+
fileMustExist,
|
|
136
|
+
timeout,
|
|
137
|
+
verbose,
|
|
138
|
+
});
|
|
139
|
+
setOptions(writeLink);
|
|
140
|
+
writeLink.close();
|
|
141
|
+
// Now open in readonly.
|
|
142
|
+
link = new SQLite3(filename, {
|
|
143
|
+
readonly: true,
|
|
144
|
+
fileMustExist,
|
|
145
|
+
timeout,
|
|
146
|
+
verbose,
|
|
147
|
+
});
|
|
86
148
|
} else {
|
|
87
|
-
throw
|
|
149
|
+
throw e;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!this.store) {
|
|
154
|
+
if (write) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
'Tried to open in write without opening in read first.'
|
|
157
|
+
);
|
|
88
158
|
}
|
|
159
|
+
this.store = new InternalStore(link);
|
|
160
|
+
} else if (write) {
|
|
161
|
+
this.store.linkWrite = link;
|
|
162
|
+
} else {
|
|
163
|
+
this.store.link = link;
|
|
164
|
+
}
|
|
165
|
+
this.store.connected = true;
|
|
166
|
+
setOptions(link);
|
|
167
|
+
} catch (e: any) {
|
|
168
|
+
if (this.store) {
|
|
169
|
+
this.store.connected = false;
|
|
170
|
+
}
|
|
171
|
+
if (filename === ':memory:') {
|
|
172
|
+
throw new NotConfiguredError(
|
|
173
|
+
"It seems the config hasn't been set up correctly. Could not connect: " +
|
|
174
|
+
e?.message
|
|
175
|
+
);
|
|
176
|
+
} else {
|
|
177
|
+
throw new UnableToConnectError('Could not connect: ' + e?.message);
|
|
89
178
|
}
|
|
90
179
|
}
|
|
91
|
-
return this.connected;
|
|
92
180
|
}
|
|
93
181
|
|
|
94
182
|
/**
|
|
@@ -97,16 +185,24 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
97
185
|
* @returns Whether this instance is connected to a SQLite3 database.
|
|
98
186
|
*/
|
|
99
187
|
public async disconnect() {
|
|
100
|
-
if (this.connected) {
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
|
|
188
|
+
if (this.store.connected) {
|
|
189
|
+
if (this.store.linkWrite && !this.config.explicitWrite) {
|
|
190
|
+
this.store.linkWrite.exec('PRAGMA optimize;');
|
|
191
|
+
this.store.linkWrite.close();
|
|
192
|
+
this.store.linkWrite = undefined;
|
|
193
|
+
}
|
|
194
|
+
if (this.config.explicitWrite) {
|
|
195
|
+
this.store.link.exec('PRAGMA optimize;');
|
|
196
|
+
}
|
|
197
|
+
this.store.link.close();
|
|
198
|
+
this.store.transactionsStarted = 0;
|
|
199
|
+
this.store.connected = false;
|
|
104
200
|
}
|
|
105
|
-
return this.connected;
|
|
201
|
+
return this.store.connected;
|
|
106
202
|
}
|
|
107
203
|
|
|
108
204
|
public async inTransaction() {
|
|
109
|
-
return this.transactionsStarted > 0;
|
|
205
|
+
return this.store.transactionsStarted > 0;
|
|
110
206
|
}
|
|
111
207
|
|
|
112
208
|
/**
|
|
@@ -115,18 +211,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
115
211
|
* @returns Whether this instance is connected to a SQLite3 database.
|
|
116
212
|
*/
|
|
117
213
|
public isConnected() {
|
|
118
|
-
return this.connected;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Check if SQLite3 DB is read only and throw error if so.
|
|
123
|
-
*/
|
|
124
|
-
private checkReadOnlyMode() {
|
|
125
|
-
if (this.config.readonly) {
|
|
126
|
-
throw new InvalidParametersError(
|
|
127
|
-
'Attempt to write to SQLite3 DB in read only mode.'
|
|
128
|
-
);
|
|
129
|
-
}
|
|
214
|
+
return this.store.connected;
|
|
130
215
|
}
|
|
131
216
|
|
|
132
217
|
/**
|
|
@@ -135,7 +220,6 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
135
220
|
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
136
221
|
*/
|
|
137
222
|
private createTables(etype: string | null = null) {
|
|
138
|
-
this.checkReadOnlyMode();
|
|
139
223
|
this.startTransaction('nymph-tablecreation');
|
|
140
224
|
try {
|
|
141
225
|
if (etype != null) {
|
|
@@ -326,7 +410,10 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
326
410
|
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
327
411
|
) {
|
|
328
412
|
return this.query(
|
|
329
|
-
() =>
|
|
413
|
+
() =>
|
|
414
|
+
(this.store.linkWrite || this.store.link)
|
|
415
|
+
.prepare(query)
|
|
416
|
+
.iterate(params),
|
|
330
417
|
`${query} -- ${JSON.stringify(params)}`,
|
|
331
418
|
etypes
|
|
332
419
|
);
|
|
@@ -340,7 +427,8 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
340
427
|
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
341
428
|
) {
|
|
342
429
|
return this.query(
|
|
343
|
-
() =>
|
|
430
|
+
() =>
|
|
431
|
+
(this.store.linkWrite || this.store.link).prepare(query).get(params),
|
|
344
432
|
`${query} -- ${JSON.stringify(params)}`,
|
|
345
433
|
etypes
|
|
346
434
|
);
|
|
@@ -354,7 +442,8 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
354
442
|
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
355
443
|
) {
|
|
356
444
|
return this.query(
|
|
357
|
-
() =>
|
|
445
|
+
() =>
|
|
446
|
+
(this.store.linkWrite || this.store.link).prepare(query).run(params),
|
|
358
447
|
`${query} -- ${JSON.stringify(params)}`,
|
|
359
448
|
etypes
|
|
360
449
|
);
|
|
@@ -366,11 +455,22 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
366
455
|
'Transaction commit attempted without a name.'
|
|
367
456
|
);
|
|
368
457
|
}
|
|
369
|
-
if (this.transactionsStarted === 0) {
|
|
458
|
+
if (this.store.transactionsStarted === 0) {
|
|
370
459
|
return true;
|
|
371
460
|
}
|
|
372
461
|
this.queryRun(`RELEASE SAVEPOINT ${SQLite3Driver.escape(name)};`);
|
|
373
|
-
this.transactionsStarted--;
|
|
462
|
+
this.store.transactionsStarted--;
|
|
463
|
+
|
|
464
|
+
if (
|
|
465
|
+
this.store.transactionsStarted === 0 &&
|
|
466
|
+
this.store.linkWrite &&
|
|
467
|
+
!this.config.explicitWrite
|
|
468
|
+
) {
|
|
469
|
+
this.store.linkWrite.exec('PRAGMA optimize;');
|
|
470
|
+
this.store.linkWrite.close();
|
|
471
|
+
this.store.linkWrite = undefined;
|
|
472
|
+
}
|
|
473
|
+
|
|
374
474
|
return true;
|
|
375
475
|
}
|
|
376
476
|
|
|
@@ -386,7 +486,6 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
386
486
|
EntityClass = className;
|
|
387
487
|
}
|
|
388
488
|
const etype = EntityClass.ETYPE;
|
|
389
|
-
this.checkReadOnlyMode();
|
|
390
489
|
await this.startTransaction('nymph-delete');
|
|
391
490
|
try {
|
|
392
491
|
this.queryRun(
|
|
@@ -449,7 +548,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
449
548
|
if (!name) {
|
|
450
549
|
throw new InvalidParametersError('Name not given for UID');
|
|
451
550
|
}
|
|
452
|
-
this.
|
|
551
|
+
await this.startTransaction('nymph-delete-uid');
|
|
453
552
|
this.queryRun(
|
|
454
553
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
455
554
|
`${this.prefix}uids`
|
|
@@ -460,6 +559,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
460
559
|
},
|
|
461
560
|
}
|
|
462
561
|
);
|
|
562
|
+
await this.commit('nymph-delete-uid');
|
|
463
563
|
return true;
|
|
464
564
|
}
|
|
465
565
|
|
|
@@ -477,7 +577,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
477
577
|
writeLine('');
|
|
478
578
|
|
|
479
579
|
// Export UIDs.
|
|
480
|
-
let uids = this.queryIter(
|
|
580
|
+
let uids: IterableIterator<any> = this.queryIter(
|
|
481
581
|
`SELECT * FROM ${SQLite3Driver.escape(
|
|
482
582
|
`${this.prefix}uids`
|
|
483
583
|
)} ORDER BY "name";`
|
|
@@ -493,7 +593,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
493
593
|
writeLine('');
|
|
494
594
|
|
|
495
595
|
// Get the etypes.
|
|
496
|
-
const tables = this.queryIter(
|
|
596
|
+
const tables: IterableIterator<any> = this.queryIter(
|
|
497
597
|
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;"
|
|
498
598
|
);
|
|
499
599
|
const etypes = [];
|
|
@@ -505,7 +605,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
505
605
|
|
|
506
606
|
for (const etype of etypes) {
|
|
507
607
|
// Export entities.
|
|
508
|
-
const dataIterator = this.queryIter(
|
|
608
|
+
const dataIterator: IterableIterator<any> = this.queryIter(
|
|
509
609
|
`SELECT e.*, d."name" AS "dname", d."value" AS "dvalue", c."string", c."number" FROM ${SQLite3Driver.escape(
|
|
510
610
|
`${this.prefix}entities_${etype}`
|
|
511
611
|
)} e LEFT JOIN ${SQLite3Driver.escape(
|
|
@@ -574,6 +674,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
574
674
|
const cTable = `c${tableSuffix}`;
|
|
575
675
|
const fTable = `f${tableSuffix}`;
|
|
576
676
|
const ieTable = `ie${tableSuffix}`;
|
|
677
|
+
const sTable = `s${tableSuffix}`;
|
|
577
678
|
const sort = options.sort ?? 'cdate';
|
|
578
679
|
const queryParts = this.iterateSelectorsForQuery(
|
|
579
680
|
formattedSelectors,
|
|
@@ -1319,18 +1420,31 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1319
1420
|
);
|
|
1320
1421
|
|
|
1321
1422
|
let sortBy: string;
|
|
1423
|
+
let sortByInner: string;
|
|
1424
|
+
let sortJoin = '';
|
|
1425
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1322
1426
|
switch (sort) {
|
|
1323
1427
|
case 'mdate':
|
|
1324
|
-
sortBy =
|
|
1428
|
+
sortBy = `${eTable}."mdate"${order}`;
|
|
1429
|
+
sortByInner = `${ieTable}."mdate"${order}`;
|
|
1325
1430
|
break;
|
|
1326
1431
|
case 'cdate':
|
|
1432
|
+
sortBy = `${eTable}."cdate"${order}`;
|
|
1433
|
+
sortByInner = `${ieTable}."cdate"${order}`;
|
|
1434
|
+
break;
|
|
1327
1435
|
default:
|
|
1328
|
-
|
|
1436
|
+
const name = `param${++count.i}`;
|
|
1437
|
+
sortJoin = `LEFT JOIN (
|
|
1438
|
+
SELECT "guid", "string", "number"
|
|
1439
|
+
FROM ${SQLite3Driver.escape(this.prefix + 'comparisons_' + etype)}
|
|
1440
|
+
WHERE "name"=@${name}
|
|
1441
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1442
|
+
) ${sTable} USING ("guid")`;
|
|
1443
|
+
sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1444
|
+
sortByInner = sortBy;
|
|
1445
|
+
params[name] = sort;
|
|
1329
1446
|
break;
|
|
1330
1447
|
}
|
|
1331
|
-
if (options.reverse) {
|
|
1332
|
-
sortBy += ' DESC';
|
|
1333
|
-
}
|
|
1334
1448
|
|
|
1335
1449
|
let query: string;
|
|
1336
1450
|
if (queryParts.length) {
|
|
@@ -1367,8 +1481,9 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1367
1481
|
FROM ${SQLite3Driver.escape(
|
|
1368
1482
|
this.prefix + 'entities_' + etype
|
|
1369
1483
|
)} ${ieTable}
|
|
1484
|
+
${sortJoin}
|
|
1370
1485
|
WHERE (${whereClause})
|
|
1371
|
-
ORDER BY ${
|
|
1486
|
+
ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
|
|
1372
1487
|
} else {
|
|
1373
1488
|
query = `SELECT
|
|
1374
1489
|
${eTable}."guid",
|
|
@@ -1388,15 +1503,17 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1388
1503
|
INNER JOIN ${SQLite3Driver.escape(
|
|
1389
1504
|
this.prefix + 'comparisons_' + etype
|
|
1390
1505
|
)} ${cTable} USING ("guid", "name")
|
|
1506
|
+
${sortJoin}
|
|
1391
1507
|
INNER JOIN (
|
|
1392
1508
|
SELECT "guid"
|
|
1393
1509
|
FROM ${SQLite3Driver.escape(
|
|
1394
1510
|
this.prefix + 'entities_' + etype
|
|
1395
1511
|
)} ${ieTable}
|
|
1512
|
+
${sortJoin}
|
|
1396
1513
|
WHERE (${whereClause})
|
|
1397
|
-
ORDER BY ${
|
|
1514
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1398
1515
|
) ${fTable} USING ("guid")
|
|
1399
|
-
ORDER BY ${
|
|
1516
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1400
1517
|
}
|
|
1401
1518
|
}
|
|
1402
1519
|
} else {
|
|
@@ -1430,7 +1547,8 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1430
1547
|
FROM ${SQLite3Driver.escape(
|
|
1431
1548
|
this.prefix + 'entities_' + etype
|
|
1432
1549
|
)} ${ieTable}
|
|
1433
|
-
|
|
1550
|
+
${sortJoin}
|
|
1551
|
+
ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
|
|
1434
1552
|
} else {
|
|
1435
1553
|
if (limit || offset) {
|
|
1436
1554
|
query = `SELECT
|
|
@@ -1451,14 +1569,16 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1451
1569
|
INNER JOIN ${SQLite3Driver.escape(
|
|
1452
1570
|
this.prefix + 'comparisons_' + etype
|
|
1453
1571
|
)} c USING ("guid", "name")
|
|
1572
|
+
${sortJoin}
|
|
1454
1573
|
INNER JOIN (
|
|
1455
1574
|
SELECT "guid"
|
|
1456
1575
|
FROM ${SQLite3Driver.escape(
|
|
1457
1576
|
this.prefix + 'entities_' + etype
|
|
1458
1577
|
)} ${ieTable}
|
|
1459
|
-
|
|
1578
|
+
${sortJoin}
|
|
1579
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1460
1580
|
) ${fTable} USING ("guid")
|
|
1461
|
-
ORDER BY ${
|
|
1581
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1462
1582
|
} else {
|
|
1463
1583
|
query = `SELECT
|
|
1464
1584
|
${eTable}."guid",
|
|
@@ -1478,7 +1598,8 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1478
1598
|
INNER JOIN ${SQLite3Driver.escape(
|
|
1479
1599
|
this.prefix + 'comparisons_' + etype
|
|
1480
1600
|
)} ${cTable} USING ("guid", "name")
|
|
1481
|
-
|
|
1601
|
+
${sortJoin}
|
|
1602
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1482
1603
|
}
|
|
1483
1604
|
}
|
|
1484
1605
|
}
|
|
@@ -1529,25 +1650,6 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1529
1650
|
options: Options<T> = {},
|
|
1530
1651
|
...selectors: Selector[]
|
|
1531
1652
|
): Promise<ReturnType<T['factorySync']>[] | string[] | number> {
|
|
1532
|
-
return this.getEntitiesSync(options, ...selectors);
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1536
|
-
options: Options<T> & { return: 'count' },
|
|
1537
|
-
...selectors: Selector[]
|
|
1538
|
-
): number;
|
|
1539
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1540
|
-
options: Options<T> & { return: 'guid' },
|
|
1541
|
-
...selectors: Selector[]
|
|
1542
|
-
): string[];
|
|
1543
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1544
|
-
options?: Options<T>,
|
|
1545
|
-
...selectors: Selector[]
|
|
1546
|
-
): ReturnType<T['factorySync']>[];
|
|
1547
|
-
protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(
|
|
1548
|
-
options: Options<T> = {},
|
|
1549
|
-
...selectors: Selector[]
|
|
1550
|
-
): ReturnType<T['factorySync']>[] | string[] | number {
|
|
1551
1653
|
const { result, process } = this.getEntitesRowLike<T>(
|
|
1552
1654
|
options,
|
|
1553
1655
|
selectors,
|
|
@@ -1586,7 +1688,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1586
1688
|
if (name == null) {
|
|
1587
1689
|
throw new InvalidParametersError('Name not given for UID.');
|
|
1588
1690
|
}
|
|
1589
|
-
const result = this.queryGet(
|
|
1691
|
+
const result: any = this.queryGet(
|
|
1590
1692
|
`SELECT "cur_uid" FROM ${SQLite3Driver.escape(
|
|
1591
1693
|
`${this.prefix}uids`
|
|
1592
1694
|
)} WHERE "name"=@name;`,
|
|
@@ -1600,7 +1702,6 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1600
1702
|
}
|
|
1601
1703
|
|
|
1602
1704
|
public async import(filename: string) {
|
|
1603
|
-
this.checkReadOnlyMode();
|
|
1604
1705
|
try {
|
|
1605
1706
|
return this.importFromFile(
|
|
1606
1707
|
filename,
|
|
@@ -1763,19 +1864,20 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1763
1864
|
if (name == null) {
|
|
1764
1865
|
throw new InvalidParametersError('Name not given for UID.');
|
|
1765
1866
|
}
|
|
1766
|
-
this.checkReadOnlyMode();
|
|
1767
1867
|
await this.startTransaction('nymph-newuid');
|
|
1768
1868
|
try {
|
|
1769
1869
|
let curUid =
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1870
|
+
(
|
|
1871
|
+
this.queryGet(
|
|
1872
|
+
`SELECT "cur_uid" FROM ${SQLite3Driver.escape(
|
|
1873
|
+
`${this.prefix}uids`
|
|
1874
|
+
)} WHERE "name"=@name;`,
|
|
1875
|
+
{
|
|
1876
|
+
params: {
|
|
1877
|
+
name,
|
|
1878
|
+
},
|
|
1879
|
+
}
|
|
1880
|
+
) as any
|
|
1779
1881
|
)?.cur_uid ?? null;
|
|
1780
1882
|
if (curUid == null) {
|
|
1781
1883
|
curUid = 1;
|
|
@@ -1816,7 +1918,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1816
1918
|
if (oldName == null || newName == null) {
|
|
1817
1919
|
throw new InvalidParametersError('Name not given for UID.');
|
|
1818
1920
|
}
|
|
1819
|
-
this.
|
|
1921
|
+
await this.startTransaction('nymph-rename-uid');
|
|
1820
1922
|
this.queryRun(
|
|
1821
1923
|
`UPDATE ${SQLite3Driver.escape(
|
|
1822
1924
|
`${this.prefix}uids`
|
|
@@ -1828,6 +1930,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1828
1930
|
},
|
|
1829
1931
|
}
|
|
1830
1932
|
);
|
|
1933
|
+
await this.commit('nymph-rename-uid');
|
|
1831
1934
|
return true;
|
|
1832
1935
|
}
|
|
1833
1936
|
|
|
@@ -1837,16 +1940,26 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1837
1940
|
'Transaction rollback attempted without a name.'
|
|
1838
1941
|
);
|
|
1839
1942
|
}
|
|
1840
|
-
if (this.transactionsStarted === 0) {
|
|
1943
|
+
if (this.store.transactionsStarted === 0) {
|
|
1841
1944
|
return true;
|
|
1842
1945
|
}
|
|
1843
1946
|
this.queryRun(`ROLLBACK TO SAVEPOINT ${SQLite3Driver.escape(name)};`);
|
|
1844
|
-
this.transactionsStarted--;
|
|
1947
|
+
this.store.transactionsStarted--;
|
|
1948
|
+
|
|
1949
|
+
if (
|
|
1950
|
+
this.store.transactionsStarted === 0 &&
|
|
1951
|
+
this.store.linkWrite &&
|
|
1952
|
+
!this.config.explicitWrite
|
|
1953
|
+
) {
|
|
1954
|
+
this.store.linkWrite.exec('PRAGMA optimize;');
|
|
1955
|
+
this.store.linkWrite.close();
|
|
1956
|
+
this.store.linkWrite = undefined;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1845
1959
|
return true;
|
|
1846
1960
|
}
|
|
1847
1961
|
|
|
1848
1962
|
public async saveEntity(entity: EntityInterface) {
|
|
1849
|
-
this.checkReadOnlyMode();
|
|
1850
1963
|
const insertData = (
|
|
1851
1964
|
guid: string,
|
|
1852
1965
|
data: EntityData,
|
|
@@ -1919,6 +2032,12 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1919
2032
|
return this.saveEntityRowLike(
|
|
1920
2033
|
entity,
|
|
1921
2034
|
async (_entity, guid, tags, data, sdata, cdate, etype) => {
|
|
2035
|
+
if (
|
|
2036
|
+
Object.keys(data).length === 0 &&
|
|
2037
|
+
Object.keys(sdata).length === 0
|
|
2038
|
+
) {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
1922
2041
|
this.queryRun(
|
|
1923
2042
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1924
2043
|
`${this.prefix}entities_${etype}`
|
|
@@ -1936,6 +2055,12 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1936
2055
|
return true;
|
|
1937
2056
|
},
|
|
1938
2057
|
async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
2058
|
+
if (
|
|
2059
|
+
Object.keys(data).length === 0 &&
|
|
2060
|
+
Object.keys(sdata).length === 0
|
|
2061
|
+
) {
|
|
2062
|
+
return false;
|
|
2063
|
+
}
|
|
1939
2064
|
const info = this.queryRun(
|
|
1940
2065
|
`UPDATE ${SQLite3Driver.escape(
|
|
1941
2066
|
`${this.prefix}entities_${etype}`
|
|
@@ -2012,7 +2137,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
2012
2137
|
if (name == null) {
|
|
2013
2138
|
throw new InvalidParametersError('Name not given for UID.');
|
|
2014
2139
|
}
|
|
2015
|
-
this.
|
|
2140
|
+
await this.startTransaction('nymph-set-uid');
|
|
2016
2141
|
this.queryRun(
|
|
2017
2142
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
2018
2143
|
`${this.prefix}uids`
|
|
@@ -2034,6 +2159,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
2034
2159
|
},
|
|
2035
2160
|
}
|
|
2036
2161
|
);
|
|
2162
|
+
await this.commit('nymph-set-uid');
|
|
2037
2163
|
return true;
|
|
2038
2164
|
}
|
|
2039
2165
|
|
|
@@ -2043,8 +2169,11 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
2043
2169
|
'Transaction start attempted without a name.'
|
|
2044
2170
|
);
|
|
2045
2171
|
}
|
|
2172
|
+
if (!this.config.explicitWrite && !this.store.linkWrite) {
|
|
2173
|
+
this._connect(true);
|
|
2174
|
+
}
|
|
2046
2175
|
this.queryRun(`SAVEPOINT ${SQLite3Driver.escape(name)};`);
|
|
2047
|
-
this.transactionsStarted++;
|
|
2176
|
+
this.store.transactionsStarted++;
|
|
2048
2177
|
return this.nymph;
|
|
2049
2178
|
}
|
|
2050
2179
|
}
|
package/src/conf/d.ts
CHANGED
|
@@ -23,9 +23,28 @@ export interface SQLite3DriverConfig {
|
|
|
23
23
|
*/
|
|
24
24
|
timeout: number;
|
|
25
25
|
/**
|
|
26
|
-
* Open
|
|
26
|
+
* Open explicitly for writing.
|
|
27
|
+
*
|
|
28
|
+
* By default, the driver will always open the DB as readonly, and attempt to
|
|
29
|
+
* open another link to perform write operations. If you know that only one
|
|
30
|
+
* instance will be writing, you can force the driver to open for writing by
|
|
31
|
+
* default, which will block any other instance from opening it for writing.
|
|
32
|
+
*
|
|
33
|
+
* One thing to note is that starting a transaction is a write operation, so
|
|
34
|
+
* as long as an instance is in a transaction, no other instances can write.
|
|
35
|
+
*
|
|
36
|
+
* PubSub also needs to open the DB, and it only needs read access.
|
|
37
|
+
*/
|
|
38
|
+
explicitWrite: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Turn on WAL mode.
|
|
41
|
+
*
|
|
42
|
+
* This will generally increase performance, but does mean that the DB must be
|
|
43
|
+
* on a local disk.
|
|
44
|
+
*
|
|
45
|
+
* See: https://www.sqlite.org/wal.html
|
|
27
46
|
*/
|
|
28
|
-
|
|
47
|
+
wal: boolean;
|
|
29
48
|
/**
|
|
30
49
|
* Function that gets called with every SQL string executed.
|
|
31
50
|
*/
|
package/src/conf/defaults.ts
CHANGED
package/typedoc.json
ADDED