better-sqlite3-multiple-ciphers 7.4.7-beta.1 → 7.5.0-beta.0
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/README.md +1 -1
- package/deps/download.sh +111 -108
- package/deps/setup.ps1 +9 -11
- package/deps/sqlite3/sqlite3.c +272560 -0
- package/deps/sqlite3/sqlite3.h +12770 -0
- package/deps/sqlite3/sqlite3ext.h +675 -0
- package/deps/sqlite3.gyp +13 -7
- package/deps/symlink.js +7 -4
- package/lib/database.js +17 -6
- package/lib/sqlite-error.js +1 -2
- package/package.json +10 -4
- package/src/better_sqlite3.cpp +46 -35
- package/src/better_sqlite3.hpp +40 -38
- package/.gitattributes +0 -1
- package/.github/workflows/prebuild.yml +0 -49
- package/.github/workflows/test.yml +0 -59
- package/benchmark/benchmark.js +0 -31
- package/benchmark/drivers.js +0 -21
- package/benchmark/index.js +0 -83
- package/benchmark/seed.js +0 -47
- package/benchmark/trials.js +0 -65
- package/benchmark/types/insert.js +0 -16
- package/benchmark/types/select-all.js +0 -14
- package/benchmark/types/select-iterate.js +0 -23
- package/benchmark/types/select.js +0 -14
- package/benchmark/types/transaction.js +0 -40
- package/deps/extract.js +0 -16
- package/deps/sqlite3.tar.gz +0 -0
- package/docs/api.md +0 -645
- package/docs/benchmark.md +0 -38
- package/docs/compilation.md +0 -76
- package/docs/integer.md +0 -79
- package/docs/performance.md +0 -39
- package/docs/threads.md +0 -97
- package/docs/tips.md +0 -35
- package/docs/troubleshooting.md +0 -23
- package/docs/unsafe.md +0 -16
- package/src/better_sqlite3.lzz +0 -88
- package/src/objects/backup.lzz +0 -138
- package/src/objects/database.lzz +0 -468
- package/src/objects/statement-iterator.lzz +0 -138
- package/src/objects/statement.lzz +0 -323
- package/src/util/bind-map.lzz +0 -73
- package/src/util/binder.lzz +0 -190
- package/src/util/constants.lzz +0 -151
- package/src/util/custom-aggregate.lzz +0 -121
- package/src/util/custom-function.lzz +0 -59
- package/src/util/custom-table.lzz +0 -397
- package/src/util/data-converter.lzz +0 -17
- package/src/util/data.lzz +0 -145
- package/src/util/macros.lzz +0 -159
- package/src/util/query-macros.lzz +0 -71
- package/test/00.setup.js +0 -25
- package/test/01.sqlite-error.js +0 -27
- package/test/10.database.open.js +0 -159
- package/test/11.database.close.js +0 -68
- package/test/12.database.pragma.js +0 -65
- package/test/13.database.prepare.js +0 -60
- package/test/14.database.exec.js +0 -46
- package/test/20.statement.run.js +0 -170
- package/test/21.statement.get.js +0 -109
- package/test/22.statement.all.js +0 -129
- package/test/23.statement.iterate.js +0 -223
- package/test/24.statement.bind.js +0 -107
- package/test/25.statement.columns.js +0 -46
- package/test/30.database.transaction.js +0 -157
- package/test/31.database.checkpoint.js +0 -62
- package/test/32.database.function.js +0 -211
- package/test/33.database.aggregate.js +0 -603
- package/test/34.database.table.js +0 -671
- package/test/35.database.load-extension.js +0 -75
- package/test/36.database.backup.js +0 -240
- package/test/37.database.serialize.js +0 -81
- package/test/40.bigints.js +0 -145
- package/test/41.at-exit.js +0 -52
- package/test/42.integrity.js +0 -531
- package/test/43.verbose.js +0 -100
- package/test/44.worker-threads.js +0 -66
- package/test/45.unsafe-mode.js +0 -52
- package/test/46.encryption.js +0 -69
- package/test/50.misc.js +0 -44
package/docs/benchmark.md
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Benchmark
|
|
2
|
-
|
|
3
|
-
To run the benchmark yourself:
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
git clone https://github.com/JoshuaWise/better-sqlite3.git
|
|
7
|
-
cd better-sqlite3
|
|
8
|
-
npm install # if you're doing this as the root user, --unsafe-perm is required
|
|
9
|
-
node benchmark
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
# Results
|
|
13
|
-
|
|
14
|
-
These results are from 03/29/2020, on a MacBook Pro (Retina, 15-inch, Mid 2014, OSX 10.11.6), using nodejs v12.16.1.
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
--- reading rows individually ---
|
|
18
|
-
better-sqlite3 x 313,899 ops/sec ±0.13%
|
|
19
|
-
node-sqlite3 x 26,780 ops/sec ±2.9%
|
|
20
|
-
|
|
21
|
-
--- reading 100 rows into an array ---
|
|
22
|
-
better-sqlite3 x 8,508 ops/sec ±0.27%
|
|
23
|
-
node-sqlite3 x 2,930 ops/sec ±0.37%
|
|
24
|
-
|
|
25
|
-
--- iterating over 100 rows ---
|
|
26
|
-
better-sqlite3 x 6,532 ops/sec ±0.32%
|
|
27
|
-
node-sqlite3 x 268 ops/sec ±3.4%
|
|
28
|
-
|
|
29
|
-
--- inserting rows individually ---
|
|
30
|
-
better-sqlite3 x 62,554 ops/sec ±7.33%
|
|
31
|
-
node-sqlite3 x 22,637 ops/sec ±4.37%
|
|
32
|
-
|
|
33
|
-
--- inserting 100 rows in a single transaction ---
|
|
34
|
-
better-sqlite3 x 4,141 ops/sec ±4.57%
|
|
35
|
-
node-sqlite3 x 265 ops/sec ±4.87%
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
> All benchmarks are executed in [WAL mode](./performance.md).
|
package/docs/compilation.md
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# Custom configuration
|
|
2
|
-
|
|
3
|
-
If you want to use a customized version of [SQLite3](https://www.sqlite.org) with `better-sqlite3-multiple-ciphers`, you can do so by specifying the directory of your [custom amalgamation](https://www.sqlite.org/amalgamation.html) during installation.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
npm install better-sqlite3-multiple-ciphers --build-from-source --sqlite3=/path/to/sqlite-amalgamation
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
However, if you simply run `npm install` while `better-sqlite3-multiple-ciphers` is listed as a dependency in your `package.json`, the required flags above will *not* be applied. Therefore, it's recommended that you remove `better-sqlite3-multiple-ciphers` from your dependency list, and instead add a [`preinstall` script](https://docs.npmjs.com/misc/scripts) like the one shown below.
|
|
10
|
-
|
|
11
|
-
```json
|
|
12
|
-
{
|
|
13
|
-
"scripts": {
|
|
14
|
-
"preinstall": "npm install better-sqlite3-multiple-ciphers@'^7.0.0' --no-save --build-from-source --sqlite3=\"$(pwd)/sqlite-amalgamation\""
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Your amalgamation directory must contain `sqlite3.c` and `sqlite3.h`. Any desired [compile time options](https://www.sqlite.org/compile.html) must be defined directly within `sqlite3.c`.
|
|
20
|
-
|
|
21
|
-
### Step by step example
|
|
22
|
-
|
|
23
|
-
If you're creating a package that relies on a custom build of `better-sqlite3-multiple-ciphers`, you can follow these steps to get started.
|
|
24
|
-
|
|
25
|
-
1. Download the SQLite3 source code from [their website](https://sqlite.com/download.html) (e.g., `sqlite-amalgamation-1234567.zip`)
|
|
26
|
-
2. Unzip the compressed archive
|
|
27
|
-
3. Move the `sqlite3.c` and `sqlite3.h` files to your project folder
|
|
28
|
-
4. Add a `preinstall` script to your `package.json`, like the one shown above
|
|
29
|
-
5. Make sure the `--sqlite3` flag points to the location of your `sqlite3.c` and `sqlite3.h` files
|
|
30
|
-
6. Define your preferred [compile time options](https://www.sqlite.org/compile.html) at the top of `sqlite3.c`
|
|
31
|
-
7. Run `npm install` in your project folder
|
|
32
|
-
|
|
33
|
-
If you're using a SQLite3 encryption extension that is a drop-in replacement for SQLite3 (such as [SEE](https://www.sqlite.org/see/doc/release/www/readme.wiki) or [sqleet](https://github.com/resilar/sqleet)), then simply replace `sqlite3.c` and `sqlite3.h` with the source files of your encryption extension.
|
|
34
|
-
|
|
35
|
-
# Bundled configuration
|
|
36
|
-
|
|
37
|
-
By default, this distribution currently uses SQLite3 **version 3.37.2** with the following [compilation options](https://www.sqlite.org/compile.html):
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
SQLITE_DQS=0
|
|
41
|
-
SQLITE_LIKE_DOESNT_MATCH_BLOBS
|
|
42
|
-
SQLITE_THREADSAFE=2
|
|
43
|
-
SQLITE_USE_URI=0
|
|
44
|
-
SQLITE_DEFAULT_MEMSTATUS=0
|
|
45
|
-
SQLITE_OMIT_DEPRECATED
|
|
46
|
-
SQLITE_OMIT_GET_TABLE
|
|
47
|
-
SQLITE_OMIT_TCL_VARIABLE
|
|
48
|
-
SQLITE_OMIT_PROGRESS_CALLBACK
|
|
49
|
-
SQLITE_OMIT_SHARED_CACHE
|
|
50
|
-
SQLITE_TRACE_SIZE_LIMIT=32
|
|
51
|
-
SQLITE_DEFAULT_CACHE_SIZE=-16000
|
|
52
|
-
SQLITE_DEFAULT_FOREIGN_KEYS=1
|
|
53
|
-
SQLITE_DEFAULT_WAL_SYNCHRONOUS=1
|
|
54
|
-
SQLITE_ENABLE_MATH_FUNCTIONS
|
|
55
|
-
SQLITE_ENABLE_DESERIALIZE
|
|
56
|
-
SQLITE_ENABLE_COLUMN_METADATA
|
|
57
|
-
SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
|
58
|
-
SQLITE_ENABLE_STAT4
|
|
59
|
-
SQLITE_ENABLE_FTS3_PARENTHESIS
|
|
60
|
-
SQLITE_ENABLE_FTS3
|
|
61
|
-
SQLITE_ENABLE_FTS4
|
|
62
|
-
SQLITE_ENABLE_FTS5
|
|
63
|
-
SQLITE_ENABLE_JSON1
|
|
64
|
-
SQLITE_ENABLE_RTREE
|
|
65
|
-
SQLITE_ENABLE_GEOPOLY
|
|
66
|
-
SQLITE_INTROSPECTION_PRAGMAS
|
|
67
|
-
SQLITE_SOUNDEX
|
|
68
|
-
HAVE_STDINT_H=1
|
|
69
|
-
HAVE_INT8_T=1
|
|
70
|
-
HAVE_INT16_T=1
|
|
71
|
-
HAVE_INT32_T=1
|
|
72
|
-
HAVE_UINT8_T=1
|
|
73
|
-
HAVE_UINT16_T=1
|
|
74
|
-
HAVE_UINT32_T=1
|
|
75
|
-
SQLITE_USER_AUTHENTICATION=0
|
|
76
|
-
```
|
package/docs/integer.md
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# The `BigInt` primitive type
|
|
2
|
-
|
|
3
|
-
SQLite3 can store data in 64-bit signed integers, which are too big for JavaScript's [number format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) to fully represent. To support this data type, `better-sqlite3` is fully compatible with [BigInts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).
|
|
4
|
-
|
|
5
|
-
```js
|
|
6
|
-
const big = BigInt('1152735103331642317');
|
|
7
|
-
big === 1152735103331642317n; // returns true
|
|
8
|
-
big.toString(); // returns "1152735103331642317"
|
|
9
|
-
typeof big; // returns "bigint"
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Binding BigInts
|
|
13
|
-
|
|
14
|
-
`BigInts` can bind to [`Statements`](./api.md#class-statement) just like regular numbers. You can also return `BigInts` from [user-defined functions](./api.md#functionname-options-function---this). However, if you provide a `BigInt` that's too large to be a 64-bit signed integer, you'll get an error so that data integrity is protected.
|
|
15
|
-
|
|
16
|
-
```js
|
|
17
|
-
db.prepare("SELECT * FROM users WHERE id=?").get(BigInt('1152735103331642317'));
|
|
18
|
-
db.prepare("INSERT INTO users (id) VALUES (?)").run(BigInt('1152735103331642317'));
|
|
19
|
-
|
|
20
|
-
db.prepare("SELECT ?").get(2n ** 63n - 1n); // returns successfully
|
|
21
|
-
db.prepare("SELECT ?").get(2n ** 63n); // throws a RangeError
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Getting BigInts from the database
|
|
25
|
-
|
|
26
|
-
By default, integers returned from the database (including the [`info.lastInsertRowid`](./api.md#runbindparameters---object) property) are normal JavaScript numbers. You can change this default as you please:
|
|
27
|
-
|
|
28
|
-
```js
|
|
29
|
-
db.defaultSafeIntegers(); // BigInts by default
|
|
30
|
-
db.defaultSafeIntegers(true); // BigInts by default
|
|
31
|
-
db.defaultSafeIntegers(false); // Numbers by default
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Additionally, you can override the default for individual [`Statements`](./api.md#class-statement) like so:
|
|
35
|
-
|
|
36
|
-
```js
|
|
37
|
-
const stmt = db.prepare(SQL);
|
|
38
|
-
|
|
39
|
-
stmt.safeIntegers(); // Safe integers ON
|
|
40
|
-
stmt.safeIntegers(true); // Safe integers ON
|
|
41
|
-
stmt.safeIntegers(false); // Safe integers OFF
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
[User-defined functions](./api.md#functionname-options-function---this) can receive `BigInts` as arguments. You can override the database's default setting like so:
|
|
45
|
-
|
|
46
|
-
```js
|
|
47
|
-
db.function('isInt', { safeIntegers: true }, (value) => {
|
|
48
|
-
return String(typeof value === 'bigint');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
db.prepare('SELECT isInt(?)').pluck().get(10); // => "false"
|
|
52
|
-
db.prepare('SELECT isInt(?)').pluck().get(10n); // => "true"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Likewise, [user-defined aggregates](./api.md#aggregatename-options---this) and [virtual tables](./api.md#tablename-definition---this) can also receive `BigInts` as arguments:
|
|
56
|
-
|
|
57
|
-
```js
|
|
58
|
-
db.aggregate('addInts', {
|
|
59
|
-
safeIntegers: true,
|
|
60
|
-
start: 0n,
|
|
61
|
-
step: (total, nextValue) => total + nextValue,
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
```js
|
|
66
|
-
db.table('sequence', {
|
|
67
|
-
safeIntegers: true,
|
|
68
|
-
columns: ['value'],
|
|
69
|
-
parameters: ['length', 'start'],
|
|
70
|
-
rows: function* (length, start = 0n) {
|
|
71
|
-
const end = start + length;
|
|
72
|
-
for (let n = start; n < end; ++n) {
|
|
73
|
-
yield { value: n };
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
It's worth noting that REAL (FLOAT) values returned from the database will always be represented as normal numbers.
|
package/docs/performance.md
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Performance
|
|
2
|
-
|
|
3
|
-
Concurrently reading and writing from an SQLite3 database can be very slow in some cases. Since concurrency is usually very important in web applications, it's recommended to turn on [WAL mode](https://www.sqlite.org/wal.html) to greatly increase overall performance.
|
|
4
|
-
|
|
5
|
-
```js
|
|
6
|
-
db.pragma('journal_mode = WAL');
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
WAL mode has a *few* disadvantages to consider:
|
|
10
|
-
|
|
11
|
-
- Transactions that involve ATTACHed databases are atomic for each individual database, but are not atomic across all databases as a set.
|
|
12
|
-
- Under rare circumstances, the [WAL file](https://www.sqlite.org/wal.html) may experience "checkpoint starvation" (see below).
|
|
13
|
-
- There are some hardware/system limitations that may affect some users, [listed here](https://www.sqlite.org/wal.html).
|
|
14
|
-
|
|
15
|
-
However, you trade those disadvantages for extremely fast performance in most web applications.
|
|
16
|
-
|
|
17
|
-
## Checkpoint starvation
|
|
18
|
-
|
|
19
|
-
Checkpoint starvation is when SQLite3 is unable to recycle the [WAL file](https://www.sqlite.org/wal.html) due to everlasting concurrent reads to the database. If this happens, the WAL file will grow without bound, leading to unacceptable amounts of disk usage and deteriorating performance.
|
|
20
|
-
|
|
21
|
-
If you don't access the database from multiple processes or threads simultaneously, you'll never encounter this issue.
|
|
22
|
-
|
|
23
|
-
If you do access the database from multiple processes or threads simultaneously, just use the [`wal_checkpoint(RESTART)`](https://www.sqlite.org/pragma.html#pragma_wal_checkpoint) pragma when the WAL file gets too big.
|
|
24
|
-
|
|
25
|
-
```js
|
|
26
|
-
setInterval(fs.stat.bind(null, 'foobar.db-wal', (err, stat) => {
|
|
27
|
-
if (err) {
|
|
28
|
-
if (err.code !== 'ENOENT') throw err;
|
|
29
|
-
} else if (stat.size > someUnacceptableSize) {
|
|
30
|
-
db.pragma('wal_checkpoint(RESTART)');
|
|
31
|
-
}
|
|
32
|
-
}), 5000).unref();
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## A note about durability
|
|
36
|
-
|
|
37
|
-
This distribution of SQLite3 uses the `SQLITE_DEFAULT_WAL_SYNCHRONOUS=1` [compile-time option](https://sqlite.org/compile.html#default_wal_synchronous), which makes databases in WAL mode default to the ["NORMAL" synchronous setting](https://sqlite.org/pragma.html#pragma_synchronous). This allows applications to achieve extreme performance, but introduces a slight loss of [durability](https://en.wikipedia.org/wiki/Durability_(database_systems)) while in WAL mode.
|
|
38
|
-
|
|
39
|
-
You can override this setting by running `db.pragma('synchronous = FULL')`.
|
package/docs/threads.md
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Worker threads
|
|
2
|
-
|
|
3
|
-
For most applications, `better-sqlite3` is fast enough to use in the main thread without blocking for a noticeable amount of time. However, if you need to perform very slow queries, you have the option of using [worker threads](https://nodejs.org/api/worker_threads.html) to keep things running smoothly. Below is an example of using a thread pool to perform queries in the background.
|
|
4
|
-
|
|
5
|
-
### worker.js
|
|
6
|
-
|
|
7
|
-
The worker logic is very simple in our case. It accepts messages from the master thread, executes each message's SQL (with any given parameters), and sends back the query results.
|
|
8
|
-
|
|
9
|
-
```js
|
|
10
|
-
const { parentPort } = require('worker_threads');
|
|
11
|
-
const db = require('better-sqlite3-multiple-ciphers')('foobar.db');
|
|
12
|
-
|
|
13
|
-
parentPort.on('message', ({ sql, parameters }) => {
|
|
14
|
-
const result = db.prepare(sql).all(...parameters);
|
|
15
|
-
parentPort.postMessage(result);
|
|
16
|
-
});
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### master.js
|
|
20
|
-
|
|
21
|
-
The master thread is responsible for spawning workers, respawning threads that crash, and accepting query jobs.
|
|
22
|
-
|
|
23
|
-
```js
|
|
24
|
-
const { Worker } = require('worker_threads');
|
|
25
|
-
const os = require('os');
|
|
26
|
-
|
|
27
|
-
/*
|
|
28
|
-
Export a function that queues pending work.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
const queue = [];
|
|
32
|
-
exports.asyncQuery = (sql, ...parameters) => {
|
|
33
|
-
return new Promise((resolve, reject) => {
|
|
34
|
-
queue.push({
|
|
35
|
-
resolve,
|
|
36
|
-
reject,
|
|
37
|
-
message: { sql, parameters },
|
|
38
|
-
});
|
|
39
|
-
drainQueue();
|
|
40
|
-
});
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/*
|
|
44
|
-
Instruct workers to drain the queue.
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
let workers = [];
|
|
48
|
-
function drainQueue() {
|
|
49
|
-
for (const worker of workers) {
|
|
50
|
-
worker.takeWork();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/*
|
|
55
|
-
Spawn workers that try to drain the queue.
|
|
56
|
-
*/
|
|
57
|
-
|
|
58
|
-
os.cpus().forEach(function spawn() {
|
|
59
|
-
const worker = new Worker('./worker.js');
|
|
60
|
-
|
|
61
|
-
let job = null; // Current item from the queue
|
|
62
|
-
let error = null; // Error that caused the worker to crash
|
|
63
|
-
|
|
64
|
-
function takeWork() {
|
|
65
|
-
if (!job && queue.length) {
|
|
66
|
-
// If there's a job in the queue, send it to the worker
|
|
67
|
-
job = queue.shift();
|
|
68
|
-
worker.postMessage(job.message);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
worker
|
|
73
|
-
.on('online', () => {
|
|
74
|
-
workers.push({ takeWork });
|
|
75
|
-
takeWork();
|
|
76
|
-
})
|
|
77
|
-
.on('message', (result) => {
|
|
78
|
-
job.resolve(result);
|
|
79
|
-
job = null;
|
|
80
|
-
takeWork(); // Check if there's more work to do
|
|
81
|
-
})
|
|
82
|
-
.on('error', (err) => {
|
|
83
|
-
console.error(err);
|
|
84
|
-
error = err;
|
|
85
|
-
})
|
|
86
|
-
.on('exit', (code) => {
|
|
87
|
-
workers = workers.filter(w => w.takeWork !== takeWork);
|
|
88
|
-
if (job) {
|
|
89
|
-
job.reject(error || new Error('worker died'));
|
|
90
|
-
}
|
|
91
|
-
if (code !== 0) {
|
|
92
|
-
console.error(`worker exited with code ${code}`);
|
|
93
|
-
spawn(); // Worker died, so spawn a new one
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
```
|
package/docs/tips.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Helpful tips for SQLite3
|
|
2
|
-
|
|
3
|
-
## Creating good tables
|
|
4
|
-
|
|
5
|
-
It's a good idea to use `INTEGER PRIMARY KEY AUTOINCREMENT` as one of the columns in a table. This ensures two things:
|
|
6
|
-
|
|
7
|
-
- `INTEGER PRIMARY KEY`: improved performance by reusing SQLite3's built-in `rowid` column.
|
|
8
|
-
- `AUTOINCREMENT`: no future row will have the same ID as an old one that was deleted. This can prevent potential bugs and security breaches.
|
|
9
|
-
|
|
10
|
-
If you don't use `INTEGER PRIMARY KEY`, then you *must* use `NOT NULL` in all of your your primary key columns. Otherwise you'll be victim to an SQLite3 bug that allows primary keys to be `NULL`.
|
|
11
|
-
|
|
12
|
-
Any column with `INTEGER PRIMARY KEY` will automatically increment when setting its value to `NULL`. But without `AUTOINCREMENT`, the behavior only ensures uniqueness from currently existing rows.
|
|
13
|
-
|
|
14
|
-
It should be noted that `NULL` values count as unique from each other. This has implications when using the `UNIQUE` contraint or any other equality test.
|
|
15
|
-
|
|
16
|
-
## Default values
|
|
17
|
-
|
|
18
|
-
When a column has a `DEFAULT` value, it only gets applied when no value is specified for an `INSERT` statement. If the `INSERT` statement specifies a `NULL` value, the `DEFAULT` value is **NOT** used.
|
|
19
|
-
|
|
20
|
-
## Foreign keys
|
|
21
|
-
|
|
22
|
-
Foreign key constraints are not enforced if the child's column value is `NULL`. To ensure that a relationship is always enforced, use `NOT NULL` on the child column.
|
|
23
|
-
|
|
24
|
-
Example:
|
|
25
|
-
```sql
|
|
26
|
-
CREATE TABLE comments (value TEXT, user_id INTEGER NOT NULL REFERENCES users);
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Foreign key clauses can be followed by `ON DELETE` and/or `ON UPDATE`, with the following possible values:
|
|
30
|
-
|
|
31
|
-
- `SET NULL`: if the parent column is deleted or updated, the child column becomes `NULL`.
|
|
32
|
-
- *NOTE: This still causes a constraint violation if the child column has `NOT NULL`*.
|
|
33
|
-
- `SET DEFAULT`: if the parent column is updated or deleted, the child column becomes its `DEFAULT` value.
|
|
34
|
-
- *NOTE: This still causes a constraint violation if the child column's `DEFAULT` value does not correspond with an actual parent row*.
|
|
35
|
-
- `CASCADE`: if the parent row is deleted, the child row is deleted; if the parent column is updated, the new value is propogated to the child column.
|
package/docs/troubleshooting.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# Troubleshooting installation
|
|
2
|
-
|
|
3
|
-
If you have trouble installing `better-sqlite3-multiple-ciphers`, follow this checklist:
|
|
4
|
-
|
|
5
|
-
1. Make sure you're using nodejs v10.20.1 or later
|
|
6
|
-
|
|
7
|
-
2. Make sure you have [`node-gyp`](https://github.com/nodejs/node-gyp#installation) globally installed, including all of [its dependencies](https://github.com/nodejs/node-gyp#on-unix). On Windows you may need to [configure some things manually](https://github.com/nodejs/node-gyp#on-windows). Use `npm ls node-gyp` to make sure none of your local packages installed an outdated version of `node-gyp` that is used over the global one.
|
|
8
|
-
|
|
9
|
-
3. If you're using [Electron](https://github.com/electron/electron), try running [`electron-rebuild`](https://www.npmjs.com/package/electron-rebuild)
|
|
10
|
-
|
|
11
|
-
4. If you're using Windows, follow these steps. Do them **in this order**, and **don't skip steps**.
|
|
12
|
-
|
|
13
|
-
1. Install the **latest** of node 10, 12, or 14.
|
|
14
|
-
2. Install **latest** Visual Studio Community and Desktop Development with C++ extension.
|
|
15
|
-
3. Install **latest** Python.
|
|
16
|
-
4. Run following commands:
|
|
17
|
-
```
|
|
18
|
-
npm config set msvs_version 2019
|
|
19
|
-
npm config set msbuild_path "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe"
|
|
20
|
-
```
|
|
21
|
-
5. Run `npm install`
|
|
22
|
-
|
|
23
|
-
If none of these solved your problem, try browsing [previous issues](https://github.com/JoshuaWise/better-sqlite3/issues?q=is%3Aissue) or open a [new issue](https://github.com/JoshuaWise/better-sqlite3/issues/new).
|
package/docs/unsafe.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Unsafe mode
|
|
2
|
-
|
|
3
|
-
By default, `better-sqlite3` prevents you from doing things that might corrupt your database or cause undefined behavior. Such unsafe operations include:
|
|
4
|
-
|
|
5
|
-
- Anything blocked by [`SQLITE_DBCONFIG_DEFENSIVE`](https://www.sqlite.org/c3ref/c_dbconfig_defensive.html#sqlitedbconfigdefensive)
|
|
6
|
-
- Mutating the database while [iterating](https://github.com/JoshuaWise/better-sqlite3/blob/master/docs/api.md#iteratebindparameters---iterator) through a query's result set
|
|
7
|
-
|
|
8
|
-
However, some advanced users might want to use these functionalities at their own risk. For this reason, users have the option of enabling "unsafe mode".
|
|
9
|
-
|
|
10
|
-
```js
|
|
11
|
-
db.unsafeMode(); // Unsafe mode ON
|
|
12
|
-
db.unsafeMode(true); // Unsafe mode ON
|
|
13
|
-
db.unsafeMode(false); // Unsafe mode OFF
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Unsafe mode can be toggled at any time, and independently for each database connection. While toggled on, `better-sqlite3` will not prevent you from performing the dangerous operations listed above.
|
package/src/better_sqlite3.lzz
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
#hdr
|
|
2
|
-
#include <climits>
|
|
3
|
-
#include <cstdio>
|
|
4
|
-
#include <cstring>
|
|
5
|
-
#include <string>
|
|
6
|
-
#include <vector>
|
|
7
|
-
#include <set>
|
|
8
|
-
#include <unordered_map>
|
|
9
|
-
#include <algorithm>
|
|
10
|
-
#include <sqlite3.h>
|
|
11
|
-
#include <node.h>
|
|
12
|
-
#include <node_object_wrap.h>
|
|
13
|
-
#include <node_buffer.h>
|
|
14
|
-
#end
|
|
15
|
-
|
|
16
|
-
#insert "util/macros.lzz"
|
|
17
|
-
#insert "util/query-macros.lzz"
|
|
18
|
-
#insert "util/constants.lzz"
|
|
19
|
-
#insert "util/bind-map.lzz"
|
|
20
|
-
struct Addon;
|
|
21
|
-
class Statement;
|
|
22
|
-
class Backup;
|
|
23
|
-
#insert "objects/database.lzz"
|
|
24
|
-
#insert "objects/statement.lzz"
|
|
25
|
-
#insert "objects/statement-iterator.lzz"
|
|
26
|
-
#insert "objects/backup.lzz"
|
|
27
|
-
#insert "util/data-converter.lzz"
|
|
28
|
-
#insert "util/custom-function.lzz"
|
|
29
|
-
#insert "util/custom-aggregate.lzz"
|
|
30
|
-
#insert "util/custom-table.lzz"
|
|
31
|
-
#insert "util/data.lzz"
|
|
32
|
-
#insert "util/binder.lzz"
|
|
33
|
-
|
|
34
|
-
struct Addon {
|
|
35
|
-
NODE_METHOD(JS_setErrorConstructor) {
|
|
36
|
-
REQUIRE_ARGUMENT_FUNCTION(first, v8::Local<v8::Function> SqliteError);
|
|
37
|
-
OnlyAddon->SqliteError.Reset(OnlyIsolate, SqliteError);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
static void Cleanup(void* ptr) {
|
|
41
|
-
Addon* addon = static_cast<Addon*>(ptr);
|
|
42
|
-
for (Database* db : addon->dbs) db->CloseHandles();
|
|
43
|
-
addon->dbs.clear();
|
|
44
|
-
delete addon;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
explicit Addon(v8::Isolate* isolate) :
|
|
48
|
-
privileged_info(NULL),
|
|
49
|
-
next_id(0),
|
|
50
|
-
cs(isolate) {}
|
|
51
|
-
|
|
52
|
-
inline sqlite3_uint64 NextId() {
|
|
53
|
-
return next_id++;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
CopyablePersistent<v8::Function> Statement;
|
|
57
|
-
CopyablePersistent<v8::Function> StatementIterator;
|
|
58
|
-
CopyablePersistent<v8::Function> Backup;
|
|
59
|
-
CopyablePersistent<v8::Function> SqliteError;
|
|
60
|
-
NODE_ARGUMENTS_POINTER privileged_info;
|
|
61
|
-
sqlite3_uint64 next_id;
|
|
62
|
-
CS cs;
|
|
63
|
-
std::set<Database*, Database::CompareDatabase> dbs;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
#src
|
|
67
|
-
NODE_MODULE_INIT(/* exports, context */) {
|
|
68
|
-
v8::Isolate* isolate = context->GetIsolate();
|
|
69
|
-
v8::HandleScope scope(isolate);
|
|
70
|
-
|
|
71
|
-
// Initialize addon instance.
|
|
72
|
-
Addon* addon = new Addon(isolate);
|
|
73
|
-
v8::Local<v8::External> data = v8::External::New(isolate, addon);
|
|
74
|
-
node::AddEnvironmentCleanupHook(isolate, Addon::Cleanup, addon);
|
|
75
|
-
|
|
76
|
-
// Create and export native-backed classes and functions.
|
|
77
|
-
exports->Set(context, InternalizedFromLatin1(isolate, "Database"), Database::Init(isolate, data)).FromJust();
|
|
78
|
-
exports->Set(context, InternalizedFromLatin1(isolate, "Statement"), Statement::Init(isolate, data)).FromJust();
|
|
79
|
-
exports->Set(context, InternalizedFromLatin1(isolate, "StatementIterator"), StatementIterator::Init(isolate, data)).FromJust();
|
|
80
|
-
exports->Set(context, InternalizedFromLatin1(isolate, "Backup"), Backup::Init(isolate, data)).FromJust();
|
|
81
|
-
exports->Set(context, InternalizedFromLatin1(isolate, "setErrorConstructor"), v8::FunctionTemplate::New(isolate, Addon::JS_setErrorConstructor, data)->GetFunction(context).ToLocalChecked()).FromJust();
|
|
82
|
-
|
|
83
|
-
// Store addon instance data.
|
|
84
|
-
addon->Statement.Reset(isolate, exports->Get(context, InternalizedFromLatin1(isolate, "Statement")).ToLocalChecked().As<v8::Function>());
|
|
85
|
-
addon->StatementIterator.Reset(isolate, exports->Get(context, InternalizedFromLatin1(isolate, "StatementIterator")).ToLocalChecked().As<v8::Function>());
|
|
86
|
-
addon->Backup.Reset(isolate, exports->Get(context, InternalizedFromLatin1(isolate, "Backup")).ToLocalChecked().As<v8::Function>());
|
|
87
|
-
}
|
|
88
|
-
#end
|
package/src/objects/backup.lzz
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
class Backup : public node::ObjectWrap {
|
|
2
|
-
public:
|
|
3
|
-
|
|
4
|
-
INIT(Init) {
|
|
5
|
-
v8::Local<v8::FunctionTemplate> t = NewConstructorTemplate(isolate, data, JS_new, "Backup");
|
|
6
|
-
SetPrototypeMethod(isolate, data, t, "transfer", JS_transfer);
|
|
7
|
-
SetPrototypeMethod(isolate, data, t, "close", JS_close);
|
|
8
|
-
return t->GetFunction(OnlyContext).ToLocalChecked();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// Used to support ordered containers.
|
|
12
|
-
static inline bool Compare(Backup const * const a, Backup const * const b) {
|
|
13
|
-
return a->id < b->id;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// Whenever this is used, db->RemoveBackup must be invoked beforehand.
|
|
17
|
-
void CloseHandles() {
|
|
18
|
-
if (alive) {
|
|
19
|
-
alive = false;
|
|
20
|
-
std::string filename(sqlite3_db_filename(dest_handle, "main"));
|
|
21
|
-
sqlite3_backup_finish(backup_handle);
|
|
22
|
-
int status = sqlite3_close(dest_handle);
|
|
23
|
-
assert(status == SQLITE_OK); ((void)status);
|
|
24
|
-
if (unlink) remove(filename.c_str());
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
~Backup() {
|
|
29
|
-
if (alive) db->RemoveBackup(this);
|
|
30
|
-
CloseHandles();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
private:
|
|
34
|
-
|
|
35
|
-
explicit Backup(
|
|
36
|
-
Database* db,
|
|
37
|
-
sqlite3* dest_handle,
|
|
38
|
-
sqlite3_backup* backup_handle,
|
|
39
|
-
sqlite3_uint64 id,
|
|
40
|
-
bool unlink
|
|
41
|
-
) :
|
|
42
|
-
node::ObjectWrap(),
|
|
43
|
-
db(db),
|
|
44
|
-
dest_handle(dest_handle),
|
|
45
|
-
backup_handle(backup_handle),
|
|
46
|
-
id(id),
|
|
47
|
-
alive(true),
|
|
48
|
-
unlink(unlink) {
|
|
49
|
-
assert(db != NULL);
|
|
50
|
-
assert(dest_handle != NULL);
|
|
51
|
-
assert(backup_handle != NULL);
|
|
52
|
-
db->AddBackup(this);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
NODE_METHOD(JS_new) {
|
|
56
|
-
UseAddon;
|
|
57
|
-
if (!addon->privileged_info) return ThrowTypeError("Disabled constructor");
|
|
58
|
-
assert(info.IsConstructCall());
|
|
59
|
-
Database* db = Unwrap<Database>(addon->privileged_info->This());
|
|
60
|
-
REQUIRE_DATABASE_OPEN(db->GetState());
|
|
61
|
-
REQUIRE_DATABASE_NOT_BUSY(db->GetState());
|
|
62
|
-
|
|
63
|
-
v8::Local<v8::Object> database = (*addon->privileged_info)[0].As<v8::Object>();
|
|
64
|
-
v8::Local<v8::String> attachedName = (*addon->privileged_info)[1].As<v8::String>();
|
|
65
|
-
v8::Local<v8::String> destFile = (*addon->privileged_info)[2].As<v8::String>();
|
|
66
|
-
bool unlink = (*addon->privileged_info)[3].As<v8::Boolean>()->Value();
|
|
67
|
-
|
|
68
|
-
UseIsolate;
|
|
69
|
-
sqlite3* dest_handle;
|
|
70
|
-
v8::String::Utf8Value dest_file(isolate, destFile);
|
|
71
|
-
v8::String::Utf8Value attached_name(isolate, attachedName);
|
|
72
|
-
int mask = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
|
|
73
|
-
|
|
74
|
-
if (sqlite3_open_v2(*dest_file, &dest_handle, mask, NULL) != SQLITE_OK) {
|
|
75
|
-
Database::ThrowSqliteError(addon, dest_handle);
|
|
76
|
-
int status = sqlite3_close(dest_handle);
|
|
77
|
-
assert(status == SQLITE_OK); ((void)status);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
sqlite3_extended_result_codes(dest_handle, 1);
|
|
82
|
-
sqlite3_limit(dest_handle, SQLITE_LIMIT_LENGTH, INT_MAX);
|
|
83
|
-
sqlite3_backup* backup_handle = sqlite3_backup_init(dest_handle, "main", db->GetHandle(), *attached_name);
|
|
84
|
-
if (backup_handle == NULL) {
|
|
85
|
-
Database::ThrowSqliteError(addon, dest_handle);
|
|
86
|
-
int status = sqlite3_close(dest_handle);
|
|
87
|
-
assert(status == SQLITE_OK); ((void)status);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
Backup* backup = new Backup(db, dest_handle, backup_handle, addon->NextId(), unlink);
|
|
92
|
-
backup->Wrap(info.This());
|
|
93
|
-
SetFrozen(isolate, OnlyContext, info.This(), addon->cs.database, database);
|
|
94
|
-
|
|
95
|
-
info.GetReturnValue().Set(info.This());
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
NODE_METHOD(JS_transfer) {
|
|
99
|
-
Backup* backup = Unwrap<Backup>(info.This());
|
|
100
|
-
REQUIRE_ARGUMENT_INT32(first, int pages);
|
|
101
|
-
REQUIRE_DATABASE_OPEN(backup->db->GetState());
|
|
102
|
-
assert(backup->db->GetState()->busy == false);
|
|
103
|
-
assert(backup->alive == true);
|
|
104
|
-
|
|
105
|
-
sqlite3_backup* backup_handle = backup->backup_handle;
|
|
106
|
-
int status = sqlite3_backup_step(backup_handle, pages) & 0xff;
|
|
107
|
-
|
|
108
|
-
Addon* addon = backup->db->GetAddon();
|
|
109
|
-
if (status == SQLITE_OK || status == SQLITE_DONE || status == SQLITE_BUSY) {
|
|
110
|
-
int total_pages = sqlite3_backup_pagecount(backup_handle);
|
|
111
|
-
int remaining_pages = sqlite3_backup_remaining(backup_handle);
|
|
112
|
-
UseIsolate;
|
|
113
|
-
UseContext;
|
|
114
|
-
v8::Local<v8::Object> result = v8::Object::New(isolate);
|
|
115
|
-
result->Set(ctx, addon->cs.totalPages.Get(isolate), v8::Int32::New(isolate, total_pages)).FromJust();
|
|
116
|
-
result->Set(ctx, addon->cs.remainingPages.Get(isolate), v8::Int32::New(isolate, remaining_pages)).FromJust();
|
|
117
|
-
info.GetReturnValue().Set(result);
|
|
118
|
-
if (status == SQLITE_DONE) backup->unlink = false;
|
|
119
|
-
} else {
|
|
120
|
-
Database::ThrowSqliteError(addon, sqlite3_errstr(status), status);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
NODE_METHOD(JS_close) {
|
|
125
|
-
Backup* backup = Unwrap<Backup>(info.This());
|
|
126
|
-
assert(backup->db->GetState()->busy == false);
|
|
127
|
-
if (backup->alive) backup->db->RemoveBackup(backup);
|
|
128
|
-
backup->CloseHandles();
|
|
129
|
-
info.GetReturnValue().Set(info.This());
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
Database* const db;
|
|
133
|
-
sqlite3* const dest_handle;
|
|
134
|
-
sqlite3_backup* const backup_handle;
|
|
135
|
-
const sqlite3_uint64 id;
|
|
136
|
-
bool alive;
|
|
137
|
-
bool unlink;
|
|
138
|
-
};
|