better-sqlite3-multiple-ciphers 7.4.7-beta.1 → 7.5.1-beta.1
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 +6 -4
- 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 +17 -7
- package/deps/symlink.js +7 -4
- package/lib/database.js +17 -6
- package/lib/sqlite-error.js +1 -2
- package/package.json +11 -5
- 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
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const Database = require('../.');
|
|
3
|
-
|
|
4
|
-
describe('Statement#bind()', function () {
|
|
5
|
-
beforeEach(function () {
|
|
6
|
-
this.db = new Database(util.next());
|
|
7
|
-
this.db.prepare('CREATE TABLE entries (a TEXT, b INTEGER, c BLOB)').run();
|
|
8
|
-
});
|
|
9
|
-
afterEach(function () {
|
|
10
|
-
this.db.close();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should permanently bind the given parameters', function () {
|
|
14
|
-
const stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
|
|
15
|
-
const buffer = Buffer.alloc(4).fill(0xdd);
|
|
16
|
-
stmt.bind('foobar', 25, buffer)
|
|
17
|
-
stmt.run();
|
|
18
|
-
buffer.fill(0xaa);
|
|
19
|
-
stmt.run();
|
|
20
|
-
const row1 = this.db.prepare('SELECT * FROM entries WHERE rowid=1').get();
|
|
21
|
-
const row2 = this.db.prepare('SELECT * FROM entries WHERE rowid=2').get();
|
|
22
|
-
expect(row1.a).to.equal(row2.a);
|
|
23
|
-
expect(row1.b).to.equal(row2.b);
|
|
24
|
-
expect(row1.c).to.deep.equal(row2.c);
|
|
25
|
-
});
|
|
26
|
-
it('should not allow you to bind temporary parameters afterwards', function () {
|
|
27
|
-
const stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
|
|
28
|
-
const buffer = Buffer.alloc(4).fill(0xdd);
|
|
29
|
-
stmt.bind('foobar', 25, buffer)
|
|
30
|
-
expect(() => stmt.run(null)).to.throw(TypeError);
|
|
31
|
-
expect(() => stmt.run(buffer)).to.throw(TypeError);
|
|
32
|
-
expect(() => stmt.run('foobar', 25, buffer)).to.throw(TypeError);
|
|
33
|
-
});
|
|
34
|
-
it('should throw an exception when invoked twice on the same statement', function () {
|
|
35
|
-
let stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
|
|
36
|
-
stmt.bind('foobar', 25, null);
|
|
37
|
-
expect(() => stmt.bind('foobar', 25, null)).to.throw(TypeError);
|
|
38
|
-
expect(() => stmt.bind()).to.throw(TypeError);
|
|
39
|
-
|
|
40
|
-
stmt = this.db.prepare('SELECT * FROM entries');
|
|
41
|
-
stmt.bind();
|
|
42
|
-
expect(() => stmt.bind()).to.throw(TypeError);
|
|
43
|
-
});
|
|
44
|
-
it('should throw an exception when invalid parameters are given', function () {
|
|
45
|
-
let stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
|
|
46
|
-
|
|
47
|
-
expect(() =>
|
|
48
|
-
stmt.bind('foo', 25)
|
|
49
|
-
).to.throw(RangeError);
|
|
50
|
-
|
|
51
|
-
expect(() =>
|
|
52
|
-
stmt.bind('foo', 25, null, null)
|
|
53
|
-
).to.throw(RangeError);
|
|
54
|
-
|
|
55
|
-
expect(() =>
|
|
56
|
-
stmt.bind('foo', new Number(25), null)
|
|
57
|
-
).to.throw(TypeError);
|
|
58
|
-
|
|
59
|
-
expect(() =>
|
|
60
|
-
stmt.bind()
|
|
61
|
-
).to.throw(RangeError);
|
|
62
|
-
|
|
63
|
-
stmt.bind('foo', 25, null);
|
|
64
|
-
|
|
65
|
-
stmt = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
|
|
66
|
-
|
|
67
|
-
expect(() =>
|
|
68
|
-
stmt.bind({ a: '123' })
|
|
69
|
-
).to.throw(RangeError);
|
|
70
|
-
|
|
71
|
-
expect(() =>
|
|
72
|
-
stmt.bind({ a: '123', 1: null })
|
|
73
|
-
).to.throw(RangeError);
|
|
74
|
-
|
|
75
|
-
expect(() =>
|
|
76
|
-
stmt.bind({ a: '123' }, null, null)
|
|
77
|
-
).to.throw(RangeError);
|
|
78
|
-
|
|
79
|
-
stmt.bind({ a: '123' }, null);
|
|
80
|
-
|
|
81
|
-
stmt = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
|
|
82
|
-
stmt.bind({ a: '123', b: null }, null);
|
|
83
|
-
});
|
|
84
|
-
it('should propagate exceptions thrown while accessing array/object members', function () {
|
|
85
|
-
const arr = [22];
|
|
86
|
-
const obj = {};
|
|
87
|
-
const err = new TypeError('foobar');
|
|
88
|
-
Object.defineProperty(arr, '0', { get: () => { throw err; } })
|
|
89
|
-
Object.defineProperty(obj, 'baz', { get: () => { throw err; } })
|
|
90
|
-
const stmt1 = this.db.prepare('SELECT ?');
|
|
91
|
-
const stmt2 = this.db.prepare('SELECT @baz');
|
|
92
|
-
expect(() => stmt1.bind(arr)).to.throw(err);
|
|
93
|
-
expect(() => stmt2.bind(obj)).to.throw(err);
|
|
94
|
-
});
|
|
95
|
-
it('should properly bind empty buffers', function () {
|
|
96
|
-
this.db.prepare('INSERT INTO entries (c) VALUES (?)').bind(Buffer.alloc(0)).run();
|
|
97
|
-
const result = this.db.prepare('SELECT c FROM entries').pluck().get();
|
|
98
|
-
expect(result).to.be.an.instanceof(Buffer);
|
|
99
|
-
expect(result.length).to.equal(0);
|
|
100
|
-
});
|
|
101
|
-
it('should properly bind empty strings', function () {
|
|
102
|
-
this.db.prepare('INSERT INTO entries (a) VALUES (?)').bind('').run();
|
|
103
|
-
const result = this.db.prepare('SELECT a FROM entries').pluck().get();
|
|
104
|
-
expect(result).to.be.a('string');
|
|
105
|
-
expect(result.length).to.equal(0);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const Database = require('../.');
|
|
3
|
-
|
|
4
|
-
describe('Statement#columns()', function () {
|
|
5
|
-
beforeEach(function () {
|
|
6
|
-
this.db = new Database(util.next());
|
|
7
|
-
this.db.prepare('CREATE TABLE entries (a TEXT, b INTEGER, c WHATthe)').run();
|
|
8
|
-
});
|
|
9
|
-
afterEach(function () {
|
|
10
|
-
this.db.close();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should throw an exception if invoked on a non-reader statement', function () {
|
|
14
|
-
const stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
|
|
15
|
-
expect(() => stmt.columns()).to.throw(TypeError);
|
|
16
|
-
});
|
|
17
|
-
it('should return an array of column descriptors', function () {
|
|
18
|
-
expect(this.db.prepare('SELECT 5.0 as d, * FROM entries').columns()).to.deep.equal([
|
|
19
|
-
{ name: 'd', column: null, table: null, database: null, type: null },
|
|
20
|
-
{ name: 'a', column: 'a', table: 'entries', database: 'main', type: 'TEXT' },
|
|
21
|
-
{ name: 'b', column: 'b', table: 'entries', database: 'main', type: 'INTEGER' },
|
|
22
|
-
{ name: 'c', column: 'c', table: 'entries', database: 'main', type: 'WHATthe' },
|
|
23
|
-
]);
|
|
24
|
-
expect(this.db.prepare('SELECT a, c as b, b FROM entries').columns()).to.deep.equal([
|
|
25
|
-
{ name: 'a', column: 'a', table: 'entries', database: 'main', type: 'TEXT' },
|
|
26
|
-
{ name: 'b', column: 'c', table: 'entries', database: 'main', type: 'WHATthe' },
|
|
27
|
-
{ name: 'b', column: 'b', table: 'entries', database: 'main', type: 'INTEGER' },
|
|
28
|
-
]);
|
|
29
|
-
});
|
|
30
|
-
it('should not return stale column descriptors after being recompiled', function () {
|
|
31
|
-
const stmt = this.db.prepare('SELECT * FROM entries');
|
|
32
|
-
expect(stmt.columns()).to.deep.equal([
|
|
33
|
-
{ name: 'a', column: 'a', table: 'entries', database: 'main', type: 'TEXT' },
|
|
34
|
-
{ name: 'b', column: 'b', table: 'entries', database: 'main', type: 'INTEGER' },
|
|
35
|
-
{ name: 'c', column: 'c', table: 'entries', database: 'main', type: 'WHATthe' },
|
|
36
|
-
]);
|
|
37
|
-
this.db.prepare('ALTER TABLE entries ADD COLUMN d FOOBAR').run();
|
|
38
|
-
stmt.get(); // Recompile
|
|
39
|
-
expect(stmt.columns()).to.deep.equal([
|
|
40
|
-
{ name: 'a', column: 'a', table: 'entries', database: 'main', type: 'TEXT' },
|
|
41
|
-
{ name: 'b', column: 'b', table: 'entries', database: 'main', type: 'INTEGER' },
|
|
42
|
-
{ name: 'c', column: 'c', table: 'entries', database: 'main', type: 'WHATthe' },
|
|
43
|
-
{ name: 'd', column: 'd', table: 'entries', database: 'main', type: 'FOOBAR' },
|
|
44
|
-
]);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const Database = require('../.');
|
|
3
|
-
|
|
4
|
-
describe('Database#transaction()', function () {
|
|
5
|
-
beforeEach(function () {
|
|
6
|
-
this.db = new Database(util.next());
|
|
7
|
-
this.db.prepare('CREATE TABLE data (x UNIQUE)').run();
|
|
8
|
-
this.db.prepare('INSERT INTO data VALUES (1), (2), (3)').run();
|
|
9
|
-
});
|
|
10
|
-
afterEach(function () {
|
|
11
|
-
this.db.close();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('should throw an exception if a function is not provided', function () {
|
|
15
|
-
expect(() => this.db.transaction(123)).to.throw(TypeError);
|
|
16
|
-
expect(() => this.db.transaction(0)).to.throw(TypeError);
|
|
17
|
-
expect(() => this.db.transaction(null)).to.throw(TypeError);
|
|
18
|
-
expect(() => this.db.transaction()).to.throw(TypeError);
|
|
19
|
-
expect(() => this.db.transaction([])).to.throw(TypeError);
|
|
20
|
-
expect(() => this.db.transaction('CREATE TABLE people (name TEXT)')).to.throw(TypeError);
|
|
21
|
-
expect(() => this.db.transaction(['CREATE TABLE people (name TEXT)'])).to.throw(TypeError);
|
|
22
|
-
});
|
|
23
|
-
it('should return a new transaction function', function () {
|
|
24
|
-
const fn = () => {};
|
|
25
|
-
const trx = this.db.transaction(fn);
|
|
26
|
-
expect(trx).to.not.equal(fn);
|
|
27
|
-
expect(trx).to.be.a('function');
|
|
28
|
-
expect(trx).to.equal(trx.default);
|
|
29
|
-
const keys = ['default', 'deferred', 'immediate', 'exclusive'];
|
|
30
|
-
for (const key of keys) {
|
|
31
|
-
const nested = trx[key];
|
|
32
|
-
expect(nested).to.not.equal(fn);
|
|
33
|
-
expect(nested).to.be.a('function');
|
|
34
|
-
expect(nested.database).to.equal(this.db);
|
|
35
|
-
expect(nested.run).to.be.undefined;
|
|
36
|
-
expect(nested.get).to.be.undefined;
|
|
37
|
-
expect(nested.all).to.be.undefined;
|
|
38
|
-
expect(nested.iterate).to.be.undefined;
|
|
39
|
-
expect(nested.reader).to.be.undefined;
|
|
40
|
-
expect(nested.source).to.be.undefined;
|
|
41
|
-
for (const key of keys) expect(nested[key]).to.equal(trx[key]);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
describe('transaction function', function () {
|
|
45
|
-
it('should execute the wrapped function', function () {
|
|
46
|
-
const trx = this.db.transaction(function () { return [this, ...arguments]; });
|
|
47
|
-
const obj = {};
|
|
48
|
-
expect(trx.call(obj, 'foo', 'bar', 123, obj)).to.deep.equal([obj, 'foo', 'bar', 123, obj]);
|
|
49
|
-
});
|
|
50
|
-
it('should execute within an isolated transaction', function () {
|
|
51
|
-
const other = new Database(util.current());
|
|
52
|
-
try {
|
|
53
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
54
|
-
expect(other.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
55
|
-
expect(this.db.inTransaction).to.be.false;
|
|
56
|
-
let ranOnce = false;
|
|
57
|
-
const trx = this.db.transaction((arg) => {
|
|
58
|
-
expect(this.db.inTransaction).to.be.true;
|
|
59
|
-
expect(arg).to.equal('foo');
|
|
60
|
-
this.db.prepare('INSERT INTO data VALUES (100)').run();
|
|
61
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 100]);
|
|
62
|
-
expect(other.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
63
|
-
ranOnce = true;
|
|
64
|
-
expect(this.db.inTransaction).to.be.true;
|
|
65
|
-
return 'bar';
|
|
66
|
-
});
|
|
67
|
-
expect(ranOnce).to.be.false;
|
|
68
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
69
|
-
expect(other.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
70
|
-
expect(this.db.inTransaction).to.be.false;
|
|
71
|
-
expect(trx('foo')).to.equal('bar');
|
|
72
|
-
expect(this.db.inTransaction).to.be.false;
|
|
73
|
-
expect(ranOnce).to.be.true;
|
|
74
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 100]);
|
|
75
|
-
expect(other.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 100]);
|
|
76
|
-
} finally {
|
|
77
|
-
other.close();
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
it('should rollback the transaction if an exception is thrown', function () {
|
|
81
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
82
|
-
expect(this.db.inTransaction).to.be.false;
|
|
83
|
-
const err = new Error('foobar');
|
|
84
|
-
let ranOnce = false;
|
|
85
|
-
const trx = this.db.transaction((arg) => {
|
|
86
|
-
expect(this.db.inTransaction).to.be.true;
|
|
87
|
-
expect(arg).to.equal('baz');
|
|
88
|
-
this.db.prepare('INSERT INTO data VALUES (100)').run();
|
|
89
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 100]);
|
|
90
|
-
ranOnce = true;
|
|
91
|
-
expect(this.db.inTransaction).to.be.true;
|
|
92
|
-
throw err;
|
|
93
|
-
});
|
|
94
|
-
expect(ranOnce).to.be.false;
|
|
95
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
96
|
-
expect(this.db.inTransaction).to.be.false;
|
|
97
|
-
expect(() => trx('baz')).to.throw(err);
|
|
98
|
-
expect(this.db.inTransaction).to.be.false;
|
|
99
|
-
expect(ranOnce).to.be.true;
|
|
100
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
101
|
-
});
|
|
102
|
-
it('should work when nested within other transaction functions', function () {
|
|
103
|
-
const stmt = this.db.prepare('INSERT INTO data VALUES (?)');
|
|
104
|
-
const insertOne = this.db.transaction(x => stmt.run(x));
|
|
105
|
-
const insertMany = this.db.transaction((...values) => values.map(insertOne));
|
|
106
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
107
|
-
insertMany(10, 20, 30);
|
|
108
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30]);
|
|
109
|
-
expect(() => insertMany(40, 50, 3)).to.throw(Database.SqliteError).with.property('code', 'SQLITE_CONSTRAINT_UNIQUE');
|
|
110
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30]);
|
|
111
|
-
});
|
|
112
|
-
it('should be able to perform partial rollbacks when nested', function () {
|
|
113
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
114
|
-
const stmt = this.db.prepare('INSERT INTO data VALUES (?)');
|
|
115
|
-
const insertOne = this.db.transaction(x => stmt.run(x).changes);
|
|
116
|
-
const insertMany = this.db.transaction((...values) => values.reduce((y, x) => y + insertOne(x), 0));
|
|
117
|
-
expect(this.db.inTransaction).to.be.false;
|
|
118
|
-
const trx = this.db.transaction(() => {
|
|
119
|
-
expect(this.db.inTransaction).to.be.true;
|
|
120
|
-
let count = 0;
|
|
121
|
-
count += insertMany(10, 20, 30);
|
|
122
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30]);
|
|
123
|
-
try {
|
|
124
|
-
insertMany(40, 50, 3, 60);
|
|
125
|
-
} catch (_) {
|
|
126
|
-
expect(this.db.inTransaction).to.be.true;
|
|
127
|
-
count += insertOne(555);
|
|
128
|
-
}
|
|
129
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30, 555]);
|
|
130
|
-
this.db.prepare('SAVEPOINT foo').run();
|
|
131
|
-
insertOne(123);
|
|
132
|
-
insertMany(456, 789);
|
|
133
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30, 555, 123, 456, 789]);
|
|
134
|
-
this.db.prepare('ROLLBACK TO foo').run();
|
|
135
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30, 555]);
|
|
136
|
-
count += insertMany(1000);
|
|
137
|
-
expect(this.db.inTransaction).to.be.true;
|
|
138
|
-
return count;
|
|
139
|
-
});
|
|
140
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
141
|
-
expect(this.db.inTransaction).to.be.false;
|
|
142
|
-
expect(trx()).to.equal(5);
|
|
143
|
-
expect(this.db.inTransaction).to.be.false;
|
|
144
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30, 555, 1000]);
|
|
145
|
-
});
|
|
146
|
-
it('should work when the transaction is rolled back internally', function () {
|
|
147
|
-
const stmt = this.db.prepare('INSERT OR ROLLBACK INTO data VALUES (?)');
|
|
148
|
-
const insertOne = this.db.transaction(x => stmt.run(x));
|
|
149
|
-
const insertMany = this.db.transaction((...values) => values.map(insertOne));
|
|
150
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3]);
|
|
151
|
-
insertMany(10, 20, 30);
|
|
152
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30]);
|
|
153
|
-
expect(() => insertMany(40, 50, 10)).to.throw(Database.SqliteError).with.property('code', 'SQLITE_CONSTRAINT_UNIQUE');
|
|
154
|
-
expect(this.db.prepare('SELECT x FROM data').pluck().all()).to.deep.equal([1, 2, 3, 10, 20, 30]);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const Database = require('../.');
|
|
4
|
-
|
|
5
|
-
describe('Database#pragma(\'wal_checkpoint(RESTART)\')', function () {
|
|
6
|
-
let db1, db2;
|
|
7
|
-
before(function () {
|
|
8
|
-
db1 = new Database(util.next());
|
|
9
|
-
db2 = new Database(util.next());
|
|
10
|
-
db1.pragma('journal_mode = WAL');
|
|
11
|
-
db1.prepare('CREATE TABLE entries (a TEXT, b INTEGER)').run();
|
|
12
|
-
db2.pragma('journal_mode = WAL');
|
|
13
|
-
db2.prepare('CREATE TABLE entries (a TEXT, b INTEGER)').run();
|
|
14
|
-
});
|
|
15
|
-
after(function () {
|
|
16
|
-
db1.close();
|
|
17
|
-
db2.close();
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
function fillWall(count, expectation) {
|
|
21
|
-
[db1, db2].forEach((db) => {
|
|
22
|
-
let size1, size2;
|
|
23
|
-
for (let i = 0; i < count; ++i) {
|
|
24
|
-
size1 = fs.statSync(`${db.name}-wal`).size;
|
|
25
|
-
db.prepare('INSERT INTO entries VALUES (?, ?)').run('bar', 999);
|
|
26
|
-
size2 = fs.statSync(`${db.name}-wal`).size;
|
|
27
|
-
expectation(size2, size1, db);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe('when used without a specified database', function () {
|
|
33
|
-
specify('every insert should increase the size of the WAL file', function () {
|
|
34
|
-
fillWall(10, (b, a) => expect(b).to.be.above(a));
|
|
35
|
-
});
|
|
36
|
-
specify('inserts after a checkpoint should NOT increase the size of the WAL file', function () {
|
|
37
|
-
db1.prepare(`ATTACH '${db2.name}' AS foobar`).run();
|
|
38
|
-
db1.pragma('wal_checkpoint(RESTART)');
|
|
39
|
-
fillWall(10, (b, a) => expect(b).to.equal(a));
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
describe('when used on a specific database', function () {
|
|
43
|
-
specify('every insert should increase the size of the WAL file', function () {
|
|
44
|
-
db1.prepare('DETACH foobar').run();
|
|
45
|
-
db1.close();
|
|
46
|
-
db2.close();
|
|
47
|
-
db1 = new Database(db1.name);
|
|
48
|
-
db2 = new Database(db2.name);
|
|
49
|
-
db1.prepare('CREATE TABLE _unused (a TEXT, b INTEGER)').run();
|
|
50
|
-
db2.prepare('CREATE TABLE _unused (a TEXT, b INTEGER)').run();
|
|
51
|
-
fillWall(10, (b, a) => expect(b).to.be.above(a));
|
|
52
|
-
});
|
|
53
|
-
specify('inserts after a checkpoint should NOT increase the size of the WAL file', function () {
|
|
54
|
-
db1.prepare(`ATTACH '${db2.name}' AS bazqux`).run();
|
|
55
|
-
db1.pragma('bazqux.wal_checkpoint(RESTART)');
|
|
56
|
-
fillWall(10, (b, a, db) => {
|
|
57
|
-
if (db === db1) expect(b).to.be.above(a);
|
|
58
|
-
else expect(b).to.be.equal(a);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const Database = require('../.');
|
|
3
|
-
|
|
4
|
-
describe('Database#function()', function () {
|
|
5
|
-
beforeEach(function () {
|
|
6
|
-
this.db = new Database(util.next());
|
|
7
|
-
this.get = (SQL, ...args) => this.db.prepare(`SELECT ${SQL}`).pluck().get(args);
|
|
8
|
-
});
|
|
9
|
-
afterEach(function () {
|
|
10
|
-
this.db.close();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should throw an exception if the correct arguments are not provided', function () {
|
|
14
|
-
expect(() => this.db.function()).to.throw(TypeError);
|
|
15
|
-
expect(() => this.db.function(null)).to.throw(TypeError);
|
|
16
|
-
expect(() => this.db.function('a')).to.throw(TypeError);
|
|
17
|
-
expect(() => this.db.function({})).to.throw(TypeError);
|
|
18
|
-
expect(() => this.db.function(() => {})).to.throw(TypeError);
|
|
19
|
-
expect(() => this.db.function(function b() {})).to.throw(TypeError);
|
|
20
|
-
expect(() => this.db.function({}, function c() {})).to.throw(TypeError);
|
|
21
|
-
expect(() => this.db.function('d', {})).to.throw(TypeError);
|
|
22
|
-
expect(() => this.db.function('e', { fn: function e() {} })).to.throw(TypeError);
|
|
23
|
-
expect(() => this.db.function('f', Object.create(Function.prototype))).to.throw(TypeError);
|
|
24
|
-
expect(() => this.db.function({ name: 'g' }, function g() {})).to.throw(TypeError);
|
|
25
|
-
expect(() => this.db.function(new String('h'), function h() {})).to.throw(TypeError);
|
|
26
|
-
});
|
|
27
|
-
it('should throw an exception if boolean options are provided as non-booleans', function () {
|
|
28
|
-
expect(() => this.db.function('a', { varargs: undefined }, () => {})).to.throw(TypeError);
|
|
29
|
-
expect(() => this.db.function('b', { deterministic: undefined }, () => {})).to.throw(TypeError);
|
|
30
|
-
expect(() => this.db.function('b', { directOnly: undefined }, () => {})).to.throw(TypeError);
|
|
31
|
-
expect(() => this.db.function('c', { safeIntegers: undefined }, () => {})).to.throw(TypeError);
|
|
32
|
-
});
|
|
33
|
-
it('should throw an exception if the provided name is empty', function () {
|
|
34
|
-
expect(() => this.db.function('', function a() {})).to.throw(TypeError);
|
|
35
|
-
expect(() => this.db.function('', { name: 'b' }, function b() {})).to.throw(TypeError);
|
|
36
|
-
});
|
|
37
|
-
it('should throw an exception if function.length is invalid', function () {
|
|
38
|
-
const length = x => Object.defineProperty(() => {}, 'length', { value: x });
|
|
39
|
-
expect(() => this.db.function('a', length(undefined))).to.throw(TypeError);
|
|
40
|
-
expect(() => this.db.function('b', length(null))).to.throw(TypeError);
|
|
41
|
-
expect(() => this.db.function('c', length('2'))).to.throw(TypeError);
|
|
42
|
-
expect(() => this.db.function('d', length(NaN))).to.throw(TypeError);
|
|
43
|
-
expect(() => this.db.function('e', length(Infinity))).to.throw(TypeError);
|
|
44
|
-
expect(() => this.db.function('f', length(2.000000001))).to.throw(TypeError);
|
|
45
|
-
expect(() => this.db.function('g', length(-0.000000001))).to.throw(TypeError);
|
|
46
|
-
expect(() => this.db.function('h', length(-2))).to.throw(TypeError);
|
|
47
|
-
expect(() => this.db.function('i', length(100.000000001))).to.throw(TypeError);
|
|
48
|
-
expect(() => this.db.function('j', length(101))).to.throw(RangeError);
|
|
49
|
-
});
|
|
50
|
-
it('should register the given function and return the database object', function () {
|
|
51
|
-
expect(this.db.function('a', () => {})).to.equal(this.db);
|
|
52
|
-
expect(this.db.function('b', {}, () => {})).to.equal(this.db);
|
|
53
|
-
expect(this.db.function('c', function x() {})).to.equal(this.db);
|
|
54
|
-
expect(this.db.function('d', {}, function y() {})).to.equal(this.db);
|
|
55
|
-
});
|
|
56
|
-
it('should enable the registered function to be executed from SQL', function () {
|
|
57
|
-
// numbers and strings
|
|
58
|
-
this.db.function('a', (a, b, c) => a + b + c);
|
|
59
|
-
expect(this.get('a(?, ?, ?)', 2, 10, 50)).to.equal(62);
|
|
60
|
-
expect(this.get('a(?, ?, ?)', 2, 10, null)).to.equal(12);
|
|
61
|
-
expect(this.get('a(?, ?, ?)', 'foo', 'z', 12)).to.equal('fooz12');
|
|
62
|
-
|
|
63
|
-
// undefined is interpreted as null
|
|
64
|
-
this.db.function('b', (a, b) => null);
|
|
65
|
-
this.db.function('c', (a, b) => {});
|
|
66
|
-
expect(this.get('b(?, ?)', 2, 10)).to.equal(null);
|
|
67
|
-
expect(this.get('c(?, ?)', 2, 10)).to.equal(null);
|
|
68
|
-
|
|
69
|
-
// buffers
|
|
70
|
-
this.db.function('d', function foo(x) { return x; });
|
|
71
|
-
const input = Buffer.alloc(8).fill(0xdd);
|
|
72
|
-
const output = this.get('d(?)', input);
|
|
73
|
-
expect(input).to.not.equal(output);
|
|
74
|
-
expect(input.equals(output)).to.be.true;
|
|
75
|
-
expect(output.equals(Buffer.alloc(8).fill(0xdd))).to.be.true;
|
|
76
|
-
|
|
77
|
-
// should not register based on function.name
|
|
78
|
-
expect(() => this.get('foo(?)', input)).to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
|
|
79
|
-
|
|
80
|
-
// zero arguments
|
|
81
|
-
this.db.function('e', () => 12);
|
|
82
|
-
expect(this.get('e()')).to.equal(12);
|
|
83
|
-
});
|
|
84
|
-
it('should use a strict number of arguments by default', function () {
|
|
85
|
-
this.db.function('fn', (a, b) => {});
|
|
86
|
-
expect(() => this.get('fn()')).to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
|
|
87
|
-
expect(() => this.get('fn(?)', 4)).to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
|
|
88
|
-
expect(() => this.get('fn(?, ?, ?)', 4, 8, 3)).to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
|
|
89
|
-
this.get('fn(?, ?)', 4, 8);
|
|
90
|
-
});
|
|
91
|
-
it('should accept a "varargs" option', function () {
|
|
92
|
-
const fn = (...args) => args.reduce((a, b) => a * b, 1);
|
|
93
|
-
Object.defineProperty(fn, 'length', { value: '-2' });
|
|
94
|
-
this.db.function('fn', { varargs: true }, fn);
|
|
95
|
-
expect(this.get('fn()')).to.equal(1);
|
|
96
|
-
expect(this.get('fn(?)', 7)).to.equal(7);
|
|
97
|
-
expect(this.get('fn(?, ?)', 4, 8)).to.equal(32);
|
|
98
|
-
expect(this.get('fn(?, ?, ?, ?, ?, ?)', 2, 3, 4, 5, 6, 7)).to.equal(5040);
|
|
99
|
-
});
|
|
100
|
-
it('should cause the function to throw when returning an invalid value', function () {
|
|
101
|
-
this.db.function('fn', x => ({}));
|
|
102
|
-
expect(() => this.get('fn(?)', 42)).to.throw(TypeError);
|
|
103
|
-
});
|
|
104
|
-
it('should propagate exceptions thrown in the registered function', function () {
|
|
105
|
-
const expectError = (name, exception) => {
|
|
106
|
-
this.db.function(name, () => { throw exception; });
|
|
107
|
-
try {
|
|
108
|
-
this.get(name + '()');
|
|
109
|
-
} catch (ex) {
|
|
110
|
-
expect(ex).to.equal(exception);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
throw new TypeError('Expected function to throw an exception');
|
|
114
|
-
};
|
|
115
|
-
expectError('a', new TypeError('foobar'));
|
|
116
|
-
expectError('b', new Error('baz'));
|
|
117
|
-
expectError('c', { yup: 'ok' });
|
|
118
|
-
expectError('d', 'foobarbazqux');
|
|
119
|
-
expectError('e', '');
|
|
120
|
-
expectError('f', null);
|
|
121
|
-
expectError('g', 123.4);
|
|
122
|
-
});
|
|
123
|
-
it('should close a statement iterator that caused its function to throw', function () {
|
|
124
|
-
this.db.prepare('CREATE TABLE iterable (value INTEGER)').run();
|
|
125
|
-
this.db.prepare('INSERT INTO iterable WITH RECURSIVE temp(x) AS (SELECT 1 UNION ALL SELECT x * 2 FROM temp LIMIT 10) SELECT * FROM temp').run();
|
|
126
|
-
|
|
127
|
-
let i = 0;
|
|
128
|
-
const err = new Error('foo');
|
|
129
|
-
this.db.function('fn', (x) => { if (++i >= 5) throw err; return x; });
|
|
130
|
-
const iterator = this.db.prepare('SELECT fn(value) FROM iterable').pluck().iterate();
|
|
131
|
-
|
|
132
|
-
let total = 0;
|
|
133
|
-
expect(() => {
|
|
134
|
-
for (const value of iterator) {
|
|
135
|
-
total += value;
|
|
136
|
-
expect(() => this.db.exec('SELECT fn(value) FROM iterable LIMIT 4')).to.throw(TypeError);
|
|
137
|
-
}
|
|
138
|
-
}).to.throw(err);
|
|
139
|
-
|
|
140
|
-
expect(total).to.equal(1 + 2 + 4 + 8);
|
|
141
|
-
expect(iterator.next()).to.deep.equal({ value: undefined, done: true });
|
|
142
|
-
expect(total).to.equal(1 + 2 + 4 + 8);
|
|
143
|
-
|
|
144
|
-
i = 0;
|
|
145
|
-
this.db.exec('SELECT fn(value) FROM iterable LIMIT 4');
|
|
146
|
-
expect(i).to.equal(4);
|
|
147
|
-
});
|
|
148
|
-
it('should be able to register multiple functions with the same name', function () {
|
|
149
|
-
this.db.function('fn', () => 0);
|
|
150
|
-
this.db.function('fn', (a) => 1);
|
|
151
|
-
this.db.function('fn', (a, b) => 2);
|
|
152
|
-
this.db.function('fn', (a, b, c) => 3);
|
|
153
|
-
this.db.function('fn', (a, b, c, d) => 4);
|
|
154
|
-
expect(this.get('fn()')).to.equal(0);
|
|
155
|
-
expect(this.get('fn(555)')).to.equal(1);
|
|
156
|
-
expect(this.get('fn(555, 555)')).to.equal(2);
|
|
157
|
-
expect(this.get('fn(555, 555, 555)')).to.equal(3);
|
|
158
|
-
expect(this.get('fn(555, 555, 555, 555)')).to.equal(4);
|
|
159
|
-
this.db.function('fn', (a, b) => 'foobar');
|
|
160
|
-
expect(this.get('fn()')).to.equal(0);
|
|
161
|
-
expect(this.get('fn(555)')).to.equal(1);
|
|
162
|
-
expect(this.get('fn(555, 555)')).to.equal('foobar');
|
|
163
|
-
expect(this.get('fn(555, 555, 555)')).to.equal(3);
|
|
164
|
-
expect(this.get('fn(555, 555, 555, 555)')).to.equal(4);
|
|
165
|
-
});
|
|
166
|
-
it('should not be able to affect bound buffers mid-query', function () {
|
|
167
|
-
const input = Buffer.alloc(1024 * 8).fill(0xbb);
|
|
168
|
-
let ranOnce = false;
|
|
169
|
-
this.db.function('fn', () => {
|
|
170
|
-
ranOnce = true;
|
|
171
|
-
input[0] = 2;
|
|
172
|
-
});
|
|
173
|
-
const output = this.get('?, fn()', input);
|
|
174
|
-
expect(ranOnce).to.be.true;
|
|
175
|
-
expect(output.equals(Buffer.alloc(1024 * 8).fill(0xbb))).to.be.true;
|
|
176
|
-
});
|
|
177
|
-
describe('should not affect external environment', function () {
|
|
178
|
-
specify('busy state', function () {
|
|
179
|
-
this.db.function('fn', (x) => {
|
|
180
|
-
expect(() => this.db.exec('SELECT 555')).to.throw(TypeError);
|
|
181
|
-
return x * 2;
|
|
182
|
-
});
|
|
183
|
-
let ranOnce = false;
|
|
184
|
-
for (const x of this.db.prepare('SELECT fn(555)').pluck().iterate()) {
|
|
185
|
-
ranOnce = true;
|
|
186
|
-
expect(x).to.equal(1110);
|
|
187
|
-
expect(() => this.db.exec('SELECT 555')).to.throw(TypeError);
|
|
188
|
-
}
|
|
189
|
-
expect(ranOnce).to.be.true;
|
|
190
|
-
this.db.exec('SELECT 555');
|
|
191
|
-
});
|
|
192
|
-
specify('was_js_error state', function () {
|
|
193
|
-
this.db.prepare('CREATE TABLE data (value INTEGER)').run();
|
|
194
|
-
const stmt = this.db.prepare('SELECT value FROM data');
|
|
195
|
-
this.db.prepare('DROP TABLE data').run();
|
|
196
|
-
|
|
197
|
-
const err = new Error('foo');
|
|
198
|
-
this.db.function('fn', () => { throw err; });
|
|
199
|
-
|
|
200
|
-
expect(() => this.db.prepare('SELECT fn()').get()).to.throw(err);
|
|
201
|
-
try { stmt.get(); } catch (ex) {
|
|
202
|
-
expect(ex).to.be.an.instanceof(Error);
|
|
203
|
-
expect(ex).to.not.equal(err);
|
|
204
|
-
expect(ex.message).to.not.equal(err.message);
|
|
205
|
-
expect(ex).to.be.an.instanceof(Database.SqliteError);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
throw new TypeError('Expected the statement to throw an exception');
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
});
|