PR-URL: https://github.com/nodejs/node/pull/61298 Fixes: https://github.com/nodejs/node/issues/61268 Reviewed-By: René <contact.9a5d6388@renegade334.me.uk> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
304 lines
8.3 KiB
JavaScript
304 lines
8.3 KiB
JavaScript
'use strict';
|
|
const { skipIfSQLiteMissing } = require('../common');
|
|
skipIfSQLiteMissing();
|
|
const { DatabaseSync } = require('node:sqlite');
|
|
const { suite, test } = require('node:test');
|
|
|
|
suite('DatabaseSync limits', () => {
|
|
test('limits object has expected properties with positive values', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
const expectedProperties = [
|
|
'length',
|
|
'sqlLength',
|
|
'column',
|
|
'exprDepth',
|
|
'compoundSelect',
|
|
'vdbeOp',
|
|
'functionArg',
|
|
'attach',
|
|
'likePatternLength',
|
|
'variableNumber',
|
|
'triggerDepth',
|
|
];
|
|
|
|
for (const prop of expectedProperties) {
|
|
t.assert.strictEqual(typeof db.limits[prop], 'number',
|
|
`${prop} should be a number`);
|
|
t.assert.ok(db.limits[prop] > 0,
|
|
`${prop} should be positive`);
|
|
}
|
|
});
|
|
|
|
test('constructor accepts limits option', (t) => {
|
|
const db = new DatabaseSync(':memory:', {
|
|
limits: {
|
|
length: 500000,
|
|
sqlLength: 50000,
|
|
column: 100,
|
|
exprDepth: 50,
|
|
compoundSelect: 10,
|
|
vdbeOp: 10000,
|
|
functionArg: 8,
|
|
attach: 5,
|
|
likePatternLength: 1000,
|
|
variableNumber: 100,
|
|
triggerDepth: 5,
|
|
}
|
|
});
|
|
|
|
t.assert.strictEqual(db.limits.length, 500000);
|
|
t.assert.strictEqual(db.limits.sqlLength, 50000);
|
|
t.assert.strictEqual(db.limits.column, 100);
|
|
t.assert.strictEqual(db.limits.exprDepth, 50);
|
|
t.assert.strictEqual(db.limits.compoundSelect, 10);
|
|
t.assert.strictEqual(db.limits.vdbeOp, 10000);
|
|
t.assert.strictEqual(db.limits.functionArg, 8);
|
|
t.assert.strictEqual(db.limits.attach, 5);
|
|
t.assert.strictEqual(db.limits.likePatternLength, 1000);
|
|
t.assert.strictEqual(db.limits.variableNumber, 100);
|
|
t.assert.strictEqual(db.limits.triggerDepth, 5);
|
|
});
|
|
|
|
test('getter returns current limit value', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
t.assert.strictEqual(typeof db.limits.length, 'number');
|
|
t.assert.ok(db.limits.length > 0);
|
|
t.assert.strictEqual(typeof db.limits.sqlLength, 'number');
|
|
t.assert.ok(db.limits.sqlLength > 0);
|
|
});
|
|
|
|
test('setter modifies limit value', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
|
|
db.limits.length = 100000;
|
|
t.assert.strictEqual(db.limits.length, 100000);
|
|
|
|
db.limits.sqlLength = 50000;
|
|
t.assert.strictEqual(db.limits.sqlLength, 50000);
|
|
|
|
db.limits.column = 50;
|
|
t.assert.strictEqual(db.limits.column, 50);
|
|
});
|
|
|
|
test('Infinity resets limit to maximum', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
const originalLength = db.limits.length;
|
|
|
|
// Set to a lower value
|
|
db.limits.length = 100;
|
|
t.assert.strictEqual(db.limits.length, 100);
|
|
|
|
// Reset to maximum using Infinity
|
|
db.limits.length = Infinity;
|
|
t.assert.strictEqual(db.limits.length, originalLength);
|
|
});
|
|
|
|
test('throws on invalid argument type', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
t.assert.throws(() => {
|
|
db.limits.length = 'invalid';
|
|
}, {
|
|
name: 'TypeError',
|
|
message: /Limit value must be a non-negative integer or Infinity/,
|
|
});
|
|
});
|
|
|
|
test('throws on negative value', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
t.assert.throws(() => {
|
|
db.limits.length = -1;
|
|
}, {
|
|
name: 'RangeError',
|
|
message: /Limit value must be non-negative/,
|
|
});
|
|
});
|
|
|
|
test('throws on null value', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
t.assert.throws(() => {
|
|
db.limits.length = null;
|
|
}, {
|
|
name: 'TypeError',
|
|
message: /Limit value must be a non-negative integer or Infinity/,
|
|
});
|
|
});
|
|
|
|
test('throws on negative Infinity', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
t.assert.throws(() => {
|
|
db.limits.length = -Infinity;
|
|
}, {
|
|
name: 'TypeError',
|
|
message: /Limit value must be a non-negative integer or Infinity/,
|
|
});
|
|
});
|
|
|
|
test('throws on getter access after close', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
db.close();
|
|
t.assert.throws(() => {
|
|
return db.limits.length;
|
|
}, {
|
|
code: 'ERR_INVALID_STATE',
|
|
message: /database is not open/,
|
|
});
|
|
});
|
|
|
|
test('throws on setter access after close', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
db.close();
|
|
t.assert.throws(() => {
|
|
db.limits.length = 100;
|
|
}, {
|
|
code: 'ERR_INVALID_STATE',
|
|
message: /database is not open/,
|
|
});
|
|
});
|
|
|
|
test('limits object is enumerable', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
const keys = Object.keys(db.limits);
|
|
t.assert.ok(keys.includes('length'));
|
|
t.assert.ok(keys.includes('sqlLength'));
|
|
t.assert.ok(keys.includes('column'));
|
|
t.assert.ok(keys.includes('exprDepth'));
|
|
t.assert.ok(keys.includes('compoundSelect'));
|
|
t.assert.ok(keys.includes('vdbeOp'));
|
|
t.assert.ok(keys.includes('functionArg'));
|
|
t.assert.ok(keys.includes('attach'));
|
|
t.assert.ok(keys.includes('likePatternLength'));
|
|
t.assert.ok(keys.includes('variableNumber'));
|
|
t.assert.ok(keys.includes('triggerDepth'));
|
|
});
|
|
|
|
test('throws on invalid limits option type', (t) => {
|
|
t.assert.throws(() => {
|
|
new DatabaseSync(':memory:', { limits: 'invalid' });
|
|
}, {
|
|
name: 'TypeError',
|
|
message: /options\.limits.*must be an object/,
|
|
});
|
|
});
|
|
|
|
test('throws on invalid limit value type in constructor', (t) => {
|
|
t.assert.throws(() => {
|
|
new DatabaseSync(':memory:', { limits: { length: 'invalid' } });
|
|
}, {
|
|
name: 'TypeError',
|
|
message: /options\.limits\.length.*must be an integer/,
|
|
});
|
|
});
|
|
|
|
test('throws on negative limit value in constructor', (t) => {
|
|
t.assert.throws(() => {
|
|
new DatabaseSync(':memory:', { limits: { length: -100 } });
|
|
}, {
|
|
name: 'RangeError',
|
|
message: /options\.limits\.length.*must be non-negative/,
|
|
});
|
|
});
|
|
|
|
test('throws on Infinity limit value in constructor', (t) => {
|
|
t.assert.throws(() => {
|
|
new DatabaseSync(':memory:', { limits: { length: Infinity } });
|
|
}, {
|
|
name: 'TypeError',
|
|
message: /options\.limits\.length.*must be an integer/,
|
|
});
|
|
});
|
|
|
|
test('partial limits in constructor', (t) => {
|
|
const db = new DatabaseSync(':memory:', {
|
|
limits: {
|
|
length: 100000,
|
|
}
|
|
});
|
|
t.assert.strictEqual(db.limits.length, 100000);
|
|
t.assert.strictEqual(typeof db.limits.sqlLength, 'number');
|
|
});
|
|
|
|
test('throws when exceeding column limit', (t) => {
|
|
const db = new DatabaseSync(':memory:', {
|
|
limits: {
|
|
column: 10,
|
|
}
|
|
});
|
|
|
|
db.exec('CREATE TABLE t1 (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)');
|
|
|
|
t.assert.throws(() => {
|
|
db.exec('CREATE TABLE t2 (c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11)');
|
|
}, {
|
|
message: /too many columns/,
|
|
});
|
|
});
|
|
|
|
test('throws when exceeding attach limit', (t) => {
|
|
const db = new DatabaseSync(':memory:', {
|
|
limits: {
|
|
attach: 0,
|
|
}
|
|
});
|
|
|
|
t.assert.throws(() => {
|
|
db.exec("ATTACH DATABASE ':memory:' AS db1");
|
|
}, {
|
|
message: /too many attached databases/,
|
|
});
|
|
});
|
|
|
|
test('throws when exceeding variable number limit', (t) => {
|
|
const db = new DatabaseSync(':memory:', {
|
|
limits: {
|
|
variableNumber: 2,
|
|
}
|
|
});
|
|
|
|
t.assert.throws(() => {
|
|
const stmt = db.prepare('SELECT ?, ?, ?');
|
|
stmt.all(1, 2, 3);
|
|
}, {
|
|
message: /too many SQL variables/,
|
|
});
|
|
});
|
|
|
|
test('throws when exceeding compound select limit', (t) => {
|
|
const db = new DatabaseSync(':memory:', {
|
|
limits: {
|
|
compoundSelect: 1,
|
|
}
|
|
});
|
|
|
|
t.assert.throws(() => {
|
|
db.exec('SELECT 1 UNION SELECT 2 UNION SELECT 3');
|
|
}, {
|
|
message: /too many terms in compound SELECT/,
|
|
});
|
|
});
|
|
|
|
test('throws when exceeding function arg limit', (t) => {
|
|
const db = new DatabaseSync(':memory:', {
|
|
limits: {
|
|
functionArg: 2,
|
|
}
|
|
});
|
|
|
|
t.assert.throws(() => {
|
|
db.exec('SELECT max(1, 2, 3)');
|
|
}, {
|
|
message: /too many arguments on function max/,
|
|
});
|
|
});
|
|
|
|
test('setter applies limit to SQLite immediately', (t) => {
|
|
const db = new DatabaseSync(':memory:');
|
|
|
|
db.limits.attach = 0;
|
|
|
|
t.assert.throws(() => {
|
|
db.exec("ATTACH DATABASE ':memory:' AS db1");
|
|
}, {
|
|
message: /too many attached databases/,
|
|
});
|
|
});
|
|
});
|