@nymphjs/driver-sqlite3 1.0.0-beta.2 → 1.0.0-beta.21

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.
@@ -21,16 +21,25 @@ import {
21
21
  SQLite3DriverConfigDefaults as defaults,
22
22
  } from './conf';
23
23
 
24
+ class InternalStore {
25
+ public link: SQLite3.Database;
26
+ public linkWrite?: SQLite3.Database;
27
+ public connected: boolean = false;
28
+ public transactionsStarted = 0;
29
+
30
+ constructor(link: SQLite3.Database) {
31
+ this.link = link;
32
+ }
33
+ }
34
+
24
35
  /**
25
36
  * The SQLite3 Nymph database driver.
26
37
  */
27
38
  export default class SQLite3Driver extends NymphDriver {
28
39
  public config: SQLite3DriverConfig;
29
40
  protected prefix: string;
30
- protected connected: boolean = false;
31
41
  // @ts-ignore: this is assigned in connect(), which is called by the constructor.
32
- protected link: SQLite3.Database;
33
- protected transactionsStarted = 0;
42
+ protected store: InternalStore;
34
43
 
35
44
  static escape(input: string) {
36
45
  if (input.indexOf('\x00') !== -1) {
@@ -42,11 +51,27 @@ export default class SQLite3Driver extends NymphDriver {
42
51
  return '"' + input.replace(/"/g, () => '""') + '"';
43
52
  }
44
53
 
45
- constructor(config: Partial<SQLite3DriverConfig>) {
54
+ constructor(config: Partial<SQLite3DriverConfig>, store?: InternalStore) {
46
55
  super();
47
56
  this.config = { ...defaults, ...config };
57
+ if (this.config.filename === ':memory:') {
58
+ this.config.explicitWrite = true;
59
+ }
48
60
  this.prefix = this.config.prefix;
49
- this.connect();
61
+ if (store) {
62
+ this.store = store;
63
+ } else {
64
+ this.connect();
65
+ }
66
+ }
67
+
68
+ /**
69
+ * This is used internally by Nymph. Don't call it yourself.
70
+ *
71
+ * @returns A clone of this instance.
72
+ */
73
+ public clone() {
74
+ return new SQLite3Driver(this.config, this.store);
50
75
  }
51
76
 
52
77
  /**
@@ -54,41 +79,104 @@ export default class SQLite3Driver extends NymphDriver {
54
79
  *
55
80
  * @returns Whether this instance is connected to a SQLite3 database.
56
81
  */
57
- public async connect() {
58
- const { filename, fileMustExist, timeout, readonly, verbose } = this.config;
82
+ public connect() {
83
+ if (this.store && this.store.connected) {
84
+ return Promise.resolve(true);
85
+ }
86
+
59
87
  // Connecting
60
- if (!this.connected) {
88
+ this._connect(false);
89
+
90
+ return Promise.resolve(this.store.connected);
91
+ }
92
+
93
+ private _connect(write: boolean) {
94
+ const { filename, fileMustExist, timeout, explicitWrite, wal, verbose } =
95
+ this.config;
96
+
97
+ try {
98
+ const setOptions = (link: SQLite3.Database) => {
99
+ // Set database and connection options.
100
+ if (wal) {
101
+ link.pragma('journal_mode = WAL;');
102
+ }
103
+ link.pragma('encoding = "UTF-8";');
104
+ link.pragma('foreign_keys = 1;');
105
+ link.pragma('case_sensitive_like = 1;');
106
+ // Create the preg_match and regexp functions.
107
+ link.function('regexp', { deterministic: true }, ((
108
+ pattern: string,
109
+ subject: string
110
+ ) => (this.posixRegexMatch(pattern, subject) ? 1 : 0)) as (
111
+ ...params: any[]
112
+ ) => any);
113
+ };
114
+
115
+ let link: SQLite3.Database;
61
116
  try {
62
- this.link = new SQLite3(filename, {
63
- readonly,
117
+ link = new SQLite3(filename, {
118
+ readonly: !explicitWrite && !write,
64
119
  fileMustExist,
65
120
  timeout,
66
121
  verbose,
67
122
  });
68
- this.connected = true;
69
- // Set database and connection options.
70
- this.link.pragma('encoding = "UTF-8";');
71
- this.link.pragma('foreign_keys = 1;');
72
- this.link.pragma('case_sensitive_like = 1;');
73
- // Create the preg_match and regexp functions.
74
- this.link.function(
75
- 'regexp',
76
- { deterministic: true },
77
- (pattern: string, subject: string) =>
78
- this.posixRegexMatch(pattern, subject) ? 1 : 0
79
- );
80
123
  } catch (e: any) {
81
- this.connected = false;
82
- if (filename === ':memory:') {
83
- throw new NotConfiguredError(
84
- "It seems the config hasn't been set up correctly."
85
- );
124
+ if (
125
+ e.code === 'SQLITE_CANTOPEN' &&
126
+ !explicitWrite &&
127
+ !write &&
128
+ !this.config.fileMustExist
129
+ ) {
130
+ // This happens when the file doesn't exist and we attempt to open it
131
+ // readonly.
132
+ // First open it in write mode.
133
+ const writeLink = new SQLite3(filename, {
134
+ readonly: false,
135
+ fileMustExist,
136
+ timeout,
137
+ verbose,
138
+ });
139
+ setOptions(writeLink);
140
+ writeLink.close();
141
+ // Now open in readonly.
142
+ link = new SQLite3(filename, {
143
+ readonly: true,
144
+ fileMustExist,
145
+ timeout,
146
+ verbose,
147
+ });
86
148
  } else {
87
- throw new UnableToConnectError('Could not connect: ' + e?.message);
149
+ throw e;
150
+ }
151
+ }
152
+
153
+ if (!this.store) {
154
+ if (write) {
155
+ throw new Error(
156
+ 'Tried to open in write without opening in read first.'
157
+ );
88
158
  }
159
+ this.store = new InternalStore(link);
160
+ } else if (write) {
161
+ this.store.linkWrite = link;
162
+ } else {
163
+ this.store.link = link;
164
+ }
165
+ this.store.connected = true;
166
+ setOptions(link);
167
+ } catch (e: any) {
168
+ if (this.store) {
169
+ this.store.connected = false;
170
+ }
171
+ if (filename === ':memory:') {
172
+ throw new NotConfiguredError(
173
+ "It seems the config hasn't been set up correctly. Could not connect: " +
174
+ e?.message
175
+ );
176
+ } else {
177
+ throw new UnableToConnectError('Could not connect: ' + e?.message);
89
178
  }
90
179
  }
91
- return this.connected;
92
180
  }
93
181
 
94
182
  /**
@@ -97,16 +185,24 @@ export default class SQLite3Driver extends NymphDriver {
97
185
  * @returns Whether this instance is connected to a SQLite3 database.
98
186
  */
99
187
  public async disconnect() {
100
- if (this.connected) {
101
- this.link.exec('PRAGMA optimize;');
102
- this.link.close();
103
- this.connected = false;
188
+ if (this.store.connected) {
189
+ if (this.store.linkWrite && !this.config.explicitWrite) {
190
+ this.store.linkWrite.exec('PRAGMA optimize;');
191
+ this.store.linkWrite.close();
192
+ this.store.linkWrite = undefined;
193
+ }
194
+ if (this.config.explicitWrite) {
195
+ this.store.link.exec('PRAGMA optimize;');
196
+ }
197
+ this.store.link.close();
198
+ this.store.transactionsStarted = 0;
199
+ this.store.connected = false;
104
200
  }
105
- return this.connected;
201
+ return this.store.connected;
106
202
  }
107
203
 
108
204
  public async inTransaction() {
109
- return this.transactionsStarted > 0;
205
+ return this.store.transactionsStarted > 0;
110
206
  }
111
207
 
112
208
  /**
@@ -115,18 +211,7 @@ export default class SQLite3Driver extends NymphDriver {
115
211
  * @returns Whether this instance is connected to a SQLite3 database.
116
212
  */
117
213
  public isConnected() {
118
- return this.connected;
119
- }
120
-
121
- /**
122
- * Check if SQLite3 DB is read only and throw error if so.
123
- */
124
- private checkReadOnlyMode() {
125
- if (this.config.readonly) {
126
- throw new InvalidParametersError(
127
- 'Attempt to write to SQLite3 DB in read only mode.'
128
- );
129
- }
214
+ return this.store.connected;
130
215
  }
131
216
 
132
217
  /**
@@ -135,7 +220,6 @@ export default class SQLite3Driver extends NymphDriver {
135
220
  * @param etype The entity type to create a table for. If this is blank, the default tables are created.
136
221
  */
137
222
  private createTables(etype: string | null = null) {
138
- this.checkReadOnlyMode();
139
223
  this.startTransaction('nymph-tablecreation');
140
224
  try {
141
225
  if (etype != null) {
@@ -326,7 +410,10 @@ export default class SQLite3Driver extends NymphDriver {
326
410
  }: { etypes?: string[]; params?: { [k: string]: any } } = {}
327
411
  ) {
328
412
  return this.query(
329
- () => this.link.prepare(query).iterate(params),
413
+ () =>
414
+ (this.store.linkWrite || this.store.link)
415
+ .prepare(query)
416
+ .iterate(params),
330
417
  `${query} -- ${JSON.stringify(params)}`,
331
418
  etypes
332
419
  );
@@ -340,7 +427,8 @@ export default class SQLite3Driver extends NymphDriver {
340
427
  }: { etypes?: string[]; params?: { [k: string]: any } } = {}
341
428
  ) {
342
429
  return this.query(
343
- () => this.link.prepare(query).get(params),
430
+ () =>
431
+ (this.store.linkWrite || this.store.link).prepare(query).get(params),
344
432
  `${query} -- ${JSON.stringify(params)}`,
345
433
  etypes
346
434
  );
@@ -354,7 +442,8 @@ export default class SQLite3Driver extends NymphDriver {
354
442
  }: { etypes?: string[]; params?: { [k: string]: any } } = {}
355
443
  ) {
356
444
  return this.query(
357
- () => this.link.prepare(query).run(params),
445
+ () =>
446
+ (this.store.linkWrite || this.store.link).prepare(query).run(params),
358
447
  `${query} -- ${JSON.stringify(params)}`,
359
448
  etypes
360
449
  );
@@ -366,11 +455,22 @@ export default class SQLite3Driver extends NymphDriver {
366
455
  'Transaction commit attempted without a name.'
367
456
  );
368
457
  }
369
- if (this.transactionsStarted === 0) {
458
+ if (this.store.transactionsStarted === 0) {
370
459
  return true;
371
460
  }
372
461
  this.queryRun(`RELEASE SAVEPOINT ${SQLite3Driver.escape(name)};`);
373
- this.transactionsStarted--;
462
+ this.store.transactionsStarted--;
463
+
464
+ if (
465
+ this.store.transactionsStarted === 0 &&
466
+ this.store.linkWrite &&
467
+ !this.config.explicitWrite
468
+ ) {
469
+ this.store.linkWrite.exec('PRAGMA optimize;');
470
+ this.store.linkWrite.close();
471
+ this.store.linkWrite = undefined;
472
+ }
473
+
374
474
  return true;
375
475
  }
376
476
 
@@ -386,7 +486,6 @@ export default class SQLite3Driver extends NymphDriver {
386
486
  EntityClass = className;
387
487
  }
388
488
  const etype = EntityClass.ETYPE;
389
- this.checkReadOnlyMode();
390
489
  await this.startTransaction('nymph-delete');
391
490
  try {
392
491
  this.queryRun(
@@ -449,7 +548,7 @@ export default class SQLite3Driver extends NymphDriver {
449
548
  if (!name) {
450
549
  throw new InvalidParametersError('Name not given for UID');
451
550
  }
452
- this.checkReadOnlyMode();
551
+ await this.startTransaction('nymph-delete-uid');
453
552
  this.queryRun(
454
553
  `DELETE FROM ${SQLite3Driver.escape(
455
554
  `${this.prefix}uids`
@@ -460,6 +559,7 @@ export default class SQLite3Driver extends NymphDriver {
460
559
  },
461
560
  }
462
561
  );
562
+ await this.commit('nymph-delete-uid');
463
563
  return true;
464
564
  }
465
565
 
@@ -477,7 +577,7 @@ export default class SQLite3Driver extends NymphDriver {
477
577
  writeLine('');
478
578
 
479
579
  // Export UIDs.
480
- let uids = this.queryIter(
580
+ let uids: IterableIterator<any> = this.queryIter(
481
581
  `SELECT * FROM ${SQLite3Driver.escape(
482
582
  `${this.prefix}uids`
483
583
  )} ORDER BY "name";`
@@ -493,7 +593,7 @@ export default class SQLite3Driver extends NymphDriver {
493
593
  writeLine('');
494
594
 
495
595
  // Get the etypes.
496
- const tables = this.queryIter(
596
+ const tables: IterableIterator<any> = this.queryIter(
497
597
  "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;"
498
598
  );
499
599
  const etypes = [];
@@ -505,7 +605,7 @@ export default class SQLite3Driver extends NymphDriver {
505
605
 
506
606
  for (const etype of etypes) {
507
607
  // Export entities.
508
- const dataIterator = this.queryIter(
608
+ const dataIterator: IterableIterator<any> = this.queryIter(
509
609
  `SELECT e.*, d."name" AS "dname", d."value" AS "dvalue", c."string", c."number" FROM ${SQLite3Driver.escape(
510
610
  `${this.prefix}entities_${etype}`
511
611
  )} e LEFT JOIN ${SQLite3Driver.escape(
@@ -574,6 +674,7 @@ export default class SQLite3Driver extends NymphDriver {
574
674
  const cTable = `c${tableSuffix}`;
575
675
  const fTable = `f${tableSuffix}`;
576
676
  const ieTable = `ie${tableSuffix}`;
677
+ const sTable = `s${tableSuffix}`;
577
678
  const sort = options.sort ?? 'cdate';
578
679
  const queryParts = this.iterateSelectorsForQuery(
579
680
  formattedSelectors,
@@ -1319,18 +1420,31 @@ export default class SQLite3Driver extends NymphDriver {
1319
1420
  );
1320
1421
 
1321
1422
  let sortBy: string;
1423
+ let sortByInner: string;
1424
+ let sortJoin = '';
1425
+ const order = options.reverse ? ' DESC' : '';
1322
1426
  switch (sort) {
1323
1427
  case 'mdate':
1324
- sortBy = '"mdate"';
1428
+ sortBy = `${eTable}."mdate"${order}`;
1429
+ sortByInner = `${ieTable}."mdate"${order}`;
1325
1430
  break;
1326
1431
  case 'cdate':
1432
+ sortBy = `${eTable}."cdate"${order}`;
1433
+ sortByInner = `${ieTable}."cdate"${order}`;
1434
+ break;
1327
1435
  default:
1328
- sortBy = '"cdate"';
1436
+ const name = `param${++count.i}`;
1437
+ sortJoin = `LEFT JOIN (
1438
+ SELECT "guid", "string", "number"
1439
+ FROM ${SQLite3Driver.escape(this.prefix + 'comparisons_' + etype)}
1440
+ WHERE "name"=@${name}
1441
+ ORDER BY "number"${order}, "string"${order}
1442
+ ) ${sTable} USING ("guid")`;
1443
+ sortBy = `${sTable}."number"${order}, ${sTable}."string"${order}`;
1444
+ sortByInner = sortBy;
1445
+ params[name] = sort;
1329
1446
  break;
1330
1447
  }
1331
- if (options.reverse) {
1332
- sortBy += ' DESC';
1333
- }
1334
1448
 
1335
1449
  let query: string;
1336
1450
  if (queryParts.length) {
@@ -1367,8 +1481,9 @@ export default class SQLite3Driver extends NymphDriver {
1367
1481
  FROM ${SQLite3Driver.escape(
1368
1482
  this.prefix + 'entities_' + etype
1369
1483
  )} ${ieTable}
1484
+ ${sortJoin}
1370
1485
  WHERE (${whereClause})
1371
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}`;
1486
+ ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
1372
1487
  } else {
1373
1488
  query = `SELECT
1374
1489
  ${eTable}."guid",
@@ -1388,15 +1503,17 @@ export default class SQLite3Driver extends NymphDriver {
1388
1503
  INNER JOIN ${SQLite3Driver.escape(
1389
1504
  this.prefix + 'comparisons_' + etype
1390
1505
  )} ${cTable} USING ("guid", "name")
1506
+ ${sortJoin}
1391
1507
  INNER JOIN (
1392
1508
  SELECT "guid"
1393
1509
  FROM ${SQLite3Driver.escape(
1394
1510
  this.prefix + 'entities_' + etype
1395
1511
  )} ${ieTable}
1512
+ ${sortJoin}
1396
1513
  WHERE (${whereClause})
1397
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}
1514
+ ORDER BY ${sortByInner}${limit}${offset}
1398
1515
  ) ${fTable} USING ("guid")
1399
- ORDER BY ${eTable}.${sortBy}`;
1516
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1400
1517
  }
1401
1518
  }
1402
1519
  } else {
@@ -1430,7 +1547,8 @@ export default class SQLite3Driver extends NymphDriver {
1430
1547
  FROM ${SQLite3Driver.escape(
1431
1548
  this.prefix + 'entities_' + etype
1432
1549
  )} ${ieTable}
1433
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}`;
1550
+ ${sortJoin}
1551
+ ORDER BY ${sortByInner}, "guid"${limit}${offset}`;
1434
1552
  } else {
1435
1553
  if (limit || offset) {
1436
1554
  query = `SELECT
@@ -1451,14 +1569,16 @@ export default class SQLite3Driver extends NymphDriver {
1451
1569
  INNER JOIN ${SQLite3Driver.escape(
1452
1570
  this.prefix + 'comparisons_' + etype
1453
1571
  )} c USING ("guid", "name")
1572
+ ${sortJoin}
1454
1573
  INNER JOIN (
1455
1574
  SELECT "guid"
1456
1575
  FROM ${SQLite3Driver.escape(
1457
1576
  this.prefix + 'entities_' + etype
1458
1577
  )} ${ieTable}
1459
- ORDER BY ${ieTable}.${sortBy}${limit}${offset}
1578
+ ${sortJoin}
1579
+ ORDER BY ${sortByInner}${limit}${offset}
1460
1580
  ) ${fTable} USING ("guid")
1461
- ORDER BY ${eTable}.${sortBy}`;
1581
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1462
1582
  } else {
1463
1583
  query = `SELECT
1464
1584
  ${eTable}."guid",
@@ -1478,7 +1598,8 @@ export default class SQLite3Driver extends NymphDriver {
1478
1598
  INNER JOIN ${SQLite3Driver.escape(
1479
1599
  this.prefix + 'comparisons_' + etype
1480
1600
  )} ${cTable} USING ("guid", "name")
1481
- ORDER BY ${eTable}.${sortBy}`;
1601
+ ${sortJoin}
1602
+ ORDER BY ${sortBy}, ${eTable}."guid"`;
1482
1603
  }
1483
1604
  }
1484
1605
  }
@@ -1575,14 +1696,18 @@ export default class SQLite3Driver extends NymphDriver {
1575
1696
  : row.value,
1576
1697
  })
1577
1698
  );
1578
- return process();
1699
+ const value = process();
1700
+ if (value instanceof Error) {
1701
+ throw value;
1702
+ }
1703
+ return value;
1579
1704
  }
1580
1705
 
1581
1706
  public async getUID(name: string) {
1582
1707
  if (name == null) {
1583
1708
  throw new InvalidParametersError('Name not given for UID.');
1584
1709
  }
1585
- const result = this.queryGet(
1710
+ const result: any = this.queryGet(
1586
1711
  `SELECT "cur_uid" FROM ${SQLite3Driver.escape(
1587
1712
  `${this.prefix}uids`
1588
1713
  )} WHERE "name"=@name;`,
@@ -1596,7 +1721,6 @@ export default class SQLite3Driver extends NymphDriver {
1596
1721
  }
1597
1722
 
1598
1723
  public async import(filename: string) {
1599
- this.checkReadOnlyMode();
1600
1724
  try {
1601
1725
  return this.importFromFile(
1602
1726
  filename,
@@ -1759,19 +1883,20 @@ export default class SQLite3Driver extends NymphDriver {
1759
1883
  if (name == null) {
1760
1884
  throw new InvalidParametersError('Name not given for UID.');
1761
1885
  }
1762
- this.checkReadOnlyMode();
1763
1886
  await this.startTransaction('nymph-newuid');
1764
1887
  try {
1765
1888
  let curUid =
1766
- this.queryGet(
1767
- `SELECT "cur_uid" FROM ${SQLite3Driver.escape(
1768
- `${this.prefix}uids`
1769
- )} WHERE "name"=@name;`,
1770
- {
1771
- params: {
1772
- name,
1773
- },
1774
- }
1889
+ (
1890
+ this.queryGet(
1891
+ `SELECT "cur_uid" FROM ${SQLite3Driver.escape(
1892
+ `${this.prefix}uids`
1893
+ )} WHERE "name"=@name;`,
1894
+ {
1895
+ params: {
1896
+ name,
1897
+ },
1898
+ }
1899
+ ) as any
1775
1900
  )?.cur_uid ?? null;
1776
1901
  if (curUid == null) {
1777
1902
  curUid = 1;
@@ -1812,7 +1937,7 @@ export default class SQLite3Driver extends NymphDriver {
1812
1937
  if (oldName == null || newName == null) {
1813
1938
  throw new InvalidParametersError('Name not given for UID.');
1814
1939
  }
1815
- this.checkReadOnlyMode();
1940
+ await this.startTransaction('nymph-rename-uid');
1816
1941
  this.queryRun(
1817
1942
  `UPDATE ${SQLite3Driver.escape(
1818
1943
  `${this.prefix}uids`
@@ -1824,6 +1949,7 @@ export default class SQLite3Driver extends NymphDriver {
1824
1949
  },
1825
1950
  }
1826
1951
  );
1952
+ await this.commit('nymph-rename-uid');
1827
1953
  return true;
1828
1954
  }
1829
1955
 
@@ -1833,16 +1959,26 @@ export default class SQLite3Driver extends NymphDriver {
1833
1959
  'Transaction rollback attempted without a name.'
1834
1960
  );
1835
1961
  }
1836
- if (this.transactionsStarted === 0) {
1962
+ if (this.store.transactionsStarted === 0) {
1837
1963
  return true;
1838
1964
  }
1839
1965
  this.queryRun(`ROLLBACK TO SAVEPOINT ${SQLite3Driver.escape(name)};`);
1840
- this.transactionsStarted--;
1966
+ this.store.transactionsStarted--;
1967
+
1968
+ if (
1969
+ this.store.transactionsStarted === 0 &&
1970
+ this.store.linkWrite &&
1971
+ !this.config.explicitWrite
1972
+ ) {
1973
+ this.store.linkWrite.exec('PRAGMA optimize;');
1974
+ this.store.linkWrite.close();
1975
+ this.store.linkWrite = undefined;
1976
+ }
1977
+
1841
1978
  return true;
1842
1979
  }
1843
1980
 
1844
1981
  public async saveEntity(entity: EntityInterface) {
1845
- this.checkReadOnlyMode();
1846
1982
  const insertData = (
1847
1983
  guid: string,
1848
1984
  data: EntityData,
@@ -2008,7 +2144,7 @@ export default class SQLite3Driver extends NymphDriver {
2008
2144
  if (name == null) {
2009
2145
  throw new InvalidParametersError('Name not given for UID.');
2010
2146
  }
2011
- this.checkReadOnlyMode();
2147
+ await this.startTransaction('nymph-set-uid');
2012
2148
  this.queryRun(
2013
2149
  `DELETE FROM ${SQLite3Driver.escape(
2014
2150
  `${this.prefix}uids`
@@ -2030,6 +2166,7 @@ export default class SQLite3Driver extends NymphDriver {
2030
2166
  },
2031
2167
  }
2032
2168
  );
2169
+ await this.commit('nymph-set-uid');
2033
2170
  return true;
2034
2171
  }
2035
2172
 
@@ -2039,8 +2176,11 @@ export default class SQLite3Driver extends NymphDriver {
2039
2176
  'Transaction start attempted without a name.'
2040
2177
  );
2041
2178
  }
2179
+ if (!this.config.explicitWrite && !this.store.linkWrite) {
2180
+ this._connect(true);
2181
+ }
2042
2182
  this.queryRun(`SAVEPOINT ${SQLite3Driver.escape(name)};`);
2043
- this.transactionsStarted++;
2183
+ this.store.transactionsStarted++;
2044
2184
  return this.nymph;
2045
2185
  }
2046
2186
  }
package/src/conf/d.ts CHANGED
@@ -23,9 +23,28 @@ export interface SQLite3DriverConfig {
23
23
  */
24
24
  timeout: number;
25
25
  /**
26
- * Open for readonly, which is needed for PubSub.
26
+ * Open explicitly for writing.
27
+ *
28
+ * By default, the driver will always open the DB as readonly, and attempt to
29
+ * open another link to perform write operations. If you know that only one
30
+ * instance will be writing, you can force the driver to open for writing by
31
+ * default, which will block any other instance from opening it for writing.
32
+ *
33
+ * One thing to note is that starting a transaction is a write operation, so
34
+ * as long as an instance is in a transaction, no other instances can write.
35
+ *
36
+ * PubSub also needs to open the DB, and it only needs read access.
37
+ */
38
+ explicitWrite: boolean;
39
+ /**
40
+ * Turn on WAL mode.
41
+ *
42
+ * This will generally increase performance, but does mean that the DB must be
43
+ * on a local disk.
44
+ *
45
+ * See: https://www.sqlite.org/wal.html
27
46
  */
28
- readonly: boolean;
47
+ wal: boolean;
29
48
  /**
30
49
  * Function that gets called with every SQL string executed.
31
50
  */
@@ -5,6 +5,7 @@ export default {
5
5
  fileMustExist: false,
6
6
  prefix: 'nymph_',
7
7
  timeout: 10000,
8
- readonly: false,
8
+ explicitWrite: false,
9
+ wal: false,
9
10
  verbose: undefined,
10
11
  } as SQLite3DriverConfig;