@nymphjs/driver-sqlite3 1.0.0-beta.11 → 1.0.0-beta.111

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