| 'use strict'; |
| const { spawnPromisified } = require('../common'); |
| const tmpdir = require('../common/tmpdir'); |
| const { join } = require('node:path'); |
| const { DatabaseSync, constants } = require('node:sqlite'); |
| const { suite, test } = require('node:test'); |
| const { pathToFileURL } = require('node:url'); |
| let cnt = 0; |
| |
| tmpdir.refresh(); |
| |
| function nextDb() { |
| return join(tmpdir.path, `database-${cnt++}.db`); |
| } |
| |
| suite('accessing the node:sqlite module', () => { |
| test('cannot be accessed without the node: scheme', (t) => { |
| t.assert.throws(() => { |
| require('sqlite'); |
| }, { |
| code: 'MODULE_NOT_FOUND', |
| message: /Cannot find module 'sqlite'/, |
| }); |
| }); |
| |
| test('can be disabled with --no-experimental-sqlite flag', async (t) => { |
| const { |
| stdout, |
| stderr, |
| code, |
| signal, |
| } = await spawnPromisified(process.execPath, [ |
| '--no-experimental-sqlite', |
| '-e', |
| 'require("node:sqlite")', |
| ]); |
| |
| t.assert.strictEqual(stdout, ''); |
| t.assert.match(stderr, /No such built-in module: node:sqlite/); |
| t.assert.notStrictEqual(code, 0); |
| t.assert.strictEqual(signal, null); |
| }); |
| }); |
| |
| test('ERR_SQLITE_ERROR is thrown for errors originating from SQLite', (t) => { |
| const db = new DatabaseSync(nextDb()); |
| t.after(() => { db.close(); }); |
| const setup = db.exec(` |
| CREATE TABLE test( |
| key INTEGER PRIMARY KEY |
| ) STRICT; |
| `); |
| t.assert.strictEqual(setup, undefined); |
| const stmt = db.prepare('INSERT INTO test (key) VALUES (?)'); |
| t.assert.deepStrictEqual(stmt.run(1), { changes: 1, lastInsertRowid: 1 }); |
| t.assert.throws(() => { |
| stmt.run(1); |
| }, { |
| code: 'ERR_SQLITE_ERROR', |
| message: 'UNIQUE constraint failed: test.key', |
| errcode: 1555, |
| errstr: 'constraint failed', |
| }); |
| }); |
| |
| test('in-memory databases are supported', (t) => { |
| const db1 = new DatabaseSync(':memory:'); |
| const db2 = new DatabaseSync(':memory:'); |
| const setup1 = db1.exec(` |
| CREATE TABLE data(key INTEGER PRIMARY KEY); |
| INSERT INTO data (key) VALUES (1); |
| `); |
| const setup2 = db2.exec(` |
| CREATE TABLE data(key INTEGER PRIMARY KEY); |
| INSERT INTO data (key) VALUES (1); |
| `); |
| t.assert.strictEqual(setup1, undefined); |
| t.assert.strictEqual(setup2, undefined); |
| t.assert.deepStrictEqual( |
| db1.prepare('SELECT * FROM data').all(), |
| [{ __proto__: null, key: 1 }] |
| ); |
| t.assert.deepStrictEqual( |
| db2.prepare('SELECT * FROM data').all(), |
| [{ __proto__: null, key: 1 }] |
| ); |
| }); |
| |
| test('sqlite constants are defined', (t) => { |
| t.assert.strictEqual(constants.SQLITE_CHANGESET_OMIT, 0); |
| t.assert.strictEqual(constants.SQLITE_CHANGESET_REPLACE, 1); |
| t.assert.strictEqual(constants.SQLITE_CHANGESET_ABORT, 2); |
| }); |
| |
| test('PRAGMAs are supported', (t) => { |
| const db = new DatabaseSync(nextDb()); |
| t.after(() => { db.close(); }); |
| t.assert.deepStrictEqual( |
| db.prepare('PRAGMA journal_mode = WAL').get(), |
| { __proto__: null, journal_mode: 'wal' }, |
| ); |
| t.assert.deepStrictEqual( |
| db.prepare('PRAGMA journal_mode').get(), |
| { __proto__: null, journal_mode: 'wal' }, |
| ); |
| }); |
| |
| test('math functions are enabled', (t) => { |
| const db = new DatabaseSync(':memory:'); |
| t.assert.deepStrictEqual( |
| db.prepare('SELECT PI() AS pi').get(), |
| { __proto__: null, pi: 3.141592653589793 }, |
| ); |
| }); |
| |
| test('Buffer is supported as the database path', (t) => { |
| const db = new DatabaseSync(Buffer.from(nextDb())); |
| t.after(() => { db.close(); }); |
| db.exec(` |
| CREATE TABLE data(key INTEGER PRIMARY KEY); |
| INSERT INTO data (key) VALUES (1); |
| `); |
| |
| t.assert.deepStrictEqual( |
| db.prepare('SELECT * FROM data').all(), |
| [{ __proto__: null, key: 1 }] |
| ); |
| }); |
| |
| test('URL is supported as the database path', (t) => { |
| const url = pathToFileURL(nextDb()); |
| const db = new DatabaseSync(url); |
| t.after(() => { db.close(); }); |
| db.exec(` |
| CREATE TABLE data(key INTEGER PRIMARY KEY); |
| INSERT INTO data (key) VALUES (1); |
| `); |
| |
| t.assert.deepStrictEqual( |
| db.prepare('SELECT * FROM data').all(), |
| [{ __proto__: null, key: 1 }] |
| ); |
| }); |
| |
| |
| suite('URI query params', () => { |
| const baseDbPath = nextDb(); |
| const baseDb = new DatabaseSync(baseDbPath); |
| baseDb.exec(` |
| CREATE TABLE data(key INTEGER PRIMARY KEY); |
| INSERT INTO data (key) VALUES (1); |
| `); |
| baseDb.close(); |
| |
| test('query params are supported with URL objects', (t) => { |
| const url = pathToFileURL(baseDbPath); |
| url.searchParams.set('mode', 'ro'); |
| const readOnlyDB = new DatabaseSync(url); |
| t.after(() => { readOnlyDB.close(); }); |
| |
| t.assert.deepStrictEqual( |
| readOnlyDB.prepare('SELECT * FROM data').all(), |
| [{ __proto__: null, key: 1 }] |
| ); |
| t.assert.throws(() => { |
| readOnlyDB.exec('INSERT INTO data (key) VALUES (1);'); |
| }, { |
| code: 'ERR_SQLITE_ERROR', |
| message: 'attempt to write a readonly database', |
| }); |
| }); |
| |
| test('query params are supported with string', (t) => { |
| const url = pathToFileURL(baseDbPath); |
| url.searchParams.set('mode', 'ro'); |
| |
| // Ensures a valid URI passed as a string is supported |
| const readOnlyDB = new DatabaseSync(url.toString()); |
| t.after(() => { readOnlyDB.close(); }); |
| |
| t.assert.deepStrictEqual( |
| readOnlyDB.prepare('SELECT * FROM data').all(), |
| [{ __proto__: null, key: 1 }] |
| ); |
| t.assert.throws(() => { |
| readOnlyDB.exec('INSERT INTO data (key) VALUES (1);'); |
| }, { |
| code: 'ERR_SQLITE_ERROR', |
| message: 'attempt to write a readonly database', |
| }); |
| }); |
| |
| test('query params are supported with Buffer', (t) => { |
| const url = pathToFileURL(baseDbPath); |
| url.searchParams.set('mode', 'ro'); |
| |
| // Ensures a valid URI passed as a Buffer is supported |
| const readOnlyDB = new DatabaseSync(Buffer.from(url.toString())); |
| t.after(() => { readOnlyDB.close(); }); |
| |
| t.assert.deepStrictEqual( |
| readOnlyDB.prepare('SELECT * FROM data').all(), |
| [{ __proto__: null, key: 1 }] |
| ); |
| t.assert.throws(() => { |
| readOnlyDB.exec('INSERT INTO data (key) VALUES (1);'); |
| }, { |
| code: 'ERR_SQLITE_ERROR', |
| message: 'attempt to write a readonly database', |
| }); |
| }); |
| }); |