@nymphjs/driver-sqlite3 1.0.0-beta.4 → 1.0.0-beta.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,170 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-beta.41](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.40...v1.0.0-beta.41) (2023-07-12)
7
+
8
+ ### Features
9
+
10
+ - remove synchronous database queries ([b579fb2](https://github.com/sciactive/nymphjs/commit/b579fb2eacd96cdd1b386a62c5c00cdbb2438f6e))
11
+
12
+ # [1.0.0-beta.40](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.39...v1.0.0-beta.40) (2023-07-10)
13
+
14
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
15
+
16
+ # [1.0.0-beta.39](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.38...v1.0.0-beta.39) (2023-07-09)
17
+
18
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
19
+
20
+ # [1.0.0-beta.38](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.37...v1.0.0-beta.38) (2023-07-09)
21
+
22
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
23
+
24
+ # [1.0.0-beta.37](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.36...v1.0.0-beta.37) (2023-07-09)
25
+
26
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
27
+
28
+ # [1.0.0-beta.36](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.35...v1.0.0-beta.36) (2023-07-09)
29
+
30
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
31
+
32
+ # [1.0.0-beta.35](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.34...v1.0.0-beta.35) (2023-06-14)
33
+
34
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
35
+
36
+ # [1.0.0-beta.34](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.33...v1.0.0-beta.34) (2023-05-13)
37
+
38
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
39
+
40
+ # [1.0.0-beta.33](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.32...v1.0.0-beta.33) (2023-05-13)
41
+
42
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
43
+
44
+ # [1.0.0-beta.32](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.31...v1.0.0-beta.32) (2023-05-13)
45
+
46
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
47
+
48
+ # [1.0.0-beta.31](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.30...v1.0.0-beta.31) (2023-05-12)
49
+
50
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
51
+
52
+ # [1.0.0-beta.30](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.29...v1.0.0-beta.30) (2023-05-12)
53
+
54
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
55
+
56
+ # [1.0.0-beta.29](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.28...v1.0.0-beta.29) (2023-05-08)
57
+
58
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
59
+
60
+ # [1.0.0-beta.28](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.27...v1.0.0-beta.28) (2023-05-05)
61
+
62
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
63
+
64
+ # [1.0.0-beta.27](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.26...v1.0.0-beta.27) (2023-05-04)
65
+
66
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
67
+
68
+ # [1.0.0-beta.26](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.25...v1.0.0-beta.26) (2023-05-04)
69
+
70
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
71
+
72
+ # [1.0.0-beta.25](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.24...v1.0.0-beta.25) (2023-05-04)
73
+
74
+ ### Bug Fixes
75
+
76
+ - don't create empty entities ([1d4d2e9](https://github.com/sciactive/nymphjs/commit/1d4d2e99af2e9cdc647bcf58ac34572836f41176))
77
+
78
+ # [1.0.0-beta.24](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2023-05-02)
79
+
80
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
81
+
82
+ # [1.0.0-beta.23](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.22...v1.0.0-beta.23) (2023-05-02)
83
+
84
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
85
+
86
+ # [1.0.0-beta.22](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.21...v1.0.0-beta.22) (2023-05-01)
87
+
88
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
89
+
90
+ # [1.0.0-beta.21](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2023-05-01)
91
+
92
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
93
+
94
+ # [1.0.0-beta.20](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2023-04-30)
95
+
96
+ ### Features
97
+
98
+ - let the user revoke all current auth tokens ([63af3b9](https://github.com/sciactive/nymphjs/commit/63af3b9a31c6c221ab40c2c8a69231675f4634a2))
99
+
100
+ # [1.0.0-beta.19](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2023-04-29)
101
+
102
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
103
+
104
+ # [1.0.0-beta.18](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2023-04-27)
105
+
106
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
107
+
108
+ # [1.0.0-beta.17](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2023-04-24)
109
+
110
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
111
+
112
+ # [1.0.0-beta.16](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2023-03-31)
113
+
114
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
115
+
116
+ # [1.0.0-beta.15](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2023-03-23)
117
+
118
+ ### Features
119
+
120
+ - add option to sort results by a property ([16384e7](https://github.com/sciactive/nymphjs/commit/16384e7bdab88abb55ccccabb06ac09f92fa8a03))
121
+
122
+ # [1.0.0-beta.14](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.13...v1.0.0-beta.14) (2023-03-17)
123
+
124
+ ### Bug Fixes
125
+
126
+ - run pragma and function on write link on new sqlite3 db ([6c28ba1](https://github.com/sciactive/nymphjs/commit/6c28ba1a396f7cec098d4e550c7b82c93979eaec))
127
+
128
+ # [1.0.0-beta.13](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.12...v1.0.0-beta.13) (2023-03-16)
129
+
130
+ ### Features
131
+
132
+ - open sqlite in readonly unless actively writing to db ([0443b91](https://github.com/sciactive/nymphjs/commit/0443b9188df3ebd557b96baf873abc4e4ddd9137))
133
+
134
+ # [1.0.0-beta.12](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.11...v1.0.0-beta.12) (2023-03-04)
135
+
136
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
137
+
138
+ # [1.0.0-beta.11](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.10...v1.0.0-beta.11) (2023-02-27)
139
+
140
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
141
+
142
+ # [1.0.0-beta.10](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.9...v1.0.0-beta.10) (2023-01-19)
143
+
144
+ ### Features
145
+
146
+ - add wal mode setting to sqlite driver ([0071d66](https://github.com/sciactive/nymphjs/commit/0071d6628534b8b35d49c0238a99fe143fd03207))
147
+
148
+ # [1.0.0-beta.9](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.8...v1.0.0-beta.9) (2023-01-09)
149
+
150
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
151
+
152
+ # [1.0.0-beta.8](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.7...v1.0.0-beta.8) (2023-01-09)
153
+
154
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
155
+
156
+ # [1.0.0-beta.7](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.6...v1.0.0-beta.7) (2023-01-05)
157
+
158
+ ### Bug Fixes
159
+
160
+ - sqlite transaction returns wrong instance of nymph if it has been cloned ([b278c76](https://github.com/sciactive/nymphjs/commit/b278c7633722cb1cca7a941187ae2f1ff8ebdc7b))
161
+
162
+ # [1.0.0-beta.6](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.5...v1.0.0-beta.6) (2023-01-05)
163
+
164
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
165
+
166
+ # [1.0.0-beta.5](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.4...v1.0.0-beta.5) (2022-11-24)
167
+
168
+ **Note:** Version bump only for package @nymphjs/driver-sqlite3
169
+
6
170
  # [1.0.0-beta.4](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.3...v1.0.0-beta.4) (2022-11-23)
7
171
 
8
172
  **Note:** Version bump only for package @nymphjs/driver-sqlite3
@@ -1,19 +1,25 @@
1
1
  import SQLite3 from 'better-sqlite3';
2
2
  import { NymphDriver, EntityConstructor, EntityInterface, FormattedSelector, Options, Selector } from '@nymphjs/nymph';
3
3
  import { SQLite3DriverConfig } from './conf';
4
+ declare class InternalStore {
5
+ link: SQLite3.Database;
6
+ linkWrite?: SQLite3.Database;
7
+ connected: boolean;
8
+ transactionsStarted: number;
9
+ constructor(link: SQLite3.Database);
10
+ }
4
11
  export default class SQLite3Driver extends NymphDriver {
5
12
  config: SQLite3DriverConfig;
6
13
  protected prefix: string;
7
- protected connected: boolean;
8
- protected link: SQLite3.Database;
9
- protected transactionsStarted: number;
14
+ protected store: InternalStore;
10
15
  static escape(input: string): string;
11
- constructor(config: Partial<SQLite3DriverConfig>);
12
- connect(): Promise<true>;
16
+ constructor(config: Partial<SQLite3DriverConfig>, store?: InternalStore);
17
+ clone(): SQLite3Driver;
18
+ connect(): Promise<boolean>;
19
+ private _connect;
13
20
  disconnect(): Promise<false>;
14
21
  inTransaction(): Promise<boolean>;
15
22
  isConnected(): boolean;
16
- private checkReadOnlyMode;
17
23
  private createTables;
18
24
  private query;
19
25
  private queryIter;
@@ -34,13 +40,6 @@ export default class SQLite3Driver extends NymphDriver {
34
40
  return: 'guid';
35
41
  }, ...selectors: Selector[]): Promise<string[]>;
36
42
  getEntities<T extends EntityConstructor = EntityConstructor>(options?: Options<T>, ...selectors: Selector[]): Promise<ReturnType<T['factorySync']>[]>;
37
- protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(options: Options<T> & {
38
- return: 'count';
39
- }, ...selectors: Selector[]): number;
40
- protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(options: Options<T> & {
41
- return: 'guid';
42
- }, ...selectors: Selector[]): string[];
43
- protected getEntitiesSync<T extends EntityConstructor = EntityConstructor>(options?: Options<T>, ...selectors: Selector[]): ReturnType<T['factorySync']>[];
44
43
  getUID(name: string): Promise<number | null>;
45
44
  import(filename: string): Promise<boolean>;
46
45
  newUID(name: string): Promise<number>;
@@ -50,3 +49,4 @@ export default class SQLite3Driver extends NymphDriver {
50
49
  setUID(name: string, curUid: number): Promise<boolean>;
51
50
  startTransaction(name: string): Promise<import("@nymphjs/nymph").Nymph>;
52
51
  }
52
+ export {};
@@ -7,6 +7,13 @@ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
7
7
  const nymph_1 = require("@nymphjs/nymph");
8
8
  const guid_1 = require("@nymphjs/guid");
9
9
  const conf_1 = require("./conf");
10
+ class InternalStore {
11
+ constructor(link) {
12
+ this.connected = false;
13
+ this.transactionsStarted = 0;
14
+ this.link = link;
15
+ }
16
+ }
10
17
  class SQLite3Driver extends nymph_1.NymphDriver {
11
18
  static escape(input) {
12
19
  if (input.indexOf('\x00') !== -1) {
@@ -14,63 +21,126 @@ class SQLite3Driver extends nymph_1.NymphDriver {
14
21
  }
15
22
  return '"' + input.replace(/"/g, () => '""') + '"';
16
23
  }
17
- constructor(config) {
24
+ constructor(config, store) {
18
25
  super();
19
- this.connected = false;
20
- this.transactionsStarted = 0;
21
26
  this.config = { ...conf_1.SQLite3DriverConfigDefaults, ...config };
27
+ if (this.config.filename === ':memory:') {
28
+ this.config.explicitWrite = true;
29
+ }
22
30
  this.prefix = this.config.prefix;
23
- this.connect();
31
+ if (store) {
32
+ this.store = store;
33
+ }
34
+ else {
35
+ this.connect();
36
+ }
37
+ }
38
+ clone() {
39
+ return new SQLite3Driver(this.config, this.store);
24
40
  }
25
- async connect() {
26
- const { filename, fileMustExist, timeout, readonly, verbose } = this.config;
27
- if (!this.connected) {
41
+ connect() {
42
+ if (this.store && this.store.connected) {
43
+ return Promise.resolve(true);
44
+ }
45
+ this._connect(false);
46
+ return Promise.resolve(this.store.connected);
47
+ }
48
+ _connect(write) {
49
+ const { filename, fileMustExist, timeout, explicitWrite, wal, verbose } = this.config;
50
+ try {
51
+ const setOptions = (link) => {
52
+ if (wal) {
53
+ link.pragma('journal_mode = WAL;');
54
+ }
55
+ link.pragma('encoding = "UTF-8";');
56
+ link.pragma('foreign_keys = 1;');
57
+ link.pragma('case_sensitive_like = 1;');
58
+ link.function('regexp', { deterministic: true }, ((pattern, subject) => (this.posixRegexMatch(pattern, subject) ? 1 : 0)));
59
+ };
60
+ let link;
28
61
  try {
29
- this.link = new better_sqlite3_1.default(filename, {
30
- readonly,
62
+ link = new better_sqlite3_1.default(filename, {
63
+ readonly: !explicitWrite && !write,
31
64
  fileMustExist,
32
65
  timeout,
33
66
  verbose,
34
67
  });
35
- this.connected = true;
36
- this.link.pragma('encoding = "UTF-8";');
37
- this.link.pragma('foreign_keys = 1;');
38
- this.link.pragma('case_sensitive_like = 1;');
39
- this.link.function('regexp', { deterministic: true }, (pattern, subject) => this.posixRegexMatch(pattern, subject) ? 1 : 0);
40
68
  }
41
69
  catch (e) {
42
- this.connected = false;
43
- if (filename === ':memory:') {
44
- throw new nymph_1.NotConfiguredError("It seems the config hasn't been set up correctly.");
70
+ if (e.code === 'SQLITE_CANTOPEN' &&
71
+ !explicitWrite &&
72
+ !write &&
73
+ !this.config.fileMustExist) {
74
+ const writeLink = new better_sqlite3_1.default(filename, {
75
+ readonly: false,
76
+ fileMustExist,
77
+ timeout,
78
+ verbose,
79
+ });
80
+ setOptions(writeLink);
81
+ writeLink.close();
82
+ link = new better_sqlite3_1.default(filename, {
83
+ readonly: true,
84
+ fileMustExist,
85
+ timeout,
86
+ verbose,
87
+ });
45
88
  }
46
89
  else {
47
- throw new nymph_1.UnableToConnectError('Could not connect: ' + e?.message);
90
+ throw e;
48
91
  }
49
92
  }
93
+ if (!this.store) {
94
+ if (write) {
95
+ throw new Error('Tried to open in write without opening in read first.');
96
+ }
97
+ this.store = new InternalStore(link);
98
+ }
99
+ else if (write) {
100
+ this.store.linkWrite = link;
101
+ }
102
+ else {
103
+ this.store.link = link;
104
+ }
105
+ this.store.connected = true;
106
+ setOptions(link);
107
+ }
108
+ catch (e) {
109
+ if (this.store) {
110
+ this.store.connected = false;
111
+ }
112
+ if (filename === ':memory:') {
113
+ throw new nymph_1.NotConfiguredError("It seems the config hasn't been set up correctly. Could not connect: " +
114
+ e?.message);
115
+ }
116
+ else {
117
+ throw new nymph_1.UnableToConnectError('Could not connect: ' + e?.message);
118
+ }
50
119
  }
51
- return this.connected;
52
120
  }
53
121
  async disconnect() {
54
- if (this.connected) {
55
- this.link.exec('PRAGMA optimize;');
56
- this.link.close();
57
- this.connected = false;
122
+ if (this.store.connected) {
123
+ if (this.store.linkWrite && !this.config.explicitWrite) {
124
+ this.store.linkWrite.exec('PRAGMA optimize;');
125
+ this.store.linkWrite.close();
126
+ this.store.linkWrite = undefined;
127
+ }
128
+ if (this.config.explicitWrite) {
129
+ this.store.link.exec('PRAGMA optimize;');
130
+ }
131
+ this.store.link.close();
132
+ this.store.transactionsStarted = 0;
133
+ this.store.connected = false;
58
134
  }
59
- return this.connected;
135
+ return this.store.connected;
60
136
  }
61
137
  async inTransaction() {
62
- return this.transactionsStarted > 0;
138
+ return this.store.transactionsStarted > 0;
63
139
  }
64
140
  isConnected() {
65
- return this.connected;
66
- }
67
- checkReadOnlyMode() {
68
- if (this.config.readonly) {
69
- throw new nymph_1.InvalidParametersError('Attempt to write to SQLite3 DB in read only mode.');
70
- }
141
+ return this.store.connected;
71
142
  }
72
143
  createTables(etype = null) {
73
- this.checkReadOnlyMode();
74
144
  this.startTransaction('nymph-tablecreation');
75
145
  try {
76
146
  if (etype != null) {
@@ -130,23 +200,32 @@ class SQLite3Driver extends nymph_1.NymphDriver {
130
200
  }
131
201
  }
132
202
  queryIter(query, { etypes = [], params = {}, } = {}) {
133
- return this.query(() => this.link.prepare(query).iterate(params), `${query} -- ${JSON.stringify(params)}`, etypes);
203
+ return this.query(() => (this.store.linkWrite || this.store.link)
204
+ .prepare(query)
205
+ .iterate(params), `${query} -- ${JSON.stringify(params)}`, etypes);
134
206
  }
135
207
  queryGet(query, { etypes = [], params = {}, } = {}) {
136
- return this.query(() => this.link.prepare(query).get(params), `${query} -- ${JSON.stringify(params)}`, etypes);
208
+ return this.query(() => (this.store.linkWrite || this.store.link).prepare(query).get(params), `${query} -- ${JSON.stringify(params)}`, etypes);
137
209
  }
138
210
  queryRun(query, { etypes = [], params = {}, } = {}) {
139
- return this.query(() => this.link.prepare(query).run(params), `${query} -- ${JSON.stringify(params)}`, etypes);
211
+ return this.query(() => (this.store.linkWrite || this.store.link).prepare(query).run(params), `${query} -- ${JSON.stringify(params)}`, etypes);
140
212
  }
141
213
  async commit(name) {
142
214
  if (name == null || typeof name !== 'string' || name.length === 0) {
143
215
  throw new nymph_1.InvalidParametersError('Transaction commit attempted without a name.');
144
216
  }
145
- if (this.transactionsStarted === 0) {
217
+ if (this.store.transactionsStarted === 0) {
146
218
  return true;
147
219
  }
148
220
  this.queryRun(`RELEASE SAVEPOINT ${SQLite3Driver.escape(name)};`);
149
- this.transactionsStarted--;
221
+ this.store.transactionsStarted--;
222
+ if (this.store.transactionsStarted === 0 &&
223
+ this.store.linkWrite &&
224
+ !this.config.explicitWrite) {
225
+ this.store.linkWrite.exec('PRAGMA optimize;');
226
+ this.store.linkWrite.close();
227
+ this.store.linkWrite = undefined;
228
+ }
150
229
  return true;
151
230
  }
152
231
  async deleteEntityByID(guid, className) {
@@ -159,7 +238,6 @@ class SQLite3Driver extends nymph_1.NymphDriver {
159
238
  EntityClass = className;
160
239
  }
161
240
  const etype = EntityClass.ETYPE;
162
- this.checkReadOnlyMode();
163
241
  await this.startTransaction('nymph-delete');
164
242
  try {
165
243
  this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=@guid;`, {
@@ -201,12 +279,13 @@ class SQLite3Driver extends nymph_1.NymphDriver {
201
279
  if (!name) {
202
280
  throw new nymph_1.InvalidParametersError('Name not given for UID');
203
281
  }
204
- this.checkReadOnlyMode();
282
+ await this.startTransaction('nymph-delete-uid');
205
283
  this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
206
284
  params: {
207
285
  name,
208
286
  },
209
287
  });
288
+ await this.commit('nymph-delete-uid');
210
289
  return true;
211
290
  }
212
291
  async exportEntities(writeLine) {
@@ -274,6 +353,7 @@ class SQLite3Driver extends nymph_1.NymphDriver {
274
353
  const cTable = `c${tableSuffix}`;
275
354
  const fTable = `f${tableSuffix}`;
276
355
  const ieTable = `ie${tableSuffix}`;
356
+ const sTable = `s${tableSuffix}`;
277
357
  const sort = options.sort ?? 'cdate';
278
358
  const queryParts = this.iterateSelectorsForQuery(formattedSelectors, (key, value, typeIsOr, typeIsNot) => {
279
359
  const clauseNot = key.startsWith('!');
@@ -1019,18 +1099,31 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1019
1099
  return curQuery;
1020
1100
  });
1021
1101
  let sortBy;
1102
+ let sortByInner;
1103
+ let sortJoin = '';
1104
+ const order = options.reverse ? ' DESC' : '';
1022
1105
  switch (sort) {
1023
1106
  case 'mdate':
1024
- sortBy = '"mdate"';
1107
+ sortBy = `${eTable}."mdate"${order}`;
1108
+ sortByInner = `${ieTable}."mdate"${order}`;
1025
1109
  break;
1026
1110
  case 'cdate':
1111
+ sortBy = `${eTable}."cdate"${order}`;
1112
+ sortByInner = `${ieTable}."cdate"${order}`;
1113
+ break;
1027
1114
  default:
1028
- sortBy = '"cdate"';
1115
+ const name = `param${++count.i}`;
1116
+ sortJoin = `LEFT JOIN (
1117
+ SELECT "guid", "string", "number"
1118
+ FROM ${SQLite3Driver.escape(this.prefix + 'comparisons_' + etype)}
1119
+ WHERE "name"=@${name}
1120
+ ORDER BY "number"${order}, "string"${order}
1121
+ ) ${sTable} USING ("guid")`;
1122
+ sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
1123
+ sortByInner = sortBy;
1124
+ params[name] = sort;
1029
1125
  break;
1030
1126
  }
1031
- if (options.reverse) {
1032
- sortBy += ' DESC';
1033
- }
1034
1127
  let query;
1035
1128
  if (queryParts.length) {
1036
1129
  if (subquery) {
@@ -1063,8 +1156,9 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1063
1156
  else if (options.return === 'guid') {
1064
1157
  query = `SELECT "guid"
1065
1158
  FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
1159
+ ${sortJoin}
1066
1160
  WHERE (${whereClause})
1067
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}`;
1161
+ ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
1068
1162
  }
1069
1163
  else {
1070
1164
  query = `SELECT
@@ -1079,13 +1173,15 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1079
1173
  FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${eTable}
1080
1174
  LEFT JOIN ${SQLite3Driver.escape(this.prefix + 'data_' + etype)} ${dTable} USING ("guid")
1081
1175
  INNER JOIN ${SQLite3Driver.escape(this.prefix + 'comparisons_' + etype)} ${cTable} USING ("guid", "name")
1176
+ ${sortJoin}
1082
1177
  INNER JOIN (
1083
1178
  SELECT "guid"
1084
1179
  FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
1180
+ ${sortJoin}
1085
1181
  WHERE (${whereClause})
1086
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}
1182
+ ORDER BY ${sortByInner}${limit}${offset}
1087
1183
  ) ${fTable} USING ("guid")
1088
- ORDER BY ${eTable}.${sortBy}`;
1184
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1089
1185
  }
1090
1186
  }
1091
1187
  }
@@ -1117,7 +1213,8 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1117
1213
  else if (options.return === 'guid') {
1118
1214
  query = `SELECT "guid"
1119
1215
  FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
1120
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}`;
1216
+ ${sortJoin}
1217
+ ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
1121
1218
  }
1122
1219
  else {
1123
1220
  if (limit || offset) {
@@ -1133,12 +1230,14 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1133
1230
  FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${eTable}
1134
1231
  LEFT JOIN ${SQLite3Driver.escape(this.prefix + 'data_' + etype)} ${dTable} USING ("guid")
1135
1232
  INNER JOIN ${SQLite3Driver.escape(this.prefix + 'comparisons_' + etype)} c USING ("guid", "name")
1233
+ ${sortJoin}
1136
1234
  INNER JOIN (
1137
1235
  SELECT "guid"
1138
1236
  FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${ieTable}
1139
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}
1237
+ ${sortJoin}
1238
+ ORDER BY ${sortByInner}${limit}${offset}
1140
1239
  ) ${fTable} USING ("guid")
1141
- ORDER BY ${eTable}.${sortBy}`;
1240
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1142
1241
  }
1143
1242
  else {
1144
1243
  query = `SELECT
@@ -1153,7 +1252,8 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1153
1252
  FROM ${SQLite3Driver.escape(this.prefix + 'entities_' + etype)} ${eTable}
1154
1253
  LEFT JOIN ${SQLite3Driver.escape(this.prefix + 'data_' + etype)} ${dTable} USING ("guid")
1155
1254
  INNER JOIN ${SQLite3Driver.escape(this.prefix + 'comparisons_' + etype)} ${cTable} USING ("guid", "name")
1156
- ORDER BY ${eTable}.${sortBy}`;
1255
+ ${sortJoin}
1256
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1157
1257
  }
1158
1258
  }
1159
1259
  }
@@ -1175,9 +1275,6 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1175
1275
  };
1176
1276
  }
1177
1277
  async getEntities(options = {}, ...selectors) {
1178
- return this.getEntitiesSync(options, ...selectors);
1179
- }
1180
- getEntitiesSync(options = {}, ...selectors) {
1181
1278
  const { result, process } = this.getEntitesRowLike(options, selectors, (options, formattedSelectors, etype) => this.performQuery(options, formattedSelectors, etype), () => {
1182
1279
  const next = result.next();
1183
1280
  return next.done ? null : next.value;
@@ -1211,7 +1308,6 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1211
1308
  return result?.cur_uid ?? null;
1212
1309
  }
1213
1310
  async import(filename) {
1214
- this.checkReadOnlyMode();
1215
1311
  try {
1216
1312
  return this.importFromFile(filename, async (guid, tags, sdata, etype) => {
1217
1313
  this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} WHERE "guid"=@guid;`, {
@@ -1317,7 +1413,6 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1317
1413
  if (name == null) {
1318
1414
  throw new nymph_1.InvalidParametersError('Name not given for UID.');
1319
1415
  }
1320
- this.checkReadOnlyMode();
1321
1416
  await this.startTransaction('nymph-newuid');
1322
1417
  try {
1323
1418
  let curUid = this.queryGet(`SELECT "cur_uid" FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
@@ -1355,28 +1450,35 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1355
1450
  if (oldName == null || newName == null) {
1356
1451
  throw new nymph_1.InvalidParametersError('Name not given for UID.');
1357
1452
  }
1358
- this.checkReadOnlyMode();
1453
+ await this.startTransaction('nymph-rename-uid');
1359
1454
  this.queryRun(`UPDATE ${SQLite3Driver.escape(`${this.prefix}uids`)} SET "name"=@newName WHERE "name"=@oldName;`, {
1360
1455
  params: {
1361
1456
  newName,
1362
1457
  oldName,
1363
1458
  },
1364
1459
  });
1460
+ await this.commit('nymph-rename-uid');
1365
1461
  return true;
1366
1462
  }
1367
1463
  async rollback(name) {
1368
1464
  if (name == null || typeof name !== 'string' || name.length === 0) {
1369
1465
  throw new nymph_1.InvalidParametersError('Transaction rollback attempted without a name.');
1370
1466
  }
1371
- if (this.transactionsStarted === 0) {
1467
+ if (this.store.transactionsStarted === 0) {
1372
1468
  return true;
1373
1469
  }
1374
1470
  this.queryRun(`ROLLBACK TO SAVEPOINT ${SQLite3Driver.escape(name)};`);
1375
- this.transactionsStarted--;
1471
+ this.store.transactionsStarted--;
1472
+ if (this.store.transactionsStarted === 0 &&
1473
+ this.store.linkWrite &&
1474
+ !this.config.explicitWrite) {
1475
+ this.store.linkWrite.exec('PRAGMA optimize;');
1476
+ this.store.linkWrite.close();
1477
+ this.store.linkWrite = undefined;
1478
+ }
1376
1479
  return true;
1377
1480
  }
1378
1481
  async saveEntity(entity) {
1379
- this.checkReadOnlyMode();
1380
1482
  const insertData = (guid, data, sdata, etype) => {
1381
1483
  const runInsertQuery = (name, value, svalue) => {
1382
1484
  if (value === undefined) {
@@ -1426,6 +1528,10 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1426
1528
  };
1427
1529
  try {
1428
1530
  return this.saveEntityRowLike(entity, async (_entity, guid, tags, data, sdata, cdate, etype) => {
1531
+ if (Object.keys(data).length === 0 &&
1532
+ Object.keys(sdata).length === 0) {
1533
+ return false;
1534
+ }
1429
1535
  this.queryRun(`INSERT INTO ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} ("guid", "tags", "cdate", "mdate") VALUES (@guid, @tags, @cdate, @cdate);`, {
1430
1536
  etypes: [etype],
1431
1537
  params: {
@@ -1437,6 +1543,10 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1437
1543
  insertData(guid, data, sdata, etype);
1438
1544
  return true;
1439
1545
  }, async (entity, guid, tags, data, sdata, mdate, etype) => {
1546
+ if (Object.keys(data).length === 0 &&
1547
+ Object.keys(sdata).length === 0) {
1548
+ return false;
1549
+ }
1440
1550
  const info = this.queryRun(`UPDATE ${SQLite3Driver.escape(`${this.prefix}entities_${etype}`)} SET "tags"=@tags, "mdate"=@mdate WHERE "guid"=@guid AND "mdate" <= @emdate;`, {
1441
1551
  etypes: [etype],
1442
1552
  params: {
@@ -1491,7 +1601,7 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1491
1601
  if (name == null) {
1492
1602
  throw new nymph_1.InvalidParametersError('Name not given for UID.');
1493
1603
  }
1494
- this.checkReadOnlyMode();
1604
+ await this.startTransaction('nymph-set-uid');
1495
1605
  this.queryRun(`DELETE FROM ${SQLite3Driver.escape(`${this.prefix}uids`)} WHERE "name"=@name;`, {
1496
1606
  params: {
1497
1607
  name,
@@ -1503,14 +1613,18 @@ class SQLite3Driver extends nymph_1.NymphDriver {
1503
1613
  curUid,
1504
1614
  },
1505
1615
  });
1616
+ await this.commit('nymph-set-uid');
1506
1617
  return true;
1507
1618
  }
1508
1619
  async startTransaction(name) {
1509
1620
  if (name == null || typeof name !== 'string' || name.length === 0) {
1510
1621
  throw new nymph_1.InvalidParametersError('Transaction start attempted without a name.');
1511
1622
  }
1623
+ if (!this.config.explicitWrite && !this.store.linkWrite) {
1624
+ this._connect(true);
1625
+ }
1512
1626
  this.queryRun(`SAVEPOINT ${SQLite3Driver.escape(name)};`);
1513
- this.transactionsStarted++;
1627
+ this.store.transactionsStarted++;
1514
1628
  return this.nymph;
1515
1629
  }
1516
1630
  }