snowflake-promise
Version:
A Promise-based, TypeScript-friendly helper for the Snowflake SDK
312 lines (301 loc) • 9.42 kB
JavaScript
// src/lib/promisify-or-not.ts
import { promisify } from "util";
function promisifyOrNot(original) {
const promisified = promisify(original);
return function(...args) {
const lastArg = args.at(-1);
if (typeof lastArg === "function") {
return original.apply(this, args);
}
return promisified.apply(this, args);
};
}
// src/lib/promisify-statement.ts
function promisifyStatement(stmt) {
if (stmt && Object.prototype.hasOwnProperty.call(stmt, "__isPromisified")) {
return stmt;
}
return new Proxy(stmt, {
get(target, prop, receiver) {
if (prop === "__isPromisified") {
return true;
}
const originalValue = Reflect.get(target, prop, receiver);
if (typeof originalValue === "function") {
switch (prop) {
// Methods with a traditional callback
case "cancel":
return promisifyOrNot(
originalValue
).bind(target);
default:
return originalValue.bind(target);
}
}
return originalValue;
}
});
}
// src/lib/promisify-options-callback-function.ts
function promisifyOptionsCallbackFunction(target, fn) {
return function(options) {
if (options && typeof options.complete === "function") {
return fn.call(target, options);
}
let rowsResolve;
let rowsReject;
const resultsPromise = new Promise((resolve, reject) => {
rowsResolve = resolve;
rowsReject = reject;
});
const newOptions = { ...options };
newOptions.complete = (err, stmt, rows) => {
if (err) {
rowsReject(err);
} else {
rowsResolve(rows);
}
};
const statement = fn.call(target, newOptions);
return {
statement: promisifyStatement(statement),
resultsPromise
};
};
}
// src/lib/promisify-connection.ts
function promisifyConnection(conn) {
if (conn && Object.prototype.hasOwnProperty.call(conn, "__isPromisified")) {
return conn;
}
const proxy = new Proxy(conn, {
get(target, prop, receiver) {
if (prop === "__isPromisified") {
return true;
}
const originalValue = Reflect.get(target, prop, receiver);
if (typeof originalValue === "function") {
switch (prop) {
// Methods that take an options object containing a `complete` property
// These methods return RowStatement objects that are wrapped with
// promisifyStatement. `getResultsFromQueryId` is not promisified due to
// its use case, which would be hard to handle and isn’t needed when using
// this Promise proxy.
case "execute":
case "fetchResult":
return promisifyOptionsCallbackFunction(target, originalValue);
// Methods with a traditional callback
case "connect":
case "connectAsync":
case "destroy":
return promisifyOrNot(originalValue).bind(target);
default:
return originalValue.bind(target);
}
}
return originalValue;
}
});
return proxy;
}
// src/legacy-compatibility/snowflake.ts
import SDK from "snowflake-sdk";
// src/legacy-compatibility/statement.ts
import { strict as assert } from "assert";
// src/legacy-compatibility/types/SnowflakeError.ts
var SnowflakeError = class extends Error {
constructor(message) {
super(message);
}
};
// src/legacy-compatibility/types/StatementNotExecutedError.ts
var StatementNotExecutedError = class extends SnowflakeError {
constructor() {
super("Statement not executed yet - call the execute() method");
}
};
// src/legacy-compatibility/statement.ts
var Statement = class {
connection;
executePromise;
stmt;
rows;
executeOptions;
logSql;
constructor(connection, executeOptions, logSql) {
this.connection = promisifyConnection(connection);
this.executeOptions = executeOptions;
this.logSql = logSql;
}
execute() {
if (this.executePromise) {
throw new StatementAlreadyExecutedError();
}
const startTime = Date.now();
const { statement, resultsPromise } = this.connection.execute(this.executeOptions);
this.stmt = statement;
this.executePromise = resultsPromise.then((rows) => {
if (this.logSql) {
const elapsed = Date.now() - startTime;
this.log(elapsed);
}
this.rows = rows;
});
return this.executePromise;
}
async cancel() {
await this.stmt?.cancel();
}
getRows() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
return this.executePromise.then(() => this.rows);
}
streamRows(options = {}) {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before streaming rows");
return this.stmt.streamRows(options);
}
getSqlText() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting SQL text");
return this.stmt.getSqlText();
}
getStatus() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting status");
return this.stmt.getStatus();
}
getColumns() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting columns");
return this.stmt.getColumns();
}
getColumn(columnIdentifier) {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting column");
return this.stmt.getColumn(columnIdentifier);
}
getNumRows() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting number of rows");
return this.stmt.getNumRows();
}
getNumUpdatedRows() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting number of updated rows");
return this.stmt.getNumUpdatedRows();
}
getSessionState() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting session state");
return this.stmt.getSessionState();
}
getRequestId() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting request ID");
return this.stmt.getRequestId();
}
getStatementId() {
if (!this.executePromise) {
throw new StatementNotExecutedError();
}
assert(this.stmt, "Statement must be executed before getting statement ID");
return this.stmt.getQueryId();
}
log(elapsedTime) {
let logMessage = "Executed";
const state = this.getSessionState();
if (state) {
logMessage += ` (${state.getCurrentDatabase()}.${state.getCurrentSchema()})`;
}
logMessage += `: ${this.getSqlText()}`;
if (logMessage[logMessage.length - 1] !== ";") {
logMessage += ";";
}
if (this.executeOptions.binds) {
logMessage += ` with binds: ${JSON.stringify(this.executeOptions.binds)};`;
}
logMessage += ` Elapsed time: ${elapsedTime}ms`;
this.logSql?.(logMessage);
}
};
// src/legacy-compatibility/types/LoggingOptions.ts
function toSdkLogLevel(logLevel) {
return logLevel.toUpperCase();
}
// src/legacy-compatibility/snowflake.ts
var Snowflake = class {
logSql;
connection;
constructor(connectionOptions, loggingOptions = {}, configureOptions) {
if (loggingOptions && loggingOptions.logLevel) {
SDK.configure({ logLevel: toSdkLogLevel(loggingOptions.logLevel) });
}
this.logSql = (loggingOptions && loggingOptions.logSql) ?? void 0;
if (typeof configureOptions === "boolean") {
console.warn(
"[snowflake-promise] the insecureConnect boolean argument is deprecated; please remove it or use the ocspFailOpen configure option"
);
} else if (typeof configureOptions === "object") {
SDK.configure(configureOptions);
}
this.connection = promisifyConnection(SDK.createConnection(connectionOptions));
}
get id() {
return this.connection.getId();
}
async connect() {
await this.connection.connect();
}
async connectAsync() {
await this.connection.connectAsync();
}
async destroy() {
await this.connection.destroy();
}
createStatement(options) {
return new Statement(this.connection, options, this.logSql);
}
/** A convenience function to execute a SQL statement and return the resulting rows. */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async execute(sqlText, binds) {
const { resultsPromise } = this.connection.execute({ sqlText, binds });
const rows = await resultsPromise;
return rows;
}
};
// src/legacy-compatibility/types/StatementAlreadyExecutedError.ts
var StatementAlreadyExecutedError = class extends SnowflakeError {
constructor() {
super("Statement already executed - it cannot be executed again");
}
};
export {
Snowflake,
SnowflakeError,
Statement,
StatementAlreadyExecutedError,
StatementNotExecutedError,
promisifyConnection
};
//# sourceMappingURL=index.js.map