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.
Files changed (81) hide show
  1. package/README.md +1 -1
  2. package/deps/download.sh +111 -108
  3. package/deps/setup.ps1 +9 -11
  4. package/deps/sqlite3/sqlite3.c +272560 -0
  5. package/deps/sqlite3/sqlite3.h +12770 -0
  6. package/deps/sqlite3/sqlite3ext.h +675 -0
  7. package/deps/sqlite3.gyp +13 -7
  8. package/deps/symlink.js +7 -4
  9. package/lib/database.js +17 -6
  10. package/lib/sqlite-error.js +1 -2
  11. package/package.json +10 -4
  12. package/src/better_sqlite3.cpp +46 -35
  13. package/src/better_sqlite3.hpp +40 -38
  14. package/.gitattributes +0 -1
  15. package/.github/workflows/prebuild.yml +0 -49
  16. package/.github/workflows/test.yml +0 -59
  17. package/benchmark/benchmark.js +0 -31
  18. package/benchmark/drivers.js +0 -21
  19. package/benchmark/index.js +0 -83
  20. package/benchmark/seed.js +0 -47
  21. package/benchmark/trials.js +0 -65
  22. package/benchmark/types/insert.js +0 -16
  23. package/benchmark/types/select-all.js +0 -14
  24. package/benchmark/types/select-iterate.js +0 -23
  25. package/benchmark/types/select.js +0 -14
  26. package/benchmark/types/transaction.js +0 -40
  27. package/deps/extract.js +0 -16
  28. package/deps/sqlite3.tar.gz +0 -0
  29. package/docs/api.md +0 -645
  30. package/docs/benchmark.md +0 -38
  31. package/docs/compilation.md +0 -76
  32. package/docs/integer.md +0 -79
  33. package/docs/performance.md +0 -39
  34. package/docs/threads.md +0 -97
  35. package/docs/tips.md +0 -35
  36. package/docs/troubleshooting.md +0 -23
  37. package/docs/unsafe.md +0 -16
  38. package/src/better_sqlite3.lzz +0 -88
  39. package/src/objects/backup.lzz +0 -138
  40. package/src/objects/database.lzz +0 -468
  41. package/src/objects/statement-iterator.lzz +0 -138
  42. package/src/objects/statement.lzz +0 -323
  43. package/src/util/bind-map.lzz +0 -73
  44. package/src/util/binder.lzz +0 -190
  45. package/src/util/constants.lzz +0 -151
  46. package/src/util/custom-aggregate.lzz +0 -121
  47. package/src/util/custom-function.lzz +0 -59
  48. package/src/util/custom-table.lzz +0 -397
  49. package/src/util/data-converter.lzz +0 -17
  50. package/src/util/data.lzz +0 -145
  51. package/src/util/macros.lzz +0 -159
  52. package/src/util/query-macros.lzz +0 -71
  53. package/test/00.setup.js +0 -25
  54. package/test/01.sqlite-error.js +0 -27
  55. package/test/10.database.open.js +0 -159
  56. package/test/11.database.close.js +0 -68
  57. package/test/12.database.pragma.js +0 -65
  58. package/test/13.database.prepare.js +0 -60
  59. package/test/14.database.exec.js +0 -46
  60. package/test/20.statement.run.js +0 -170
  61. package/test/21.statement.get.js +0 -109
  62. package/test/22.statement.all.js +0 -129
  63. package/test/23.statement.iterate.js +0 -223
  64. package/test/24.statement.bind.js +0 -107
  65. package/test/25.statement.columns.js +0 -46
  66. package/test/30.database.transaction.js +0 -157
  67. package/test/31.database.checkpoint.js +0 -62
  68. package/test/32.database.function.js +0 -211
  69. package/test/33.database.aggregate.js +0 -603
  70. package/test/34.database.table.js +0 -671
  71. package/test/35.database.load-extension.js +0 -75
  72. package/test/36.database.backup.js +0 -240
  73. package/test/37.database.serialize.js +0 -81
  74. package/test/40.bigints.js +0 -145
  75. package/test/41.at-exit.js +0 -52
  76. package/test/42.integrity.js +0 -531
  77. package/test/43.verbose.js +0 -100
  78. package/test/44.worker-threads.js +0 -66
  79. package/test/45.unsafe-mode.js +0 -52
  80. package/test/46.encryption.js +0 -69
  81. package/test/50.misc.js +0 -44
