Files

148 lines
5.7 KiB
JavaScript

'use strict';
const Database = require('../.');
describe('Statement#bind()', function () {
beforeEach(function () {
this.db = new Database(util.next());
this.db.prepare('CREATE TABLE entries (a TEXT, b INTEGER, c BLOB)').run();
});
afterEach(function () {
this.db.close();
});
it('should permanently bind the given parameters', function () {
const stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
const buffer = Buffer.alloc(4).fill(0xdd);
stmt.bind('foobar', 25, buffer)
stmt.run();
buffer.fill(0xaa);
stmt.run();
const row1 = this.db.prepare('SELECT * FROM entries WHERE rowid=1').get();
const row2 = this.db.prepare('SELECT * FROM entries WHERE rowid=2').get();
expect(row1.a).to.equal(row2.a);
expect(row1.b).to.equal(row2.b);
expect(row1.c).to.deep.equal(row2.c);
});
it('should not allow you to bind temporary parameters afterwards', function () {
const stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
const buffer = Buffer.alloc(4).fill(0xdd);
stmt.bind('foobar', 25, buffer)
expect(() => stmt.run(null)).to.throw(TypeError);
expect(() => stmt.run(buffer)).to.throw(TypeError);
expect(() => stmt.run('foobar', 25, buffer)).to.throw(TypeError);
});
it('should throw an exception when invoked twice on the same statement', function () {
let stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
stmt.bind('foobar', 25, null);
expect(() => stmt.bind('foobar', 25, null)).to.throw(TypeError);
expect(() => stmt.bind()).to.throw(TypeError);
stmt = this.db.prepare('SELECT * FROM entries');
stmt.bind();
expect(() => stmt.bind()).to.throw(TypeError);
});
it('should throw an exception when invalid parameters are given', function () {
let stmt = this.db.prepare('INSERT INTO entries VALUES (?, ?, ?)');
expect(() =>
stmt.bind('foo', 25)
).to.throw(RangeError);
expect(() =>
stmt.bind('foo', 25, null, null)
).to.throw(RangeError);
expect(() =>
stmt.bind('foo', new Number(25), null)
).to.throw(TypeError);
expect(() =>
stmt.bind()
).to.throw(RangeError);
stmt.bind('foo', 25, null);
stmt = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
expect(() =>
stmt.bind({ a: '123' })
).to.throw(RangeError);
expect(() =>
stmt.bind({ a: '123', 1: null })
).to.throw(RangeError);
expect(() =>
stmt.bind({ a: '123' }, null, null)
).to.throw(RangeError);
stmt.bind({ a: '123' }, null);
stmt = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
stmt.bind({ a: '123', b: null }, null);
});
it('should not treat class instances as plain objects', function () {
const stmt = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
expect(() => stmt.bind(new (class { get a() { return "123"; } })(), null)).to.throw(TypeError);
expect(() => stmt.bind(Object.assign(new Date(), { a: 123 }), null)).to.throw(TypeError);
expect(() => stmt.bind(Object.assign(new Number(123), { a: 123 }), null)).to.throw(TypeError);
expect(() => stmt.bind(Object.assign(() => {}, { a: 123 }), null)).to.throw(TypeError);
});
it('should not treat class instances from other realms as plain objects', function () {
const vm = require('vm');
const context = vm.createContext({});
// A class instance from another realm is not a plain object, so passing it
// where named parameters are expected must throw.
const foreignInstance = vm.runInContext('(new (class { get a() { return "123"; } })())', context);
const stmt = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
expect(() => stmt.bind(foreignInstance, null)).to.throw(TypeError);
});
it('should recognize plain objects created in other realms', function () {
const vm = require('vm');
const context = vm.createContext({});
// A plain object created in another realm has a different Object.prototype
// identity, but should still be recognized as a plain object.
const foreignPlainObject = vm.runInContext('({ a: "123", b: null })', context);
expect(Object.getPrototypeOf(foreignPlainObject)).to.not.equal(Object.prototype);
const stmt = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
stmt.bind(foreignPlainObject, null);
stmt.run();
const row = this.db.prepare('SELECT * FROM entries WHERE rowid=1').get();
expect(row.a).to.equal('123');
// An object created via Object.create(null) in another realm is plain too.
const foreignNullProtoObject = vm.runInContext('Object.assign(Object.create(null), { a: "456", b: null })', context);
const stmt2 = this.db.prepare('INSERT INTO entries VALUES (@a, @a, ?)');
stmt2.bind(foreignNullProtoObject, null);
stmt2.run();
const row2 = this.db.prepare('SELECT * FROM entries WHERE rowid=2').get();
expect(row2.a).to.equal('456');
});
it('should propagate exceptions thrown while accessing array/object members', function () {
const arr = [22];
const obj = {};
const err = new TypeError('foobar');
Object.defineProperty(arr, '0', { get: () => { throw err; } })
Object.defineProperty(obj, 'baz', { get: () => { throw err; } })
const stmt1 = this.db.prepare('SELECT ?');
const stmt2 = this.db.prepare('SELECT @baz');
expect(() => stmt1.bind(arr)).to.throw(err);
expect(() => stmt2.bind(obj)).to.throw(err);
});
it('should properly bind empty buffers', function () {
this.db.prepare('INSERT INTO entries (c) VALUES (?)').bind(Buffer.alloc(0)).run();
const result = this.db.prepare('SELECT c FROM entries').pluck().get();
expect(result).to.be.an.instanceof(Buffer);
expect(result.length).to.equal(0);
});
it('should properly bind empty strings', function () {
this.db.prepare('INSERT INTO entries (a) VALUES (?)').bind('').run();
const result = this.db.prepare('SELECT a FROM entries').pluck().get();
expect(result).to.be.a('string');
expect(result.length).to.equal(0);
});
});