duckdb-async
Version:
Promise wrappers for DuckDb NodeJS API
461 lines (460 loc) • 18.3 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Statement = exports.Database = exports.Connection = exports.OPEN_SHAREDCACHE = exports.OPEN_READWRITE = exports.OPEN_READONLY = exports.OPEN_PRIVATECACHE = exports.OPEN_FULLMUTEX = exports.OPEN_CREATE = exports.QueryResult = void 0;
/**
* A wrapper around DuckDb node.js API that mirrors that
* API but uses Promises instead of callbacks.
*
*/
const duckdb = __importStar(require("duckdb"));
const util = __importStar(require("util"));
var duckdb_1 = require("duckdb");
Object.defineProperty(exports, "QueryResult", { enumerable: true, get: function () { return duckdb_1.QueryResult; } });
Object.defineProperty(exports, "OPEN_CREATE", { enumerable: true, get: function () { return duckdb_1.OPEN_CREATE; } });
Object.defineProperty(exports, "OPEN_FULLMUTEX", { enumerable: true, get: function () { return duckdb_1.OPEN_FULLMUTEX; } });
Object.defineProperty(exports, "OPEN_PRIVATECACHE", { enumerable: true, get: function () { return duckdb_1.OPEN_PRIVATECACHE; } });
Object.defineProperty(exports, "OPEN_READONLY", { enumerable: true, get: function () { return duckdb_1.OPEN_READONLY; } });
Object.defineProperty(exports, "OPEN_READWRITE", { enumerable: true, get: function () { return duckdb_1.OPEN_READWRITE; } });
Object.defineProperty(exports, "OPEN_SHAREDCACHE", { enumerable: true, get: function () { return duckdb_1.OPEN_SHAREDCACHE; } });
/*
* Implmentation note:
* Although the method types exposed to users of this library
* are reasonably precise, the unfortunate excessive use of
* `any` in this utility function is because writing a precise
* type for a generic higher order function like
* `util.promisify` is beyond the current capabilities of the
* TypeScript type system.
* See https://github.com/Microsoft/TypeScript/issues/5453
* for detailed discussion.
*/
function methodPromisify(methodFn) {
return util.promisify((target, ...args) => methodFn.bind(target)(...args));
}
const connAllAsync = methodPromisify(duckdb.Connection.prototype.all);
const connArrowIPCAll = methodPromisify(duckdb.Connection.prototype.arrowIPCAll);
const connExecAsync = methodPromisify(duckdb.Connection.prototype.exec);
const connPrepareAsync = methodPromisify(duckdb.Connection.prototype.prepare);
const connRunAsync = methodPromisify(duckdb.Connection.prototype.run);
const connUnregisterUdfAsync = methodPromisify(duckdb.Connection.prototype.unregister_udf);
const connRegisterBufferAsync = methodPromisify(duckdb.Connection.prototype.register_buffer);
const connUnregisterBufferAsync = methodPromisify(duckdb.Connection.prototype.unregister_buffer);
const connCloseAsync = methodPromisify(duckdb.Connection.prototype.close);
class Connection {
constructor(ddb, resolve, reject) {
this.conn = null;
this.conn = new duckdb.Connection(ddb, (err, res) => {
if (err) {
this.conn = null;
reject(err);
}
resolve(this);
});
}
/**
* Static method to create a new Connection object. Provided because constructors can not return Promises,
* and the DuckDb Node.JS API uses a callback in the Database constructor
*/
static create(db) {
return new Promise((resolve, reject) => {
new Connection(db.get_ddb_internal(), resolve, reject);
});
}
async all(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.all: uninitialized connection");
}
return connAllAsync(this.conn, sql, ...args);
}
async arrowIPCAll(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.arrowIPCAll: uninitialized connection");
}
return connArrowIPCAll(this.conn, sql, ...args);
}
/**
* Executes the sql query and invokes the callback for each row of result data.
* Since promises can only resolve once, this method uses the same callback
* based API of the underlying DuckDb NodeJS API
* @param sql query to execute
* @param args parameters for template query
* @returns
*/
each(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.each: uninitialized connection");
}
this.conn.each(sql, ...args);
}
/**
* Execute one or more SQL statements, without returning results.
* @param sql queries or statements to executes (semicolon separated)
* @param args parameters if `sql` is a parameterized template
* @returns `Promise<void>` that resolves when all statements have been executed.
*/
async exec(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.exec: uninitialized connection");
}
return connExecAsync(this.conn, sql, ...args);
}
prepareSync(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.prepareSync: uninitialized connection");
}
const ddbStmt = this.conn.prepare(sql, ...args);
return Statement.create_internal(ddbStmt);
}
async prepare(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.prepare: uninitialized connection");
}
const stmt = await connPrepareAsync(this.conn, sql, ...args);
return Statement.create_internal(stmt);
}
runSync(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.runSync: uninitialized connection");
}
// We need the 'as any' cast here, because run dynamically checks
// types of args to determine if a callback function was passed in
const ddbStmt = this.conn.run(sql, ...args);
return Statement.create_internal(ddbStmt);
}
async run(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.runSync: uninitialized connection");
}
const stmt = await connRunAsync(this.conn, sql, ...args);
return Statement.create_internal(stmt);
}
register_udf(name, return_type, fun) {
if (!this.conn) {
throw new Error("Connection.register_udf: uninitialized connection");
}
this.conn.register_udf(name, return_type, fun);
}
async unregister_udf(name) {
if (!this.conn) {
throw new Error("Connection.unregister_udf: uninitialized connection");
}
return connUnregisterUdfAsync(this.conn, name);
}
register_bulk(name, return_type, fun) {
if (!this.conn) {
throw new Error("Connection.register_bulk: uninitialized connection");
}
this.conn.register_bulk(name, return_type, fun);
}
stream(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.stream: uninitialized connection");
}
return this.conn.stream(sql, ...args);
}
arrowIPCStream(sql, ...args) {
if (!this.conn) {
throw new Error("Connection.arrowIPCStream: uninitialized connection");
}
return this.conn.arrowIPCStream(sql, ...args);
}
register_buffer(name, array, force) {
if (!this.conn) {
throw new Error("Connection.register_buffer: uninitialized connection");
}
return connRegisterBufferAsync(this.conn, name, array, force);
}
unregister_buffer(name) {
if (!this.conn) {
throw new Error("Connection.unregister_buffer: uninitialized connection");
}
return connUnregisterBufferAsync(this.conn, name);
}
async close() {
if (!this.conn) {
throw new Error("Connection.close: uninitialized connection");
}
await connCloseAsync(this.conn);
this.conn = null;
return;
}
}
exports.Connection = Connection;
const dbCloseAsync = methodPromisify(duckdb.Database.prototype.close);
const dbAllAsync = methodPromisify(duckdb.Database.prototype.all);
const dbArrowIPCAll = methodPromisify(duckdb.Database.prototype.arrowIPCAll);
const dbExecAsync = methodPromisify(duckdb.Database.prototype.exec);
const dbPrepareAsync = methodPromisify(duckdb.Database.prototype.prepare);
const dbRunAsync = methodPromisify(duckdb.Database.prototype.run);
const dbUnregisterUdfAsync = methodPromisify(duckdb.Database.prototype.unregister_udf);
const dbSerializeAsync = methodPromisify(duckdb.Database.prototype.serialize);
const dbParallelizeAsync = methodPromisify(duckdb.Database.prototype.parallelize);
const dbWaitAsync = methodPromisify(duckdb.Database.prototype.wait);
const dbRegisterBufferAsync = methodPromisify(duckdb.Database.prototype.register_buffer);
const dbUnregisterBufferAsync = methodPromisify(duckdb.Database.prototype.unregister_buffer);
class Database {
constructor(path, accessMode, resolve, reject) {
this.db = null;
if (typeof accessMode === "number") {
accessMode = {
access_mode: accessMode == duckdb.OPEN_READONLY ? "read_only" : "read_write"
};
}
accessMode["duckdb_api"] = "nodejs-async";
this.db = new duckdb.Database(path, accessMode, (err, res) => {
if (err) {
reject(err);
}
resolve(this);
});
}
/**
* Static method to create a new Database object. Provided because constructors can not return Promises,
* and the DuckDb Node.JS API uses a callback in the Database constructor
*/
/**
* Static method to create a new Database object from the specified file. Provided as a static
* method because some initialization may happen asynchronously.
* @param path path to database file to open, or ":memory:"
* @returns a promise that resolves to newly created Database object
*/
static create(path, accessMode) {
const trueAccessMode = accessMode ?? duckdb.OPEN_READWRITE; // defaults to OPEN_READWRITE
return new Promise((resolve, reject) => {
new Database(path, trueAccessMode, resolve, reject);
});
}
async close() {
if (!this.db) {
throw new Error("Database.close: uninitialized database");
}
await dbCloseAsync(this.db);
this.db = null;
return;
}
// accessor to get internal duckdb Database object -- internal use only
get_ddb_internal() {
if (!this.db) {
throw new Error("Database.get_ddb_internal: uninitialized database");
}
return this.db;
}
connect() {
return Connection.create(this);
}
async all(sql, ...args) {
if (!this.db) {
throw new Error("Database.all: uninitialized database");
}
return dbAllAsync(this.db, sql, ...args);
}
async arrowIPCAll(sql, ...args) {
if (!this.db) {
throw new Error("Database.arrowIPCAll: uninitialized connection");
}
return dbArrowIPCAll(this.db, sql, ...args);
}
/**
* Executes the sql query and invokes the callback for each row of result data.
* Since promises can only resolve once, this method uses the same callback
* based API of the underlying DuckDb NodeJS API
* @param sql query to execute
* @param args parameters for template query
* @returns
*/
each(sql, ...args) {
if (!this.db) {
throw new Error("Database.each: uninitialized database");
}
this.db.each(sql, ...args);
}
/**
* Execute one or more SQL statements, without returning results.
* @param sql queries or statements to executes (semicolon separated)
* @param args parameters if `sql` is a parameterized template
* @returns `Promise<void>` that resolves when all statements have been executed.
*/
async exec(sql, ...args) {
if (!this.db) {
throw new Error("Database.exec: uninitialized database");
}
return dbExecAsync(this.db, sql, ...args);
}
prepareSync(sql, ...args) {
if (!this.db) {
throw new Error("Database.prepareSync: uninitialized database");
}
const ddbStmt = this.db.prepare(sql, ...args);
return Statement.create_internal(ddbStmt);
}
async prepare(sql, ...args) {
if (!this.db) {
throw new Error("Database.prepare: uninitialized database");
}
const stmt = await dbPrepareAsync(this.db, sql, ...args);
return Statement.create_internal(stmt);
}
runSync(sql, ...args) {
if (!this.db) {
throw new Error("Database.runSync: uninitialized database");
}
// We need the 'as any' cast here, because run dynamically checks
// types of args to determine if a callback function was passed in
const ddbStmt = this.db.run(sql, ...args);
return Statement.create_internal(ddbStmt);
}
async run(sql, ...args) {
if (!this.db) {
throw new Error("Database.runSync: uninitialized database");
}
const stmt = await dbRunAsync(this.db, sql, ...args);
return Statement.create_internal(stmt);
}
register_udf(name, return_type, fun) {
if (!this.db) {
throw new Error("Database.register: uninitialized database");
}
this.db.register_udf(name, return_type, fun);
}
async unregister_udf(name) {
if (!this.db) {
throw new Error("Database.unregister: uninitialized database");
}
return dbUnregisterUdfAsync(this.db, name);
}
stream(sql, ...args) {
if (!this.db) {
throw new Error("Database.stream: uninitialized database");
}
return this.db.stream(sql, ...args);
}
arrowIPCStream(sql, ...args) {
if (!this.db) {
throw new Error("Database.arrowIPCStream: uninitialized database");
}
return this.db.arrowIPCStream(sql, ...args);
}
serialize() {
if (!this.db) {
throw new Error("Database.serialize: uninitialized database");
}
return dbSerializeAsync(this.db);
}
parallelize() {
if (!this.db) {
throw new Error("Database.parallelize: uninitialized database");
}
return dbParallelizeAsync(this.db);
}
wait() {
if (!this.db) {
throw new Error("Database.wait: uninitialized database");
}
return dbWaitAsync(this.db);
}
interrupt() {
if (!this.db) {
throw new Error("Database.interrupt: uninitialized database");
}
return this.db.interrupt();
}
register_buffer(name, array, force) {
if (!this.db) {
throw new Error("Database.register_buffer: uninitialized database");
}
return dbRegisterBufferAsync(this.db, name, array, force);
}
unregister_buffer(name) {
if (!this.db) {
throw new Error("Database.unregister_buffer: uninitialized database");
}
return dbUnregisterBufferAsync(this.db, name);
}
registerReplacementScan(replacementScan) {
if (!this.db) {
throw new Error("Database.registerReplacementScan: uninitialized database");
}
return this.db.registerReplacementScan(replacementScan);
}
}
exports.Database = Database;
const stmtRunAsync = methodPromisify(duckdb.Statement.prototype.run);
const stmtFinalizeAsync = methodPromisify(duckdb.Statement.prototype.finalize);
const stmtAllAsync = methodPromisify(duckdb.Statement.prototype.all);
const stmtArrowIPCAllAsync = methodPromisify(duckdb.Statement.prototype.arrowIPCAll);
class Statement {
/**
* Construct an async wrapper from a statement
*/
constructor(stmt) {
this.stmt = stmt;
}
/**
* create a Statement object that wraps a duckdb.Statement.
* This is intended for internal use only, and should not be called directly.
* Use `Database.prepare()` or `Database.run()` to create Statement objects.
*/
static create_internal(stmt) {
return new Statement(stmt);
}
async all(...args) {
return stmtAllAsync(this.stmt, ...args);
}
async arrowIPCAll(...args) {
return stmtArrowIPCAllAsync(this.stmt, ...args);
}
/**
* Executes the sql query and invokes the callback for each row of result data.
* Since promises can only resolve once, this method uses the same callback
* based API of the underlying DuckDb NodeJS API
* @param args parameters for template query, followed by a NodeJS style
* callback function invoked for each result row.
*
* @returns
*/
each(...args) {
this.stmt.each(...args);
}
/**
* Call `duckdb.Statement.run` directly without awaiting completion.
* @param args arguments passed to duckdb.Statement.run()
* @returns this
*/
runSync(...args) {
this.stmt.run(...args);
return this;
}
async run(...args) {
await stmtRunAsync(this.stmt, ...args);
return this;
}
async finalize() {
return stmtFinalizeAsync(this.stmt);
}
columns() {
return this.stmt.columns();
}
}
exports.Statement = Statement;