@nymphjs/driver-sqlite3 1.0.0-beta.6 → 1.0.0-beta.61
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 +250 -0
- package/dist/SQLite3Driver.d.ts +54 -16
- package/dist/SQLite3Driver.js +437 -162
- package/dist/SQLite3Driver.js.map +1 -1
- package/dist/SQLite3Driver.test.js +43 -6
- package/dist/SQLite3Driver.test.js.map +1 -1
- package/dist/conf/d.d.ts +59 -1
- package/dist/conf/defaults.js +3 -1
- package/dist/conf/defaults.js.map +1 -1
- package/package.json +13 -13
- package/src/SQLite3Driver.test.ts +43 -8
- package/src/SQLite3Driver.ts +675 -391
- package/src/conf/d.ts +35 -2
- package/src/conf/defaults.ts +3 -1
- package/tsconfig.json +1 -1
- package/typedoc.json +4 -0
package/src/SQLite3Driver.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import SQLite3 from 'better-sqlite3';
|
|
2
2
|
import {
|
|
3
3
|
NymphDriver,
|
|
4
|
-
EntityConstructor,
|
|
5
|
-
EntityData,
|
|
6
|
-
EntityInterface,
|
|
7
|
-
|
|
4
|
+
type EntityConstructor,
|
|
5
|
+
type EntityData,
|
|
6
|
+
type EntityInterface,
|
|
7
|
+
type EntityInstanceType,
|
|
8
|
+
type SerializedEntityData,
|
|
9
|
+
type FormattedSelector,
|
|
10
|
+
type Options,
|
|
11
|
+
type Selector,
|
|
12
|
+
EntityUniqueConstraintError,
|
|
8
13
|
InvalidParametersError,
|
|
9
14
|
NotConfiguredError,
|
|
10
15
|
QueryFailedError,
|
|
11
16
|
UnableToConnectError,
|
|
12
|
-
FormattedSelector,
|
|
13
|
-
Options,
|
|
14
|
-
Selector,
|
|
15
17
|
xor,
|
|
16
18
|
} from '@nymphjs/nymph';
|
|
17
19
|
import { makeTableSuffix } from '@nymphjs/guid';
|
|
@@ -21,32 +23,57 @@ import {
|
|
|
21
23
|
SQLite3DriverConfigDefaults as defaults,
|
|
22
24
|
} from './conf';
|
|
23
25
|
|
|
26
|
+
class InternalStore {
|
|
27
|
+
public link: SQLite3.Database;
|
|
28
|
+
public linkWrite?: SQLite3.Database;
|
|
29
|
+
public connected: boolean = false;
|
|
30
|
+
public transactionsStarted = 0;
|
|
31
|
+
|
|
32
|
+
constructor(link: SQLite3.Database) {
|
|
33
|
+
this.link = link;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
/**
|
|
25
38
|
* The SQLite3 Nymph database driver.
|
|
26
39
|
*/
|
|
27
40
|
export default class SQLite3Driver extends NymphDriver {
|
|
28
41
|
public config: SQLite3DriverConfig;
|
|
29
42
|
protected prefix: string;
|
|
30
|
-
protected connected: boolean = false;
|
|
31
43
|
// @ts-ignore: this is assigned in connect(), which is called by the constructor.
|
|
32
|
-
protected
|
|
33
|
-
protected transactionsStarted = 0;
|
|
44
|
+
protected store: InternalStore;
|
|
34
45
|
|
|
35
46
|
static escape(input: string) {
|
|
36
47
|
if (input.indexOf('\x00') !== -1) {
|
|
37
48
|
throw new InvalidParametersError(
|
|
38
|
-
'SQLite3 identifiers (like entity ETYPE) cannot contain null characters.'
|
|
49
|
+
'SQLite3 identifiers (like entity ETYPE) cannot contain null characters.',
|
|
39
50
|
);
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
return '"' + input.replace(/"/g, () => '""') + '"';
|
|
43
54
|
}
|
|
44
55
|
|
|
45
|
-
constructor(config: Partial<SQLite3DriverConfig
|
|
56
|
+
constructor(config: Partial<SQLite3DriverConfig>, store?: InternalStore) {
|
|
46
57
|
super();
|
|
47
58
|
this.config = { ...defaults, ...config };
|
|
59
|
+
if (this.config.filename === ':memory:') {
|
|
60
|
+
this.config.explicitWrite = true;
|
|
61
|
+
}
|
|
48
62
|
this.prefix = this.config.prefix;
|
|
49
|
-
|
|
63
|
+
if (store) {
|
|
64
|
+
this.store = store;
|
|
65
|
+
} else {
|
|
66
|
+
this.connect();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
72
|
+
*
|
|
73
|
+
* @returns A clone of this instance.
|
|
74
|
+
*/
|
|
75
|
+
public clone() {
|
|
76
|
+
return new SQLite3Driver(this.config, this.store);
|
|
50
77
|
}
|
|
51
78
|
|
|
52
79
|
/**
|
|
@@ -54,41 +81,107 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
54
81
|
*
|
|
55
82
|
* @returns Whether this instance is connected to a SQLite3 database.
|
|
56
83
|
*/
|
|
57
|
-
public
|
|
58
|
-
|
|
84
|
+
public connect() {
|
|
85
|
+
if (this.store && this.store.connected) {
|
|
86
|
+
return Promise.resolve(true);
|
|
87
|
+
}
|
|
88
|
+
|
|
59
89
|
// Connecting
|
|
60
|
-
|
|
90
|
+
this._connect(false);
|
|
91
|
+
|
|
92
|
+
return Promise.resolve(this.store.connected);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private _connect(write: boolean) {
|
|
96
|
+
const { filename, fileMustExist, timeout, explicitWrite, wal, verbose } =
|
|
97
|
+
this.config;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const setOptions = (link: SQLite3.Database) => {
|
|
101
|
+
// Set database and connection options.
|
|
102
|
+
if (wal) {
|
|
103
|
+
link.pragma('journal_mode = WAL;');
|
|
104
|
+
}
|
|
105
|
+
link.pragma('encoding = "UTF-8";');
|
|
106
|
+
link.pragma('foreign_keys = 1;');
|
|
107
|
+
link.pragma('case_sensitive_like = 1;');
|
|
108
|
+
for (let pragma of this.config.pragmas) {
|
|
109
|
+
link.pragma(pragma);
|
|
110
|
+
}
|
|
111
|
+
// Create the preg_match and regexp functions.
|
|
112
|
+
link.function('regexp', { deterministic: true }, ((
|
|
113
|
+
pattern: string,
|
|
114
|
+
subject: string,
|
|
115
|
+
) => (this.posixRegexMatch(pattern, subject) ? 1 : 0)) as (
|
|
116
|
+
...params: any[]
|
|
117
|
+
) => any);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let link: SQLite3.Database;
|
|
61
121
|
try {
|
|
62
|
-
|
|
63
|
-
readonly,
|
|
122
|
+
link = new SQLite3(filename, {
|
|
123
|
+
readonly: !explicitWrite && !write,
|
|
64
124
|
fileMustExist,
|
|
65
125
|
timeout,
|
|
66
126
|
verbose,
|
|
67
127
|
});
|
|
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
128
|
} catch (e: any) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
129
|
+
if (
|
|
130
|
+
e.code === 'SQLITE_CANTOPEN' &&
|
|
131
|
+
!explicitWrite &&
|
|
132
|
+
!write &&
|
|
133
|
+
!this.config.fileMustExist
|
|
134
|
+
) {
|
|
135
|
+
// This happens when the file doesn't exist and we attempt to open it
|
|
136
|
+
// readonly.
|
|
137
|
+
// First open it in write mode.
|
|
138
|
+
const writeLink = new SQLite3(filename, {
|
|
139
|
+
readonly: false,
|
|
140
|
+
fileMustExist,
|
|
141
|
+
timeout,
|
|
142
|
+
verbose,
|
|
143
|
+
});
|
|
144
|
+
setOptions(writeLink);
|
|
145
|
+
writeLink.close();
|
|
146
|
+
// Now open in readonly.
|
|
147
|
+
link = new SQLite3(filename, {
|
|
148
|
+
readonly: true,
|
|
149
|
+
fileMustExist,
|
|
150
|
+
timeout,
|
|
151
|
+
verbose,
|
|
152
|
+
});
|
|
86
153
|
} else {
|
|
87
|
-
throw
|
|
154
|
+
throw e;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!this.store) {
|
|
159
|
+
if (write) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
'Tried to open in write without opening in read first.',
|
|
162
|
+
);
|
|
88
163
|
}
|
|
164
|
+
this.store = new InternalStore(link);
|
|
165
|
+
} else if (write) {
|
|
166
|
+
this.store.linkWrite = link;
|
|
167
|
+
} else {
|
|
168
|
+
this.store.link = link;
|
|
169
|
+
}
|
|
170
|
+
this.store.connected = true;
|
|
171
|
+
setOptions(link);
|
|
172
|
+
} catch (e: any) {
|
|
173
|
+
if (this.store) {
|
|
174
|
+
this.store.connected = false;
|
|
175
|
+
}
|
|
176
|
+
if (filename === ':memory:') {
|
|
177
|
+
throw new NotConfiguredError(
|
|
178
|
+
"It seems the config hasn't been set up correctly. Could not connect: " +
|
|
179
|
+
e?.message,
|
|
180
|
+
);
|
|
181
|
+
} else {
|
|
182
|
+
throw new UnableToConnectError('Could not connect: ' + e?.message);
|
|
89
183
|
}
|
|
90
184
|
}
|
|
91
|
-
return this.connected;
|
|
92
185
|
}
|
|
93
186
|
|
|
94
187
|
/**
|
|
@@ -97,16 +190,24 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
97
190
|
* @returns Whether this instance is connected to a SQLite3 database.
|
|
98
191
|
*/
|
|
99
192
|
public async disconnect() {
|
|
100
|
-
if (this.connected) {
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
|
|
193
|
+
if (this.store.connected) {
|
|
194
|
+
if (this.store.linkWrite && !this.config.explicitWrite) {
|
|
195
|
+
this.store.linkWrite.exec('PRAGMA optimize;');
|
|
196
|
+
this.store.linkWrite.close();
|
|
197
|
+
this.store.linkWrite = undefined;
|
|
198
|
+
}
|
|
199
|
+
if (this.config.explicitWrite) {
|
|
200
|
+
this.store.link.exec('PRAGMA optimize;');
|
|
201
|
+
}
|
|
202
|
+
this.store.link.close();
|
|
203
|
+
this.store.transactionsStarted = 0;
|
|
204
|
+
this.store.connected = false;
|
|
104
205
|
}
|
|
105
|
-
return this.connected;
|
|
206
|
+
return this.store.connected;
|
|
106
207
|
}
|
|
107
208
|
|
|
108
209
|
public async inTransaction() {
|
|
109
|
-
return this.transactionsStarted > 0;
|
|
210
|
+
return this.store.transactionsStarted > 0;
|
|
110
211
|
}
|
|
111
212
|
|
|
112
213
|
/**
|
|
@@ -115,18 +216,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
115
216
|
* @returns Whether this instance is connected to a SQLite3 database.
|
|
116
217
|
*/
|
|
117
218
|
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
|
-
}
|
|
219
|
+
return this.store.connected;
|
|
130
220
|
}
|
|
131
221
|
|
|
132
222
|
/**
|
|
@@ -135,158 +225,173 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
135
225
|
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
136
226
|
*/
|
|
137
227
|
private createTables(etype: string | null = null) {
|
|
138
|
-
this.checkReadOnlyMode();
|
|
139
228
|
this.startTransaction('nymph-tablecreation');
|
|
140
229
|
try {
|
|
141
230
|
if (etype != null) {
|
|
142
231
|
// Create the entity table.
|
|
143
232
|
this.queryRun(
|
|
144
233
|
`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(
|
|
145
|
-
`${this.prefix}entities_${etype}
|
|
146
|
-
)} ("guid" CHARACTER(24) PRIMARY KEY, "tags" TEXT, "cdate" REAL NOT NULL, "mdate" REAL NOT NULL)
|
|
234
|
+
`${this.prefix}entities_${etype}`,
|
|
235
|
+
)} ("guid" CHARACTER(24) PRIMARY KEY, "tags" TEXT, "cdate" REAL NOT NULL, "mdate" REAL NOT NULL);`,
|
|
147
236
|
);
|
|
148
237
|
this.queryRun(
|
|
149
238
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
150
|
-
`${this.prefix}entities_${etype}_id_cdate
|
|
239
|
+
`${this.prefix}entities_${etype}_id_cdate`,
|
|
151
240
|
)} ON ${SQLite3Driver.escape(
|
|
152
|
-
`${this.prefix}entities_${etype}
|
|
153
|
-
)} ("cdate")
|
|
241
|
+
`${this.prefix}entities_${etype}`,
|
|
242
|
+
)} ("cdate");`,
|
|
154
243
|
);
|
|
155
244
|
this.queryRun(
|
|
156
245
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
157
|
-
`${this.prefix}entities_${etype}_id_mdate
|
|
246
|
+
`${this.prefix}entities_${etype}_id_mdate`,
|
|
158
247
|
)} ON ${SQLite3Driver.escape(
|
|
159
|
-
`${this.prefix}entities_${etype}
|
|
160
|
-
)} ("mdate")
|
|
248
|
+
`${this.prefix}entities_${etype}`,
|
|
249
|
+
)} ("mdate");`,
|
|
161
250
|
);
|
|
162
251
|
this.queryRun(
|
|
163
252
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
164
|
-
`${this.prefix}entities_${etype}_id_tags
|
|
253
|
+
`${this.prefix}entities_${etype}_id_tags`,
|
|
165
254
|
)} ON ${SQLite3Driver.escape(
|
|
166
|
-
`${this.prefix}entities_${etype}
|
|
167
|
-
)} ("tags")
|
|
255
|
+
`${this.prefix}entities_${etype}`,
|
|
256
|
+
)} ("tags");`,
|
|
168
257
|
);
|
|
169
258
|
// Create the data table.
|
|
170
259
|
this.queryRun(
|
|
171
260
|
`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(
|
|
172
|
-
`${this.prefix}data_${etype}
|
|
261
|
+
`${this.prefix}data_${etype}`,
|
|
173
262
|
)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(
|
|
174
|
-
`${this.prefix}entities_${etype}
|
|
175
|
-
)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "value" TEXT NOT NULL, PRIMARY KEY("guid", "name"))
|
|
263
|
+
`${this.prefix}entities_${etype}`,
|
|
264
|
+
)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "value" TEXT NOT NULL, PRIMARY KEY("guid", "name"));`,
|
|
176
265
|
);
|
|
177
266
|
this.queryRun(
|
|
178
267
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
179
|
-
`${this.prefix}data_${etype}_id_guid
|
|
268
|
+
`${this.prefix}data_${etype}_id_guid`,
|
|
180
269
|
)} ON ${SQLite3Driver.escape(
|
|
181
|
-
`${this.prefix}data_${etype}
|
|
182
|
-
)} ("guid")
|
|
270
|
+
`${this.prefix}data_${etype}`,
|
|
271
|
+
)} ("guid");`,
|
|
183
272
|
);
|
|
184
273
|
this.queryRun(
|
|
185
274
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
186
|
-
`${this.prefix}data_${etype}_id_name
|
|
275
|
+
`${this.prefix}data_${etype}_id_name`,
|
|
187
276
|
)} ON ${SQLite3Driver.escape(
|
|
188
|
-
`${this.prefix}data_${etype}
|
|
189
|
-
)} ("name")
|
|
277
|
+
`${this.prefix}data_${etype}`,
|
|
278
|
+
)} ("name");`,
|
|
190
279
|
);
|
|
191
280
|
this.queryRun(
|
|
192
281
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
193
|
-
`${this.prefix}data_${etype}_id_value
|
|
282
|
+
`${this.prefix}data_${etype}_id_value`,
|
|
194
283
|
)} ON ${SQLite3Driver.escape(
|
|
195
|
-
`${this.prefix}data_${etype}
|
|
196
|
-
)} ("value")
|
|
284
|
+
`${this.prefix}data_${etype}`,
|
|
285
|
+
)} ("value");`,
|
|
197
286
|
);
|
|
198
287
|
this.queryRun(
|
|
199
288
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
200
|
-
`${this.prefix}data_${etype}_id_guid__name_user
|
|
289
|
+
`${this.prefix}data_${etype}_id_guid__name_user`,
|
|
201
290
|
)} ON ${SQLite3Driver.escape(
|
|
202
|
-
`${this.prefix}data_${etype}
|
|
203
|
-
)} ("guid") WHERE "name" = \'user\'
|
|
291
|
+
`${this.prefix}data_${etype}`,
|
|
292
|
+
)} ("guid") WHERE "name" = \'user\';`,
|
|
204
293
|
);
|
|
205
294
|
this.queryRun(
|
|
206
295
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
207
|
-
`${this.prefix}data_${etype}_id_guid__name_group
|
|
296
|
+
`${this.prefix}data_${etype}_id_guid__name_group`,
|
|
208
297
|
)} ON ${SQLite3Driver.escape(
|
|
209
|
-
`${this.prefix}data_${etype}
|
|
210
|
-
)} ("guid") WHERE "name" = \'group\'
|
|
298
|
+
`${this.prefix}data_${etype}`,
|
|
299
|
+
)} ("guid") WHERE "name" = \'group\';`,
|
|
211
300
|
);
|
|
212
301
|
// Create the comparisons table.
|
|
213
302
|
this.queryRun(
|
|
214
303
|
`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(
|
|
215
|
-
`${this.prefix}comparisons_${etype}
|
|
304
|
+
`${this.prefix}comparisons_${etype}`,
|
|
216
305
|
)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(
|
|
217
|
-
`${this.prefix}entities_${etype}
|
|
218
|
-
)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "truthy" INTEGER, "string" TEXT, "number" REAL, PRIMARY KEY("guid", "name"))
|
|
306
|
+
`${this.prefix}entities_${etype}`,
|
|
307
|
+
)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "truthy" INTEGER, "string" TEXT, "number" REAL, PRIMARY KEY("guid", "name"));`,
|
|
308
|
+
);
|
|
309
|
+
this.queryRun(
|
|
310
|
+
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
311
|
+
`${this.prefix}comparisons_${etype}_id_guid`,
|
|
312
|
+
)} ON ${SQLite3Driver.escape(
|
|
313
|
+
`${this.prefix}comparisons_${etype}`,
|
|
314
|
+
)} ("guid");`,
|
|
219
315
|
);
|
|
220
316
|
this.queryRun(
|
|
221
317
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
222
|
-
`${this.prefix}comparisons_${etype}
|
|
318
|
+
`${this.prefix}comparisons_${etype}_id_name`,
|
|
223
319
|
)} ON ${SQLite3Driver.escape(
|
|
224
|
-
`${this.prefix}comparisons_${etype}
|
|
225
|
-
)} ("
|
|
320
|
+
`${this.prefix}comparisons_${etype}`,
|
|
321
|
+
)} ("name");`,
|
|
226
322
|
);
|
|
227
323
|
this.queryRun(
|
|
228
324
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
229
|
-
`${this.prefix}comparisons_${etype}
|
|
325
|
+
`${this.prefix}comparisons_${etype}_id_name__truthy`,
|
|
230
326
|
)} ON ${SQLite3Driver.escape(
|
|
231
|
-
`${this.prefix}comparisons_${etype}
|
|
232
|
-
)} ("name")
|
|
327
|
+
`${this.prefix}comparisons_${etype}`,
|
|
328
|
+
)} ("name") WHERE "truthy" = 1;`,
|
|
233
329
|
);
|
|
234
330
|
this.queryRun(
|
|
235
331
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
236
|
-
`${this.prefix}comparisons_${etype}
|
|
332
|
+
`${this.prefix}comparisons_${etype}_id_string`,
|
|
237
333
|
)} ON ${SQLite3Driver.escape(
|
|
238
|
-
`${this.prefix}comparisons_${etype}
|
|
239
|
-
)} ("
|
|
334
|
+
`${this.prefix}comparisons_${etype}`,
|
|
335
|
+
)} ("string");`,
|
|
240
336
|
);
|
|
241
337
|
// Create the references table.
|
|
242
338
|
this.queryRun(
|
|
243
339
|
`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(
|
|
244
|
-
`${this.prefix}references_${etype}
|
|
340
|
+
`${this.prefix}references_${etype}`,
|
|
245
341
|
)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(
|
|
246
|
-
`${this.prefix}entities_${etype}
|
|
247
|
-
)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "reference" CHARACTER(24) NOT NULL, PRIMARY KEY("guid", "name", "reference"))
|
|
342
|
+
`${this.prefix}entities_${etype}`,
|
|
343
|
+
)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "reference" CHARACTER(24) NOT NULL, PRIMARY KEY("guid", "name", "reference"));`,
|
|
248
344
|
);
|
|
249
345
|
this.queryRun(
|
|
250
346
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
251
|
-
`${this.prefix}references_${etype}_id_guid
|
|
347
|
+
`${this.prefix}references_${etype}_id_guid`,
|
|
252
348
|
)} ON ${SQLite3Driver.escape(
|
|
253
|
-
`${this.prefix}references_${etype}
|
|
254
|
-
)} ("guid")
|
|
349
|
+
`${this.prefix}references_${etype}`,
|
|
350
|
+
)} ("guid");`,
|
|
255
351
|
);
|
|
256
352
|
this.queryRun(
|
|
257
353
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
258
|
-
`${this.prefix}references_${etype}_id_name
|
|
354
|
+
`${this.prefix}references_${etype}_id_name`,
|
|
259
355
|
)} ON ${SQLite3Driver.escape(
|
|
260
|
-
`${this.prefix}references_${etype}
|
|
261
|
-
)} ("name")
|
|
356
|
+
`${this.prefix}references_${etype}`,
|
|
357
|
+
)} ("name");`,
|
|
262
358
|
);
|
|
263
359
|
this.queryRun(
|
|
264
360
|
`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(
|
|
265
|
-
`${this.prefix}references_${etype}_id_reference
|
|
361
|
+
`${this.prefix}references_${etype}_id_reference`,
|
|
266
362
|
)} ON ${SQLite3Driver.escape(
|
|
267
|
-
`${this.prefix}references_${etype}
|
|
268
|
-
)} ("reference")
|
|
363
|
+
`${this.prefix}references_${etype}`,
|
|
364
|
+
)} ("reference");`,
|
|
365
|
+
);
|
|
366
|
+
// Create the unique strings table.
|
|
367
|
+
this.queryRun(
|
|
368
|
+
`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(
|
|
369
|
+
`${this.prefix}uniques_${etype}`,
|
|
370
|
+
)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(
|
|
371
|
+
`${this.prefix}entities_${etype}`,
|
|
372
|
+
)} ("guid") ON DELETE CASCADE, "unique" TEXT NOT NULL UNIQUE, PRIMARY KEY("guid", "unique"));`,
|
|
269
373
|
);
|
|
270
374
|
} else {
|
|
271
375
|
// Create the UID table.
|
|
272
376
|
this.queryRun(
|
|
273
377
|
`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(
|
|
274
|
-
`${this.prefix}uids
|
|
275
|
-
)} ("name" TEXT PRIMARY KEY NOT NULL, "cur_uid" INTEGER NOT NULL)
|
|
378
|
+
`${this.prefix}uids`,
|
|
379
|
+
)} ("name" TEXT PRIMARY KEY NOT NULL, "cur_uid" INTEGER NOT NULL);`,
|
|
276
380
|
);
|
|
277
381
|
}
|
|
278
|
-
this.commit('nymph-tablecreation');
|
|
279
|
-
return true;
|
|
280
382
|
} catch (e: any) {
|
|
281
383
|
this.rollback('nymph-tablecreation');
|
|
282
384
|
throw e;
|
|
283
385
|
}
|
|
386
|
+
|
|
387
|
+
this.commit('nymph-tablecreation');
|
|
388
|
+
return true;
|
|
284
389
|
}
|
|
285
390
|
|
|
286
391
|
private query<T extends () => any>(
|
|
287
392
|
runQuery: T,
|
|
288
393
|
query: string,
|
|
289
|
-
etypes: string[] = []
|
|
394
|
+
etypes: string[] = [],
|
|
290
395
|
): ReturnType<T> {
|
|
291
396
|
try {
|
|
292
397
|
return runQuery();
|
|
@@ -306,13 +411,18 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
306
411
|
} catch (e2: any) {
|
|
307
412
|
throw new QueryFailedError(
|
|
308
413
|
'Query failed: ' + e2?.code + ' - ' + e2?.message,
|
|
309
|
-
query
|
|
414
|
+
query,
|
|
310
415
|
);
|
|
311
416
|
}
|
|
417
|
+
} else if (
|
|
418
|
+
errorCode === 'SQLITE_CONSTRAINT_UNIQUE' &&
|
|
419
|
+
errorMsg.match(/^UNIQUE constraint failed: /)
|
|
420
|
+
) {
|
|
421
|
+
throw new EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
312
422
|
} else {
|
|
313
423
|
throw new QueryFailedError(
|
|
314
424
|
'Query failed: ' + e?.code + ' - ' + e?.message,
|
|
315
|
-
query
|
|
425
|
+
query,
|
|
316
426
|
);
|
|
317
427
|
}
|
|
318
428
|
}
|
|
@@ -323,12 +433,15 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
323
433
|
{
|
|
324
434
|
etypes = [],
|
|
325
435
|
params = {},
|
|
326
|
-
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
436
|
+
}: { etypes?: string[]; params?: { [k: string]: any } } = {},
|
|
327
437
|
) {
|
|
328
438
|
return this.query(
|
|
329
|
-
() =>
|
|
439
|
+
() =>
|
|
440
|
+
(this.store.linkWrite || this.store.link)
|
|
441
|
+
.prepare(query)
|
|
442
|
+
.iterate(params),
|
|
330
443
|
`${query} -- ${JSON.stringify(params)}`,
|
|
331
|
-
etypes
|
|
444
|
+
etypes,
|
|
332
445
|
);
|
|
333
446
|
}
|
|
334
447
|
|
|
@@ -337,12 +450,13 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
337
450
|
{
|
|
338
451
|
etypes = [],
|
|
339
452
|
params = {},
|
|
340
|
-
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
453
|
+
}: { etypes?: string[]; params?: { [k: string]: any } } = {},
|
|
341
454
|
) {
|
|
342
455
|
return this.query(
|
|
343
|
-
() =>
|
|
456
|
+
() =>
|
|
457
|
+
(this.store.linkWrite || this.store.link).prepare(query).get(params),
|
|
344
458
|
`${query} -- ${JSON.stringify(params)}`,
|
|
345
|
-
etypes
|
|
459
|
+
etypes,
|
|
346
460
|
);
|
|
347
461
|
}
|
|
348
462
|
|
|
@@ -351,32 +465,44 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
351
465
|
{
|
|
352
466
|
etypes = [],
|
|
353
467
|
params = {},
|
|
354
|
-
}: { etypes?: string[]; params?: { [k: string]: any } } = {}
|
|
468
|
+
}: { etypes?: string[]; params?: { [k: string]: any } } = {},
|
|
355
469
|
) {
|
|
356
470
|
return this.query(
|
|
357
|
-
() =>
|
|
471
|
+
() =>
|
|
472
|
+
(this.store.linkWrite || this.store.link).prepare(query).run(params),
|
|
358
473
|
`${query} -- ${JSON.stringify(params)}`,
|
|
359
|
-
etypes
|
|
474
|
+
etypes,
|
|
360
475
|
);
|
|
361
476
|
}
|
|
362
477
|
|
|
363
478
|
public async commit(name: string) {
|
|
364
479
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
365
480
|
throw new InvalidParametersError(
|
|
366
|
-
'Transaction commit attempted without a name.'
|
|
481
|
+
'Transaction commit attempted without a name.',
|
|
367
482
|
);
|
|
368
483
|
}
|
|
369
|
-
if (this.transactionsStarted === 0) {
|
|
484
|
+
if (this.store.transactionsStarted === 0) {
|
|
370
485
|
return true;
|
|
371
486
|
}
|
|
372
487
|
this.queryRun(`RELEASE SAVEPOINT ${SQLite3Driver.escape(name)};`);
|
|
373
|
-
this.transactionsStarted--;
|
|
488
|
+
this.store.transactionsStarted--;
|
|
489
|
+
|
|
490
|
+
if (
|
|
491
|
+
this.store.transactionsStarted === 0 &&
|
|
492
|
+
this.store.linkWrite &&
|
|
493
|
+
!this.config.explicitWrite
|
|
494
|
+
) {
|
|
495
|
+
this.store.linkWrite.exec('PRAGMA optimize;');
|
|
496
|
+
this.store.linkWrite.close();
|
|
497
|
+
this.store.linkWrite = undefined;
|
|
498
|
+
}
|
|
499
|
+
|
|
374
500
|
return true;
|
|
375
501
|
}
|
|
376
502
|
|
|
377
503
|
public async deleteEntityByID(
|
|
378
504
|
guid: string,
|
|
379
|
-
className?: EntityConstructor | string | null
|
|
505
|
+
className?: EntityConstructor | string | null,
|
|
380
506
|
) {
|
|
381
507
|
let EntityClass: EntityConstructor;
|
|
382
508
|
if (typeof className === 'string' || className == null) {
|
|
@@ -386,80 +512,93 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
386
512
|
EntityClass = className;
|
|
387
513
|
}
|
|
388
514
|
const etype = EntityClass.ETYPE;
|
|
389
|
-
this.checkReadOnlyMode();
|
|
390
515
|
await this.startTransaction('nymph-delete');
|
|
391
516
|
try {
|
|
392
517
|
this.queryRun(
|
|
393
518
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
394
|
-
`${this.prefix}entities_${etype}
|
|
519
|
+
`${this.prefix}entities_${etype}`,
|
|
395
520
|
)} WHERE "guid"=@guid;`,
|
|
396
521
|
{
|
|
397
522
|
etypes: [etype],
|
|
398
523
|
params: {
|
|
399
524
|
guid,
|
|
400
525
|
},
|
|
401
|
-
}
|
|
526
|
+
},
|
|
402
527
|
);
|
|
403
528
|
this.queryRun(
|
|
404
529
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
405
|
-
`${this.prefix}data_${etype}
|
|
530
|
+
`${this.prefix}data_${etype}`,
|
|
406
531
|
)} WHERE "guid"=@guid;`,
|
|
407
532
|
{
|
|
408
533
|
etypes: [etype],
|
|
409
534
|
params: {
|
|
410
535
|
guid,
|
|
411
536
|
},
|
|
412
|
-
}
|
|
537
|
+
},
|
|
413
538
|
);
|
|
414
539
|
this.queryRun(
|
|
415
540
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
416
|
-
`${this.prefix}comparisons_${etype}
|
|
541
|
+
`${this.prefix}comparisons_${etype}`,
|
|
417
542
|
)} WHERE "guid"=@guid;`,
|
|
418
543
|
{
|
|
419
544
|
etypes: [etype],
|
|
420
545
|
params: {
|
|
421
546
|
guid,
|
|
422
547
|
},
|
|
423
|
-
}
|
|
548
|
+
},
|
|
424
549
|
);
|
|
425
550
|
this.queryRun(
|
|
426
551
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
427
|
-
`${this.prefix}references_${etype}
|
|
552
|
+
`${this.prefix}references_${etype}`,
|
|
428
553
|
)} WHERE "guid"=@guid;`,
|
|
429
554
|
{
|
|
430
555
|
etypes: [etype],
|
|
431
556
|
params: {
|
|
432
557
|
guid,
|
|
433
558
|
},
|
|
434
|
-
}
|
|
559
|
+
},
|
|
560
|
+
);
|
|
561
|
+
this.queryRun(
|
|
562
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
563
|
+
`${this.prefix}uniques_${etype}`,
|
|
564
|
+
)} WHERE "guid"=@guid;`,
|
|
565
|
+
{
|
|
566
|
+
etypes: [etype],
|
|
567
|
+
params: {
|
|
568
|
+
guid,
|
|
569
|
+
},
|
|
570
|
+
},
|
|
435
571
|
);
|
|
436
|
-
await this.commit('nymph-delete');
|
|
437
|
-
// Remove any cached versions of this entity.
|
|
438
|
-
if (this.nymph.config.cache) {
|
|
439
|
-
this.cleanCache(guid);
|
|
440
|
-
}
|
|
441
|
-
return true;
|
|
442
572
|
} catch (e: any) {
|
|
573
|
+
this.nymph.config.debugError('sqlite3', `Delete entity error: "${e}"`);
|
|
443
574
|
await this.rollback('nymph-delete');
|
|
444
575
|
throw e;
|
|
445
576
|
}
|
|
577
|
+
|
|
578
|
+
await this.commit('nymph-delete');
|
|
579
|
+
// Remove any cached versions of this entity.
|
|
580
|
+
if (this.nymph.config.cache) {
|
|
581
|
+
this.cleanCache(guid);
|
|
582
|
+
}
|
|
583
|
+
return true;
|
|
446
584
|
}
|
|
447
585
|
|
|
448
586
|
public async deleteUID(name: string) {
|
|
449
587
|
if (!name) {
|
|
450
588
|
throw new InvalidParametersError('Name not given for UID');
|
|
451
589
|
}
|
|
452
|
-
this.
|
|
590
|
+
await this.startTransaction('nymph-delete-uid');
|
|
453
591
|
this.queryRun(
|
|
454
592
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
455
|
-
`${this.prefix}uids
|
|
593
|
+
`${this.prefix}uids`,
|
|
456
594
|
)} WHERE "name"=@name;`,
|
|
457
595
|
{
|
|
458
596
|
params: {
|
|
459
597
|
name,
|
|
460
598
|
},
|
|
461
|
-
}
|
|
599
|
+
},
|
|
462
600
|
);
|
|
601
|
+
await this.commit('nymph-delete-uid');
|
|
463
602
|
return true;
|
|
464
603
|
}
|
|
465
604
|
|
|
@@ -477,10 +616,10 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
477
616
|
writeLine('');
|
|
478
617
|
|
|
479
618
|
// Export UIDs.
|
|
480
|
-
let uids = this.queryIter(
|
|
619
|
+
let uids: IterableIterator<any> = this.queryIter(
|
|
481
620
|
`SELECT * FROM ${SQLite3Driver.escape(
|
|
482
|
-
`${this.prefix}uids
|
|
483
|
-
)} ORDER BY "name"
|
|
621
|
+
`${this.prefix}uids`,
|
|
622
|
+
)} ORDER BY "name";`,
|
|
484
623
|
);
|
|
485
624
|
for (const uid of uids) {
|
|
486
625
|
writeLine(`<${uid.name}>[${uid.cur_uid}]`);
|
|
@@ -493,8 +632,8 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
493
632
|
writeLine('');
|
|
494
633
|
|
|
495
634
|
// Get the etypes.
|
|
496
|
-
const tables = this.queryIter(
|
|
497
|
-
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;"
|
|
635
|
+
const tables: IterableIterator<any> = this.queryIter(
|
|
636
|
+
"SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;",
|
|
498
637
|
);
|
|
499
638
|
const etypes = [];
|
|
500
639
|
for (const table of tables) {
|
|
@@ -505,14 +644,14 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
505
644
|
|
|
506
645
|
for (const etype of etypes) {
|
|
507
646
|
// Export entities.
|
|
508
|
-
const dataIterator = this.queryIter(
|
|
647
|
+
const dataIterator: IterableIterator<any> = this.queryIter(
|
|
509
648
|
`SELECT e.*, d."name" AS "dname", d."value" AS "dvalue", c."string", c."number" FROM ${SQLite3Driver.escape(
|
|
510
|
-
`${this.prefix}entities_${etype}
|
|
649
|
+
`${this.prefix}entities_${etype}`,
|
|
511
650
|
)} e LEFT JOIN ${SQLite3Driver.escape(
|
|
512
|
-
`${this.prefix}data_${etype}
|
|
651
|
+
`${this.prefix}data_${etype}`,
|
|
513
652
|
)} d USING ("guid") INNER JOIN ${SQLite3Driver.escape(
|
|
514
|
-
`${this.prefix}comparisons_${etype}
|
|
515
|
-
)} c USING ("guid", "name") ORDER BY e."guid"
|
|
653
|
+
`${this.prefix}comparisons_${etype}`,
|
|
654
|
+
)} c USING ("guid", "name") ORDER BY e."guid";`,
|
|
516
655
|
)[Symbol.iterator]();
|
|
517
656
|
let datum = dataIterator.next();
|
|
518
657
|
while (!datum.done) {
|
|
@@ -564,7 +703,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
564
703
|
params: { [k: string]: any } = {},
|
|
565
704
|
subquery = false,
|
|
566
705
|
tableSuffix = '',
|
|
567
|
-
etypes: string[] = []
|
|
706
|
+
etypes: string[] = [],
|
|
568
707
|
) {
|
|
569
708
|
if (typeof options.class?.alterOptions === 'function') {
|
|
570
709
|
options = options.class.alterOptions(options);
|
|
@@ -574,10 +713,11 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
574
713
|
const cTable = `c${tableSuffix}`;
|
|
575
714
|
const fTable = `f${tableSuffix}`;
|
|
576
715
|
const ieTable = `ie${tableSuffix}`;
|
|
716
|
+
const sTable = `s${tableSuffix}`;
|
|
577
717
|
const sort = options.sort ?? 'cdate';
|
|
578
718
|
const queryParts = this.iterateSelectorsForQuery(
|
|
579
719
|
formattedSelectors,
|
|
580
|
-
(key, value, typeIsOr, typeIsNot) => {
|
|
720
|
+
({ key, value, typeIsOr, typeIsNot }) => {
|
|
581
721
|
const clauseNot = key.startsWith('!');
|
|
582
722
|
let curQuery = '';
|
|
583
723
|
for (const curValue of value) {
|
|
@@ -1267,7 +1407,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1267
1407
|
params,
|
|
1268
1408
|
true,
|
|
1269
1409
|
tableSuffix,
|
|
1270
|
-
etypes
|
|
1410
|
+
etypes,
|
|
1271
1411
|
);
|
|
1272
1412
|
if (curQuery) {
|
|
1273
1413
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1282,7 +1422,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1282
1422
|
case '!qref':
|
|
1283
1423
|
const [qrefOptions, ...qrefSelectors] = curValue[1] as [
|
|
1284
1424
|
Options,
|
|
1285
|
-
...FormattedSelector[]
|
|
1425
|
+
...FormattedSelector[],
|
|
1286
1426
|
];
|
|
1287
1427
|
const QrefEntityClass = qrefOptions.class as EntityConstructor;
|
|
1288
1428
|
etypes.push(QrefEntityClass.ETYPE);
|
|
@@ -1294,7 +1434,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1294
1434
|
params,
|
|
1295
1435
|
false,
|
|
1296
1436
|
makeTableSuffix(),
|
|
1297
|
-
etypes
|
|
1437
|
+
etypes,
|
|
1298
1438
|
);
|
|
1299
1439
|
if (curQuery) {
|
|
1300
1440
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -1315,22 +1455,35 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1315
1455
|
}
|
|
1316
1456
|
}
|
|
1317
1457
|
return curQuery;
|
|
1318
|
-
}
|
|
1458
|
+
},
|
|
1319
1459
|
);
|
|
1320
1460
|
|
|
1321
1461
|
let sortBy: string;
|
|
1462
|
+
let sortByInner: string;
|
|
1463
|
+
let sortJoin = '';
|
|
1464
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1322
1465
|
switch (sort) {
|
|
1323
1466
|
case 'mdate':
|
|
1324
|
-
sortBy =
|
|
1467
|
+
sortBy = `${eTable}."mdate"${order}`;
|
|
1468
|
+
sortByInner = `${ieTable}."mdate"${order}`;
|
|
1325
1469
|
break;
|
|
1326
1470
|
case 'cdate':
|
|
1471
|
+
sortBy = `${eTable}."cdate"${order}`;
|
|
1472
|
+
sortByInner = `${ieTable}."cdate"${order}`;
|
|
1473
|
+
break;
|
|
1327
1474
|
default:
|
|
1328
|
-
|
|
1475
|
+
const name = `param${++count.i}`;
|
|
1476
|
+
sortJoin = `LEFT JOIN (
|
|
1477
|
+
SELECT "guid", "string", "number"
|
|
1478
|
+
FROM ${SQLite3Driver.escape(this.prefix + 'comparisons_' + etype)}
|
|
1479
|
+
WHERE "name"=@${name}
|
|
1480
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1481
|
+
) ${sTable} USING ("guid")`;
|
|
1482
|
+
sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1483
|
+
sortByInner = sortBy;
|
|
1484
|
+
params[name] = sort;
|
|
1329
1485
|
break;
|
|
1330
1486
|
}
|
|
1331
|
-
if (options.reverse) {
|
|
1332
|
-
sortBy += ' DESC';
|
|
1333
|
-
}
|
|
1334
1487
|
|
|
1335
1488
|
let query: string;
|
|
1336
1489
|
if (queryParts.length) {
|
|
@@ -1351,24 +1504,25 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1351
1504
|
query = `SELECT COUNT("guid") AS "count" FROM (
|
|
1352
1505
|
SELECT "guid"
|
|
1353
1506
|
FROM ${SQLite3Driver.escape(
|
|
1354
|
-
this.prefix + 'entities_' + etype
|
|
1507
|
+
this.prefix + 'entities_' + etype,
|
|
1355
1508
|
)} ${ieTable}
|
|
1356
1509
|
WHERE (${whereClause})${limit}${offset}
|
|
1357
1510
|
)`;
|
|
1358
1511
|
} else {
|
|
1359
1512
|
query = `SELECT COUNT("guid") AS "count"
|
|
1360
1513
|
FROM ${SQLite3Driver.escape(
|
|
1361
|
-
this.prefix + 'entities_' + etype
|
|
1514
|
+
this.prefix + 'entities_' + etype,
|
|
1362
1515
|
)} ${ieTable}
|
|
1363
1516
|
WHERE (${whereClause})`;
|
|
1364
1517
|
}
|
|
1365
1518
|
} else if (options.return === 'guid') {
|
|
1366
1519
|
query = `SELECT "guid"
|
|
1367
1520
|
FROM ${SQLite3Driver.escape(
|
|
1368
|
-
this.prefix + 'entities_' + etype
|
|
1521
|
+
this.prefix + 'entities_' + etype,
|
|
1369
1522
|
)} ${ieTable}
|
|
1523
|
+
${sortJoin}
|
|
1370
1524
|
WHERE (${whereClause})
|
|
1371
|
-
ORDER BY ${
|
|
1525
|
+
ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
|
|
1372
1526
|
} else {
|
|
1373
1527
|
query = `SELECT
|
|
1374
1528
|
${eTable}."guid",
|
|
@@ -1380,23 +1534,25 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1380
1534
|
${cTable}."string",
|
|
1381
1535
|
${cTable}."number"
|
|
1382
1536
|
FROM ${SQLite3Driver.escape(
|
|
1383
|
-
this.prefix + 'entities_' + etype
|
|
1537
|
+
this.prefix + 'entities_' + etype,
|
|
1384
1538
|
)} ${eTable}
|
|
1385
1539
|
LEFT JOIN ${SQLite3Driver.escape(
|
|
1386
|
-
this.prefix + 'data_' + etype
|
|
1540
|
+
this.prefix + 'data_' + etype,
|
|
1387
1541
|
)} ${dTable} USING ("guid")
|
|
1388
1542
|
INNER JOIN ${SQLite3Driver.escape(
|
|
1389
|
-
this.prefix + 'comparisons_' + etype
|
|
1543
|
+
this.prefix + 'comparisons_' + etype,
|
|
1390
1544
|
)} ${cTable} USING ("guid", "name")
|
|
1545
|
+
${sortJoin}
|
|
1391
1546
|
INNER JOIN (
|
|
1392
1547
|
SELECT "guid"
|
|
1393
1548
|
FROM ${SQLite3Driver.escape(
|
|
1394
|
-
this.prefix + 'entities_' + etype
|
|
1549
|
+
this.prefix + 'entities_' + etype,
|
|
1395
1550
|
)} ${ieTable}
|
|
1551
|
+
${sortJoin}
|
|
1396
1552
|
WHERE (${whereClause})
|
|
1397
|
-
ORDER BY ${
|
|
1553
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1398
1554
|
) ${fTable} USING ("guid")
|
|
1399
|
-
ORDER BY ${
|
|
1555
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1400
1556
|
}
|
|
1401
1557
|
}
|
|
1402
1558
|
} else {
|
|
@@ -1416,21 +1572,22 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1416
1572
|
query = `SELECT COUNT("guid") AS "count" FROM (
|
|
1417
1573
|
SELECT "guid"
|
|
1418
1574
|
FROM ${SQLite3Driver.escape(
|
|
1419
|
-
this.prefix + 'entities_' + etype
|
|
1575
|
+
this.prefix + 'entities_' + etype,
|
|
1420
1576
|
)} ${ieTable}${limit}${offset}
|
|
1421
1577
|
)`;
|
|
1422
1578
|
} else {
|
|
1423
1579
|
query = `SELECT COUNT("guid") AS "count"
|
|
1424
1580
|
FROM ${SQLite3Driver.escape(
|
|
1425
|
-
this.prefix + 'entities_' + etype
|
|
1581
|
+
this.prefix + 'entities_' + etype,
|
|
1426
1582
|
)} ${ieTable}`;
|
|
1427
1583
|
}
|
|
1428
1584
|
} else if (options.return === 'guid') {
|
|
1429
1585
|
query = `SELECT "guid"
|
|
1430
1586
|
FROM ${SQLite3Driver.escape(
|
|
1431
|
-
this.prefix + 'entities_' + etype
|
|
1587
|
+
this.prefix + 'entities_' + etype,
|
|
1432
1588
|
)} ${ieTable}
|
|
1433
|
-
|
|
1589
|
+
${sortJoin}
|
|
1590
|
+
ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
|
|
1434
1591
|
} else {
|
|
1435
1592
|
if (limit || offset) {
|
|
1436
1593
|
query = `SELECT
|
|
@@ -1443,22 +1600,24 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1443
1600
|
${cTable}."string",
|
|
1444
1601
|
${cTable}."number"
|
|
1445
1602
|
FROM ${SQLite3Driver.escape(
|
|
1446
|
-
this.prefix + 'entities_' + etype
|
|
1603
|
+
this.prefix + 'entities_' + etype,
|
|
1447
1604
|
)} ${eTable}
|
|
1448
1605
|
LEFT JOIN ${SQLite3Driver.escape(
|
|
1449
|
-
this.prefix + 'data_' + etype
|
|
1606
|
+
this.prefix + 'data_' + etype,
|
|
1450
1607
|
)} ${dTable} USING ("guid")
|
|
1451
1608
|
INNER JOIN ${SQLite3Driver.escape(
|
|
1452
|
-
this.prefix + 'comparisons_' + etype
|
|
1609
|
+
this.prefix + 'comparisons_' + etype,
|
|
1453
1610
|
)} c USING ("guid", "name")
|
|
1611
|
+
${sortJoin}
|
|
1454
1612
|
INNER JOIN (
|
|
1455
1613
|
SELECT "guid"
|
|
1456
1614
|
FROM ${SQLite3Driver.escape(
|
|
1457
|
-
this.prefix + 'entities_' + etype
|
|
1615
|
+
this.prefix + 'entities_' + etype,
|
|
1458
1616
|
)} ${ieTable}
|
|
1459
|
-
|
|
1617
|
+
${sortJoin}
|
|
1618
|
+
ORDER BY ${sortByInner}${limit}${offset}
|
|
1460
1619
|
) ${fTable} USING ("guid")
|
|
1461
|
-
ORDER BY ${
|
|
1620
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1462
1621
|
} else {
|
|
1463
1622
|
query = `SELECT
|
|
1464
1623
|
${eTable}."guid",
|
|
@@ -1470,15 +1629,16 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1470
1629
|
${cTable}."string",
|
|
1471
1630
|
${cTable}."number"
|
|
1472
1631
|
FROM ${SQLite3Driver.escape(
|
|
1473
|
-
this.prefix + 'entities_' + etype
|
|
1632
|
+
this.prefix + 'entities_' + etype,
|
|
1474
1633
|
)} ${eTable}
|
|
1475
1634
|
LEFT JOIN ${SQLite3Driver.escape(
|
|
1476
|
-
this.prefix + 'data_' + etype
|
|
1635
|
+
this.prefix + 'data_' + etype,
|
|
1477
1636
|
)} ${dTable} USING ("guid")
|
|
1478
1637
|
INNER JOIN ${SQLite3Driver.escape(
|
|
1479
|
-
this.prefix + 'comparisons_' + etype
|
|
1638
|
+
this.prefix + 'comparisons_' + etype,
|
|
1480
1639
|
)} ${cTable} USING ("guid", "name")
|
|
1481
|
-
|
|
1640
|
+
${sortJoin}
|
|
1641
|
+
ORDER BY ${sortBy}, ${eTable}."guid"`;
|
|
1482
1642
|
}
|
|
1483
1643
|
}
|
|
1484
1644
|
}
|
|
@@ -1498,14 +1658,14 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1498
1658
|
protected performQuery(
|
|
1499
1659
|
options: Options,
|
|
1500
1660
|
formattedSelectors: FormattedSelector[],
|
|
1501
|
-
etype: string
|
|
1661
|
+
etype: string,
|
|
1502
1662
|
): {
|
|
1503
1663
|
result: any;
|
|
1504
1664
|
} {
|
|
1505
1665
|
const { query, params, etypes } = this.makeEntityQuery(
|
|
1506
1666
|
options,
|
|
1507
1667
|
formattedSelectors,
|
|
1508
|
-
etype
|
|
1668
|
+
etype,
|
|
1509
1669
|
);
|
|
1510
1670
|
const result = this.queryIter(query, { etypes, params })[Symbol.iterator]();
|
|
1511
1671
|
return {
|
|
@@ -1524,35 +1684,16 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1524
1684
|
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
1525
1685
|
options?: Options<T>,
|
|
1526
1686
|
...selectors: Selector[]
|
|
1527
|
-
): Promise<
|
|
1687
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
1528
1688
|
public async getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
1529
1689
|
options: Options<T> = {},
|
|
1530
1690
|
...selectors: Selector[]
|
|
1531
|
-
): Promise<
|
|
1532
|
-
|
|
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
|
-
const { result, process } = this.getEntitesRowLike<T>(
|
|
1691
|
+
): Promise<EntityInstanceType<T>[] | string[] | number> {
|
|
1692
|
+
const { result, process } = this.getEntitiesRowLike<T>(
|
|
1552
1693
|
options,
|
|
1553
1694
|
selectors,
|
|
1554
|
-
(options,
|
|
1555
|
-
this.performQuery(options,
|
|
1695
|
+
({ options, selectors, etype }) =>
|
|
1696
|
+
this.performQuery(options, selectors, etype),
|
|
1556
1697
|
() => {
|
|
1557
1698
|
const next: any = result.next();
|
|
1558
1699
|
return next.done ? null : next.value;
|
|
@@ -1573,7 +1714,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1573
1714
|
: row.value === 'S'
|
|
1574
1715
|
? JSON.stringify(row.string)
|
|
1575
1716
|
: row.value,
|
|
1576
|
-
})
|
|
1717
|
+
}),
|
|
1577
1718
|
);
|
|
1578
1719
|
const value = process();
|
|
1579
1720
|
if (value instanceof Error) {
|
|
@@ -1586,175 +1727,243 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1586
1727
|
if (name == null) {
|
|
1587
1728
|
throw new InvalidParametersError('Name not given for UID.');
|
|
1588
1729
|
}
|
|
1589
|
-
const result = this.queryGet(
|
|
1730
|
+
const result: any = this.queryGet(
|
|
1590
1731
|
`SELECT "cur_uid" FROM ${SQLite3Driver.escape(
|
|
1591
|
-
`${this.prefix}uids
|
|
1732
|
+
`${this.prefix}uids`,
|
|
1592
1733
|
)} WHERE "name"=@name;`,
|
|
1593
1734
|
{
|
|
1594
1735
|
params: {
|
|
1595
1736
|
name: name,
|
|
1596
1737
|
},
|
|
1597
|
-
}
|
|
1738
|
+
},
|
|
1598
1739
|
);
|
|
1599
1740
|
return (result?.cur_uid as number | null) ?? null;
|
|
1600
1741
|
}
|
|
1601
1742
|
|
|
1602
|
-
public async import(filename: string) {
|
|
1603
|
-
this.checkReadOnlyMode();
|
|
1743
|
+
public async import(filename: string, transaction?: boolean) {
|
|
1604
1744
|
try {
|
|
1605
1745
|
return this.importFromFile(
|
|
1606
1746
|
filename,
|
|
1607
1747
|
async (guid, tags, sdata, etype) => {
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
params: {
|
|
1626
|
-
guid,
|
|
1748
|
+
try {
|
|
1749
|
+
await this.startTransaction(`nymph-import-entity-${guid}`);
|
|
1750
|
+
|
|
1751
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
1752
|
+
delete sdata.cdate;
|
|
1753
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
1754
|
+
delete sdata.mdate;
|
|
1755
|
+
|
|
1756
|
+
this.queryRun(
|
|
1757
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
1758
|
+
`${this.prefix}entities_${etype}`,
|
|
1759
|
+
)} WHERE "guid"=@guid;`,
|
|
1760
|
+
{
|
|
1761
|
+
etypes: [etype],
|
|
1762
|
+
params: {
|
|
1763
|
+
guid,
|
|
1764
|
+
},
|
|
1627
1765
|
},
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1766
|
+
);
|
|
1767
|
+
this.queryRun(
|
|
1768
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
1769
|
+
`${this.prefix}data_${etype}`,
|
|
1770
|
+
)} WHERE "guid"=@guid;`,
|
|
1771
|
+
{
|
|
1772
|
+
etypes: [etype],
|
|
1773
|
+
params: {
|
|
1774
|
+
guid,
|
|
1775
|
+
},
|
|
1638
1776
|
},
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1777
|
+
);
|
|
1778
|
+
this.queryRun(
|
|
1779
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
1780
|
+
`${this.prefix}comparisons_${etype}`,
|
|
1781
|
+
)} WHERE "guid"=@guid;`,
|
|
1782
|
+
{
|
|
1783
|
+
etypes: [etype],
|
|
1784
|
+
params: {
|
|
1785
|
+
guid,
|
|
1786
|
+
},
|
|
1649
1787
|
},
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
tags: ',' + tags.join(',') + ',',
|
|
1661
|
-
cdate: Number(JSON.parse(sdata.cdate)),
|
|
1662
|
-
mdate: Number(JSON.parse(sdata.mdate)),
|
|
1788
|
+
);
|
|
1789
|
+
this.queryRun(
|
|
1790
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
1791
|
+
`${this.prefix}references_${etype}`,
|
|
1792
|
+
)} WHERE "guid"=@guid;`,
|
|
1793
|
+
{
|
|
1794
|
+
etypes: [etype],
|
|
1795
|
+
params: {
|
|
1796
|
+
guid,
|
|
1797
|
+
},
|
|
1663
1798
|
},
|
|
1664
|
-
|
|
1665
|
-
);
|
|
1666
|
-
delete sdata.cdate;
|
|
1667
|
-
delete sdata.mdate;
|
|
1668
|
-
for (const name in sdata) {
|
|
1669
|
-
const value = sdata[name];
|
|
1670
|
-
const uvalue = JSON.parse(value);
|
|
1671
|
-
if (value === undefined) {
|
|
1672
|
-
continue;
|
|
1673
|
-
}
|
|
1674
|
-
const storageValue =
|
|
1675
|
-
typeof uvalue === 'number'
|
|
1676
|
-
? 'N'
|
|
1677
|
-
: typeof uvalue === 'string'
|
|
1678
|
-
? 'S'
|
|
1679
|
-
: value;
|
|
1799
|
+
);
|
|
1680
1800
|
this.queryRun(
|
|
1681
|
-
`
|
|
1682
|
-
`${this.prefix}
|
|
1683
|
-
)}
|
|
1801
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
1802
|
+
`${this.prefix}uniques_${etype}`,
|
|
1803
|
+
)} WHERE "guid"=@guid;`,
|
|
1684
1804
|
{
|
|
1685
1805
|
etypes: [etype],
|
|
1686
1806
|
params: {
|
|
1687
1807
|
guid,
|
|
1688
|
-
name,
|
|
1689
|
-
storageValue,
|
|
1690
1808
|
},
|
|
1691
|
-
}
|
|
1809
|
+
},
|
|
1692
1810
|
);
|
|
1811
|
+
|
|
1693
1812
|
this.queryRun(
|
|
1694
1813
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1695
|
-
`${this.prefix}
|
|
1696
|
-
)} ("guid", "
|
|
1814
|
+
`${this.prefix}entities_${etype}`,
|
|
1815
|
+
)} ("guid", "tags", "cdate", "mdate") VALUES (@guid, @tags, @cdate, @mdate);`,
|
|
1697
1816
|
{
|
|
1698
1817
|
etypes: [etype],
|
|
1699
1818
|
params: {
|
|
1700
1819
|
guid,
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
number: Number(uvalue),
|
|
1820
|
+
tags: ',' + tags.join(',') + ',',
|
|
1821
|
+
cdate,
|
|
1822
|
+
mdate,
|
|
1705
1823
|
},
|
|
1706
|
-
}
|
|
1824
|
+
},
|
|
1707
1825
|
);
|
|
1708
|
-
const
|
|
1709
|
-
|
|
1826
|
+
for (const name in sdata) {
|
|
1827
|
+
const value = sdata[name];
|
|
1828
|
+
const uvalue = JSON.parse(value);
|
|
1829
|
+
if (value === undefined) {
|
|
1830
|
+
continue;
|
|
1831
|
+
}
|
|
1832
|
+
const storageValue =
|
|
1833
|
+
typeof uvalue === 'number'
|
|
1834
|
+
? 'N'
|
|
1835
|
+
: typeof uvalue === 'string'
|
|
1836
|
+
? 'S'
|
|
1837
|
+
: value;
|
|
1710
1838
|
this.queryRun(
|
|
1711
1839
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1712
|
-
`${this.prefix}
|
|
1713
|
-
)} ("guid", "name", "
|
|
1840
|
+
`${this.prefix}data_${etype}`,
|
|
1841
|
+
)} ("guid", "name", "value") VALUES (@guid, @name, @storageValue);`,
|
|
1714
1842
|
{
|
|
1715
1843
|
etypes: [etype],
|
|
1716
1844
|
params: {
|
|
1717
1845
|
guid,
|
|
1718
1846
|
name,
|
|
1719
|
-
|
|
1847
|
+
storageValue,
|
|
1720
1848
|
},
|
|
1721
|
-
}
|
|
1849
|
+
},
|
|
1850
|
+
);
|
|
1851
|
+
this.queryRun(
|
|
1852
|
+
`INSERT INTO ${SQLite3Driver.escape(
|
|
1853
|
+
`${this.prefix}comparisons_${etype}`,
|
|
1854
|
+
)} ("guid", "name", "truthy", "string", "number") VALUES (@guid, @name, @truthy, @string, @number);`,
|
|
1855
|
+
{
|
|
1856
|
+
etypes: [etype],
|
|
1857
|
+
params: {
|
|
1858
|
+
guid,
|
|
1859
|
+
name,
|
|
1860
|
+
truthy: uvalue ? 1 : 0,
|
|
1861
|
+
string: `${uvalue}`,
|
|
1862
|
+
number: Number(uvalue),
|
|
1863
|
+
},
|
|
1864
|
+
},
|
|
1722
1865
|
);
|
|
1866
|
+
const references = this.findReferences(value);
|
|
1867
|
+
for (const reference of references) {
|
|
1868
|
+
this.queryRun(
|
|
1869
|
+
`INSERT INTO ${SQLite3Driver.escape(
|
|
1870
|
+
`${this.prefix}references_${etype}`,
|
|
1871
|
+
)} ("guid", "name", "reference") VALUES (@guid, @name, @reference);`,
|
|
1872
|
+
{
|
|
1873
|
+
etypes: [etype],
|
|
1874
|
+
params: {
|
|
1875
|
+
guid,
|
|
1876
|
+
name,
|
|
1877
|
+
reference,
|
|
1878
|
+
},
|
|
1879
|
+
},
|
|
1880
|
+
);
|
|
1881
|
+
}
|
|
1723
1882
|
}
|
|
1883
|
+
const uniques = await this.nymph
|
|
1884
|
+
.getEntityClassByEtype(etype)
|
|
1885
|
+
.getUniques({ guid, cdate, mdate, tags, data: {}, sdata });
|
|
1886
|
+
for (const unique of uniques) {
|
|
1887
|
+
try {
|
|
1888
|
+
this.queryRun(
|
|
1889
|
+
`INSERT INTO ${SQLite3Driver.escape(
|
|
1890
|
+
`${this.prefix}uniques_${etype}`,
|
|
1891
|
+
)} ("guid", "unique") VALUES (@guid, @unique);`,
|
|
1892
|
+
{
|
|
1893
|
+
etypes: [etype],
|
|
1894
|
+
params: {
|
|
1895
|
+
guid,
|
|
1896
|
+
unique,
|
|
1897
|
+
},
|
|
1898
|
+
},
|
|
1899
|
+
);
|
|
1900
|
+
} catch (e: any) {
|
|
1901
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
1902
|
+
this.nymph.config.debugError(
|
|
1903
|
+
'sqlite3',
|
|
1904
|
+
`Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
throw e;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
await this.commit(`nymph-import-entity-${guid}`);
|
|
1911
|
+
} catch (e: any) {
|
|
1912
|
+
this.nymph.config.debugError(
|
|
1913
|
+
'sqlite3',
|
|
1914
|
+
`Import entity error: "${e}"`,
|
|
1915
|
+
);
|
|
1916
|
+
await this.rollback(`nymph-import-entity-${guid}`);
|
|
1917
|
+
throw e;
|
|
1724
1918
|
}
|
|
1725
1919
|
},
|
|
1726
1920
|
async (name, curUid) => {
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1921
|
+
try {
|
|
1922
|
+
await this.startTransaction(`nymph-import-uid-${name}`);
|
|
1923
|
+
this.queryRun(
|
|
1924
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
1925
|
+
`${this.prefix}uids`,
|
|
1926
|
+
)} WHERE "name"=@name;`,
|
|
1927
|
+
{
|
|
1928
|
+
params: {
|
|
1929
|
+
name,
|
|
1930
|
+
},
|
|
1734
1931
|
},
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1932
|
+
);
|
|
1933
|
+
this.queryRun(
|
|
1934
|
+
`INSERT INTO ${SQLite3Driver.escape(
|
|
1935
|
+
`${this.prefix}uids`,
|
|
1936
|
+
)} ("name", "cur_uid") VALUES (@name, @curUid);`,
|
|
1937
|
+
{
|
|
1938
|
+
params: {
|
|
1939
|
+
name,
|
|
1940
|
+
curUid,
|
|
1941
|
+
},
|
|
1745
1942
|
},
|
|
1746
|
-
|
|
1747
|
-
|
|
1943
|
+
);
|
|
1944
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
1945
|
+
} catch (e: any) {
|
|
1946
|
+
this.nymph.config.debugError('sqlite3', `Import UID error: "${e}"`);
|
|
1947
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
1948
|
+
throw e;
|
|
1949
|
+
}
|
|
1748
1950
|
},
|
|
1749
1951
|
async () => {
|
|
1750
|
-
|
|
1952
|
+
if (transaction) {
|
|
1953
|
+
await this.startTransaction('nymph-import');
|
|
1954
|
+
}
|
|
1751
1955
|
},
|
|
1752
1956
|
async () => {
|
|
1753
|
-
|
|
1754
|
-
|
|
1957
|
+
if (transaction) {
|
|
1958
|
+
await this.commit('nymph-import');
|
|
1959
|
+
}
|
|
1960
|
+
},
|
|
1755
1961
|
);
|
|
1756
1962
|
} catch (e: any) {
|
|
1757
|
-
|
|
1963
|
+
this.nymph.config.debugError('sqlite3', `Import error: "${e}"`);
|
|
1964
|
+
if (transaction) {
|
|
1965
|
+
await this.rollback('nymph-import');
|
|
1966
|
+
}
|
|
1758
1967
|
throw e;
|
|
1759
1968
|
}
|
|
1760
1969
|
}
|
|
@@ -1763,95 +1972,111 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1763
1972
|
if (name == null) {
|
|
1764
1973
|
throw new InvalidParametersError('Name not given for UID.');
|
|
1765
1974
|
}
|
|
1766
|
-
this.checkReadOnlyMode();
|
|
1767
1975
|
await this.startTransaction('nymph-newuid');
|
|
1976
|
+
let curUid: number | undefined = undefined;
|
|
1768
1977
|
try {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1978
|
+
curUid =
|
|
1979
|
+
(
|
|
1980
|
+
this.queryGet(
|
|
1981
|
+
`SELECT "cur_uid" FROM ${SQLite3Driver.escape(
|
|
1982
|
+
`${this.prefix}uids`,
|
|
1983
|
+
)} WHERE "name"=@name;`,
|
|
1984
|
+
{
|
|
1985
|
+
params: {
|
|
1986
|
+
name,
|
|
1987
|
+
},
|
|
1777
1988
|
},
|
|
1778
|
-
|
|
1989
|
+
) as any
|
|
1779
1990
|
)?.cur_uid ?? null;
|
|
1780
1991
|
if (curUid == null) {
|
|
1781
1992
|
curUid = 1;
|
|
1782
1993
|
this.queryRun(
|
|
1783
1994
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1784
|
-
`${this.prefix}uids
|
|
1995
|
+
`${this.prefix}uids`,
|
|
1785
1996
|
)} ("name", "cur_uid") VALUES (@name, @curUid);`,
|
|
1786
1997
|
{
|
|
1787
1998
|
params: {
|
|
1788
1999
|
name,
|
|
1789
2000
|
curUid,
|
|
1790
2001
|
},
|
|
1791
|
-
}
|
|
2002
|
+
},
|
|
1792
2003
|
);
|
|
1793
2004
|
} else {
|
|
1794
2005
|
curUid++;
|
|
1795
2006
|
this.queryRun(
|
|
1796
2007
|
`UPDATE ${SQLite3Driver.escape(
|
|
1797
|
-
`${this.prefix}uids
|
|
2008
|
+
`${this.prefix}uids`,
|
|
1798
2009
|
)} SET "cur_uid"=@curUid WHERE "name"=@name;`,
|
|
1799
2010
|
{
|
|
1800
2011
|
params: {
|
|
1801
2012
|
curUid,
|
|
1802
2013
|
name,
|
|
1803
2014
|
},
|
|
1804
|
-
}
|
|
2015
|
+
},
|
|
1805
2016
|
);
|
|
1806
2017
|
}
|
|
1807
|
-
await this.commit('nymph-newuid');
|
|
1808
|
-
return curUid as number;
|
|
1809
2018
|
} catch (e: any) {
|
|
2019
|
+
this.nymph.config.debugError('sqlite3', `New UID error: "${e}"`);
|
|
1810
2020
|
await this.rollback('nymph-newuid');
|
|
1811
2021
|
throw e;
|
|
1812
2022
|
}
|
|
2023
|
+
|
|
2024
|
+
await this.commit('nymph-newuid');
|
|
2025
|
+
return curUid as number;
|
|
1813
2026
|
}
|
|
1814
2027
|
|
|
1815
2028
|
public async renameUID(oldName: string, newName: string) {
|
|
1816
2029
|
if (oldName == null || newName == null) {
|
|
1817
2030
|
throw new InvalidParametersError('Name not given for UID.');
|
|
1818
2031
|
}
|
|
1819
|
-
this.
|
|
2032
|
+
await this.startTransaction('nymph-rename-uid');
|
|
1820
2033
|
this.queryRun(
|
|
1821
2034
|
`UPDATE ${SQLite3Driver.escape(
|
|
1822
|
-
`${this.prefix}uids
|
|
2035
|
+
`${this.prefix}uids`,
|
|
1823
2036
|
)} SET "name"=@newName WHERE "name"=@oldName;`,
|
|
1824
2037
|
{
|
|
1825
2038
|
params: {
|
|
1826
2039
|
newName,
|
|
1827
2040
|
oldName,
|
|
1828
2041
|
},
|
|
1829
|
-
}
|
|
2042
|
+
},
|
|
1830
2043
|
);
|
|
2044
|
+
await this.commit('nymph-rename-uid');
|
|
1831
2045
|
return true;
|
|
1832
2046
|
}
|
|
1833
2047
|
|
|
1834
2048
|
public async rollback(name: string) {
|
|
1835
2049
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1836
2050
|
throw new InvalidParametersError(
|
|
1837
|
-
'Transaction rollback attempted without a name.'
|
|
2051
|
+
'Transaction rollback attempted without a name.',
|
|
1838
2052
|
);
|
|
1839
2053
|
}
|
|
1840
|
-
if (this.transactionsStarted === 0) {
|
|
2054
|
+
if (this.store.transactionsStarted === 0) {
|
|
1841
2055
|
return true;
|
|
1842
2056
|
}
|
|
1843
2057
|
this.queryRun(`ROLLBACK TO SAVEPOINT ${SQLite3Driver.escape(name)};`);
|
|
1844
|
-
this.transactionsStarted--;
|
|
2058
|
+
this.store.transactionsStarted--;
|
|
2059
|
+
|
|
2060
|
+
if (
|
|
2061
|
+
this.store.transactionsStarted === 0 &&
|
|
2062
|
+
this.store.linkWrite &&
|
|
2063
|
+
!this.config.explicitWrite
|
|
2064
|
+
) {
|
|
2065
|
+
this.store.linkWrite.exec('PRAGMA optimize;');
|
|
2066
|
+
this.store.linkWrite.close();
|
|
2067
|
+
this.store.linkWrite = undefined;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
1845
2070
|
return true;
|
|
1846
2071
|
}
|
|
1847
2072
|
|
|
1848
2073
|
public async saveEntity(entity: EntityInterface) {
|
|
1849
|
-
this.checkReadOnlyMode();
|
|
1850
2074
|
const insertData = (
|
|
1851
2075
|
guid: string,
|
|
1852
2076
|
data: EntityData,
|
|
1853
2077
|
sdata: SerializedEntityData,
|
|
1854
|
-
|
|
2078
|
+
uniques: string[],
|
|
2079
|
+
etype: string,
|
|
1855
2080
|
) => {
|
|
1856
2081
|
const runInsertQuery = (name: string, value: any, svalue: string) => {
|
|
1857
2082
|
if (value === undefined) {
|
|
@@ -1865,7 +2090,7 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1865
2090
|
: svalue;
|
|
1866
2091
|
this.queryRun(
|
|
1867
2092
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1868
|
-
`${this.prefix}data_${etype}
|
|
2093
|
+
`${this.prefix}data_${etype}`,
|
|
1869
2094
|
)} ("guid", "name", "value") VALUES (@guid, @name, @storageValue);`,
|
|
1870
2095
|
{
|
|
1871
2096
|
etypes: [etype],
|
|
@@ -1874,11 +2099,11 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1874
2099
|
name,
|
|
1875
2100
|
storageValue,
|
|
1876
2101
|
},
|
|
1877
|
-
}
|
|
2102
|
+
},
|
|
1878
2103
|
);
|
|
1879
2104
|
this.queryRun(
|
|
1880
2105
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1881
|
-
`${this.prefix}comparisons_${etype}
|
|
2106
|
+
`${this.prefix}comparisons_${etype}`,
|
|
1882
2107
|
)} ("guid", "name", "truthy", "string", "number") VALUES (@guid, @name, @truthy, @string, @number);`,
|
|
1883
2108
|
{
|
|
1884
2109
|
etypes: [etype],
|
|
@@ -1889,13 +2114,13 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1889
2114
|
string: `${value}`,
|
|
1890
2115
|
number: Number(value),
|
|
1891
2116
|
},
|
|
1892
|
-
}
|
|
2117
|
+
},
|
|
1893
2118
|
);
|
|
1894
2119
|
const references = this.findReferences(svalue);
|
|
1895
2120
|
for (const reference of references) {
|
|
1896
2121
|
this.queryRun(
|
|
1897
2122
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1898
|
-
`${this.prefix}references_${etype}
|
|
2123
|
+
`${this.prefix}references_${etype}`,
|
|
1899
2124
|
)} ("guid", "name", "reference") VALUES (@guid, @name, @reference);`,
|
|
1900
2125
|
{
|
|
1901
2126
|
etypes: [etype],
|
|
@@ -1904,10 +2129,34 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1904
2129
|
name,
|
|
1905
2130
|
reference,
|
|
1906
2131
|
},
|
|
1907
|
-
}
|
|
2132
|
+
},
|
|
1908
2133
|
);
|
|
1909
2134
|
}
|
|
1910
2135
|
};
|
|
2136
|
+
for (const unique of uniques) {
|
|
2137
|
+
try {
|
|
2138
|
+
this.queryRun(
|
|
2139
|
+
`INSERT INTO ${SQLite3Driver.escape(
|
|
2140
|
+
`${this.prefix}uniques_${etype}`,
|
|
2141
|
+
)} ("guid", "unique") VALUES (@guid, @unique);`,
|
|
2142
|
+
{
|
|
2143
|
+
etypes: [etype],
|
|
2144
|
+
params: {
|
|
2145
|
+
guid,
|
|
2146
|
+
unique,
|
|
2147
|
+
},
|
|
2148
|
+
},
|
|
2149
|
+
);
|
|
2150
|
+
} catch (e: any) {
|
|
2151
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2152
|
+
this.nymph.config.debugError(
|
|
2153
|
+
'sqlite3',
|
|
2154
|
+
`Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`,
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
throw e;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
1911
2160
|
for (const name in data) {
|
|
1912
2161
|
runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
1913
2162
|
}
|
|
@@ -1915,13 +2164,20 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1915
2164
|
runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
1916
2165
|
}
|
|
1917
2166
|
};
|
|
2167
|
+
let inTransaction = false;
|
|
1918
2168
|
try {
|
|
1919
2169
|
return this.saveEntityRowLike(
|
|
1920
2170
|
entity,
|
|
1921
|
-
async (
|
|
2171
|
+
async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
2172
|
+
if (
|
|
2173
|
+
Object.keys(data).length === 0 &&
|
|
2174
|
+
Object.keys(sdata).length === 0
|
|
2175
|
+
) {
|
|
2176
|
+
return false;
|
|
2177
|
+
}
|
|
1922
2178
|
this.queryRun(
|
|
1923
2179
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
1924
|
-
`${this.prefix}entities_${etype}
|
|
2180
|
+
`${this.prefix}entities_${etype}`,
|
|
1925
2181
|
)} ("guid", "tags", "cdate", "mdate") VALUES (@guid, @tags, @cdate, @cdate);`,
|
|
1926
2182
|
{
|
|
1927
2183
|
etypes: [etype],
|
|
@@ -1930,15 +2186,21 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1930
2186
|
tags: ',' + tags.join(',') + ',',
|
|
1931
2187
|
cdate,
|
|
1932
2188
|
},
|
|
1933
|
-
}
|
|
2189
|
+
},
|
|
1934
2190
|
);
|
|
1935
|
-
insertData(guid, data, sdata, etype);
|
|
2191
|
+
insertData(guid, data, sdata, uniques, etype);
|
|
1936
2192
|
return true;
|
|
1937
2193
|
},
|
|
1938
|
-
async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
2194
|
+
async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
2195
|
+
if (
|
|
2196
|
+
Object.keys(data).length === 0 &&
|
|
2197
|
+
Object.keys(sdata).length === 0
|
|
2198
|
+
) {
|
|
2199
|
+
return false;
|
|
2200
|
+
}
|
|
1939
2201
|
const info = this.queryRun(
|
|
1940
2202
|
`UPDATE ${SQLite3Driver.escape(
|
|
1941
|
-
`${this.prefix}entities_${etype}
|
|
2203
|
+
`${this.prefix}entities_${etype}`,
|
|
1942
2204
|
)} SET "tags"=@tags, "mdate"=@mdate WHERE "guid"=@guid AND "mdate" <= @emdate;`,
|
|
1943
2205
|
{
|
|
1944
2206
|
etypes: [etype],
|
|
@@ -1948,62 +2210,80 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
1948
2210
|
guid,
|
|
1949
2211
|
emdate: Number(entity.mdate),
|
|
1950
2212
|
},
|
|
1951
|
-
}
|
|
2213
|
+
},
|
|
1952
2214
|
);
|
|
1953
2215
|
let success = false;
|
|
1954
2216
|
if (info.changes === 1) {
|
|
1955
2217
|
this.queryRun(
|
|
1956
2218
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
1957
|
-
`${this.prefix}data_${etype}
|
|
2219
|
+
`${this.prefix}data_${etype}`,
|
|
1958
2220
|
)} WHERE "guid"=@guid;`,
|
|
1959
2221
|
{
|
|
1960
2222
|
etypes: [etype],
|
|
1961
2223
|
params: {
|
|
1962
2224
|
guid,
|
|
1963
2225
|
},
|
|
1964
|
-
}
|
|
2226
|
+
},
|
|
1965
2227
|
);
|
|
1966
2228
|
this.queryRun(
|
|
1967
2229
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
1968
|
-
`${this.prefix}comparisons_${etype}
|
|
2230
|
+
`${this.prefix}comparisons_${etype}`,
|
|
1969
2231
|
)} WHERE "guid"=@guid;`,
|
|
1970
2232
|
{
|
|
1971
2233
|
etypes: [etype],
|
|
1972
2234
|
params: {
|
|
1973
2235
|
guid,
|
|
1974
2236
|
},
|
|
1975
|
-
}
|
|
2237
|
+
},
|
|
1976
2238
|
);
|
|
1977
2239
|
this.queryRun(
|
|
1978
2240
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
1979
|
-
`${this.prefix}references_${etype}
|
|
2241
|
+
`${this.prefix}references_${etype}`,
|
|
1980
2242
|
)} WHERE "guid"=@guid;`,
|
|
1981
2243
|
{
|
|
1982
2244
|
etypes: [etype],
|
|
1983
2245
|
params: {
|
|
1984
2246
|
guid,
|
|
1985
2247
|
},
|
|
1986
|
-
}
|
|
2248
|
+
},
|
|
1987
2249
|
);
|
|
1988
|
-
|
|
2250
|
+
this.queryRun(
|
|
2251
|
+
`DELETE FROM ${SQLite3Driver.escape(
|
|
2252
|
+
`${this.prefix}uniques_${etype}`,
|
|
2253
|
+
)} WHERE "guid"=@guid;`,
|
|
2254
|
+
{
|
|
2255
|
+
etypes: [etype],
|
|
2256
|
+
params: {
|
|
2257
|
+
guid,
|
|
2258
|
+
},
|
|
2259
|
+
},
|
|
2260
|
+
);
|
|
2261
|
+
insertData(guid, data, sdata, uniques, etype);
|
|
1989
2262
|
success = true;
|
|
1990
2263
|
}
|
|
1991
2264
|
return success;
|
|
1992
2265
|
},
|
|
1993
2266
|
async () => {
|
|
1994
2267
|
await this.startTransaction('nymph-save');
|
|
2268
|
+
inTransaction = true;
|
|
1995
2269
|
},
|
|
1996
2270
|
async (success) => {
|
|
1997
|
-
if (
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2271
|
+
if (inTransaction) {
|
|
2272
|
+
inTransaction = false;
|
|
2273
|
+
if (success) {
|
|
2274
|
+
await this.commit('nymph-save');
|
|
2275
|
+
} else {
|
|
2276
|
+
await this.rollback('nymph-save');
|
|
2277
|
+
}
|
|
2001
2278
|
}
|
|
2002
2279
|
return success;
|
|
2003
|
-
}
|
|
2280
|
+
},
|
|
2004
2281
|
);
|
|
2005
2282
|
} catch (e: any) {
|
|
2006
|
-
|
|
2283
|
+
this.nymph.config.debugError('sqlite3', `Save entity error: "${e}"`);
|
|
2284
|
+
if (inTransaction) {
|
|
2285
|
+
await this.rollback('nymph-save');
|
|
2286
|
+
}
|
|
2007
2287
|
throw e;
|
|
2008
2288
|
}
|
|
2009
2289
|
}
|
|
@@ -2012,39 +2292,43 @@ export default class SQLite3Driver extends NymphDriver {
|
|
|
2012
2292
|
if (name == null) {
|
|
2013
2293
|
throw new InvalidParametersError('Name not given for UID.');
|
|
2014
2294
|
}
|
|
2015
|
-
this.
|
|
2295
|
+
await this.startTransaction('nymph-set-uid');
|
|
2016
2296
|
this.queryRun(
|
|
2017
2297
|
`DELETE FROM ${SQLite3Driver.escape(
|
|
2018
|
-
`${this.prefix}uids
|
|
2298
|
+
`${this.prefix}uids`,
|
|
2019
2299
|
)} WHERE "name"=@name;`,
|
|
2020
2300
|
{
|
|
2021
2301
|
params: {
|
|
2022
2302
|
name,
|
|
2023
2303
|
},
|
|
2024
|
-
}
|
|
2304
|
+
},
|
|
2025
2305
|
);
|
|
2026
2306
|
this.queryRun(
|
|
2027
2307
|
`INSERT INTO ${SQLite3Driver.escape(
|
|
2028
|
-
`${this.prefix}uids
|
|
2308
|
+
`${this.prefix}uids`,
|
|
2029
2309
|
)} ("name", "cur_uid") VALUES (@name, @curUid);`,
|
|
2030
2310
|
{
|
|
2031
2311
|
params: {
|
|
2032
2312
|
name,
|
|
2033
2313
|
curUid,
|
|
2034
2314
|
},
|
|
2035
|
-
}
|
|
2315
|
+
},
|
|
2036
2316
|
);
|
|
2317
|
+
await this.commit('nymph-set-uid');
|
|
2037
2318
|
return true;
|
|
2038
2319
|
}
|
|
2039
2320
|
|
|
2040
2321
|
public async startTransaction(name: string) {
|
|
2041
2322
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
2042
2323
|
throw new InvalidParametersError(
|
|
2043
|
-
'Transaction start attempted without a name.'
|
|
2324
|
+
'Transaction start attempted without a name.',
|
|
2044
2325
|
);
|
|
2045
2326
|
}
|
|
2327
|
+
if (!this.config.explicitWrite && !this.store.linkWrite) {
|
|
2328
|
+
this._connect(true);
|
|
2329
|
+
}
|
|
2046
2330
|
this.queryRun(`SAVEPOINT ${SQLite3Driver.escape(name)};`);
|
|
2047
|
-
this.transactionsStarted++;
|
|
2331
|
+
this.store.transactionsStarted++;
|
|
2048
2332
|
return this.nymph;
|
|
2049
2333
|
}
|
|
2050
2334
|
}
|