@nymphjs/driver-sqlite3 1.0.0-beta.13 → 1.0.0-beta.131
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 +590 -0
- package/README.md +5 -3
- package/dist/SQLite3Driver.d.ts +108 -17
- package/dist/SQLite3Driver.js +1501 -572
- package/dist/SQLite3Driver.js.map +1 -1
- package/dist/SQLite3Driver.test.js +28 -22
- package/dist/SQLite3Driver.test.js.map +1 -1
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +2 -4
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +63 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -24
- package/dist/index.js.map +1 -1
- package/jest.config.js +31 -5
- package/package.json +32 -21
- package/src/SQLite3Driver.test.ts +23 -9
- package/src/SQLite3Driver.ts +2218 -901
- package/src/conf/defaults.ts +2 -2
- package/src/conf/index.ts +63 -2
- package/src/index.ts +2 -2
- package/tsconfig.json +6 -3
- package/typedoc.json +4 -0
- package/dist/conf/d.d.ts +0 -9
- package/dist/conf/d.js +0 -3
- package/dist/conf/d.js.map +0 -1
- package/src/conf/d.ts +0 -52
package/dist/SQLite3Driver.js
CHANGED
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
7
|
-
const nymph_1 = require("@nymphjs/nymph");
|
|
8
|
-
const guid_1 = require("@nymphjs/guid");
|
|
9
|
-
const conf_1 = require("./conf");
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { NymphDriver, EntityConflictError, EntityInvalidDataError, EntityUniqueConstraintError, InvalidParametersError, NotConfiguredError, QueryFailedError, UnableToConnectError, xor, } from '@nymphjs/nymph';
|
|
3
|
+
import { makeTableSuffix } from '@nymphjs/guid';
|
|
4
|
+
import { SQLite3DriverConfigDefaults as defaults, } from './conf/index.js';
|
|
10
5
|
class InternalStore {
|
|
6
|
+
link;
|
|
7
|
+
linkWrite;
|
|
8
|
+
connected = false;
|
|
9
|
+
transactionsStarted = 0;
|
|
11
10
|
constructor(link) {
|
|
12
|
-
this.connected = false;
|
|
13
|
-
this.transactionsStarted = 0;
|
|
14
11
|
this.link = link;
|
|
15
12
|
}
|
|
16
13
|
}
|
|
17
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The SQLite3 Nymph database driver.
|
|
16
|
+
*/
|
|
17
|
+
export default class SQLite3Driver extends NymphDriver {
|
|
18
|
+
config;
|
|
19
|
+
prefix;
|
|
20
|
+
// @ts-ignore: this is assigned in connect(), which is called by the constructor.
|
|
21
|
+
store;
|
|
18
22
|
static escape(input) {
|
|
19
23
|
if (input.indexOf('\x00') !== -1) {
|
|
20
|
-
throw new
|
|
24
|
+
throw new InvalidParametersError('SQLite3 identifiers (like entity ETYPE) cannot contain null characters.');
|
|
21
25
|
}
|
|
22
26
|
return '"' + input.replace(/"/g, () => '""') + '"';
|
|
23
27
|
}
|
|
28
|
+
static escapeValue(input) {
|
|
29
|
+
return "'" + input.replace(/'/g, () => "''") + "'";
|
|
30
|
+
}
|
|
24
31
|
constructor(config, store) {
|
|
25
32
|
super();
|
|
26
|
-
this.config = { ...
|
|
33
|
+
this.config = { ...defaults, ...config };
|
|
27
34
|
if (this.config.filename === ':memory:') {
|
|
28
35
|
this.config.explicitWrite = true;
|
|
29
36
|
}
|
|
@@ -35,45 +42,69 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
35
42
|
this.connect();
|
|
36
43
|
}
|
|
37
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* This is used internally by Nymph. Don't call it yourself.
|
|
47
|
+
*
|
|
48
|
+
* @returns A clone of this instance.
|
|
49
|
+
*/
|
|
38
50
|
clone() {
|
|
39
51
|
return new SQLite3Driver(this.config, this.store);
|
|
40
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Connect to the SQLite3 database.
|
|
55
|
+
*
|
|
56
|
+
* @returns Whether this instance is connected to a SQLite3 database.
|
|
57
|
+
*/
|
|
41
58
|
connect() {
|
|
42
59
|
if (this.store && this.store.connected) {
|
|
43
60
|
return Promise.resolve(true);
|
|
44
61
|
}
|
|
62
|
+
// Connecting
|
|
45
63
|
this._connect(false);
|
|
46
64
|
return Promise.resolve(this.store.connected);
|
|
47
65
|
}
|
|
48
66
|
_connect(write) {
|
|
49
|
-
const { filename, fileMustExist, timeout, explicitWrite, wal
|
|
67
|
+
const { filename, fileMustExist, timeout, explicitWrite, wal } = this.config;
|
|
50
68
|
try {
|
|
69
|
+
const setOptions = (link) => {
|
|
70
|
+
// Set database and connection options.
|
|
71
|
+
if (wal) {
|
|
72
|
+
link.exec('PRAGMA journal_mode = WAL;');
|
|
73
|
+
}
|
|
74
|
+
link.exec('PRAGMA encoding = "UTF-8";');
|
|
75
|
+
link.exec('PRAGMA foreign_keys = 1;');
|
|
76
|
+
link.exec('PRAGMA case_sensitive_like = 1;');
|
|
77
|
+
for (let pragma of this.config.pragmas) {
|
|
78
|
+
link.exec(`PRAGMA ${pragma}`);
|
|
79
|
+
}
|
|
80
|
+
// Create the preg_match and regexp functions.
|
|
81
|
+
link.function('regexp', { deterministic: true }, ((pattern, subject) => (this.posixRegexMatch(pattern, subject) ? 1 : 0)));
|
|
82
|
+
};
|
|
51
83
|
let link;
|
|
52
84
|
try {
|
|
53
|
-
link = new
|
|
54
|
-
|
|
55
|
-
fileMustExist,
|
|
85
|
+
link = new DatabaseSync(filename, {
|
|
86
|
+
readOnly: !explicitWrite && !write,
|
|
56
87
|
timeout,
|
|
57
|
-
verbose,
|
|
58
88
|
});
|
|
59
89
|
}
|
|
60
90
|
catch (e) {
|
|
61
|
-
if (e.code === '
|
|
91
|
+
if (e.code === 'ERR_SQLITE_CANTOPEN' &&
|
|
62
92
|
!explicitWrite &&
|
|
63
93
|
!write &&
|
|
64
|
-
!
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
94
|
+
!fileMustExist) {
|
|
95
|
+
// This happens when the file doesn't exist and we attempt to open it
|
|
96
|
+
// readonly.
|
|
97
|
+
// First open it in write mode.
|
|
98
|
+
const writeLink = new DatabaseSync(filename, {
|
|
99
|
+
readOnly: false,
|
|
68
100
|
timeout,
|
|
69
|
-
verbose,
|
|
70
101
|
});
|
|
102
|
+
setOptions(writeLink);
|
|
71
103
|
writeLink.close();
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
104
|
+
// Now open in readonly.
|
|
105
|
+
link = new DatabaseSync(filename, {
|
|
106
|
+
readOnly: true,
|
|
75
107
|
timeout,
|
|
76
|
-
verbose,
|
|
77
108
|
});
|
|
78
109
|
}
|
|
79
110
|
else {
|
|
@@ -93,27 +124,26 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
93
124
|
this.store.link = link;
|
|
94
125
|
}
|
|
95
126
|
this.store.connected = true;
|
|
96
|
-
|
|
97
|
-
link.pragma('journal_mode = WAL;');
|
|
98
|
-
}
|
|
99
|
-
link.pragma('encoding = "UTF-8";');
|
|
100
|
-
link.pragma('foreign_keys = 1;');
|
|
101
|
-
link.pragma('case_sensitive_like = 1;');
|
|
102
|
-
link.function('regexp', { deterministic: true }, (pattern, subject) => this.posixRegexMatch(pattern, subject) ? 1 : 0);
|
|
127
|
+
setOptions(link);
|
|
103
128
|
}
|
|
104
129
|
catch (e) {
|
|
105
130
|
if (this.store) {
|
|
106
131
|
this.store.connected = false;
|
|
107
132
|
}
|
|
108
133
|
if (filename === ':memory:') {
|
|
109
|
-
throw new
|
|
134
|
+
throw new NotConfiguredError("It seems the config hasn't been set up correctly. Could not connect: " +
|
|
110
135
|
e?.message);
|
|
111
136
|
}
|
|
112
137
|
else {
|
|
113
|
-
throw new
|
|
138
|
+
throw new UnableToConnectError('Could not connect: ' + e?.message);
|
|
114
139
|
}
|
|
115
140
|
}
|
|
116
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Disconnect from the SQLite3 database.
|
|
144
|
+
*
|
|
145
|
+
* @returns Whether this instance is connected to a SQLite3 database.
|
|
146
|
+
*/
|
|
117
147
|
async disconnect() {
|
|
118
148
|
if (this.store.connected) {
|
|
119
149
|
if (this.store.linkWrite && !this.config.explicitWrite) {
|
|
@@ -133,51 +163,102 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
133
163
|
async inTransaction() {
|
|
134
164
|
return this.store.transactionsStarted > 0;
|
|
135
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Check connection status.
|
|
168
|
+
*
|
|
169
|
+
* @returns Whether this instance is connected to a SQLite3 database.
|
|
170
|
+
*/
|
|
136
171
|
isConnected() {
|
|
137
172
|
return this.store.connected;
|
|
138
173
|
}
|
|
174
|
+
createEntitiesTable(etype) {
|
|
175
|
+
// Create the entity table.
|
|
176
|
+
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid" CHARACTER(24) PRIMARY KEY, "tags" TEXT, "cdate" REAL NOT NULL, "mdate" REAL NOT NULL, "user" CHARACTER(24), "group" CHARACTER(24), "acUser" INT(1), "acGroup" INT(1), "acOther" INT(1), "acRead" TEXT, "acWrite" TEXT, "acFull" TEXT);`);
|
|
177
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_cdate`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("cdate");`);
|
|
178
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_mdate`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("mdate");`);
|
|
179
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_tags`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("tags");`);
|
|
180
|
+
this.createEntitiesTilmeldIndexes(etype);
|
|
181
|
+
}
|
|
182
|
+
addTilmeldColumnsAndIndexes(etype) {
|
|
183
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "user" CHARACTER(24);`);
|
|
184
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "group" CHARACTER(24);`);
|
|
185
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "acUser" INT(1);`);
|
|
186
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "acGroup" INT(1);`);
|
|
187
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "acOther" INT(1);`);
|
|
188
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "acRead" TEXT;`);
|
|
189
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "acWrite" TEXT;`);
|
|
190
|
+
this.queryRun(`ALTER TABLE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ADD COLUMN "acFull" TEXT;`);
|
|
191
|
+
this.createEntitiesTilmeldIndexes(etype);
|
|
192
|
+
}
|
|
193
|
+
createEntitiesTilmeldIndexes(etype) {
|
|
194
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_user_acUser`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("user", "acUser");`);
|
|
195
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_group_acGroup`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("group", "acGroup");`);
|
|
196
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_acUser`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("acUser");`);
|
|
197
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_acGroup`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("acGroup");`);
|
|
198
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_acOther`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("acOther");`);
|
|
199
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_acRead`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("acRead");`);
|
|
200
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_acWrite`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("acWrite");`);
|
|
201
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}entities_${etype}_id_acFull`)} ON ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("acFull");`);
|
|
202
|
+
}
|
|
203
|
+
createDataTable(etype) {
|
|
204
|
+
// Create the data table.
|
|
205
|
+
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "value" CHARACTER(1) NOT NULL, "json" BLOB, "string" TEXT, "number" REAL, "truthy" INTEGER, PRIMARY KEY("guid", "name"));`);
|
|
206
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_name_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("name", "guid");`);
|
|
207
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_name_string_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("name", "string", "guid");`);
|
|
208
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_name_number_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("name", "number", "guid");`);
|
|
209
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_name_truthy_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("name", "truthy", "guid");`);
|
|
210
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_name_json_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("name", "json", "guid")`);
|
|
211
|
+
}
|
|
212
|
+
createReferencesTable(etype) {
|
|
213
|
+
// Create the references table.
|
|
214
|
+
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "reference" CHARACTER(24) NOT NULL, PRIMARY KEY("guid", "name", "reference"));`);
|
|
215
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}_id_name_reference_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} ("name", "reference", "guid");`);
|
|
216
|
+
}
|
|
217
|
+
createTokensTable(etype) {
|
|
218
|
+
// Create the tokens table.
|
|
219
|
+
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "token" INTEGER NOT NULL, "position" INTEGER NOT NULL, "stem" INTEGER NOT NULL, PRIMARY KEY("guid", "name", "token", "position"));`);
|
|
220
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}_id_name_token_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} ("name", "token", "guid");`);
|
|
221
|
+
}
|
|
222
|
+
createUniquesTable(etype) {
|
|
223
|
+
// Create the unique strings table.
|
|
224
|
+
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}uniques_${etype}`)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid") ON DELETE CASCADE, "unique" TEXT NOT NULL UNIQUE, PRIMARY KEY("guid", "unique"));`);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Create entity tables in the database.
|
|
228
|
+
*
|
|
229
|
+
* @param etype The entity type to create a table for. If this is blank, the default tables are created.
|
|
230
|
+
*/
|
|
139
231
|
createTables(etype = null) {
|
|
140
232
|
this.startTransaction('nymph-tablecreation');
|
|
141
233
|
try {
|
|
142
234
|
if (etype != null) {
|
|
143
|
-
this.
|
|
144
|
-
this.
|
|
145
|
-
this.
|
|
146
|
-
this.
|
|
147
|
-
this.
|
|
148
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("guid");`);
|
|
149
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_name`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("name");`);
|
|
150
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_value`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("value");`);
|
|
151
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_guid__name_user`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("guid") WHERE "name" = \'user\';`);
|
|
152
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_guid__name_group`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("guid") WHERE "name" = \'group\';`);
|
|
153
|
-
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}`)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "truthy" INTEGER, "string" TEXT, "number" REAL, PRIMARY KEY("guid", "name"));`);
|
|
154
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}_id_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}`)} ("guid");`);
|
|
155
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}_id_name`)} ON ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}`)} ("name");`);
|
|
156
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}_id_name__truthy`)} ON ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}`)} ("name") WHERE "truthy" = 1;`);
|
|
157
|
-
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} ("guid" CHARACTER(24) NOT NULL REFERENCES ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid") ON DELETE CASCADE, "name" TEXT NOT NULL, "reference" CHARACTER(24) NOT NULL, PRIMARY KEY("guid", "name", "reference"));`);
|
|
158
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}_id_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} ("guid");`);
|
|
159
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}_id_name`)} ON ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} ("name");`);
|
|
160
|
-
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}_id_reference`)} ON ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} ("reference");`);
|
|
235
|
+
this.createEntitiesTable(etype);
|
|
236
|
+
this.createDataTable(etype);
|
|
237
|
+
this.createReferencesTable(etype);
|
|
238
|
+
this.createTokensTable(etype);
|
|
239
|
+
this.createUniquesTable(etype);
|
|
161
240
|
}
|
|
162
241
|
else {
|
|
242
|
+
// Create the UID table.
|
|
163
243
|
this.queryRun(`CREATE TABLE IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}uids`)} ("name" TEXT PRIMARY KEY NOT NULL, "cur_uid" INTEGER NOT NULL);`);
|
|
164
244
|
}
|
|
165
|
-
this.commit('nymph-tablecreation');
|
|
166
|
-
return true;
|
|
167
245
|
}
|
|
168
246
|
catch (e) {
|
|
169
247
|
this.rollback('nymph-tablecreation');
|
|
170
248
|
throw e;
|
|
171
249
|
}
|
|
250
|
+
this.commit('nymph-tablecreation');
|
|
251
|
+
return true;
|
|
172
252
|
}
|
|
173
253
|
query(runQuery, query, etypes = []) {
|
|
174
254
|
try {
|
|
255
|
+
this.nymph.config.debugInfo('sqlite3:query', query);
|
|
175
256
|
return runQuery();
|
|
176
257
|
}
|
|
177
258
|
catch (e) {
|
|
178
259
|
const errorCode = e?.code;
|
|
179
260
|
const errorMsg = e?.message;
|
|
180
|
-
if (errorCode === '
|
|
261
|
+
if (errorCode === 'ERR_SQLITE_ERROR' &&
|
|
181
262
|
errorMsg.match(/^no such table: /) &&
|
|
182
263
|
this.createTables()) {
|
|
183
264
|
for (let etype of etypes) {
|
|
@@ -187,15 +268,19 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
187
268
|
return runQuery();
|
|
188
269
|
}
|
|
189
270
|
catch (e2) {
|
|
190
|
-
throw new
|
|
271
|
+
throw new QueryFailedError('Query failed: ' + e2?.code + ' - ' + e2?.message, query, e2?.code);
|
|
191
272
|
}
|
|
192
273
|
}
|
|
274
|
+
else if (errorCode === 'ERR_SQLITE_ERROR' &&
|
|
275
|
+
errorMsg.match(/^UNIQUE constraint failed: /)) {
|
|
276
|
+
throw new EntityUniqueConstraintError(`Unique constraint violation.`);
|
|
277
|
+
}
|
|
193
278
|
else {
|
|
194
|
-
throw new
|
|
279
|
+
throw new QueryFailedError('Query failed: ' + e?.code + ' - ' + e?.message, query, e?.code);
|
|
195
280
|
}
|
|
196
281
|
}
|
|
197
282
|
}
|
|
198
|
-
|
|
283
|
+
queryArray(query, { etypes = [], params = {}, } = {}) {
|
|
199
284
|
return this.query(() => (this.store.linkWrite || this.store.link)
|
|
200
285
|
.prepare(query)
|
|
201
286
|
.iterate(params), `${query} -- ${JSON.stringify(params)}`, etypes);
|
|
@@ -208,7 +293,7 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
208
293
|
}
|
|
209
294
|
async commit(name) {
|
|
210
295
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
211
|
-
throw new
|
|
296
|
+
throw new InvalidParametersError('Transaction commit attempted without a name.');
|
|
212
297
|
}
|
|
213
298
|
if (this.store.transactionsStarted === 0) {
|
|
214
299
|
return true;
|
|
@@ -248,32 +333,40 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
248
333
|
guid,
|
|
249
334
|
},
|
|
250
335
|
});
|
|
251
|
-
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}
|
|
336
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=@guid;`, {
|
|
252
337
|
etypes: [etype],
|
|
253
338
|
params: {
|
|
254
339
|
guid,
|
|
255
340
|
},
|
|
256
341
|
});
|
|
257
|
-
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}
|
|
342
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=@guid;`, {
|
|
343
|
+
etypes: [etype],
|
|
344
|
+
params: {
|
|
345
|
+
guid,
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=@guid;`, {
|
|
258
349
|
etypes: [etype],
|
|
259
350
|
params: {
|
|
260
351
|
guid,
|
|
261
352
|
},
|
|
262
353
|
});
|
|
263
|
-
await this.commit('nymph-delete');
|
|
264
|
-
if (this.nymph.config.cache) {
|
|
265
|
-
this.cleanCache(guid);
|
|
266
|
-
}
|
|
267
|
-
return true;
|
|
268
354
|
}
|
|
269
355
|
catch (e) {
|
|
356
|
+
this.nymph.config.debugError('sqlite3', `Delete entity error: "${e}"`);
|
|
270
357
|
await this.rollback('nymph-delete');
|
|
271
358
|
throw e;
|
|
272
359
|
}
|
|
360
|
+
await this.commit('nymph-delete');
|
|
361
|
+
// Remove any cached versions of this entity.
|
|
362
|
+
if (this.nymph.config.cache) {
|
|
363
|
+
this.cleanCache(guid);
|
|
364
|
+
}
|
|
365
|
+
return true;
|
|
273
366
|
}
|
|
274
367
|
async deleteUID(name) {
|
|
275
368
|
if (!name) {
|
|
276
|
-
throw new
|
|
369
|
+
throw new InvalidParametersError('Name not given for UID');
|
|
277
370
|
}
|
|
278
371
|
await this.startTransaction('nymph-delete-uid');
|
|
279
372
|
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
@@ -284,73 +377,227 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
284
377
|
await this.commit('nymph-delete-uid');
|
|
285
378
|
return true;
|
|
286
379
|
}
|
|
287
|
-
async
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
380
|
+
async getIndexes(etype) {
|
|
381
|
+
const indexes = [];
|
|
382
|
+
for (let [scope, suffix] of [
|
|
383
|
+
['data', '_string_guid'],
|
|
384
|
+
['references', '_reference_guid'],
|
|
385
|
+
['tokens', '_token_guid'],
|
|
386
|
+
]) {
|
|
387
|
+
const indexDefinitions = this.queryArray(`SELECT "name", "sql" FROM "sqlite_master" WHERE "type"='index' AND "name" LIKE @pattern;`, {
|
|
388
|
+
params: {
|
|
389
|
+
pattern: `${this.prefix}${scope}_${etype}_id_custom_%${suffix}`,
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
for (const indexDefinition of indexDefinitions) {
|
|
393
|
+
indexes.push({
|
|
394
|
+
scope,
|
|
395
|
+
name: indexDefinition.name.substring(`${this.prefix}${scope}_${etype}_id_custom_`.length, indexDefinition.name.length - suffix.length),
|
|
396
|
+
property: (indexDefinition.sql.match(/WHERE\s+"name"\s*=\s*'(.*)'/) ?? [])[1] ?? '',
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return indexes;
|
|
401
|
+
}
|
|
402
|
+
async addIndex(etype, definition) {
|
|
403
|
+
this.checkIndexName(definition.name);
|
|
404
|
+
await this.deleteIndex(etype, definition.scope, definition.name);
|
|
405
|
+
if (definition.scope === 'data') {
|
|
406
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_string_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("string", "guid") WHERE "name"=${SQLite3Driver.escapeValue(definition.property)};`);
|
|
407
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_number_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("number", "guid") WHERE "name"=${SQLite3Driver.escapeValue(definition.property)};`);
|
|
408
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_truthy_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("truthy", "guid") WHERE "name"=${SQLite3Driver.escapeValue(definition.property)};`);
|
|
409
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${definition.name}_json_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("json", "guid") WHERE "name"=${SQLite3Driver.escapeValue(definition.property)};`);
|
|
410
|
+
}
|
|
411
|
+
else if (definition.scope === 'references') {
|
|
412
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}_id_custom_${definition.name}_reference_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} ("reference", "guid") WHERE "name"=${SQLite3Driver.escapeValue(definition.property)};`);
|
|
413
|
+
}
|
|
414
|
+
else if (definition.scope === 'tokens') {
|
|
415
|
+
this.queryRun(`CREATE INDEX IF NOT EXISTS ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}_id_custom_${definition.name}_token_guid`)} ON ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} ("token", "guid") WHERE "name"=${SQLite3Driver.escapeValue(definition.property)};`);
|
|
416
|
+
}
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
async deleteIndex(etype, scope, name) {
|
|
420
|
+
this.checkIndexName(name);
|
|
421
|
+
if (scope === 'data') {
|
|
422
|
+
this.queryRun(`DROP INDEX IF EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${name}_string_guid`)};`);
|
|
423
|
+
this.queryRun(`DROP INDEX IF EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${name}_number_guid`)};`);
|
|
424
|
+
this.queryRun(`DROP INDEX IF EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${name}_truthy_guid`)};`);
|
|
425
|
+
this.queryRun(`DROP INDEX IF EXISTS ${SQLite3Driver.escape(`${this.prefix}data_${etype}_id_custom_${name}_json_guid`)};`);
|
|
426
|
+
}
|
|
427
|
+
else if (scope === 'references') {
|
|
428
|
+
this.queryRun(`DROP INDEX IF EXISTS ${SQLite3Driver.escape(`${this.prefix}references_${etype}_id_custom_${name}_reference_guid`)};`);
|
|
429
|
+
}
|
|
430
|
+
else if (scope === 'tokens') {
|
|
431
|
+
this.queryRun(`DROP INDEX IF EXISTS ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}_id_custom_${name}_token_guid`)};`);
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
async getEtypes() {
|
|
436
|
+
const tables = this.queryArray(`SELECT "name" FROM "sqlite_master" WHERE "type"='table' AND "name" LIKE @prefix;`, {
|
|
437
|
+
params: {
|
|
438
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
439
|
+
},
|
|
440
|
+
});
|
|
308
441
|
const etypes = [];
|
|
309
442
|
for (const table of tables) {
|
|
310
|
-
|
|
311
|
-
|
|
443
|
+
etypes.push(table.name.substr((this.prefix + 'entities_').length));
|
|
444
|
+
}
|
|
445
|
+
return etypes;
|
|
446
|
+
}
|
|
447
|
+
async *exportDataIterator() {
|
|
448
|
+
if (yield {
|
|
449
|
+
type: 'comment',
|
|
450
|
+
content: `#nex2
|
|
451
|
+
# Nymph Entity Exchange v2
|
|
452
|
+
# http://nymph.io
|
|
453
|
+
#
|
|
454
|
+
# Generation Time: ${new Date().toLocaleString()}
|
|
455
|
+
`,
|
|
456
|
+
}) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (yield {
|
|
460
|
+
type: 'comment',
|
|
461
|
+
content: `
|
|
462
|
+
|
|
463
|
+
#
|
|
464
|
+
# UIDs
|
|
465
|
+
#
|
|
466
|
+
|
|
467
|
+
`,
|
|
468
|
+
}) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
// Export UIDs.
|
|
472
|
+
let uids = this.queryArray(`SELECT * FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} ORDER BY "name";`);
|
|
473
|
+
for (const uid of uids) {
|
|
474
|
+
if (yield { type: 'uid', content: `<${uid.name}>[${uid.cur_uid}]\n` }) {
|
|
475
|
+
return;
|
|
312
476
|
}
|
|
313
477
|
}
|
|
478
|
+
if (yield {
|
|
479
|
+
type: 'comment',
|
|
480
|
+
content: `
|
|
481
|
+
|
|
482
|
+
#
|
|
483
|
+
# Entities
|
|
484
|
+
#
|
|
485
|
+
|
|
486
|
+
`,
|
|
487
|
+
}) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
// Get the etypes.
|
|
491
|
+
const etypes = await this.getEtypes();
|
|
314
492
|
for (const etype of etypes) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
493
|
+
// Export entities.
|
|
494
|
+
const entityIterator = this.exportEntityInternal(etype);
|
|
495
|
+
for await (let content of entityIterator) {
|
|
496
|
+
yield { type: 'entity', content };
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
async exportEntity(etype, guid) {
|
|
501
|
+
const entityIterator = this.exportEntityInternal(etype, guid);
|
|
502
|
+
let exportContent = '';
|
|
503
|
+
for await (let content of entityIterator) {
|
|
504
|
+
exportContent += content;
|
|
505
|
+
}
|
|
506
|
+
return exportContent;
|
|
507
|
+
}
|
|
508
|
+
async *exportEntityInternal(etype, guid) {
|
|
509
|
+
// Export entities.
|
|
510
|
+
const dataIterator = this.queryArray(`SELECT e."guid", e."tags", e."cdate", e."mdate", e."user", e."group", e."acUser", e."acGroup", e."acOther", e."acRead", e."acWrite", e."acFull", d."name", d."value", json(d."json") as "json", d."string", d."number" FROM ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} e LEFT JOIN ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} d USING ("guid")${guid == null ? '' : ` WHERE e."guid"=@guid`} ORDER BY e."guid", d."name";`, guid == null ? undefined : { params: { guid } })[Symbol.iterator]();
|
|
511
|
+
let datum = dataIterator.next();
|
|
512
|
+
while (!datum.done) {
|
|
513
|
+
const guid = datum.value.guid;
|
|
514
|
+
const tags = datum.value.tags.slice(1, -1);
|
|
515
|
+
const cdate = datum.value.cdate;
|
|
516
|
+
const mdate = datum.value.mdate;
|
|
517
|
+
const user = datum.value.user;
|
|
518
|
+
const group = datum.value.group;
|
|
519
|
+
const acUser = datum.value.acUser;
|
|
520
|
+
const acGroup = datum.value.acGroup;
|
|
521
|
+
const acOther = datum.value.acOther;
|
|
522
|
+
const acRead = datum.value.acRead?.slice(1, -1).split(',');
|
|
523
|
+
const acWrite = datum.value.acWrite?.slice(1, -1).split(',');
|
|
524
|
+
const acFull = datum.value.acFull?.slice(1, -1).split(',');
|
|
525
|
+
let currentEntityExport = [];
|
|
526
|
+
currentEntityExport.push(`{${guid}}<${etype}>[${tags}]`);
|
|
527
|
+
currentEntityExport.push(`\tcdate=${JSON.stringify(cdate)}`);
|
|
528
|
+
currentEntityExport.push(`\tmdate=${JSON.stringify(mdate)}`);
|
|
529
|
+
if (this.nymph.tilmeld != null) {
|
|
530
|
+
if (user != null) {
|
|
531
|
+
currentEntityExport.push(`\tuser=${JSON.stringify(['nymph_entity_reference', user, 'User'])}`);
|
|
335
532
|
}
|
|
336
|
-
|
|
337
|
-
|
|
533
|
+
if (group != null) {
|
|
534
|
+
currentEntityExport.push(`\tgroup=${JSON.stringify(['nymph_entity_reference', group, 'Group'])}`);
|
|
535
|
+
}
|
|
536
|
+
if (acUser != null) {
|
|
537
|
+
currentEntityExport.push(`\tacUser=${JSON.stringify(acUser)}`);
|
|
538
|
+
}
|
|
539
|
+
if (acGroup != null) {
|
|
540
|
+
currentEntityExport.push(`\tacGroup=${JSON.stringify(acGroup)}`);
|
|
541
|
+
}
|
|
542
|
+
if (acOther != null) {
|
|
543
|
+
currentEntityExport.push(`\tacOther=${JSON.stringify(acOther)}`);
|
|
544
|
+
}
|
|
545
|
+
if (acRead != null) {
|
|
546
|
+
currentEntityExport.push(`\tacRead=${JSON.stringify(acRead)}`);
|
|
547
|
+
}
|
|
548
|
+
if (acWrite != null) {
|
|
549
|
+
currentEntityExport.push(`\tacWrite=${JSON.stringify(acWrite)}`);
|
|
550
|
+
}
|
|
551
|
+
if (acFull != null) {
|
|
552
|
+
currentEntityExport.push(`\tacFull=${JSON.stringify(acFull)}`);
|
|
338
553
|
}
|
|
339
554
|
}
|
|
555
|
+
if (datum.value.name != null) {
|
|
556
|
+
// This do will keep going and adding the data until the
|
|
557
|
+
// next entity is reached. datum will end on the next entity.
|
|
558
|
+
do {
|
|
559
|
+
const value = datum.value.value === 'N'
|
|
560
|
+
? JSON.stringify(datum.value.number)
|
|
561
|
+
: datum.value.value === 'S'
|
|
562
|
+
? JSON.stringify(datum.value.string)
|
|
563
|
+
: datum.value.value === 'J'
|
|
564
|
+
? datum.value.json
|
|
565
|
+
: datum.value.value;
|
|
566
|
+
currentEntityExport.push(`\t${datum.value.name}=${value}`);
|
|
567
|
+
datum = dataIterator.next();
|
|
568
|
+
} while (!datum.done && datum.value.guid === guid);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
// Make sure that datum is incremented :)
|
|
572
|
+
datum = dataIterator.next();
|
|
573
|
+
}
|
|
574
|
+
currentEntityExport.push('');
|
|
575
|
+
if (yield currentEntityExport.join('\n')) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
340
578
|
}
|
|
341
|
-
return;
|
|
342
579
|
}
|
|
343
|
-
|
|
580
|
+
/**
|
|
581
|
+
* Generate the SQLite3 query.
|
|
582
|
+
* @param options The options array.
|
|
583
|
+
* @param formattedSelectors The formatted selector array.
|
|
584
|
+
* @param etype
|
|
585
|
+
* @param count Used to track internal params.
|
|
586
|
+
* @param params Used to store internal params.
|
|
587
|
+
* @param subquery Whether only a subquery should be returned.
|
|
588
|
+
* @returns The SQL query.
|
|
589
|
+
*/
|
|
590
|
+
makeEntityQuery(options, formattedSelectors, etype, count = { i: 0 }, params = {}, subquery = false, tableSuffix = '', etypes = [], guidSelector = undefined) {
|
|
344
591
|
if (typeof options.class?.alterOptions === 'function') {
|
|
345
592
|
options = options.class.alterOptions(options);
|
|
346
593
|
}
|
|
347
594
|
const eTable = `e${tableSuffix}`;
|
|
348
595
|
const dTable = `d${tableSuffix}`;
|
|
349
|
-
const cTable = `c${tableSuffix}`;
|
|
350
596
|
const fTable = `f${tableSuffix}`;
|
|
351
597
|
const ieTable = `ie${tableSuffix}`;
|
|
352
|
-
const
|
|
353
|
-
const
|
|
598
|
+
const sTable = `s${tableSuffix}`;
|
|
599
|
+
const sort = options.sort === undefined ? 'cdate' : options.sort;
|
|
600
|
+
const queryParts = this.iterateSelectorsForQuery(formattedSelectors, ({ key, value, typeIsOr, typeIsNot }) => {
|
|
354
601
|
const clauseNot = key.startsWith('!');
|
|
355
602
|
let curQuery = '';
|
|
356
603
|
for (const curValue of value) {
|
|
@@ -363,7 +610,7 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
363
610
|
}
|
|
364
611
|
const guid = `param${++count.i}`;
|
|
365
612
|
curQuery +=
|
|
366
|
-
(
|
|
613
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
367
614
|
ieTable +
|
|
368
615
|
'."guid"=@' +
|
|
369
616
|
guid;
|
|
@@ -378,7 +625,7 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
378
625
|
}
|
|
379
626
|
const tag = `param${++count.i}`;
|
|
380
627
|
curQuery +=
|
|
381
|
-
(
|
|
628
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
382
629
|
ieTable +
|
|
383
630
|
'."tags" LIKE @' +
|
|
384
631
|
tag +
|
|
@@ -398,17 +645,38 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
398
645
|
if (curQuery) {
|
|
399
646
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
400
647
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
648
|
+
if (curVar === 'cdate' ||
|
|
649
|
+
curVar === 'mdate' ||
|
|
650
|
+
(this.nymph.tilmeld != null &&
|
|
651
|
+
(curVar === 'user' ||
|
|
652
|
+
curVar === 'group' ||
|
|
653
|
+
curVar === 'acUser' ||
|
|
654
|
+
curVar === 'acGroup' ||
|
|
655
|
+
curVar === 'acOther' ||
|
|
656
|
+
curVar === 'acRead' ||
|
|
657
|
+
curVar === 'acWrite' ||
|
|
658
|
+
curVar === 'acFull'))) {
|
|
659
|
+
curQuery +=
|
|
660
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
661
|
+
'(' +
|
|
662
|
+
ieTable +
|
|
663
|
+
'.' +
|
|
664
|
+
SQLite3Driver.escape(curVar) +
|
|
665
|
+
' IS NOT NULL)';
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
const name = `param${++count.i}`;
|
|
669
|
+
curQuery +=
|
|
670
|
+
ieTable +
|
|
671
|
+
'."guid" ' +
|
|
672
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
673
|
+
'IN (SELECT "guid" FROM ' +
|
|
674
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
675
|
+
' WHERE "name"=@' +
|
|
676
|
+
name +
|
|
677
|
+
')';
|
|
678
|
+
params[name] = curVar;
|
|
679
|
+
}
|
|
412
680
|
}
|
|
413
681
|
break;
|
|
414
682
|
case 'truthy':
|
|
@@ -417,30 +685,34 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
417
685
|
if (curQuery) {
|
|
418
686
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
419
687
|
}
|
|
420
|
-
if (curVar === 'cdate'
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
688
|
+
if (curVar === 'cdate' ||
|
|
689
|
+
curVar === 'mdate' ||
|
|
690
|
+
(this.nymph.tilmeld != null &&
|
|
691
|
+
(curVar === 'user' ||
|
|
692
|
+
curVar === 'group' ||
|
|
693
|
+
curVar === 'acUser' ||
|
|
694
|
+
curVar === 'acGroup' ||
|
|
695
|
+
curVar === 'acOther' ||
|
|
696
|
+
curVar === 'acRead' ||
|
|
697
|
+
curVar === 'acWrite' ||
|
|
698
|
+
curVar === 'acFull'))) {
|
|
429
699
|
curQuery +=
|
|
430
|
-
(
|
|
700
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
431
701
|
'(' +
|
|
432
702
|
ieTable +
|
|
433
|
-
'.
|
|
434
|
-
|
|
703
|
+
'.' +
|
|
704
|
+
SQLite3Driver.escape(curVar) +
|
|
705
|
+
' IS NOT NULL)';
|
|
435
706
|
}
|
|
436
707
|
else {
|
|
437
708
|
const name = `param${++count.i}`;
|
|
438
709
|
curQuery +=
|
|
439
|
-
(
|
|
710
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
711
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
712
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
713
|
+
' WHERE "guid"=' +
|
|
440
714
|
ieTable +
|
|
441
|
-
'."guid"
|
|
442
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
443
|
-
' WHERE "name"=@' +
|
|
715
|
+
'."guid" AND "name"=@' +
|
|
444
716
|
name +
|
|
445
717
|
' AND "truthy"=1)';
|
|
446
718
|
params[name] = curVar;
|
|
@@ -449,31 +721,58 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
449
721
|
break;
|
|
450
722
|
case 'equal':
|
|
451
723
|
case '!equal':
|
|
452
|
-
if (curValue[0] === 'cdate'
|
|
724
|
+
if (curValue[0] === 'cdate' ||
|
|
725
|
+
curValue[0] === 'mdate' ||
|
|
726
|
+
(this.nymph.tilmeld != null &&
|
|
727
|
+
(curValue[0] === 'acUser' ||
|
|
728
|
+
curValue[0] === 'acGroup' ||
|
|
729
|
+
curValue[0] === 'acOther'))) {
|
|
453
730
|
if (curQuery) {
|
|
454
731
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
455
732
|
}
|
|
456
|
-
const
|
|
733
|
+
const value = `param${++count.i}`;
|
|
457
734
|
curQuery +=
|
|
458
|
-
(
|
|
735
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
459
736
|
ieTable +
|
|
460
|
-
'.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
737
|
+
'.' +
|
|
738
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
739
|
+
'=@' +
|
|
740
|
+
value;
|
|
741
|
+
params[value] = Number(curValue[1]);
|
|
464
742
|
}
|
|
465
|
-
else if (
|
|
743
|
+
else if (this.nymph.tilmeld != null &&
|
|
744
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
466
745
|
if (curQuery) {
|
|
467
746
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
468
747
|
}
|
|
469
|
-
const
|
|
748
|
+
const value = `param${++count.i}`;
|
|
470
749
|
curQuery +=
|
|
471
|
-
(
|
|
750
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
472
751
|
ieTable +
|
|
473
|
-
'.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
752
|
+
'.' +
|
|
753
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
754
|
+
'=@' +
|
|
755
|
+
value;
|
|
756
|
+
params[value] = `${curValue[1]}`;
|
|
757
|
+
}
|
|
758
|
+
else if (this.nymph.tilmeld != null &&
|
|
759
|
+
(curValue[0] === 'acRead' ||
|
|
760
|
+
curValue[0] === 'acWrite' ||
|
|
761
|
+
curValue[0] === 'acFull')) {
|
|
762
|
+
if (curQuery) {
|
|
763
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
764
|
+
}
|
|
765
|
+
const value = `param${++count.i}`;
|
|
766
|
+
curQuery +=
|
|
767
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
768
|
+
ieTable +
|
|
769
|
+
'.' +
|
|
770
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
771
|
+
'=@' +
|
|
772
|
+
value;
|
|
773
|
+
params[value] = Array.isArray(curValue[1])
|
|
774
|
+
? ',' + curValue[1].join(',') + ','
|
|
775
|
+
: '';
|
|
477
776
|
}
|
|
478
777
|
else if (typeof curValue[1] === 'number') {
|
|
479
778
|
if (curQuery) {
|
|
@@ -482,11 +781,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
482
781
|
const name = `param${++count.i}`;
|
|
483
782
|
const value = `param${++count.i}`;
|
|
484
783
|
curQuery +=
|
|
485
|
-
(
|
|
784
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
785
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
786
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
787
|
+
' WHERE "guid"=' +
|
|
486
788
|
ieTable +
|
|
487
|
-
'."guid"
|
|
488
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
489
|
-
' WHERE "name"=@' +
|
|
789
|
+
'."guid" AND "name"=@' +
|
|
490
790
|
name +
|
|
491
791
|
' AND "number"=@' +
|
|
492
792
|
value +
|
|
@@ -501,11 +801,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
501
801
|
const name = `param${++count.i}`;
|
|
502
802
|
const value = `param${++count.i}`;
|
|
503
803
|
curQuery +=
|
|
504
|
-
(
|
|
804
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
805
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
806
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
807
|
+
' WHERE "guid"=' +
|
|
505
808
|
ieTable +
|
|
506
|
-
'."guid"
|
|
507
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
508
|
-
' WHERE "name"=@' +
|
|
809
|
+
'."guid" AND "name"=@' +
|
|
509
810
|
name +
|
|
510
811
|
' AND "string"=@' +
|
|
511
812
|
value +
|
|
@@ -528,134 +829,273 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
528
829
|
const name = `param${++count.i}`;
|
|
529
830
|
const value = `param${++count.i}`;
|
|
530
831
|
curQuery +=
|
|
531
|
-
(
|
|
532
|
-
|
|
533
|
-
'."guid" IN (SELECT "guid" FROM ' +
|
|
832
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
833
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
534
834
|
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
535
|
-
' WHERE "
|
|
835
|
+
' WHERE "guid"=' +
|
|
836
|
+
ieTable +
|
|
837
|
+
'."guid" AND "name"=@' +
|
|
536
838
|
name +
|
|
537
|
-
' AND "
|
|
839
|
+
' AND "json"=jsonb(@' +
|
|
538
840
|
value +
|
|
539
|
-
')';
|
|
841
|
+
'))';
|
|
540
842
|
params[name] = curValue[0];
|
|
541
843
|
params[value] = svalue;
|
|
542
844
|
}
|
|
543
845
|
break;
|
|
544
846
|
case 'contain':
|
|
545
847
|
case '!contain':
|
|
546
|
-
if (curValue[0] === 'cdate'
|
|
848
|
+
if (curValue[0] === 'cdate' ||
|
|
849
|
+
curValue[0] === 'mdate' ||
|
|
850
|
+
(this.nymph.tilmeld != null &&
|
|
851
|
+
(curValue[0] === 'acUser' ||
|
|
852
|
+
curValue[0] === 'acGroup' ||
|
|
853
|
+
curValue[0] === 'acOther'))) {
|
|
547
854
|
if (curQuery) {
|
|
548
855
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
549
856
|
}
|
|
550
|
-
const
|
|
857
|
+
const value = `param${++count.i}`;
|
|
551
858
|
curQuery +=
|
|
552
|
-
(
|
|
859
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
553
860
|
ieTable +
|
|
554
|
-
'.
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
861
|
+
'.' +
|
|
862
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
863
|
+
'=@' +
|
|
864
|
+
value;
|
|
865
|
+
params[value] = Number(curValue[1]);
|
|
558
866
|
}
|
|
559
|
-
else if (
|
|
867
|
+
else if (this.nymph.tilmeld != null &&
|
|
868
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
560
869
|
if (curQuery) {
|
|
561
870
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
562
871
|
}
|
|
563
|
-
const
|
|
872
|
+
const value = `param${++count.i}`;
|
|
564
873
|
curQuery +=
|
|
565
|
-
(
|
|
874
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
566
875
|
ieTable +
|
|
567
|
-
'.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
876
|
+
'.' +
|
|
877
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
878
|
+
'=@' +
|
|
879
|
+
value;
|
|
880
|
+
params[value] = `${curValue[1]}`;
|
|
881
|
+
}
|
|
882
|
+
else if (this.nymph.tilmeld != null &&
|
|
883
|
+
(curValue[0] === 'acRead' ||
|
|
884
|
+
curValue[0] === 'acWrite' ||
|
|
885
|
+
curValue[0] === 'acFull')) {
|
|
886
|
+
if (curQuery) {
|
|
887
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
888
|
+
}
|
|
889
|
+
const id = `param${++count.i}`;
|
|
890
|
+
curQuery +=
|
|
891
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
892
|
+
ieTable +
|
|
893
|
+
'.' +
|
|
894
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
895
|
+
' LIKE @' +
|
|
896
|
+
id +
|
|
897
|
+
" ESCAPE '\\'";
|
|
898
|
+
params[id] =
|
|
899
|
+
'%,' +
|
|
900
|
+
curValue[1]
|
|
901
|
+
.replace('\\', '\\\\')
|
|
902
|
+
.replace('%', '\\%')
|
|
903
|
+
.replace('_', '\\_') +
|
|
904
|
+
',%';
|
|
571
905
|
}
|
|
572
906
|
else {
|
|
907
|
+
const containTableSuffix = makeTableSuffix();
|
|
573
908
|
if (curQuery) {
|
|
574
909
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
575
910
|
}
|
|
576
911
|
let svalue;
|
|
577
|
-
let stringValue;
|
|
578
912
|
if (curValue[1] instanceof Object &&
|
|
579
913
|
typeof curValue[1].toReference === 'function') {
|
|
580
914
|
svalue = JSON.stringify(curValue[1].toReference());
|
|
581
|
-
stringValue = `${curValue[1].toReference()}`;
|
|
582
915
|
}
|
|
583
916
|
else {
|
|
584
917
|
svalue = JSON.stringify(curValue[1]);
|
|
585
|
-
stringValue = `${curValue[1]}`;
|
|
586
918
|
}
|
|
587
919
|
const name = `param${++count.i}`;
|
|
588
920
|
const value = `param${++count.i}`;
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
(
|
|
593
|
-
|
|
921
|
+
curQuery +=
|
|
922
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
923
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
924
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
925
|
+
' d' +
|
|
926
|
+
containTableSuffix +
|
|
927
|
+
' WHERE "guid"=' +
|
|
928
|
+
ieTable +
|
|
929
|
+
'."guid" AND "name"=@' +
|
|
930
|
+
name +
|
|
931
|
+
' AND json(@' +
|
|
932
|
+
value +
|
|
933
|
+
') IN (SELECT json_quote("value") FROM json_each(d' +
|
|
934
|
+
containTableSuffix +
|
|
935
|
+
'."json")))';
|
|
936
|
+
params[name] = curValue[0];
|
|
937
|
+
params[value] = svalue;
|
|
938
|
+
}
|
|
939
|
+
break;
|
|
940
|
+
case 'search':
|
|
941
|
+
case '!search':
|
|
942
|
+
if (curValue[0] === 'cdate' ||
|
|
943
|
+
curValue[0] === 'mdate' ||
|
|
944
|
+
(this.nymph.tilmeld != null &&
|
|
945
|
+
(curValue[0] === 'user' ||
|
|
946
|
+
curValue[0] === 'group' ||
|
|
947
|
+
curValue[0] === 'acUser' ||
|
|
948
|
+
curValue[0] === 'acGroup' ||
|
|
949
|
+
curValue[0] === 'acOther' ||
|
|
950
|
+
curValue[0] === 'acRead' ||
|
|
951
|
+
curValue[0] === 'acWrite' ||
|
|
952
|
+
curValue[0] === 'acFull'))) {
|
|
953
|
+
if (curQuery) {
|
|
954
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
955
|
+
}
|
|
956
|
+
curQuery += (xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(0)';
|
|
957
|
+
}
|
|
958
|
+
else {
|
|
959
|
+
if (curQuery) {
|
|
960
|
+
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
961
|
+
}
|
|
962
|
+
const parsedFTSQuery = this.tokenizer.parseSearchQuery(curValue[1]);
|
|
963
|
+
if (!parsedFTSQuery.length) {
|
|
964
|
+
curQuery += (xor(typeIsNot, clauseNot) ? 'NOT ' : '') + '(0)';
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
const name = `param${++count.i}`;
|
|
968
|
+
const queryPartToken = (term) => {
|
|
969
|
+
const value = `param${++count.i}`;
|
|
970
|
+
params[value] = term.token;
|
|
971
|
+
return ('EXISTS (SELECT "guid" FROM ' +
|
|
972
|
+
SQLite3Driver.escape(this.prefix + 'tokens_' + etype) +
|
|
973
|
+
' WHERE "guid"=' +
|
|
594
974
|
ieTable +
|
|
595
|
-
'."guid"
|
|
596
|
-
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
597
|
-
' WHERE "name"=@' +
|
|
975
|
+
'."guid" AND "name"=@' +
|
|
598
976
|
name +
|
|
599
|
-
' AND
|
|
977
|
+
' AND "token"=@' +
|
|
600
978
|
value +
|
|
601
|
-
'
|
|
979
|
+
(term.nostemmed ? ' AND "stem"=0' : '') +
|
|
980
|
+
')');
|
|
981
|
+
};
|
|
982
|
+
const queryPartSeries = (series) => {
|
|
983
|
+
const tokenTableSuffix = makeTableSuffix();
|
|
984
|
+
const tokenParts = series.tokens.map((token, i) => {
|
|
985
|
+
const value = `param${++count.i}`;
|
|
986
|
+
params[value] = token.token;
|
|
987
|
+
return {
|
|
988
|
+
fromClause: i === 0
|
|
989
|
+
? 'FROM ' +
|
|
990
|
+
SQLite3Driver.escape(this.prefix + 'tokens_' + etype) +
|
|
991
|
+
' t' +
|
|
992
|
+
tokenTableSuffix +
|
|
993
|
+
'0'
|
|
994
|
+
: 'JOIN ' +
|
|
995
|
+
SQLite3Driver.escape(this.prefix + 'tokens_' + etype) +
|
|
996
|
+
' t' +
|
|
997
|
+
tokenTableSuffix +
|
|
998
|
+
i +
|
|
999
|
+
' ON t' +
|
|
1000
|
+
tokenTableSuffix +
|
|
1001
|
+
i +
|
|
1002
|
+
'."guid" = t' +
|
|
1003
|
+
tokenTableSuffix +
|
|
1004
|
+
'0."guid" AND t' +
|
|
1005
|
+
tokenTableSuffix +
|
|
1006
|
+
i +
|
|
1007
|
+
'."name" = t' +
|
|
1008
|
+
tokenTableSuffix +
|
|
1009
|
+
'0."name" AND t' +
|
|
1010
|
+
tokenTableSuffix +
|
|
1011
|
+
i +
|
|
1012
|
+
'."position" = t' +
|
|
1013
|
+
tokenTableSuffix +
|
|
1014
|
+
'0."position" + ' +
|
|
1015
|
+
i,
|
|
1016
|
+
whereClause: 't' +
|
|
1017
|
+
tokenTableSuffix +
|
|
1018
|
+
i +
|
|
1019
|
+
'."token"=@' +
|
|
1020
|
+
value +
|
|
1021
|
+
(token.nostemmed
|
|
1022
|
+
? ' AND t' + tokenTableSuffix + i + '."stem"=0'
|
|
1023
|
+
: ''),
|
|
1024
|
+
};
|
|
1025
|
+
});
|
|
1026
|
+
return ('EXISTS (SELECT t' +
|
|
1027
|
+
tokenTableSuffix +
|
|
1028
|
+
'0."guid" ' +
|
|
1029
|
+
tokenParts.map((part) => part.fromClause).join(' ') +
|
|
1030
|
+
' WHERE t' +
|
|
1031
|
+
tokenTableSuffix +
|
|
1032
|
+
'0."guid"=' +
|
|
602
1033
|
ieTable +
|
|
603
|
-
'."guid"
|
|
604
|
-
|
|
605
|
-
'
|
|
1034
|
+
'."guid" AND t' +
|
|
1035
|
+
tokenTableSuffix +
|
|
1036
|
+
'0."name"=@' +
|
|
606
1037
|
name +
|
|
607
|
-
' AND
|
|
608
|
-
|
|
609
|
-
'))
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
1038
|
+
' AND ' +
|
|
1039
|
+
tokenParts.map((part) => part.whereClause).join(' AND ') +
|
|
1040
|
+
')');
|
|
1041
|
+
};
|
|
1042
|
+
const queryPartTerm = (term) => {
|
|
1043
|
+
if (term.type === 'series') {
|
|
1044
|
+
return queryPartSeries(term);
|
|
1045
|
+
}
|
|
1046
|
+
else if (term.type === 'not') {
|
|
1047
|
+
return 'NOT ' + queryPartTerm(term.operand);
|
|
1048
|
+
}
|
|
1049
|
+
else if (term.type === 'or') {
|
|
1050
|
+
let queryParts = [];
|
|
1051
|
+
for (let operand of term.operands) {
|
|
1052
|
+
queryParts.push(queryPartTerm(operand));
|
|
1053
|
+
}
|
|
1054
|
+
return '(' + queryParts.join(' OR ') + ')';
|
|
1055
|
+
}
|
|
1056
|
+
return queryPartToken(term);
|
|
1057
|
+
};
|
|
1058
|
+
// Run through the query and add terms.
|
|
1059
|
+
let termStrings = [];
|
|
1060
|
+
for (let term of parsedFTSQuery) {
|
|
1061
|
+
termStrings.push(queryPartTerm(term));
|
|
1062
|
+
}
|
|
613
1063
|
curQuery +=
|
|
614
|
-
(
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
name +
|
|
620
|
-
' AND instr("value", @' +
|
|
621
|
-
value +
|
|
622
|
-
'))';
|
|
1064
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1065
|
+
'(' +
|
|
1066
|
+
termStrings.join(' AND ') +
|
|
1067
|
+
')';
|
|
1068
|
+
params[name] = curValue[0];
|
|
623
1069
|
}
|
|
624
|
-
params[name] = curValue[0];
|
|
625
|
-
params[value] = svalue;
|
|
626
1070
|
}
|
|
627
1071
|
break;
|
|
628
1072
|
case 'match':
|
|
629
1073
|
case '!match':
|
|
630
|
-
if (curValue[0] === 'cdate'
|
|
1074
|
+
if (curValue[0] === 'cdate' ||
|
|
1075
|
+
curValue[0] === 'mdate' ||
|
|
1076
|
+
(this.nymph.tilmeld != null &&
|
|
1077
|
+
(curValue[0] === 'user' ||
|
|
1078
|
+
curValue[0] === 'group' ||
|
|
1079
|
+
curValue[0] === 'acUser' ||
|
|
1080
|
+
curValue[0] === 'acGroup' ||
|
|
1081
|
+
curValue[0] === 'acOther' ||
|
|
1082
|
+
curValue[0] === 'acRead' ||
|
|
1083
|
+
curValue[0] === 'acWrite' ||
|
|
1084
|
+
curValue[0] === 'acFull'))) {
|
|
631
1085
|
if (curQuery) {
|
|
632
1086
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
633
1087
|
}
|
|
634
|
-
const
|
|
635
|
-
curQuery +=
|
|
636
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
637
|
-
'(' +
|
|
638
|
-
ieTable +
|
|
639
|
-
'."cdate" REGEXP @' +
|
|
640
|
-
cdate +
|
|
641
|
-
')';
|
|
642
|
-
params[cdate] = curValue[1];
|
|
643
|
-
break;
|
|
644
|
-
}
|
|
645
|
-
else if (curValue[0] === 'mdate') {
|
|
646
|
-
if (curQuery) {
|
|
647
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
648
|
-
}
|
|
649
|
-
const mdate = `param${++count.i}`;
|
|
1088
|
+
const value = `param${++count.i}`;
|
|
650
1089
|
curQuery +=
|
|
651
|
-
(
|
|
1090
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
652
1091
|
'(' +
|
|
653
1092
|
ieTable +
|
|
654
|
-
'.
|
|
655
|
-
|
|
1093
|
+
'.' +
|
|
1094
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1095
|
+
' REGEXP @' +
|
|
1096
|
+
value +
|
|
656
1097
|
')';
|
|
657
|
-
params[
|
|
658
|
-
break;
|
|
1098
|
+
params[value] = curValue[1];
|
|
659
1099
|
}
|
|
660
1100
|
else {
|
|
661
1101
|
if (curQuery) {
|
|
@@ -664,11 +1104,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
664
1104
|
const name = `param${++count.i}`;
|
|
665
1105
|
const value = `param${++count.i}`;
|
|
666
1106
|
curQuery +=
|
|
667
|
-
(
|
|
1107
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1108
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1109
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1110
|
+
' WHERE "guid"=' +
|
|
668
1111
|
ieTable +
|
|
669
|
-
'."guid"
|
|
670
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
671
|
-
' WHERE "name"=@' +
|
|
1112
|
+
'."guid" AND "name"=@' +
|
|
672
1113
|
name +
|
|
673
1114
|
' AND "string" REGEXP @' +
|
|
674
1115
|
value +
|
|
@@ -679,35 +1120,31 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
679
1120
|
break;
|
|
680
1121
|
case 'imatch':
|
|
681
1122
|
case '!imatch':
|
|
682
|
-
if (curValue[0] === 'cdate'
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
'
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
')';
|
|
694
|
-
params[cdate] = curValue[1];
|
|
695
|
-
break;
|
|
696
|
-
}
|
|
697
|
-
else if (curValue[0] === 'mdate') {
|
|
1123
|
+
if (curValue[0] === 'cdate' ||
|
|
1124
|
+
curValue[0] === 'mdate' ||
|
|
1125
|
+
(this.nymph.tilmeld != null &&
|
|
1126
|
+
(curValue[0] === 'user' ||
|
|
1127
|
+
curValue[0] === 'group' ||
|
|
1128
|
+
curValue[0] === 'acUser' ||
|
|
1129
|
+
curValue[0] === 'acGroup' ||
|
|
1130
|
+
curValue[0] === 'acOther' ||
|
|
1131
|
+
curValue[0] === 'acRead' ||
|
|
1132
|
+
curValue[0] === 'acWrite' ||
|
|
1133
|
+
curValue[0] === 'acFull'))) {
|
|
698
1134
|
if (curQuery) {
|
|
699
1135
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
700
1136
|
}
|
|
701
|
-
const
|
|
1137
|
+
const value = `param${++count.i}`;
|
|
702
1138
|
curQuery +=
|
|
703
|
-
(
|
|
704
|
-
'(' +
|
|
1139
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1140
|
+
'(lower(' +
|
|
705
1141
|
ieTable +
|
|
706
|
-
'.
|
|
707
|
-
|
|
708
|
-
')'
|
|
709
|
-
|
|
710
|
-
|
|
1142
|
+
'.' +
|
|
1143
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1144
|
+
') REGEXP lower(@' +
|
|
1145
|
+
value +
|
|
1146
|
+
'))';
|
|
1147
|
+
params[value] = curValue[1];
|
|
711
1148
|
}
|
|
712
1149
|
else {
|
|
713
1150
|
if (curQuery) {
|
|
@@ -716,11 +1153,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
716
1153
|
const name = `param${++count.i}`;
|
|
717
1154
|
const value = `param${++count.i}`;
|
|
718
1155
|
curQuery +=
|
|
719
|
-
(
|
|
1156
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1157
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1158
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1159
|
+
' WHERE "guid"=' +
|
|
720
1160
|
ieTable +
|
|
721
|
-
'."guid"
|
|
722
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
723
|
-
' WHERE "name"=@' +
|
|
1161
|
+
'."guid" AND "name"=@' +
|
|
724
1162
|
name +
|
|
725
1163
|
' AND lower("string") REGEXP lower(@' +
|
|
726
1164
|
value +
|
|
@@ -731,35 +1169,31 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
731
1169
|
break;
|
|
732
1170
|
case 'like':
|
|
733
1171
|
case '!like':
|
|
734
|
-
if (curValue[0] === 'cdate'
|
|
1172
|
+
if (curValue[0] === 'cdate' ||
|
|
1173
|
+
curValue[0] === 'mdate' ||
|
|
1174
|
+
(this.nymph.tilmeld != null &&
|
|
1175
|
+
(curValue[0] === 'user' ||
|
|
1176
|
+
curValue[0] === 'group' ||
|
|
1177
|
+
curValue[0] === 'acUser' ||
|
|
1178
|
+
curValue[0] === 'acGroup' ||
|
|
1179
|
+
curValue[0] === 'acOther' ||
|
|
1180
|
+
curValue[0] === 'acRead' ||
|
|
1181
|
+
curValue[0] === 'acWrite' ||
|
|
1182
|
+
curValue[0] === 'acFull'))) {
|
|
735
1183
|
if (curQuery) {
|
|
736
1184
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
737
1185
|
}
|
|
738
|
-
const
|
|
739
|
-
curQuery +=
|
|
740
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
741
|
-
'(' +
|
|
742
|
-
ieTable +
|
|
743
|
-
'."cdate" LIKE @' +
|
|
744
|
-
cdate +
|
|
745
|
-
" ESCAPE '\\')";
|
|
746
|
-
params[cdate] = curValue[1];
|
|
747
|
-
break;
|
|
748
|
-
}
|
|
749
|
-
else if (curValue[0] === 'mdate') {
|
|
750
|
-
if (curQuery) {
|
|
751
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
752
|
-
}
|
|
753
|
-
const mdate = `param${++count.i}`;
|
|
1186
|
+
const value = `param${++count.i}`;
|
|
754
1187
|
curQuery +=
|
|
755
|
-
(
|
|
1188
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
756
1189
|
'(' +
|
|
757
1190
|
ieTable +
|
|
758
|
-
'.
|
|
759
|
-
|
|
1191
|
+
'.' +
|
|
1192
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1193
|
+
' LIKE @' +
|
|
1194
|
+
value +
|
|
760
1195
|
" ESCAPE '\\')";
|
|
761
|
-
params[
|
|
762
|
-
break;
|
|
1196
|
+
params[value] = curValue[1];
|
|
763
1197
|
}
|
|
764
1198
|
else {
|
|
765
1199
|
if (curQuery) {
|
|
@@ -768,11 +1202,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
768
1202
|
const name = `param${++count.i}`;
|
|
769
1203
|
const value = `param${++count.i}`;
|
|
770
1204
|
curQuery +=
|
|
771
|
-
(
|
|
1205
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1206
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1207
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1208
|
+
' WHERE "guid"=' +
|
|
772
1209
|
ieTable +
|
|
773
|
-
'."guid"
|
|
774
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
775
|
-
' WHERE "name"=@' +
|
|
1210
|
+
'."guid" AND "name"=@' +
|
|
776
1211
|
name +
|
|
777
1212
|
' AND "string" LIKE @' +
|
|
778
1213
|
value +
|
|
@@ -783,35 +1218,31 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
783
1218
|
break;
|
|
784
1219
|
case 'ilike':
|
|
785
1220
|
case '!ilike':
|
|
786
|
-
if (curValue[0] === 'cdate'
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
'
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
" ESCAPE '\\')";
|
|
798
|
-
params[cdate] = curValue[1];
|
|
799
|
-
break;
|
|
800
|
-
}
|
|
801
|
-
else if (curValue[0] === 'mdate') {
|
|
1221
|
+
if (curValue[0] === 'cdate' ||
|
|
1222
|
+
curValue[0] === 'mdate' ||
|
|
1223
|
+
(this.nymph.tilmeld != null &&
|
|
1224
|
+
(curValue[0] === 'user' ||
|
|
1225
|
+
curValue[0] === 'group' ||
|
|
1226
|
+
curValue[0] === 'acUser' ||
|
|
1227
|
+
curValue[0] === 'acGroup' ||
|
|
1228
|
+
curValue[0] === 'acOther' ||
|
|
1229
|
+
curValue[0] === 'acRead' ||
|
|
1230
|
+
curValue[0] === 'acWrite' ||
|
|
1231
|
+
curValue[0] === 'acFull'))) {
|
|
802
1232
|
if (curQuery) {
|
|
803
1233
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
804
1234
|
}
|
|
805
|
-
const
|
|
1235
|
+
const value = `param${++count.i}`;
|
|
806
1236
|
curQuery +=
|
|
807
|
-
(
|
|
808
|
-
'(' +
|
|
1237
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1238
|
+
'(lower(' +
|
|
809
1239
|
ieTable +
|
|
810
|
-
'.
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1240
|
+
'.' +
|
|
1241
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1242
|
+
') LIKE lower(@' +
|
|
1243
|
+
value +
|
|
1244
|
+
") ESCAPE '\\')";
|
|
1245
|
+
params[value] = curValue[1];
|
|
815
1246
|
}
|
|
816
1247
|
else {
|
|
817
1248
|
if (curQuery) {
|
|
@@ -820,11 +1251,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
820
1251
|
const name = `param${++count.i}`;
|
|
821
1252
|
const value = `param${++count.i}`;
|
|
822
1253
|
curQuery +=
|
|
823
|
-
(
|
|
1254
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1255
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1256
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1257
|
+
' WHERE "guid"=' +
|
|
824
1258
|
ieTable +
|
|
825
|
-
'."guid"
|
|
826
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
827
|
-
' WHERE "name"=@' +
|
|
1259
|
+
'."guid" AND "name"=@' +
|
|
828
1260
|
name +
|
|
829
1261
|
' AND lower("string") LIKE lower(@' +
|
|
830
1262
|
value +
|
|
@@ -835,31 +1267,29 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
835
1267
|
break;
|
|
836
1268
|
case 'gt':
|
|
837
1269
|
case '!gt':
|
|
838
|
-
if (curValue[0] === 'cdate'
|
|
1270
|
+
if (curValue[0] === 'cdate' ||
|
|
1271
|
+
curValue[0] === 'mdate' ||
|
|
1272
|
+
(this.nymph.tilmeld != null &&
|
|
1273
|
+
(curValue[0] === 'user' ||
|
|
1274
|
+
curValue[0] === 'group' ||
|
|
1275
|
+
curValue[0] === 'acUser' ||
|
|
1276
|
+
curValue[0] === 'acGroup' ||
|
|
1277
|
+
curValue[0] === 'acOther' ||
|
|
1278
|
+
curValue[0] === 'acRead' ||
|
|
1279
|
+
curValue[0] === 'acWrite' ||
|
|
1280
|
+
curValue[0] === 'acFull'))) {
|
|
839
1281
|
if (curQuery) {
|
|
840
1282
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
841
1283
|
}
|
|
842
|
-
const
|
|
843
|
-
curQuery +=
|
|
844
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
845
|
-
ieTable +
|
|
846
|
-
'."cdate">@' +
|
|
847
|
-
cdate;
|
|
848
|
-
params[cdate] = Number(curValue[1]);
|
|
849
|
-
break;
|
|
850
|
-
}
|
|
851
|
-
else if (curValue[0] === 'mdate') {
|
|
852
|
-
if (curQuery) {
|
|
853
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
854
|
-
}
|
|
855
|
-
const mdate = `param${++count.i}`;
|
|
1284
|
+
const value = `param${++count.i}`;
|
|
856
1285
|
curQuery +=
|
|
857
|
-
(
|
|
1286
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
858
1287
|
ieTable +
|
|
859
|
-
'.
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1288
|
+
'.' +
|
|
1289
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1290
|
+
'>@' +
|
|
1291
|
+
value;
|
|
1292
|
+
params[value] = Number(curValue[1]);
|
|
863
1293
|
}
|
|
864
1294
|
else {
|
|
865
1295
|
if (curQuery) {
|
|
@@ -868,11 +1298,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
868
1298
|
const name = `param${++count.i}`;
|
|
869
1299
|
const value = `param${++count.i}`;
|
|
870
1300
|
curQuery +=
|
|
871
|
-
(
|
|
1301
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1302
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1303
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1304
|
+
' WHERE "guid"=' +
|
|
872
1305
|
ieTable +
|
|
873
|
-
'."guid"
|
|
874
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
875
|
-
' WHERE "name"=@' +
|
|
1306
|
+
'."guid" AND "name"=@' +
|
|
876
1307
|
name +
|
|
877
1308
|
' AND "number">@' +
|
|
878
1309
|
value +
|
|
@@ -883,31 +1314,29 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
883
1314
|
break;
|
|
884
1315
|
case 'gte':
|
|
885
1316
|
case '!gte':
|
|
886
|
-
if (curValue[0] === 'cdate'
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
'
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
break;
|
|
898
|
-
}
|
|
899
|
-
else if (curValue[0] === 'mdate') {
|
|
1317
|
+
if (curValue[0] === 'cdate' ||
|
|
1318
|
+
curValue[0] === 'mdate' ||
|
|
1319
|
+
(this.nymph.tilmeld != null &&
|
|
1320
|
+
(curValue[0] === 'user' ||
|
|
1321
|
+
curValue[0] === 'group' ||
|
|
1322
|
+
curValue[0] === 'acUser' ||
|
|
1323
|
+
curValue[0] === 'acGroup' ||
|
|
1324
|
+
curValue[0] === 'acOther' ||
|
|
1325
|
+
curValue[0] === 'acRead' ||
|
|
1326
|
+
curValue[0] === 'acWrite' ||
|
|
1327
|
+
curValue[0] === 'acFull'))) {
|
|
900
1328
|
if (curQuery) {
|
|
901
1329
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
902
1330
|
}
|
|
903
|
-
const
|
|
1331
|
+
const value = `param${++count.i}`;
|
|
904
1332
|
curQuery +=
|
|
905
|
-
(
|
|
1333
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
906
1334
|
ieTable +
|
|
907
|
-
'.
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1335
|
+
'.' +
|
|
1336
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1337
|
+
'>=@' +
|
|
1338
|
+
value;
|
|
1339
|
+
params[value] = Number(curValue[1]);
|
|
911
1340
|
}
|
|
912
1341
|
else {
|
|
913
1342
|
if (curQuery) {
|
|
@@ -916,11 +1345,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
916
1345
|
const name = `param${++count.i}`;
|
|
917
1346
|
const value = `param${++count.i}`;
|
|
918
1347
|
curQuery +=
|
|
919
|
-
(
|
|
1348
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1349
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1350
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1351
|
+
' WHERE "guid"=' +
|
|
920
1352
|
ieTable +
|
|
921
|
-
'."guid"
|
|
922
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
923
|
-
' WHERE "name"=@' +
|
|
1353
|
+
'."guid" AND "name"=@' +
|
|
924
1354
|
name +
|
|
925
1355
|
' AND "number">=@' +
|
|
926
1356
|
value +
|
|
@@ -931,31 +1361,29 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
931
1361
|
break;
|
|
932
1362
|
case 'lt':
|
|
933
1363
|
case '!lt':
|
|
934
|
-
if (curValue[0] === 'cdate'
|
|
1364
|
+
if (curValue[0] === 'cdate' ||
|
|
1365
|
+
curValue[0] === 'mdate' ||
|
|
1366
|
+
(this.nymph.tilmeld != null &&
|
|
1367
|
+
(curValue[0] === 'user' ||
|
|
1368
|
+
curValue[0] === 'group' ||
|
|
1369
|
+
curValue[0] === 'acUser' ||
|
|
1370
|
+
curValue[0] === 'acGroup' ||
|
|
1371
|
+
curValue[0] === 'acOther' ||
|
|
1372
|
+
curValue[0] === 'acRead' ||
|
|
1373
|
+
curValue[0] === 'acWrite' ||
|
|
1374
|
+
curValue[0] === 'acFull'))) {
|
|
935
1375
|
if (curQuery) {
|
|
936
1376
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
937
1377
|
}
|
|
938
|
-
const
|
|
939
|
-
curQuery +=
|
|
940
|
-
((0, nymph_1.xor)(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
941
|
-
ieTable +
|
|
942
|
-
'."cdate"<@' +
|
|
943
|
-
cdate;
|
|
944
|
-
params[cdate] = Number(curValue[1]);
|
|
945
|
-
break;
|
|
946
|
-
}
|
|
947
|
-
else if (curValue[0] === 'mdate') {
|
|
948
|
-
if (curQuery) {
|
|
949
|
-
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
950
|
-
}
|
|
951
|
-
const mdate = `param${++count.i}`;
|
|
1378
|
+
const value = `param${++count.i}`;
|
|
952
1379
|
curQuery +=
|
|
953
|
-
(
|
|
1380
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
954
1381
|
ieTable +
|
|
955
|
-
'.
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1382
|
+
'.' +
|
|
1383
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1384
|
+
'<@' +
|
|
1385
|
+
value;
|
|
1386
|
+
params[value] = Number(curValue[1]);
|
|
959
1387
|
}
|
|
960
1388
|
else {
|
|
961
1389
|
if (curQuery) {
|
|
@@ -964,11 +1392,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
964
1392
|
const name = `param${++count.i}`;
|
|
965
1393
|
const value = `param${++count.i}`;
|
|
966
1394
|
curQuery +=
|
|
967
|
-
(
|
|
1395
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1396
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1397
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1398
|
+
' WHERE "guid"=' +
|
|
968
1399
|
ieTable +
|
|
969
|
-
'."guid"
|
|
970
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
971
|
-
' WHERE "name"=@' +
|
|
1400
|
+
'."guid" AND "name"=@' +
|
|
972
1401
|
name +
|
|
973
1402
|
' AND "number"<@' +
|
|
974
1403
|
value +
|
|
@@ -979,31 +1408,29 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
979
1408
|
break;
|
|
980
1409
|
case 'lte':
|
|
981
1410
|
case '!lte':
|
|
982
|
-
if (curValue[0] === 'cdate'
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
'
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
break;
|
|
994
|
-
}
|
|
995
|
-
else if (curValue[0] === 'mdate') {
|
|
1411
|
+
if (curValue[0] === 'cdate' ||
|
|
1412
|
+
curValue[0] === 'mdate' ||
|
|
1413
|
+
(this.nymph.tilmeld != null &&
|
|
1414
|
+
(curValue[0] === 'user' ||
|
|
1415
|
+
curValue[0] === 'group' ||
|
|
1416
|
+
curValue[0] === 'acUser' ||
|
|
1417
|
+
curValue[0] === 'acGroup' ||
|
|
1418
|
+
curValue[0] === 'acOther' ||
|
|
1419
|
+
curValue[0] === 'acRead' ||
|
|
1420
|
+
curValue[0] === 'acWrite' ||
|
|
1421
|
+
curValue[0] === 'acFull'))) {
|
|
996
1422
|
if (curQuery) {
|
|
997
1423
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
998
1424
|
}
|
|
999
|
-
const
|
|
1425
|
+
const value = `param${++count.i}`;
|
|
1000
1426
|
curQuery +=
|
|
1001
|
-
(
|
|
1427
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1002
1428
|
ieTable +
|
|
1003
|
-
'.
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1429
|
+
'.' +
|
|
1430
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1431
|
+
'<=@' +
|
|
1432
|
+
value;
|
|
1433
|
+
params[value] = Number(curValue[1]);
|
|
1007
1434
|
}
|
|
1008
1435
|
else {
|
|
1009
1436
|
if (curQuery) {
|
|
@@ -1012,11 +1439,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1012
1439
|
const name = `param${++count.i}`;
|
|
1013
1440
|
const value = `param${++count.i}`;
|
|
1014
1441
|
curQuery +=
|
|
1015
|
-
(
|
|
1442
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1443
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1444
|
+
SQLite3Driver.escape(this.prefix + 'data_' + etype) +
|
|
1445
|
+
' WHERE "guid"=' +
|
|
1016
1446
|
ieTable +
|
|
1017
|
-
'."guid"
|
|
1018
|
-
SQLite3Driver.escape(this.prefix + 'comparisons_' + etype) +
|
|
1019
|
-
' WHERE "name"=@' +
|
|
1447
|
+
'."guid" AND "name"=@' +
|
|
1020
1448
|
name +
|
|
1021
1449
|
' AND "number"<=@' +
|
|
1022
1450
|
value +
|
|
@@ -1040,71 +1468,205 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1040
1468
|
if (curQuery) {
|
|
1041
1469
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1042
1470
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1471
|
+
if (this.nymph.tilmeld != null &&
|
|
1472
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
1473
|
+
const guid = `param${++count.i}`;
|
|
1474
|
+
curQuery +=
|
|
1475
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1476
|
+
ieTable +
|
|
1477
|
+
'.' +
|
|
1478
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1479
|
+
'=@' +
|
|
1480
|
+
guid;
|
|
1481
|
+
params[guid] = curQguid;
|
|
1482
|
+
}
|
|
1483
|
+
else if (this.nymph.tilmeld != null &&
|
|
1484
|
+
(curValue[0] === 'acRead' ||
|
|
1485
|
+
curValue[0] === 'acWrite' ||
|
|
1486
|
+
curValue[0] === 'acFull')) {
|
|
1487
|
+
const guid = `param${++count.i}`;
|
|
1488
|
+
curQuery +=
|
|
1489
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1490
|
+
ieTable +
|
|
1491
|
+
'.' +
|
|
1492
|
+
SQLite3Driver.escape(curValue[0]) +
|
|
1493
|
+
' LIKE @' +
|
|
1494
|
+
guid +
|
|
1495
|
+
" ESCAPE '\\'";
|
|
1496
|
+
params[guid] =
|
|
1497
|
+
'%,' +
|
|
1498
|
+
curQguid
|
|
1499
|
+
.replace('\\', '\\\\')
|
|
1500
|
+
.replace('%', '\\%')
|
|
1501
|
+
.replace('_', '\\_') +
|
|
1502
|
+
',%';
|
|
1503
|
+
}
|
|
1504
|
+
else {
|
|
1505
|
+
const name = `param${++count.i}`;
|
|
1506
|
+
const guid = `param${++count.i}`;
|
|
1507
|
+
curQuery +=
|
|
1508
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1509
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1510
|
+
SQLite3Driver.escape(this.prefix + 'references_' + etype) +
|
|
1511
|
+
' WHERE "guid"=' +
|
|
1512
|
+
ieTable +
|
|
1513
|
+
'."guid" AND "name"=@' +
|
|
1514
|
+
name +
|
|
1515
|
+
' AND "reference"=@' +
|
|
1516
|
+
guid +
|
|
1517
|
+
')';
|
|
1518
|
+
params[name] = curValue[0];
|
|
1519
|
+
params[guid] = curQguid;
|
|
1520
|
+
}
|
|
1057
1521
|
break;
|
|
1058
1522
|
case 'selector':
|
|
1059
1523
|
case '!selector':
|
|
1060
|
-
const
|
|
1524
|
+
const innerquery = this.makeEntityQuery({ ...options, sort: null, limit: undefined }, [curValue], etype, count, params, true, tableSuffix, etypes);
|
|
1061
1525
|
if (curQuery) {
|
|
1062
1526
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1063
1527
|
}
|
|
1064
1528
|
curQuery +=
|
|
1065
|
-
(
|
|
1529
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1066
1530
|
'(' +
|
|
1067
|
-
|
|
1531
|
+
innerquery.query +
|
|
1068
1532
|
')';
|
|
1069
1533
|
break;
|
|
1070
1534
|
case 'qref':
|
|
1071
1535
|
case '!qref':
|
|
1536
|
+
const referenceTableSuffix = makeTableSuffix();
|
|
1072
1537
|
const [qrefOptions, ...qrefSelectors] = curValue[1];
|
|
1073
1538
|
const QrefEntityClass = qrefOptions.class;
|
|
1074
1539
|
etypes.push(QrefEntityClass.ETYPE);
|
|
1075
|
-
const qrefQuery = this.makeEntityQuery({ ...qrefOptions, return: 'guid', class: QrefEntityClass }, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, (0, guid_1.makeTableSuffix)(), etypes);
|
|
1076
1540
|
if (curQuery) {
|
|
1077
1541
|
curQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
1078
1542
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1543
|
+
if (this.nymph.tilmeld != null &&
|
|
1544
|
+
(curValue[0] === 'user' || curValue[0] === 'group')) {
|
|
1545
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1546
|
+
...qrefOptions,
|
|
1547
|
+
sort: qrefOptions.sort ?? null,
|
|
1548
|
+
return: 'guid',
|
|
1549
|
+
class: QrefEntityClass,
|
|
1550
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' +
|
|
1551
|
+
referenceTableSuffix +
|
|
1552
|
+
'.' +
|
|
1553
|
+
SQLite3Driver.escape(curValue[0]));
|
|
1554
|
+
curQuery +=
|
|
1555
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1556
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1557
|
+
SQLite3Driver.escape(this.prefix + 'entities_' + etype) +
|
|
1558
|
+
' r' +
|
|
1559
|
+
referenceTableSuffix +
|
|
1560
|
+
' WHERE r' +
|
|
1561
|
+
referenceTableSuffix +
|
|
1562
|
+
'."guid"=' +
|
|
1563
|
+
ieTable +
|
|
1564
|
+
'."guid" AND EXISTS (' +
|
|
1565
|
+
qrefQuery.query +
|
|
1566
|
+
'))';
|
|
1567
|
+
}
|
|
1568
|
+
else if (this.nymph.tilmeld != null &&
|
|
1569
|
+
(curValue[0] === 'acRead' ||
|
|
1570
|
+
curValue[0] === 'acWrite' ||
|
|
1571
|
+
curValue[0] === 'acFull')) {
|
|
1572
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1573
|
+
...qrefOptions,
|
|
1574
|
+
sort: qrefOptions.sort ?? null,
|
|
1575
|
+
return: 'guid',
|
|
1576
|
+
class: QrefEntityClass,
|
|
1577
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' + referenceTableSuffix + '."ref"');
|
|
1578
|
+
const splitTableSuffix = makeTableSuffix();
|
|
1579
|
+
curQuery +=
|
|
1580
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1581
|
+
`EXISTS (SELECT "guid", "ref" FROM (
|
|
1582
|
+
WITH RECURSIVE "spl${splitTableSuffix}" AS (
|
|
1583
|
+
SELECT
|
|
1584
|
+
"guid",
|
|
1585
|
+
SUBSTR(${SQLite3Driver.escape(curValue[0])}, 1, INSTR(${SQLite3Driver.escape(curValue[0])}, ',') - 1) AS "ref",
|
|
1586
|
+
SUBSTR(${SQLite3Driver.escape(curValue[0])}, INSTR(${SQLite3Driver.escape(curValue[0])}, ',') + 1) AS "remainder"
|
|
1587
|
+
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)}
|
|
1588
|
+
UNION ALL
|
|
1589
|
+
SELECT
|
|
1590
|
+
"guid",
|
|
1591
|
+
SUBSTR("remainder", 1, INSTR("remainder", ',') - 1) AS "ref",
|
|
1592
|
+
SUBSTR("remainder", INSTR("remainder", ',') + 1) AS "remainder"
|
|
1593
|
+
FROM "spl${splitTableSuffix}" WHERE "remainder" != ''
|
|
1594
|
+
)
|
|
1595
|
+
SELECT "guid", "ref" FROM "spl${splitTableSuffix}" WHERE "ref" != ''
|
|
1596
|
+
) ` +
|
|
1597
|
+
' r' +
|
|
1598
|
+
referenceTableSuffix +
|
|
1599
|
+
' WHERE r' +
|
|
1600
|
+
referenceTableSuffix +
|
|
1601
|
+
'."guid"=' +
|
|
1602
|
+
ieTable +
|
|
1603
|
+
'."guid" AND EXISTS (' +
|
|
1604
|
+
qrefQuery.query +
|
|
1605
|
+
'))';
|
|
1606
|
+
}
|
|
1607
|
+
else {
|
|
1608
|
+
const qrefQuery = this.makeEntityQuery({
|
|
1609
|
+
...qrefOptions,
|
|
1610
|
+
sort: qrefOptions.sort ?? null,
|
|
1611
|
+
return: 'guid',
|
|
1612
|
+
class: QrefEntityClass,
|
|
1613
|
+
}, qrefSelectors, QrefEntityClass.ETYPE, count, params, false, makeTableSuffix(), etypes, 'r' + referenceTableSuffix + '."reference"');
|
|
1614
|
+
const qrefName = `param${++count.i}`;
|
|
1615
|
+
curQuery +=
|
|
1616
|
+
(xor(typeIsNot, clauseNot) ? 'NOT ' : '') +
|
|
1617
|
+
'EXISTS (SELECT "guid" FROM ' +
|
|
1618
|
+
SQLite3Driver.escape(this.prefix + 'references_' + etype) +
|
|
1619
|
+
' r' +
|
|
1620
|
+
referenceTableSuffix +
|
|
1621
|
+
' WHERE r' +
|
|
1622
|
+
referenceTableSuffix +
|
|
1623
|
+
'."guid"=' +
|
|
1624
|
+
ieTable +
|
|
1625
|
+
'."guid" AND r' +
|
|
1626
|
+
referenceTableSuffix +
|
|
1627
|
+
'."name"=@' +
|
|
1628
|
+
qrefName +
|
|
1629
|
+
' AND EXISTS (' +
|
|
1630
|
+
qrefQuery.query +
|
|
1631
|
+
'))';
|
|
1632
|
+
params[qrefName] = curValue[0];
|
|
1633
|
+
}
|
|
1091
1634
|
break;
|
|
1092
1635
|
}
|
|
1093
1636
|
}
|
|
1094
1637
|
return curQuery;
|
|
1095
1638
|
});
|
|
1096
1639
|
let sortBy;
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1640
|
+
let sortByInner;
|
|
1641
|
+
let sortJoin = '';
|
|
1642
|
+
const order = options.reverse ? ' DESC' : '';
|
|
1643
|
+
if (sort == null) {
|
|
1644
|
+
sortBy = '';
|
|
1645
|
+
sortByInner = '';
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
switch (sort) {
|
|
1649
|
+
case 'mdate':
|
|
1650
|
+
sortBy = `ORDER BY ${eTable}."mdate"${order}`;
|
|
1651
|
+
sortByInner = `ORDER BY ${ieTable}."mdate"${order}`;
|
|
1652
|
+
break;
|
|
1653
|
+
case 'cdate':
|
|
1654
|
+
sortBy = `ORDER BY ${eTable}."cdate"${order}`;
|
|
1655
|
+
sortByInner = `ORDER BY ${ieTable}."cdate"${order}`;
|
|
1656
|
+
break;
|
|
1657
|
+
default:
|
|
1658
|
+
const name = `param${++count.i}`;
|
|
1659
|
+
sortJoin = `LEFT JOIN (
|
|
1660
|
+
SELECT "guid", "string", "number"
|
|
1661
|
+
FROM ${SQLite3Driver.escape(this.prefix + 'data_' + etype)}
|
|
1662
|
+
WHERE "name"=@${name}
|
|
1663
|
+
ORDER BY "number"${order}, "string"${order}
|
|
1664
|
+
) ${sTable} USING ("guid")`;
|
|
1665
|
+
sortBy = `ORDER BY ${sTable}."number"${order}, ${sTable}."string"${order}`;
|
|
1666
|
+
sortByInner = sortBy;
|
|
1667
|
+
params[name] = sort;
|
|
1668
|
+
break;
|
|
1669
|
+
}
|
|
1108
1670
|
}
|
|
1109
1671
|
let query;
|
|
1110
1672
|
if (queryParts.length) {
|
|
@@ -1121,25 +1683,29 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1121
1683
|
offset = ` OFFSET ${Math.floor(Number(options.offset))}`;
|
|
1122
1684
|
}
|
|
1123
1685
|
const whereClause = queryParts.join(') AND (');
|
|
1686
|
+
const guidClause = guidSelector
|
|
1687
|
+
? `${ieTable}."guid"=${guidSelector} AND `
|
|
1688
|
+
: '';
|
|
1124
1689
|
if (options.return === 'count') {
|
|
1125
1690
|
if (limit || offset) {
|
|
1126
1691
|
query = `SELECT COUNT("guid") AS "count" FROM (
|
|
1127
1692
|
SELECT "guid"
|
|
1128
1693
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
|
|
1129
|
-
WHERE (${whereClause})${limit}${offset}
|
|
1694
|
+
WHERE ${guidClause}(${whereClause})${limit}${offset}
|
|
1130
1695
|
)`;
|
|
1131
1696
|
}
|
|
1132
1697
|
else {
|
|
1133
1698
|
query = `SELECT COUNT("guid") AS "count"
|
|
1134
1699
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
|
|
1135
|
-
WHERE (${whereClause})`;
|
|
1700
|
+
WHERE ${guidClause}(${whereClause})`;
|
|
1136
1701
|
}
|
|
1137
1702
|
}
|
|
1138
1703
|
else if (options.return === 'guid') {
|
|
1139
1704
|
query = `SELECT "guid"
|
|
1140
1705
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
|
|
1141
|
-
|
|
1142
|
-
|
|
1706
|
+
${sortJoin}
|
|
1707
|
+
WHERE ${guidClause}(${whereClause})
|
|
1708
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}"guid"${limit}${offset}`;
|
|
1143
1709
|
}
|
|
1144
1710
|
else {
|
|
1145
1711
|
query = `SELECT
|
|
@@ -1147,20 +1713,30 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1147
1713
|
${eTable}."tags",
|
|
1148
1714
|
${eTable}."cdate",
|
|
1149
1715
|
${eTable}."mdate",
|
|
1716
|
+
${eTable}."user",
|
|
1717
|
+
${eTable}."group",
|
|
1718
|
+
${eTable}."acUser",
|
|
1719
|
+
${eTable}."acGroup",
|
|
1720
|
+
${eTable}."acOther",
|
|
1721
|
+
${eTable}."acRead",
|
|
1722
|
+
${eTable}."acWrite",
|
|
1723
|
+
${eTable}."acFull",
|
|
1150
1724
|
${dTable}."name",
|
|
1151
1725
|
${dTable}."value",
|
|
1152
|
-
${
|
|
1153
|
-
${
|
|
1726
|
+
json(${dTable}."json") as "json",
|
|
1727
|
+
${dTable}."string",
|
|
1728
|
+
${dTable}."number"
|
|
1154
1729
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${eTable}
|
|
1155
1730
|
LEFT JOIN ${SQLite3Driver.escape(this.prefix + 'data_' + etype)} ${dTable} USING ("guid")
|
|
1156
|
-
|
|
1731
|
+
${sortJoin}
|
|
1157
1732
|
INNER JOIN (
|
|
1158
1733
|
SELECT "guid"
|
|
1159
1734
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
|
|
1160
|
-
|
|
1161
|
-
|
|
1735
|
+
${sortJoin}
|
|
1736
|
+
WHERE ${guidClause}(${whereClause})
|
|
1737
|
+
${sortByInner}${limit}${offset}
|
|
1162
1738
|
) ${fTable} USING ("guid")
|
|
1163
|
-
ORDER BY ${eTable}
|
|
1739
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1164
1740
|
}
|
|
1165
1741
|
}
|
|
1166
1742
|
}
|
|
@@ -1177,22 +1753,27 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1177
1753
|
if ('offset' in options) {
|
|
1178
1754
|
offset = ` OFFSET ${Math.floor(Number(options.offset))}`;
|
|
1179
1755
|
}
|
|
1756
|
+
const guidClause = guidSelector
|
|
1757
|
+
? ` WHERE ${ieTable}."guid"=${guidSelector}`
|
|
1758
|
+
: '';
|
|
1180
1759
|
if (options.return === 'count') {
|
|
1181
1760
|
if (limit || offset) {
|
|
1182
1761
|
query = `SELECT COUNT("guid") AS "count" FROM (
|
|
1183
1762
|
SELECT "guid"
|
|
1184
|
-
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}${limit}${offset}
|
|
1763
|
+
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}${guidClause}${limit}${offset}
|
|
1185
1764
|
)`;
|
|
1186
1765
|
}
|
|
1187
1766
|
else {
|
|
1188
1767
|
query = `SELECT COUNT("guid") AS "count"
|
|
1189
|
-
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}`;
|
|
1768
|
+
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}${guidClause}`;
|
|
1190
1769
|
}
|
|
1191
1770
|
}
|
|
1192
1771
|
else if (options.return === 'guid') {
|
|
1193
1772
|
query = `SELECT "guid"
|
|
1194
1773
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
|
|
1195
|
-
|
|
1774
|
+
${sortJoin}
|
|
1775
|
+
${guidClause}
|
|
1776
|
+
${sortByInner ? sortByInner + ', ' : 'ORDER BY '}"guid"${limit}${offset}`;
|
|
1196
1777
|
}
|
|
1197
1778
|
else {
|
|
1198
1779
|
if (limit || offset) {
|
|
@@ -1201,19 +1782,30 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1201
1782
|
${eTable}."tags",
|
|
1202
1783
|
${eTable}."cdate",
|
|
1203
1784
|
${eTable}."mdate",
|
|
1785
|
+
${eTable}."user",
|
|
1786
|
+
${eTable}."group",
|
|
1787
|
+
${eTable}."acUser",
|
|
1788
|
+
${eTable}."acGroup",
|
|
1789
|
+
${eTable}."acOther",
|
|
1790
|
+
${eTable}."acRead",
|
|
1791
|
+
${eTable}."acWrite",
|
|
1792
|
+
${eTable}."acFull",
|
|
1204
1793
|
${dTable}."name",
|
|
1205
1794
|
${dTable}."value",
|
|
1206
|
-
${
|
|
1207
|
-
${
|
|
1795
|
+
json(${dTable}."json") as "json",
|
|
1796
|
+
${dTable}."string",
|
|
1797
|
+
${dTable}."number"
|
|
1208
1798
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${eTable}
|
|
1209
1799
|
LEFT JOIN ${SQLite3Driver.escape(this.prefix + 'data_' + etype)} ${dTable} USING ("guid")
|
|
1210
|
-
|
|
1800
|
+
${sortJoin}
|
|
1211
1801
|
INNER JOIN (
|
|
1212
1802
|
SELECT "guid"
|
|
1213
1803
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
|
|
1214
|
-
|
|
1804
|
+
${sortJoin}
|
|
1805
|
+
${guidClause}
|
|
1806
|
+
${sortByInner}${limit}${offset}
|
|
1215
1807
|
) ${fTable} USING ("guid")
|
|
1216
|
-
ORDER BY ${eTable}
|
|
1808
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1217
1809
|
}
|
|
1218
1810
|
else {
|
|
1219
1811
|
query = `SELECT
|
|
@@ -1221,14 +1813,24 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1221
1813
|
${eTable}."tags",
|
|
1222
1814
|
${eTable}."cdate",
|
|
1223
1815
|
${eTable}."mdate",
|
|
1816
|
+
${eTable}."user",
|
|
1817
|
+
${eTable}."group",
|
|
1818
|
+
${eTable}."acUser",
|
|
1819
|
+
${eTable}."acGroup",
|
|
1820
|
+
${eTable}."acOther",
|
|
1821
|
+
${eTable}."acRead",
|
|
1822
|
+
${eTable}."acWrite",
|
|
1823
|
+
${eTable}."acFull",
|
|
1224
1824
|
${dTable}."name",
|
|
1225
1825
|
${dTable}."value",
|
|
1226
|
-
${
|
|
1227
|
-
${
|
|
1826
|
+
json(${dTable}."json") as "json",
|
|
1827
|
+
${dTable}."string",
|
|
1828
|
+
${dTable}."number"
|
|
1228
1829
|
FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${eTable}
|
|
1229
1830
|
LEFT JOIN ${SQLite3Driver.escape(this.prefix + 'data_' + etype)} ${dTable} USING ("guid")
|
|
1230
|
-
|
|
1231
|
-
|
|
1831
|
+
${sortJoin}
|
|
1832
|
+
${guidSelector ? `WHERE ${eTable}."guid"=${guidSelector}` : ''}
|
|
1833
|
+
${sortBy ? sortBy + ', ' : 'ORDER BY '}${eTable}."guid"`;
|
|
1232
1834
|
}
|
|
1233
1835
|
}
|
|
1234
1836
|
}
|
|
@@ -1244,29 +1846,41 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1244
1846
|
}
|
|
1245
1847
|
performQuery(options, formattedSelectors, etype) {
|
|
1246
1848
|
const { query, params, etypes } = this.makeEntityQuery(options, formattedSelectors, etype);
|
|
1247
|
-
const result = this.
|
|
1849
|
+
const result = this.queryArray(query, { etypes, params })[Symbol.iterator]();
|
|
1248
1850
|
return {
|
|
1249
1851
|
result,
|
|
1250
1852
|
};
|
|
1251
1853
|
}
|
|
1252
1854
|
async getEntities(options = {}, ...selectors) {
|
|
1253
|
-
|
|
1254
|
-
}
|
|
1255
|
-
getEntitiesSync(options = {}, ...selectors) {
|
|
1256
|
-
const { result, process } = this.getEntitesRowLike(options, selectors, (options, formattedSelectors, etype) => this.performQuery(options, formattedSelectors, etype), () => {
|
|
1855
|
+
const { result, process } = this.getEntitiesRowLike(options, selectors, ({ options, selectors, etype }) => this.performQuery(options, selectors, etype), () => {
|
|
1257
1856
|
const next = result.next();
|
|
1258
1857
|
return next.done ? null : next.value;
|
|
1259
1858
|
}, () => undefined, (row) => Number(row.count), (row) => row.guid, (row) => ({
|
|
1260
|
-
tags: row.tags.length > 2
|
|
1859
|
+
tags: row.tags.length > 2
|
|
1860
|
+
? row.tags
|
|
1861
|
+
.slice(1, -1)
|
|
1862
|
+
.split(',')
|
|
1863
|
+
.filter((tag) => tag)
|
|
1864
|
+
: [],
|
|
1261
1865
|
cdate: Number(row.cdate),
|
|
1262
1866
|
mdate: Number(row.mdate),
|
|
1867
|
+
user: row.user,
|
|
1868
|
+
group: row.group,
|
|
1869
|
+
acUser: row.acUser,
|
|
1870
|
+
acGroup: row.acGroup,
|
|
1871
|
+
acOther: row.acOther,
|
|
1872
|
+
acRead: row.acRead?.slice(1, -1).split(',') ?? [],
|
|
1873
|
+
acWrite: row.acWrite?.slice(1, -1).split(',') ?? [],
|
|
1874
|
+
acFull: row.acFull?.slice(1, -1).split(',') ?? [],
|
|
1263
1875
|
}), (row) => ({
|
|
1264
1876
|
name: row.name,
|
|
1265
1877
|
svalue: row.value === 'N'
|
|
1266
1878
|
? JSON.stringify(row.number)
|
|
1267
1879
|
: row.value === 'S'
|
|
1268
1880
|
? JSON.stringify(row.string)
|
|
1269
|
-
: row.value
|
|
1881
|
+
: row.value === 'J'
|
|
1882
|
+
? row.json
|
|
1883
|
+
: row.value,
|
|
1270
1884
|
}));
|
|
1271
1885
|
const value = process();
|
|
1272
1886
|
if (value instanceof Error) {
|
|
@@ -1276,7 +1890,7 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1276
1890
|
}
|
|
1277
1891
|
async getUID(name) {
|
|
1278
1892
|
if (name == null) {
|
|
1279
|
-
throw new
|
|
1893
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1280
1894
|
}
|
|
1281
1895
|
const result = this.queryGet(`SELECT "cur_uid" FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
1282
1896
|
params: {
|
|
@@ -1285,9 +1899,18 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1285
1899
|
});
|
|
1286
1900
|
return result?.cur_uid ?? null;
|
|
1287
1901
|
}
|
|
1288
|
-
async
|
|
1902
|
+
async importEntity(entity) {
|
|
1903
|
+
return await this.importEntityInternal(entity);
|
|
1904
|
+
}
|
|
1905
|
+
async importEntityTokens(entity) {
|
|
1906
|
+
return await this.importEntityInternal(entity, { only: 'tokens' });
|
|
1907
|
+
}
|
|
1908
|
+
async importEntityTilmeldAC(entity) {
|
|
1909
|
+
return await this.importEntityInternal(entity, { only: 'tilmeldAC' });
|
|
1910
|
+
}
|
|
1911
|
+
async importEntityInternal({ guid, cdate, mdate, tags, sdata, etype, }, { only = undefined } = {}) {
|
|
1289
1912
|
try {
|
|
1290
|
-
|
|
1913
|
+
if (only == null) {
|
|
1291
1914
|
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=@guid;`, {
|
|
1292
1915
|
etypes: [etype],
|
|
1293
1916
|
params: {
|
|
@@ -1300,29 +1923,48 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1300
1923
|
guid,
|
|
1301
1924
|
},
|
|
1302
1925
|
});
|
|
1303
|
-
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}
|
|
1926
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=@guid;`, {
|
|
1304
1927
|
etypes: [etype],
|
|
1305
1928
|
params: {
|
|
1306
1929
|
guid,
|
|
1307
1930
|
},
|
|
1308
1931
|
});
|
|
1309
|
-
|
|
1932
|
+
}
|
|
1933
|
+
if (only == null || only === 'tokens') {
|
|
1934
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=@guid;`, {
|
|
1310
1935
|
etypes: [etype],
|
|
1311
1936
|
params: {
|
|
1312
1937
|
guid,
|
|
1313
1938
|
},
|
|
1314
1939
|
});
|
|
1315
|
-
|
|
1940
|
+
}
|
|
1941
|
+
if (only == null) {
|
|
1942
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=@guid;`, {
|
|
1943
|
+
etypes: [etype],
|
|
1944
|
+
params: {
|
|
1945
|
+
guid,
|
|
1946
|
+
},
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
if (only == null) {
|
|
1950
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } = this.removeAndReturnACValues(etype, {}, sdata);
|
|
1951
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate", "user", "group", "acUser", "acGroup", "acOther", "acRead", "acWrite", "acFull") VALUES (@guid, @tags, @cdate, @mdate, @user, @group, @acUser, @acGroup, @acOther, @acRead, @acWrite, @acFull);`, {
|
|
1316
1952
|
etypes: [etype],
|
|
1317
1953
|
params: {
|
|
1318
1954
|
guid,
|
|
1319
1955
|
tags: ',' + tags.join(',') + ',',
|
|
1320
|
-
cdate
|
|
1321
|
-
mdate
|
|
1956
|
+
cdate,
|
|
1957
|
+
mdate,
|
|
1958
|
+
user,
|
|
1959
|
+
group,
|
|
1960
|
+
acUser,
|
|
1961
|
+
acGroup,
|
|
1962
|
+
acOther,
|
|
1963
|
+
acRead: acRead && ',' + acRead.join(',') + ',',
|
|
1964
|
+
acWrite: acWrite && ',' + acWrite.join(',') + ',',
|
|
1965
|
+
acFull: acFull && ',' + acFull.join(',') + ',',
|
|
1322
1966
|
},
|
|
1323
1967
|
});
|
|
1324
|
-
delete sdata.cdate;
|
|
1325
|
-
delete sdata.mdate;
|
|
1326
1968
|
for (const name in sdata) {
|
|
1327
1969
|
const value = sdata[name];
|
|
1328
1970
|
const uvalue = JSON.parse(value);
|
|
@@ -1333,23 +1975,18 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1333
1975
|
? 'N'
|
|
1334
1976
|
: typeof uvalue === 'string'
|
|
1335
1977
|
? 'S'
|
|
1336
|
-
:
|
|
1337
|
-
|
|
1978
|
+
: 'J';
|
|
1979
|
+
const jsonValue = storageValue === 'J' ? value : null;
|
|
1980
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (@guid, @name, @storageValue, jsonb(@jsonValue), @string, @number, @truthy);`, {
|
|
1338
1981
|
etypes: [etype],
|
|
1339
1982
|
params: {
|
|
1340
1983
|
guid,
|
|
1341
1984
|
name,
|
|
1342
1985
|
storageValue,
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}`)} ("guid", "name", "truthy", "string", "number") VALUES (@guid, @name, @truthy, @string, @number);`, {
|
|
1346
|
-
etypes: [etype],
|
|
1347
|
-
params: {
|
|
1348
|
-
guid,
|
|
1349
|
-
name,
|
|
1350
|
-
truthy: uvalue ? 1 : 0,
|
|
1351
|
-
string: `${uvalue}`,
|
|
1986
|
+
jsonValue,
|
|
1987
|
+
string: storageValue === 'J' ? null : `${uvalue}`,
|
|
1352
1988
|
number: Number(uvalue),
|
|
1989
|
+
truthy: uvalue ? 1 : 0,
|
|
1353
1990
|
},
|
|
1354
1991
|
});
|
|
1355
1992
|
const references = this.findReferences(value);
|
|
@@ -1364,40 +2001,132 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1364
2001
|
});
|
|
1365
2002
|
}
|
|
1366
2003
|
}
|
|
1367
|
-
}
|
|
1368
|
-
|
|
2004
|
+
}
|
|
2005
|
+
if (only === 'tilmeldAC') {
|
|
2006
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull } = this.removeAndReturnACValues(etype, {}, sdata);
|
|
2007
|
+
this.queryRun(`UPDATE OR IGNORE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} SET "user"=@user, "group"=@group, "acUser"=@acUser, "acGroup"=@acGroup, "acOther"=@acOther, "acRead"=@acRead, "acWrite"=@acWrite, "acFull"=@acFull WHERE "guid"=@guid;`, {
|
|
2008
|
+
etypes: [etype],
|
|
1369
2009
|
params: {
|
|
1370
|
-
|
|
2010
|
+
user,
|
|
2011
|
+
group,
|
|
2012
|
+
acUser,
|
|
2013
|
+
acGroup,
|
|
2014
|
+
acOther,
|
|
2015
|
+
acRead: acRead && ',' + acRead.join(',') + ',',
|
|
2016
|
+
acWrite: acWrite && ',' + acWrite.join(',') + ',',
|
|
2017
|
+
acFull: acFull && ',' + acFull.join(',') + ',',
|
|
2018
|
+
guid,
|
|
1371
2019
|
},
|
|
1372
2020
|
});
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
2021
|
+
}
|
|
2022
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
2023
|
+
if (only == null || only === 'tokens') {
|
|
2024
|
+
for (let name in sdata) {
|
|
2025
|
+
let tokenString = null;
|
|
2026
|
+
try {
|
|
2027
|
+
tokenString = EntityClass.getFTSText(name, JSON.parse(sdata[name]));
|
|
2028
|
+
}
|
|
2029
|
+
catch (e) {
|
|
2030
|
+
// Ignore error.
|
|
2031
|
+
}
|
|
2032
|
+
if (tokenString != null) {
|
|
2033
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2034
|
+
while (tokens.length) {
|
|
2035
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2036
|
+
const params = {
|
|
2037
|
+
guid,
|
|
2038
|
+
name,
|
|
2039
|
+
};
|
|
2040
|
+
const values = [];
|
|
2041
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2042
|
+
const token = currentTokens[i];
|
|
2043
|
+
params['token' + i] = token.token;
|
|
2044
|
+
params['position' + i] = token.position;
|
|
2045
|
+
params['stem' + i] = token.stem ? 1 : 0;
|
|
2046
|
+
values.push('(@guid, @name, @token' +
|
|
2047
|
+
i +
|
|
2048
|
+
', @position' +
|
|
2049
|
+
i +
|
|
2050
|
+
', @stem' +
|
|
2051
|
+
i +
|
|
2052
|
+
')');
|
|
2053
|
+
}
|
|
2054
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
2055
|
+
etypes: [etype],
|
|
2056
|
+
params,
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
if (only == null) {
|
|
2063
|
+
const uniques = await EntityClass.getUniques({
|
|
2064
|
+
guid,
|
|
2065
|
+
cdate,
|
|
2066
|
+
mdate,
|
|
2067
|
+
tags,
|
|
2068
|
+
data: {},
|
|
2069
|
+
sdata,
|
|
1378
2070
|
});
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
2071
|
+
for (const unique of uniques) {
|
|
2072
|
+
try {
|
|
2073
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (@guid, @unique);`, {
|
|
2074
|
+
etypes: [etype],
|
|
2075
|
+
params: {
|
|
2076
|
+
guid,
|
|
2077
|
+
unique,
|
|
2078
|
+
},
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
catch (e) {
|
|
2082
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2083
|
+
this.nymph.config.debugError('sqlite3', `Import entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
2084
|
+
}
|
|
2085
|
+
throw e;
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
catch (e) {
|
|
2091
|
+
this.nymph.config.debugError('sqlite3', `Import entity error: "${e}"`);
|
|
2092
|
+
throw e;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
async importUID({ name, value }) {
|
|
2096
|
+
try {
|
|
2097
|
+
await this.startTransaction(`nymph-import-uid-${name}`);
|
|
2098
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
2099
|
+
params: {
|
|
2100
|
+
name,
|
|
2101
|
+
},
|
|
1383
2102
|
});
|
|
2103
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @value);`, {
|
|
2104
|
+
params: {
|
|
2105
|
+
name,
|
|
2106
|
+
value,
|
|
2107
|
+
},
|
|
2108
|
+
});
|
|
2109
|
+
await this.commit(`nymph-import-uid-${name}`);
|
|
1384
2110
|
}
|
|
1385
2111
|
catch (e) {
|
|
1386
|
-
|
|
2112
|
+
this.nymph.config.debugError('sqlite3', `Import UID error: "${e}"`);
|
|
2113
|
+
await this.rollback(`nymph-import-uid-${name}`);
|
|
1387
2114
|
throw e;
|
|
1388
2115
|
}
|
|
1389
2116
|
}
|
|
1390
2117
|
async newUID(name) {
|
|
1391
2118
|
if (name == null) {
|
|
1392
|
-
throw new
|
|
2119
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1393
2120
|
}
|
|
1394
2121
|
await this.startTransaction('nymph-newuid');
|
|
2122
|
+
let curUid = undefined;
|
|
1395
2123
|
try {
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
2124
|
+
curUid =
|
|
2125
|
+
this.queryGet(`SELECT "cur_uid" FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
2126
|
+
params: {
|
|
2127
|
+
name,
|
|
2128
|
+
},
|
|
2129
|
+
})?.cur_uid ?? null;
|
|
1401
2130
|
if (curUid == null) {
|
|
1402
2131
|
curUid = 1;
|
|
1403
2132
|
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}uids`)} ("name", "cur_uid") VALUES (@name, @curUid);`, {
|
|
@@ -1416,17 +2145,18 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1416
2145
|
},
|
|
1417
2146
|
});
|
|
1418
2147
|
}
|
|
1419
|
-
await this.commit('nymph-newuid');
|
|
1420
|
-
return curUid;
|
|
1421
2148
|
}
|
|
1422
2149
|
catch (e) {
|
|
2150
|
+
this.nymph.config.debugError('sqlite3', `New UID error: "${e}"`);
|
|
1423
2151
|
await this.rollback('nymph-newuid');
|
|
1424
2152
|
throw e;
|
|
1425
2153
|
}
|
|
2154
|
+
await this.commit('nymph-newuid');
|
|
2155
|
+
return curUid;
|
|
1426
2156
|
}
|
|
1427
2157
|
async renameUID(oldName, newName) {
|
|
1428
2158
|
if (oldName == null || newName == null) {
|
|
1429
|
-
throw new
|
|
2159
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1430
2160
|
}
|
|
1431
2161
|
await this.startTransaction('nymph-rename-uid');
|
|
1432
2162
|
this.queryRun(`UPDATE ${SQLite3Driver.escape(`${this.prefix}uids`)} SET "name"=@newName WHERE "name"=@oldName;`, {
|
|
@@ -1440,7 +2170,7 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1440
2170
|
}
|
|
1441
2171
|
async rollback(name) {
|
|
1442
2172
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1443
|
-
throw new
|
|
2173
|
+
throw new InvalidParametersError('Transaction rollback attempted without a name.');
|
|
1444
2174
|
}
|
|
1445
2175
|
if (this.store.transactionsStarted === 0) {
|
|
1446
2176
|
return true;
|
|
@@ -1457,7 +2187,8 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1457
2187
|
return true;
|
|
1458
2188
|
}
|
|
1459
2189
|
async saveEntity(entity) {
|
|
1460
|
-
const insertData = (guid, data, sdata, etype) => {
|
|
2190
|
+
const insertData = (guid, data, sdata, uniques, etype) => {
|
|
2191
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
1461
2192
|
const runInsertQuery = (name, value, svalue) => {
|
|
1462
2193
|
if (value === undefined) {
|
|
1463
2194
|
return;
|
|
@@ -1466,23 +2197,18 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1466
2197
|
? 'N'
|
|
1467
2198
|
: typeof value === 'string'
|
|
1468
2199
|
? 'S'
|
|
1469
|
-
:
|
|
1470
|
-
|
|
2200
|
+
: 'J';
|
|
2201
|
+
const jsonValue = storageValue === 'J' ? svalue : null;
|
|
2202
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} ("guid", "name", "value", "json", "string", "number", "truthy") VALUES (@guid, @name, @storageValue, jsonb(@jsonValue), @string, @number, @truthy);`, {
|
|
1471
2203
|
etypes: [etype],
|
|
1472
2204
|
params: {
|
|
1473
2205
|
guid,
|
|
1474
2206
|
name,
|
|
1475
2207
|
storageValue,
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}comparisons_${etype}`)} ("guid", "name", "truthy", "string", "number") VALUES (@guid, @name, @truthy, @string, @number);`, {
|
|
1479
|
-
etypes: [etype],
|
|
1480
|
-
params: {
|
|
1481
|
-
guid,
|
|
1482
|
-
name,
|
|
1483
|
-
truthy: value ? 1 : 0,
|
|
1484
|
-
string: `${value}`,
|
|
2208
|
+
jsonValue,
|
|
2209
|
+
string: storageValue === 'J' ? null : `${value}`,
|
|
1485
2210
|
number: Number(value),
|
|
2211
|
+
truthy: value ? 1 : 0,
|
|
1486
2212
|
},
|
|
1487
2213
|
});
|
|
1488
2214
|
const references = this.findReferences(svalue);
|
|
@@ -1496,7 +2222,59 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1496
2222
|
},
|
|
1497
2223
|
});
|
|
1498
2224
|
}
|
|
2225
|
+
let tokenString = null;
|
|
2226
|
+
try {
|
|
2227
|
+
tokenString = EntityClass.getFTSText(name, value);
|
|
2228
|
+
}
|
|
2229
|
+
catch (e) {
|
|
2230
|
+
// Ignore error.
|
|
2231
|
+
}
|
|
2232
|
+
if (tokenString != null) {
|
|
2233
|
+
const tokens = this.tokenizer.tokenize(tokenString);
|
|
2234
|
+
while (tokens.length) {
|
|
2235
|
+
const currentTokens = tokens.splice(0, 100);
|
|
2236
|
+
const params = {
|
|
2237
|
+
guid,
|
|
2238
|
+
name,
|
|
2239
|
+
};
|
|
2240
|
+
const values = [];
|
|
2241
|
+
for (let i = 0; i < currentTokens.length; i++) {
|
|
2242
|
+
const token = currentTokens[i];
|
|
2243
|
+
params['token' + i] = token.token;
|
|
2244
|
+
params['position' + i] = token.position;
|
|
2245
|
+
params['stem' + i] = token.stem ? 1 : 0;
|
|
2246
|
+
values.push('(@guid, @name, @token' +
|
|
2247
|
+
i +
|
|
2248
|
+
', @position' +
|
|
2249
|
+
i +
|
|
2250
|
+
', @stem' +
|
|
2251
|
+
i +
|
|
2252
|
+
')');
|
|
2253
|
+
}
|
|
2254
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} ("guid", "name", "token", "position", "stem") VALUES ${values.join(', ')};`, {
|
|
2255
|
+
etypes: [etype],
|
|
2256
|
+
params,
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
1499
2260
|
};
|
|
2261
|
+
for (const unique of uniques) {
|
|
2262
|
+
try {
|
|
2263
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}uniques_${etype}`)} ("guid", "unique") VALUES (@guid, @unique);`, {
|
|
2264
|
+
etypes: [etype],
|
|
2265
|
+
params: {
|
|
2266
|
+
guid,
|
|
2267
|
+
unique,
|
|
2268
|
+
},
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
catch (e) {
|
|
2272
|
+
if (e instanceof EntityUniqueConstraintError) {
|
|
2273
|
+
this.nymph.config.debugError('sqlite3', `Save entity unique constraint violation for GUID "${guid}" on etype "${etype}": "${unique}"`);
|
|
2274
|
+
}
|
|
2275
|
+
throw e;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
1500
2278
|
for (const name in data) {
|
|
1501
2279
|
runInsertQuery(name, data[name], JSON.stringify(data[name]));
|
|
1502
2280
|
}
|
|
@@ -1504,24 +2282,55 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1504
2282
|
runInsertQuery(name, JSON.parse(sdata[name]), sdata[name]);
|
|
1505
2283
|
}
|
|
1506
2284
|
};
|
|
2285
|
+
let inTransaction = false;
|
|
1507
2286
|
try {
|
|
1508
|
-
return this.saveEntityRowLike(entity, async (
|
|
1509
|
-
|
|
2287
|
+
return this.saveEntityRowLike(entity, async ({ guid, tags, data, sdata, uniques, cdate, etype }) => {
|
|
2288
|
+
if (Object.keys(data).length === 0 &&
|
|
2289
|
+
Object.keys(sdata).length === 0) {
|
|
2290
|
+
throw new EntityInvalidDataError('Entity contains no data.');
|
|
2291
|
+
}
|
|
2292
|
+
let processedData = this.removeAndReturnACValues(etype, { ...data }, { ...sdata });
|
|
2293
|
+
({ data, sdata } = processedData);
|
|
2294
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull, } = processedData;
|
|
2295
|
+
this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate", "user", "group", "acUser", "acGroup", "acOther", "acRead", "acWrite", "acFull") VALUES (@guid, @tags, @cdate, @cdate, @user, @group, @acUser, @acGroup, @acOther, @acRead, @acWrite, @acFull);`, {
|
|
1510
2296
|
etypes: [etype],
|
|
1511
2297
|
params: {
|
|
1512
2298
|
guid,
|
|
1513
2299
|
tags: ',' + tags.join(',') + ',',
|
|
1514
2300
|
cdate,
|
|
2301
|
+
user,
|
|
2302
|
+
group,
|
|
2303
|
+
acUser,
|
|
2304
|
+
acGroup,
|
|
2305
|
+
acOther,
|
|
2306
|
+
acRead: acRead && ',' + acRead.join(',') + ',',
|
|
2307
|
+
acWrite: acWrite && ',' + acWrite.join(',') + ',',
|
|
2308
|
+
acFull: acFull && ',' + acFull.join(',') + ',',
|
|
1515
2309
|
},
|
|
1516
2310
|
});
|
|
1517
|
-
insertData(guid, data, sdata, etype);
|
|
2311
|
+
insertData(guid, data, sdata, uniques, etype);
|
|
1518
2312
|
return true;
|
|
1519
|
-
}, async (entity, guid, tags, data, sdata, mdate, etype) => {
|
|
1520
|
-
|
|
2313
|
+
}, async ({ entity, guid, tags, data, sdata, uniques, mdate, etype }) => {
|
|
2314
|
+
if (Object.keys(data).length === 0 &&
|
|
2315
|
+
Object.keys(sdata).length === 0) {
|
|
2316
|
+
throw new EntityInvalidDataError('Entity contains no data.');
|
|
2317
|
+
}
|
|
2318
|
+
let processedData = this.removeAndReturnACValues(etype, { ...data }, { ...sdata });
|
|
2319
|
+
({ data, sdata } = processedData);
|
|
2320
|
+
let { user, group, acUser, acGroup, acOther, acRead, acWrite, acFull, } = processedData;
|
|
2321
|
+
const info = this.queryRun(`UPDATE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} SET "tags"=@tags, "mdate"=@mdate, "user"=@user, "group"=@group, "acUser"=@acUser, "acGroup"=@acGroup, "acOther"=@acOther, "acRead"=@acRead, "acWrite"=@acWrite, "acFull"=@acFull WHERE "guid"=@guid AND "mdate" <= @emdate;`, {
|
|
1521
2322
|
etypes: [etype],
|
|
1522
2323
|
params: {
|
|
1523
2324
|
tags: ',' + tags.join(',') + ',',
|
|
1524
2325
|
mdate,
|
|
2326
|
+
user,
|
|
2327
|
+
group,
|
|
2328
|
+
acUser,
|
|
2329
|
+
acGroup,
|
|
2330
|
+
acOther,
|
|
2331
|
+
acRead: acRead && ',' + acRead.join(',') + ',',
|
|
2332
|
+
acWrite: acWrite && ',' + acWrite.join(',') + ',',
|
|
2333
|
+
acFull: acFull && ',' + acFull.join(',') + ',',
|
|
1525
2334
|
guid,
|
|
1526
2335
|
emdate: Number(entity.mdate),
|
|
1527
2336
|
},
|
|
@@ -1534,42 +2343,58 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1534
2343
|
guid,
|
|
1535
2344
|
},
|
|
1536
2345
|
});
|
|
1537
|
-
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}
|
|
2346
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} WHERE "guid"=@guid;`, {
|
|
2347
|
+
etypes: [etype],
|
|
2348
|
+
params: {
|
|
2349
|
+
guid,
|
|
2350
|
+
},
|
|
2351
|
+
});
|
|
2352
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} WHERE "guid"=@guid;`, {
|
|
1538
2353
|
etypes: [etype],
|
|
1539
2354
|
params: {
|
|
1540
2355
|
guid,
|
|
1541
2356
|
},
|
|
1542
2357
|
});
|
|
1543
|
-
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}
|
|
2358
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uniques_${etype}`)} WHERE "guid"=@guid;`, {
|
|
1544
2359
|
etypes: [etype],
|
|
1545
2360
|
params: {
|
|
1546
2361
|
guid,
|
|
1547
2362
|
},
|
|
1548
2363
|
});
|
|
1549
|
-
insertData(guid, data, sdata, etype);
|
|
2364
|
+
insertData(guid, data, sdata, uniques, etype);
|
|
1550
2365
|
success = true;
|
|
1551
2366
|
}
|
|
2367
|
+
if (!success && info.changes === 0) {
|
|
2368
|
+
throw new EntityConflictError("Entity either doesn't exist or is newer in the DB.");
|
|
2369
|
+
}
|
|
1552
2370
|
return success;
|
|
1553
2371
|
}, async () => {
|
|
1554
2372
|
await this.startTransaction('nymph-save');
|
|
2373
|
+
inTransaction = true;
|
|
1555
2374
|
}, async (success) => {
|
|
1556
|
-
if (
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
2375
|
+
if (inTransaction) {
|
|
2376
|
+
inTransaction = false;
|
|
2377
|
+
if (success) {
|
|
2378
|
+
success = await this.commit('nymph-save');
|
|
2379
|
+
}
|
|
2380
|
+
else {
|
|
2381
|
+
await this.rollback('nymph-save');
|
|
2382
|
+
}
|
|
1561
2383
|
}
|
|
1562
2384
|
return success;
|
|
1563
2385
|
});
|
|
1564
2386
|
}
|
|
1565
2387
|
catch (e) {
|
|
1566
|
-
|
|
2388
|
+
this.nymph.config.debugError('sqlite3', `Save entity error: "${e}"`);
|
|
2389
|
+
if (inTransaction) {
|
|
2390
|
+
await this.rollback('nymph-save');
|
|
2391
|
+
}
|
|
1567
2392
|
throw e;
|
|
1568
2393
|
}
|
|
1569
2394
|
}
|
|
1570
2395
|
async setUID(name, curUid) {
|
|
1571
2396
|
if (name == null) {
|
|
1572
|
-
throw new
|
|
2397
|
+
throw new InvalidParametersError('Name not given for UID.');
|
|
1573
2398
|
}
|
|
1574
2399
|
await this.startTransaction('nymph-set-uid');
|
|
1575
2400
|
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
|
|
@@ -1586,9 +2411,12 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1586
2411
|
await this.commit('nymph-set-uid');
|
|
1587
2412
|
return true;
|
|
1588
2413
|
}
|
|
2414
|
+
async internalTransaction(name) {
|
|
2415
|
+
await this.startTransaction(name);
|
|
2416
|
+
}
|
|
1589
2417
|
async startTransaction(name) {
|
|
1590
2418
|
if (name == null || typeof name !== 'string' || name.length === 0) {
|
|
1591
|
-
throw new
|
|
2419
|
+
throw new InvalidParametersError('Transaction start attempted without a name.');
|
|
1592
2420
|
}
|
|
1593
2421
|
if (!this.config.explicitWrite && !this.store.linkWrite) {
|
|
1594
2422
|
this._connect(true);
|
|
@@ -1597,6 +2425,107 @@ class SQLite3Driver extends nymph_1.NymphDriver {
|
|
|
1597
2425
|
this.store.transactionsStarted++;
|
|
1598
2426
|
return this.nymph;
|
|
1599
2427
|
}
|
|
2428
|
+
async removeTilmeldOldRows(etype) {
|
|
2429
|
+
await this.startTransaction('nymph-remove-tilmeld-rows');
|
|
2430
|
+
try {
|
|
2431
|
+
for (let name of [
|
|
2432
|
+
'user',
|
|
2433
|
+
'group',
|
|
2434
|
+
'acUser',
|
|
2435
|
+
'acGroup',
|
|
2436
|
+
'acOther',
|
|
2437
|
+
'acRead',
|
|
2438
|
+
'acWrite',
|
|
2439
|
+
'acFull',
|
|
2440
|
+
]) {
|
|
2441
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}data_${etype}`)} WHERE "name"=@name;`, {
|
|
2442
|
+
etypes: [etype],
|
|
2443
|
+
params: {
|
|
2444
|
+
name,
|
|
2445
|
+
},
|
|
2446
|
+
});
|
|
2447
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}references_${etype}`)} WHERE "name"=@name;`, {
|
|
2448
|
+
etypes: [etype],
|
|
2449
|
+
params: {
|
|
2450
|
+
name,
|
|
2451
|
+
},
|
|
2452
|
+
});
|
|
2453
|
+
this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}tokens_${etype}`)} WHERE "name"=@name;`, {
|
|
2454
|
+
etypes: [etype],
|
|
2455
|
+
params: {
|
|
2456
|
+
name,
|
|
2457
|
+
},
|
|
2458
|
+
});
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
catch (e) {
|
|
2462
|
+
this.nymph.config.debugError('sqlite3', `Remove tilmeld rows error: "${e}"`);
|
|
2463
|
+
await this.rollback('nymph-remove-tilmeld-rows');
|
|
2464
|
+
throw e;
|
|
2465
|
+
}
|
|
2466
|
+
await this.commit('nymph-remove-tilmeld-rows');
|
|
2467
|
+
return true;
|
|
2468
|
+
}
|
|
2469
|
+
async needsMigration() {
|
|
2470
|
+
const table = this.queryGet(`SELECT "name" FROM "sqlite_master" WHERE "type"='table' AND "name" LIKE @prefix LIMIT 1;`, {
|
|
2471
|
+
params: {
|
|
2472
|
+
prefix: this.prefix + 'data_' + '%',
|
|
2473
|
+
},
|
|
2474
|
+
});
|
|
2475
|
+
if (table?.name) {
|
|
2476
|
+
const result = this.queryGet(`SELECT 1 AS "exists" FROM pragma_table_info(@table) WHERE "name"='json';`, {
|
|
2477
|
+
params: {
|
|
2478
|
+
table: table.name,
|
|
2479
|
+
},
|
|
2480
|
+
});
|
|
2481
|
+
if (!result?.exists) {
|
|
2482
|
+
return 'json';
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
const table2 = this.queryGet(`SELECT "name" FROM "sqlite_master" WHERE "type"='table' AND "name" LIKE @tokenTable LIMIT 1;`, {
|
|
2486
|
+
params: {
|
|
2487
|
+
tokenTable: this.prefix + 'tokens_' + '%',
|
|
2488
|
+
},
|
|
2489
|
+
});
|
|
2490
|
+
if (!table2 || !table2.name) {
|
|
2491
|
+
return 'tokens';
|
|
2492
|
+
}
|
|
2493
|
+
const table3 = this.queryGet(`SELECT "name" FROM "sqlite_master" WHERE "type"='table' AND "name" LIKE @prefix LIMIT 1;`, {
|
|
2494
|
+
params: {
|
|
2495
|
+
prefix: this.prefix + 'entities_' + '%',
|
|
2496
|
+
},
|
|
2497
|
+
});
|
|
2498
|
+
if (table3?.name) {
|
|
2499
|
+
const result = this.queryGet(`SELECT 1 AS "exists" FROM pragma_table_info(@table) WHERE "name"='user';`, {
|
|
2500
|
+
params: {
|
|
2501
|
+
table: table3.name,
|
|
2502
|
+
},
|
|
2503
|
+
});
|
|
2504
|
+
if (!result?.exists) {
|
|
2505
|
+
return 'tilmeldColumns';
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return false;
|
|
2509
|
+
}
|
|
2510
|
+
async liveMigration(migrationType) {
|
|
2511
|
+
if (migrationType === 'tokenTables') {
|
|
2512
|
+
const etypes = await this.getEtypes();
|
|
2513
|
+
for (let etype of etypes) {
|
|
2514
|
+
this.createTokensTable(etype);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
else if (migrationType === 'tilmeldColumns') {
|
|
2518
|
+
const etypes = await this.getEtypes();
|
|
2519
|
+
for (let etype of etypes) {
|
|
2520
|
+
this.addTilmeldColumnsAndIndexes(etype);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
else if (migrationType === 'tilmeldRemoveOldRows') {
|
|
2524
|
+
const etypes = await this.getEtypes();
|
|
2525
|
+
for (let etype of etypes) {
|
|
2526
|
+
await this.removeTilmeldOldRows(etype);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
1600
2530
|
}
|
|
1601
|
-
exports.default = SQLite3Driver;
|
|
1602
2531
|
//# sourceMappingURL=SQLite3Driver.js.map
|