@@ -1,603 +0,0 @@
1
- 'use strict';
2
- const Database = require('../.');
3
-
4
- describe('Database#aggregate()', function () {
5
- beforeEach(function () {
6
- this.db = new Database(util.next());
7
- this.db.prepare('CREATE TABLE empty (_)').run();
8
- this.db.prepare('CREATE TABLE ints (_)').run();
9
- this.db.prepare('CREATE TABLE texts (_)').run();
10
- this.db.prepare('INSERT INTO ints VALUES (?), (?), (?), (?), (?), (?), (?)').run(3, 5, 7, 11, 13, 17, 19);
11
- this.db.prepare('INSERT INTO texts VALUES (?), (?), (?), (?), (?), (?), (?)').run('a', 'b', 'c', 'd', 'e', 'f', 'g');
12
- this.get = (SQL, ...args) => this.db.prepare(`SELECT ${SQL}`).pluck().get(args);
13
- this.all = (SQL, ...args) => this.db.prepare(`SELECT ${SQL} WINDOW win AS (ORDER BY rowid ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) ORDER BY rowid`).pluck().all(args);
14
- });
15
- afterEach(function () {
16
- this.db.close();
17
- });
18
-
19
- it('should throw an exception if the correct arguments are not provided', function () {
20
- expect(() => this.db.aggregate()).to.throw(TypeError);
21
- expect(() => this.db.aggregate(null)).to.throw(TypeError);
22
- expect(() => this.db.aggregate('a')).to.throw(TypeError);
23
- expect(() => this.db.aggregate({})).to.throw(TypeError);
24
- expect(() => this.db.aggregate({ step: () => {} })).to.throw(TypeError);
25
- expect(() => this.db.aggregate({ name: 'b', step: function b() {} })).to.throw(TypeError);
26
- expect(() => this.db.aggregate(() => {})).to.throw(TypeError);
27
- expect(() => this.db.aggregate(function c() {})).to.throw(TypeError);
28
- expect(() => this.db.aggregate({}, function d() {})).to.throw(TypeError);
29
- expect(() => this.db.aggregate({ name: 'e', step: function e() {} }, function e() {})).to.throw(TypeError);
30
- expect(() => this.db.aggregate('f')).to.throw(TypeError);
31
- expect(() => this.db.aggregate('g', null)).to.throw(TypeError);
32
- expect(() => this.db.aggregate('h', {})).to.throw(TypeError);
33
- expect(() => this.db.aggregate('i', function i() {})).to.throw(TypeError);
34
- expect(() => this.db.aggregate('j', {}, function j() {})).to.throw(TypeError);
35
- expect(() => this.db.aggregate('k', { name: 'k' }, function k() {})).to.throw(TypeError);
36
- expect(() => this.db.aggregate('l', { inverse: function l() {} })).to.throw(TypeError);
37
- expect(() => this.db.aggregate('m', { result: function m() {} })).to.throw(TypeError);
38
- expect(() => this.db.aggregate(new String('n'), { step: function n() {} })).to.throw(TypeError);
39
- });
40
- it('should throw an exception if boolean options are provided as non-booleans', function () {
41
- expect(() => this.db.aggregate('a', { step: () => {}, varargs: undefined })).to.throw(TypeError);
42
- expect(() => this.db.aggregate('b', { step: () => {}, deterministic: undefined })).to.throw(TypeError);
43
- expect(() => this.db.aggregate('b', { step: () => {}, directOnly: undefined })).to.throw(TypeError);
44
- expect(() => this.db.aggregate('c', { step: () => {}, safeIntegers: undefined })).to.throw(TypeError);
45
- });
46
- it('should throw an exception if function options are provided as non-fns', function () {
47
- expect(() => this.db.aggregate('a', { step: undefined })).to.throw(TypeError);
48
- expect(() => this.db.aggregate('b', { step: null })).to.throw(TypeError);
49
- expect(() => this.db.aggregate('c', { step: false })).to.throw(TypeError);
50
- expect(() => this.db.aggregate('d', { step: true })).to.throw(TypeError);
51
- expect(() => this.db.aggregate('e', { step: Object.create(Function.prototype) })).to.throw(TypeError);
52
- expect(() => this.db.aggregate('f', { step: () => {}, inverse: false })).to.throw(TypeError);
53
- expect(() => this.db.aggregate('g', { step: () => {}, inverse: true })).to.throw(TypeError);
54
- expect(() => this.db.aggregate('h', { step: () => {}, inverse: Object.create(Function.prototype) })).to.throw(TypeError);
55
- expect(() => this.db.aggregate('i', { step: () => {}, result: false })).to.throw(TypeError);
56
- expect(() => this.db.aggregate('j', { step: () => {}, result: true })).to.throw(TypeError);
57
- expect(() => this.db.aggregate('k', { step: () => {}, result: Object.create(Function.prototype) })).to.throw(TypeError);
58
- });
59
- it('should throw an exception if the provided name is empty', function () {
60
- expect(() => this.db.aggregate('', { step: () => {} })).to.throw(TypeError);
61
- expect(() => this.db.aggregate('', { name: 'a', step: () => {} })).to.throw(TypeError);
62
- expect(() => this.db.aggregate('', { name: 'b', step: function b() {} })).to.throw(TypeError);
63
- });
64
- it('should throw an exception if step.length or inverse.length is invalid', function () {
65
- const length = x => Object.defineProperty(() => {}, 'length', { value: x });
66
- expect(() => this.db.aggregate('a', { step: length(undefined) })).to.throw(TypeError);
67
- expect(() => this.db.aggregate('b', { step: length(null) })).to.throw(TypeError);
68
- expect(() => this.db.aggregate('c', { step: length('2') })).to.throw(TypeError);
69
- expect(() => this.db.aggregate('d', { step: length(NaN) })).to.throw(TypeError);
70
- expect(() => this.db.aggregate('e', { step: length(Infinity) })).to.throw(TypeError);
71
- expect(() => this.db.aggregate('f', { step: length(2.000000001) })).to.throw(TypeError);
72
- expect(() => this.db.aggregate('g', { step: length(-0.000000001) })).to.throw(TypeError);
73
- expect(() => this.db.aggregate('h', { step: length(-2) })).to.throw(TypeError);
74
- expect(() => this.db.aggregate('i', { step: length(100.000000001) })).to.throw(TypeError);
75
- expect(() => this.db.aggregate('j', { step: length(102) })).to.throw(RangeError);
76
- expect(() => this.db.aggregate('aa', { step: () => {}, inverse: length(undefined) })).to.throw(TypeError);
77
- expect(() => this.db.aggregate('bb', { step: () => {}, inverse: length(null) })).to.throw(TypeError);
78
- expect(() => this.db.aggregate('cc', { step: () => {}, inverse: length('2') })).to.throw(TypeError);
79
- expect(() => this.db.aggregate('dd', { step: () => {}, inverse: length(NaN) })).to.throw(TypeError);
80
- expect(() => this.db.aggregate('ee', { step: () => {}, inverse: length(Infinity) })).to.throw(TypeError);
81
- expect(() => this.db.aggregate('ff', { step: () => {}, inverse: length(2.000000001) })).to.throw(TypeError);
82
- expect(() => this.db.aggregate('gg', { step: () => {}, inverse: length(-0.000000001) })).to.throw(TypeError);
83
- expect(() => this.db.aggregate('hh', { step: () => {}, inverse: length(-2) })).to.throw(TypeError);
84
- expect(() => this.db.aggregate('ii', { step: () => {}, inverse: length(100.000000001) })).to.throw(TypeError);
85
- expect(() => this.db.aggregate('jj', { step: () => {}, inverse: length(102) })).to.throw(RangeError);
86
- });
87
- it('should register an aggregate function and return the database object', function () {
88
- const length = x => Object.defineProperty(() => {}, 'length', { value: x });
89
- expect(this.db.aggregate('a', { step: () => {} })).to.equal(this.db);
90
- expect(this.db.aggregate('b', { step: function x() {} })).to.equal(this.db);
91
- expect(this.db.aggregate('c', { step: length(1) })).to.equal(this.db);
92
- expect(this.db.aggregate('d', { step: length(101) })).to.equal(this.db);
93
- });
94
- it('should enable the registered aggregate function to be executed from SQL', function () {
95
- // numbers
96
- this.db.aggregate('a', { step: (ctx, a, b) => a * b + ctx });
97
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(150);
98
-
99
- // strings
100
- this.db.aggregate('b', { step: (ctx, a, b) => a + b + ctx });
101
- expect(this.get('b(_, ?) FROM texts', '!')).to.equal('g!f!e!d!c!b!a!null');
102
-
103
- // starting value is null
104
- this.db.aggregate('c', { step: (ctx, x) => null });
105
- this.db.aggregate('d', { step: (ctx, x) => ctx });
106
- this.db.aggregate('e', { step: (ctx, x) => {} });
107
- expect(this.get('c(_) FROM ints')).to.equal(null);
108
- expect(this.get('d(_) FROM ints')).to.equal(null);
109
- expect(this.get('e(_) FROM ints')).to.equal(null);
110
-
111
- // buffers
112
- this.db.aggregate('f', { step: (ctx, x) => x });
113
- const input = Buffer.alloc(8).fill(0xdd);
114
- const output = this.get('f(?)', input);
115
- expect(input).to.not.equal(output);
116
- expect(input.equals(output)).to.be.true;
117
- expect(output.equals(Buffer.alloc(8).fill(0xdd))).to.be.true;
118
-
119
- // zero arguments
120
- this.db.aggregate('g', { step: (ctx) => 'z' + ctx });
121
- this.db.aggregate('h', { step: (ctx) => 12 });
122
- this.db.aggregate('i', { step: () => 44 });
123
- expect(this.get('g()')).to.equal('znull');
124
- expect(this.get('h()')).to.equal(12);
125
- expect(this.get('i()')).to.equal(44);
126
- expect(this.get('g() FROM empty')).to.equal(null);
127
- expect(this.get('h() FROM empty')).to.equal(null);
128
- expect(this.get('i() FROM empty')).to.equal(null);
129
- expect(this.get('g() FROM ints')).to.equal('zzzzzzznull');
130
- expect(this.get('h() FROM ints')).to.equal(12);
131
- expect(this.get('i() FROM ints')).to.equal(44);
132
- expect(this.get('g(*) FROM ints')).to.equal('zzzzzzznull');
133
- expect(this.get('h(*) FROM ints')).to.equal(12);
134
- expect(this.get('i(*) FROM ints')).to.equal(44);
135
- });
136
- it('should use a strict number of arguments by default', function () {
137
- this.db.aggregate('agg', { step: (ctx, a, b) => {} });
138
- expect(() => this.get('agg()')).to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
139
- expect(() => this.get('agg(?)', 4)).to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
140
- expect(() => this.get('agg(?, ?, ?)', 4, 8, 3)).to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
141
- this.get('agg(?, ?)', 4, 8);
142
- });
143
- it('should accept a "varargs" option', function () {
144
- const step = (ctx, ...args) => args.reduce((a, b) => a * b, 1) + ctx;
145
- Object.defineProperty(step, 'length', { value: '-2' });
146
- this.db.aggregate('agg', { varargs: true, step });
147
- expect(this.get('agg()')).to.equal(1);
148
- expect(this.get('agg(?)', 7)).to.equal(7);
149
- expect(this.get('agg(?, ?)', 4, 8)).to.equal(32);
150
- expect(this.get('agg(?, ?, ?, ?, ?, ?)', 2, 3, 4, 5, 6, 7)).to.equal(5040);
151
- });
152
- it('should accept an optional start value', function () {
153
- this.db.aggregate('a', { start: 10000, step: (ctx, a, b) => a * b + ++ctx });
154
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(10157);
155
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(10157);
156
-
157
- this.db.aggregate('b', { start: { foo: 1000 }, step: (ctx, a, b) => a * b + (ctx.foo ? ++ctx.foo : ++ctx) });
158
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(1157);
159
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(1158);
160
-
161
- let ranOnce = false;
162
- this.db.aggregate('c', { start: undefined, step: (ctx, a, b) => {
163
- if (ranOnce) expect(ctx).to.be.NaN;
164
- else expect(ctx).to.be.undefined;
165
- ranOnce = true;
166
- return a * b + ++ctx;
167
- } });
168
- expect(this.get('c(_, ?) FROM ints', 2)).to.equal(null);
169
- expect(ranOnce).to.be.true;
170
- ranOnce = false;
171
- expect(this.get('c(_, ?) FROM ints', 2)).to.equal(null);
172
- expect(ranOnce).to.be.true;
173
- });
174
- it('should accept an optional start() function', function () {
175
- let start = 10000;
176
-
177
- this.db.aggregate('a', { start: () => start++, step: (ctx, a, b) => a * b + ctx });
178
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(10150);
179
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(10151);
180
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(10152);
181
-
182
- this.db.aggregate('b', { start: () => ({ foo: start-- }), step: (ctx, a, b) => a * b + (ctx.foo || ctx) });
183
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(10153);
184
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(10152);
185
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(10151);
186
-
187
- let ranOnce = false;
188
- this.db.aggregate('c', { start: () => undefined, step: (ctx, a, b) => {
189
- if (ranOnce) expect(ctx).to.be.NaN;
190
- else expect(ctx).to.be.undefined;
191
- ranOnce = true;
192
- return a * b + ++ctx;
193
- } });
194
- expect(this.get('c(_, ?) FROM ints', 2)).to.equal(null);
195
- expect(ranOnce).to.be.true;
196
- ranOnce = false;
197
- expect(this.get('c(_, ?) FROM ints', 2)).to.equal(null);
198
- expect(ranOnce).to.be.true;
199
- });
200
- it('should not change the aggregate value when step() returns undefined', function () {
201
- this.db.aggregate('a', { start: 10000, step: (ctx, a, b) => a === 11 ? undefined : a * b + ctx });
202
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(10128);
203
- this.db.aggregate('b', { start: () => 1000, step: (ctx, a, b) => {} });
204
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(1000);
205
- this.db.aggregate('c', { start: () => 1000, step: (ctx, a, b) => null });
206
- expect(this.get('c(_, ?) FROM ints', 2)).to.equal(null);
207
- });
208
- it('should accept a result() transformer function', function () {
209
- this.db.aggregate('a', {
210
- start: 10000,
211
- step: (ctx, a, b) => a * b + ctx,
212
- result: ctx => ctx / 2,
213
- });
214
- expect(this.get('a(_, ?) FROM ints', 2)).to.equal(5075);
215
- this.db.aggregate('b', {
216
- start: () => ({ foo: 1000 }),
217
- step: (ctx, a, b) => { ctx.foo += a * b; },
218
- result: ctx => ctx.foo,
219
- });
220
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(1150);
221
- expect(this.get('b(_, ?) FROM ints', 2)).to.equal(1150); // should play well when ran multiple times
222
- this.db.aggregate('c', {
223
- start: () => ({ foo: 1000 }),
224
- step: (ctx, a, b) => { ctx.foo += 1; },
225
- result: ctx => ctx.foo,
226
- });
227
- expect(this.get('c(_, ?) FROM empty', 2)).to.equal(1000);
228
- });
229
- it('should interpret undefined as null within a result() function', function () {
230
- this.db.aggregate('agg', {
231
- start: 10000,
232
- step: (ctx, a, b) => a * b + ctx,
233
- result: () => {},
234
- });
235
- expect(this.get('agg(_, ?) FROM ints', 2)).to.equal(null);
236
- });
237
- it('should accept an inverse() function to support aggregate window functions', function () {
238
- this.db.aggregate('agg', {
239
- start: () => 10000,
240
- step: (ctx, a, b) => a * b + ctx,
241
- });
242
- expect(() => this.all('agg(_, ?) OVER win FROM ints', 2))
243
- .to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
244
- this.db.aggregate('wn', {
245
- start: () => 10000,
246
- step: (ctx, a, b) => a * b + ctx,
247
- inverse: (ctx, a, b) => ctx - a * b,
248
- });
249
- expect(this.all('wn(_, ?) OVER win FROM ints', 2))
250
- .to.deep.equal([10016, 10030, 10046, 10062, 10082, 10098, 10072]);
251
- });
252
- it('should not change the aggregate value when inverse() returns undefined', function () {
253
- this.db.aggregate('a', {
254
- start: () => 10000,
255
- step: (ctx, a, b) => a * b + ctx,
256
- inverse: (ctx, a, b) => a === 11 ? undefined : ctx - a * b,
257
- });
258
- expect(this.all('a(_, ?) OVER win FROM ints', 2))
259
- .to.deep.equal([10016, 10030, 10046, 10062, 10082, 10120, 10094]);
260
- this.db.aggregate('b', {
261
- start: () => 10000,
262
- step: (ctx, a, b) => ctx ? a * b + ctx : null,
263
- inverse: (ctx, a, b) => null,
264
- });
265
- expect(this.all('b(_, ?) OVER win FROM ints', 2))
266
- .to.deep.equal([10016, 10030, null, null, null, null, null]);
267
- });
268
- it('should potentially call result() multiple times for window functions', function () {
269
- let startCount = 0;
270
- let stepCount = 0;
271
- let inverseCount = 0;
272
- let resultCount = 0;
273
- this.db.aggregate('wn', {
274
- start: () => {
275
- startCount += 1;
276
- return { foo: 1000, results: 0 };
277
- },
278
- step: (ctx, a, b) => {
279
- stepCount += 1;
280
- ctx.foo += a * b;
281
- },
282
- inverse: (ctx, a, b) => {
283
- inverseCount += 1;
284
- ctx.foo -= a * b;
285
- },
286
- result: (ctx) => {
287
- resultCount += 1;
288
- return ctx.foo + ctx.results++ * 10000;
289
- },
290
- });
291
- expect(this.all('wn(_, ?) OVER win FROM ints', 2))
292
- .to.deep.equal([1016, 11030, 21046, 31062, 41082, 51098, 61072]);
293
- expect(startCount).to.equal(1);
294
- expect(stepCount).to.equal(7);
295
- expect(inverseCount).to.equal(5);
296
- expect(resultCount).to.equal(7);
297
- expect(this.all('wn(_, ?) OVER win FROM ints', 2)) // should play well when ran multiple times
298
- .to.deep.equal([1016, 11030, 21046, 31062, 41082, 51098, 61072]);
299
- expect(startCount).to.equal(2);
300
- expect(stepCount).to.equal(14);
301
- expect(inverseCount).to.equal(10);
302
- expect(resultCount).to.equal(14);
303
- expect(this.all('wn(_, ?) OVER win FROM empty', 2))
304
- .to.deep.equal([]);
305
- expect(startCount).to.equal(2);
306
- expect(stepCount).to.equal(14);
307
- expect(inverseCount).to.equal(10);
308
- expect(resultCount).to.equal(14);
309
- });
310
- it('should infer argument count from the greater of step() and inverse()', function () {
311
- this.db.aggregate('a', {
312
- start: () => 10000,
313
- step: (ctx, a) => a + ctx,
314
- inverse: (ctx, a, b) => ctx - a,
315
- });
316
- expect(this.all('a(_, ?) OVER win FROM ints', 2))
317
- .to.deep.equal([10008, 10015, 10023, 10031, 10041, 10049, 10036]);
318
- expect(() => this.all('a(_) OVER win FROM ints'))
319
- .to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
320
- this.db.aggregate('b', {
321
- start: () => 10000,
322
- step: (ctx, a, b) => a + ctx,
323
- inverse: (ctx, a) => ctx - a,
324
- });
325
- expect(this.all('b(_, ?) OVER win FROM ints', 2))
326
- .to.deep.equal([10008, 10015, 10023, 10031, 10041, 10049, 10036]);
327
- expect(() => this.all('b(_) OVER win FROM ints'))
328
- .to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
329
- this.db.aggregate('c', {
330
- start: (a, b, c, d, e) => 10000,
331
- step: () => {},
332
- inverse: (ctx, a) => --ctx,
333
- result: (ctx, a, b, c, d, e) => ctx,
334
- });
335
- expect(this.all('c(_) OVER win FROM ints'))
336
- .to.deep.equal([10000, 10000, 9999, 9998, 9997, 9996, 9995]);
337
- expect(() => this.all('c() OVER win FROM ints'))
338
- .to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
339
- expect(() => this.all('c(*) OVER win FROM ints'))
340
- .to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
341
- expect(() => this.all('c(_, ?) OVER win FROM ints', 2))
342
- .to.throw(Database.SqliteError).with.property('code', 'SQLITE_ERROR');
343
- });
344
- it('should throw an exception if the database is busy', function () {
345
- let ranOnce = false;
346
- for (const x of this.db.prepare('SELECT 2').pluck().iterate()) {
347
- expect(x).to.equal(2);
348
- ranOnce = true;
349
- expect(() => this.db.aggregate('a', { step: () => {} })).to.throw(TypeError);
350
- }
351
- expect(ranOnce).to.be.true;
352
- this.db.aggregate('b', { step: () => {} });
353
- });
354
- it('should cause the database to become busy when executing the aggregate', function () {
355
- let checkCount = 0;
356
- const expectBusy = () => {
357
- expect(() => this.db.exec('SELECT a()')).to.throw(TypeError);
358
- expect(() => this.db.prepare('SELECT 555')).to.throw(TypeError);
359
- expect(() => this.db.pragma('cache_size')).to.throw(TypeError);
360
- expect(() => this.db.function('x', () => {})).to.throw(TypeError);
361
- expect(() => this.db.aggregate('y', { step: () => {} })).to.throw(TypeError);
362
- checkCount += 1;
363
- };
364
- this.db.aggregate('a', { step: () => {} });
365
- this.db.aggregate('b', { start: expectBusy, step: expectBusy, inverse: expectBusy, result: expectBusy });
366
-
367
- expect(this.all('b(*) OVER win FROM ints')).to.deep.equal([null, null, null, null, null, null, null]);
368
- expect(checkCount).to.equal(20);
369
- checkCount = 0;
370
-
371
- expect(this.db.exec('SELECT b(*) OVER win FROM ints WINDOW win AS (ORDER BY rowid ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) ORDER BY rowid')).to.equal(this.db);
372
- expect(checkCount).to.equal(20);
373
-
374
- this.db.exec('SELECT a()');
375
- this.db.prepare('SELECT 555');
376
- this.db.pragma('cache_size');
377
- this.db.function('xx', () => {});
378
- this.db.aggregate('yy', { step: () => {} });
379
- });
380
- it('should cause the aggregate to throw when returning an invalid value', function () {
381
- this.db.aggregate('a', {
382
- start: () => ({}),
383
- step: () => ({}),
384
- inverse: () => ({}),
385
- result: () => 42,
386
- });
387
- this.db.aggregate('b', {
388
- start: () => 42,
389
- step: () => 42,
390
- inverse: () => 42,
391
- result: () => ({}),
392
- });
393
- this.db.aggregate('c', {
394
- step: () => {},
395
- result: () => 42,
396
- });
397
- this.db.aggregate('d', {
398
- step: () => {},
399
- result: () => ({}),
400
- });
401
- expect(this.all('a(*) OVER win FROM ints')).to.deep.equal([42, 42, 42, 42, 42, 42, 42]);
402
- expect(() => this.all('b(*) OVER win FROM ints')).to.throw(TypeError);
403
- expect(this.get('c(*) FROM ints')).to.equal(42);
404
- expect(this.get('c(*) FROM empty')).to.equal(42);
405
- expect(() => this.get('d(*) FROM ints')).to.throw(TypeError);
406
- expect(() => this.get('d(*) FROM empty')).to.throw(TypeError);
407
- });
408
- it('should close a statement iterator that caused its aggregate to throw', function () {
409
- this.db.prepare('CREATE TABLE iterable (value INTEGER)').run();
410
- 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();
411
-
412
- let i = 0;
413
- const err = new Error('foo');
414
- this.db.aggregate('wn', {
415
- step: (ctx, x) => { if (++i >= 5) throw err; return x; },
416
- inverse: () => {},
417
- });
418
- const iterator = this.db.prepare('SELECT wn(value) OVER (ROWS CURRENT ROW) FROM iterable').pluck().iterate();
419
-
420
- let total = 0;
421
- expect(() => {
422
- for (const value of iterator) {
423
- total += value;
424
- expect(() => this.db.exec('SELECT wn(value) OVER (ROWS CURRENT ROW) FROM iterable LIMIT 4')).to.throw(TypeError);
425
- }
426
- }).to.throw(err);
427
-
428
- expect(total).to.equal(1 + 2 + 4 + 8);
429
- expect(iterator.next()).to.deep.equal({ value: undefined, done: true });
430
- expect(total).to.equal(1 + 2 + 4 + 8);
431
-
432
- i = 0;
433
- this.db.exec('SELECT wn(value) OVER (ROWS CURRENT ROW) FROM iterable LIMIT 4');
434
- expect(i).to.equal(4);
435
- });
436
- it('should be able to register multiple aggregates with the same name', function () {
437
- this.db.aggregate('agg', { step: (ctx) => 0 });
438
- this.db.aggregate('agg', { step: (ctx, a) => 1 });
439
- this.db.aggregate('agg', { step: (ctx, a, b) => 2 });
440
- this.db.aggregate('agg', { step: (ctx, a, b, c) => 3, inverse: () => {} });
441
- this.db.aggregate('agg', { step: (ctx, a, b, c, d) => 4 });
442
- expect(this.get('agg()')).to.equal(0);
443
- expect(this.get('agg(555)')).to.equal(1);
444
- expect(this.get('agg(555, 555)')).to.equal(2);
445
- expect(this.get('agg(555, 555, 555)')).to.equal(3);
446
- expect(this.get('agg(555, 555, 555, 555)')).to.equal(4);
447
- this.db.aggregate('agg', { step: (ctx, a, b) => 'foo', inverse: () => {} });
448
- this.db.aggregate('agg', { step: (ctx, a, b, c) => 'bar' });
449
- expect(this.get('agg()')).to.equal(0);
450
- expect(this.get('agg(555)')).to.equal(1);
451
- expect(this.get('agg(555, 555)')).to.equal('foo');
452
- expect(this.get('agg(555, 555, 555)')).to.equal('bar');
453
- expect(this.get('agg(555, 555, 555, 555)')).to.equal(4);
454
- });
455
- it('should not be able to affect bound buffers mid-query', function () {
456
- const input = Buffer.alloc(1024 * 8).fill(0xbb);
457
- let startCalled = false;
458
- let stepCalled = false;
459
- this.db.aggregate('agg', {
460
- start: () => {
461
- startCalled = true;
462
- input[0] = 2;
463
- },
464
- step: () => {
465
- stepCalled = true;
466
- input[0] = 2;
467
- },
468
- });
469
- const output = this.get('?, agg(*) FROM ints', input);
470
- expect(startCalled).to.be.true;
471
- expect(stepCalled).to.be.true;
472
- expect(output.equals(Buffer.alloc(1024 * 8).fill(0xbb))).to.be.true;
473
- });
474
- describe('should propagate exceptions', function () {
475
- const exceptions = [new TypeError('foobar'), new Error('baz'), { yup: 'ok' }, 'foobarbazqux', '', null, 123.4];
476
- const expectError = (exception, fn) => {
477
- try { fn(); } catch (ex) {
478
- expect(ex).to.equal(exception);
479
- return;
480
- }
481
- throw new TypeError('Expected aggregate to throw an exception');
482
- };
483
-
484
- specify('thrown in the start() function', function () {
485
- exceptions.forEach((exception, index) => {
486
- const calls = [];
487
- this.db.aggregate(`wn${index}`, {
488
- start: () => { calls.push('a'); throw exception; },
489
- step: () => { calls.push('b'); },
490
- inverse: () => { calls.push('c'); },
491
- result: () => { calls.push('d'); },
492
- });
493
- expectError(exception, () => this.get(`wn${index}() FROM empty`));
494
- expect(calls.splice(0)).to.deep.equal(['a']);
495
- expectError(exception, () => this.get(`wn${index}() FROM ints`));
496
- expect(calls.splice(0)).to.deep.equal(['a']);
497
- expectError(exception, () => this.all(`wn${index}() OVER win FROM ints`));
498
- expect(calls.splice(0)).to.deep.equal(['a']);
499
- });
500
- });
501
- specify('thrown in the step() function', function () {
502
- exceptions.forEach((exception, index) => {
503
- const calls = [];
504
- this.db.aggregate(`wn${index}`, {
505
- start: () => { calls.push('a'); },
506
- step: () => { calls.push('b'); throw exception; },
507
- inverse: () => { calls.push('c'); },
508
- result: () => { calls.push('d'); },
509
- });
510
- expect(this.get(`wn${index}() FROM empty`)).to.equal(null);
511
- expect(calls.splice(0)).to.deep.equal(['a', 'd']);
512
- expectError(exception, () => this.get(`wn${index}() FROM ints`));
513
- expect(calls.splice(0)).to.deep.equal(['a', 'b']);
514
- expectError(exception, () => this.all(`wn${index}() OVER win FROM ints`));
515
- expect(calls.splice(0)).to.deep.equal(['a', 'b']);
516
- });
517
- });
518
- specify('thrown in the inverse() function', function () {
519
- exceptions.forEach((exception, index) => {
520
- const calls = [];
521
- this.db.aggregate(`wn${index}`, {
522
- start: () => { calls.push('a'); },
523
- step: () => { calls.push('b'); },
524
- inverse: () => { calls.push('c'); throw exception; },
525
- result: () => { calls.push('d'); },
526
- });
527
- expect(this.get(`wn${index}() FROM empty`)).to.equal(null);
528
- expect(calls.splice(0)).to.deep.equal(['a', 'd']);
529
- expect(this.get(`wn${index}() FROM ints`)).to.equal(null);
530
- expect(calls.splice(0)).to.deep.equal(['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'd']);
531
- expectError(exception, () => this.all(`wn${index}() OVER win FROM ints`));
532
- expect(calls.length).to.be.above(2);
533
- expect(calls.indexOf('c')).to.equal(calls.length - 1);
534
- });
535
- });
536
- specify('thrown in the result() function', function () {
537
- exceptions.forEach((exception, index) => {
538
- const calls = [];
539
- this.db.aggregate(`wn${index}`, {
540
- start: () => { calls.push('a'); },
541
- step: () => { calls.push('b'); },
542
- inverse: () => { calls.push('c'); },
543
- result: () => { calls.push('d'); throw exception; },
544
- });
545
- expectError(exception, () => this.get(`wn${index}() FROM empty`));
546
- expect(calls.splice(0)).to.deep.equal(['a', 'd']);
547
- expectError(exception, () => this.get(`wn${index}() FROM ints`));
548
- expect(calls.splice(0)).to.deep.equal(['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'd']);
549
- expectError(exception, () => this.all(`wn${index}() OVER win FROM ints`));
550
- expect(calls.splice(0)).to.deep.equal(['a', 'b', 'b', 'd']);
551
- });
552
- });
553
- specify('thrown due to returning an invalid value', function () {
554
- const calls = [];
555
- this.db.aggregate('wn', {
556
- start: () => { calls.push('a'); },
557
- step: () => { calls.push('b'); },
558
- inverse: () => { calls.push('c'); },
559
- result: () => { calls.push('d'); return {}; },
560
- });
561
- expect(() => this.get('wn() FROM empty')).to.throw(TypeError);
562
- expect(calls.splice(0)).to.deep.equal(['a', 'd']);
563
- expect(() => this.get('wn() FROM ints')).to.throw(TypeError);;
564
- expect(calls.splice(0)).to.deep.equal(['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'd']);
565
- expect(() => this.all('wn() OVER win FROM ints')).to.throw(TypeError);;
566
- expect(calls.splice(0)).to.deep.equal(['a', 'b', 'b', 'd']);
567
- });
568
- });
569
- describe('should not affect external environment', function () {
570
- specify('busy state', function () {
571
- this.db.aggregate('agg', { step: (ctx, x) => {
572
- expect(() => this.db.exec('SELECT 555')).to.throw(TypeError);
573
- return x * 2 + ctx;
574
- } });
575
- let ranOnce = false;
576
- for (const x of this.db.prepare('SELECT agg(555)').pluck().iterate()) {
577
- ranOnce = true;
578
- expect(x).to.equal(1110);
579
- expect(() => this.db.exec('SELECT 555')).to.throw(TypeError);
580
- }
581
- expect(ranOnce).to.be.true;
582
- this.db.exec('SELECT 555');
583
- });
584
- specify('was_js_error state', function () {
585
- this.db.prepare('CREATE TABLE data (value INTEGER)').run();
586
- const stmt = this.db.prepare('SELECT value FROM data');
587
- this.db.prepare('DROP TABLE data').run();
588
-
589
- const err = new Error('foo');
590
- this.db.aggregate('agg', { step: () => { throw err; } });
591
-
592
- expect(() => this.db.prepare('SELECT agg()').get()).to.throw(err);
593
- try { stmt.get(); } catch (ex) {
594
- expect(ex).to.be.an.instanceof(Error);
595
- expect(ex).to.not.equal(err);
596
- expect(ex.message).to.not.equal(err.message);
597
- expect(ex).to.be.an.instanceof(Database.SqliteError);
598
- return;
599
- }
600
- throw new TypeError('Expected the statement to throw an exception');
601
- });
602
- });
603
- });