blob: 1a038df054451b161b4dd701b369b9cf57d5a5e8 [file] [edit]
'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/,
});
});
});