UNPKG

node-firebird-driver-native

Version:
157 lines (122 loc) 5.42 kB
import { AttachmentImpl } from './attachment'; import { ResultSetImpl } from './resultset'; import { TransactionImpl } from './transaction'; import { ExecuteOptions, ExecuteQueryOptions, PrepareOptions } from 'node-firebird-driver'; import { AbstractStatement, commonInfo, getPortableInteger, statementInfo } from 'node-firebird-driver/dist/lib/impl'; import { createDataReader, createDataWriter, createDescriptors, fixMetadata, DataReader, DataWriter } from './fb-util'; import * as fb from 'node-firebird-native-api'; import { TextDecoder } from 'util'; /** Statement implementation. */ export class StatementImpl extends AbstractStatement { // Override declarations. override attachment: AttachmentImpl; override hasResultSet: boolean; statementHandle?: fb.Statement; inMetadata?: fb.MessageMetadata; outMetadata?: fb.MessageMetadata; inBuffer: Uint8Array; outBuffer: Uint8Array; dataWriter: DataWriter; dataReader: DataReader; static async prepare(attachment: AttachmentImpl, transaction: TransactionImpl, sqlStmt: string, options?: PrepareOptions): Promise<StatementImpl> { const statement = new StatementImpl(attachment); return await attachment.client.statusAction(async status => { //// FIXME: options/flags, dialect statement.statementHandle = await attachment!.attachmentHandle!.prepareAsync(status, transaction?.transactionHandle, 0, sqlStmt, 3, fb.Statement.PREPARE_PREFETCH_ALL); statement.hasResultSet = (statement.statementHandle!.getFlagsSync(status) & fb.Statement.FLAG_HAS_CURSOR) != 0; statement.inMetadata = fixMetadata(status, await statement.statementHandle!.getInputMetadataAsync(status)); statement.outMetadata = fixMetadata(status, await statement.statementHandle!.getOutputMetadataAsync(status)); if (statement.inMetadata) { statement.inBuffer = new Uint8Array(statement.inMetadata.getMessageLengthSync(status)); statement.dataWriter = createDataWriter(createDescriptors(status, statement.inMetadata)); } if (statement.outMetadata) { statement.outBuffer = new Uint8Array(statement.outMetadata.getMessageLengthSync(status)); statement.dataReader = createDataReader(createDescriptors(status, statement.outMetadata)); } return statement; }); } /** Disposes this statement's resources. */ protected async internalDispose(): Promise<void> { if (this.outMetadata) { this.outMetadata.releaseSync(); this.outMetadata = undefined; } if (this.inMetadata) { this.inMetadata.releaseSync(); this.inMetadata = undefined; } await this.attachment.client.statusAction(status => this.statementHandle!.freeAsync(status)); this.statementHandle = undefined; } /** Executes a prepared statement that uses the SET TRANSACTION command. Returns the new transaction. */ protected async internalExecuteTransaction(transaction: TransactionImpl): Promise<TransactionImpl> { throw new Error('Uninplemented method: executeTransaction.'); } /** Executes a prepared statement that has no result set. */ protected async internalExecute(transaction: TransactionImpl, parameters?: Array<any>, options?: ExecuteOptions): Promise<Array<any>> { return await this.attachment.client.statusAction(async status => { await this.dataWriter(this.attachment, transaction, this.inBuffer, parameters); const newTransaction = await this.statementHandle!.executeAsync(status, transaction?.transactionHandle, this.inMetadata, this.inBuffer, this.outMetadata, this.outBuffer); if (newTransaction && transaction?.transactionHandle != newTransaction) { //// FIXME: newTransaction.releaseSync(); } return this.outMetadata ? await this.dataReader(this.attachment, transaction, this.outBuffer) : []; }); } /** Executes a prepared statement that has result set. */ protected async internalExecuteQuery(transaction: TransactionImpl, parameters?: Array<any>, options?: ExecuteQueryOptions): Promise<ResultSetImpl> { return await ResultSetImpl.open(this, transaction as TransactionImpl, parameters, options); } async setCursorName(cursorName: string): Promise<void> { return await this.attachment.client.statusAction(async status => await this.statementHandle!.setCursorNameAsync(status, cursorName) ); } async getExecPathText(): Promise<string | undefined> { return await this.attachment.client.statusAction(async status => { const infoReq = new Uint8Array([statementInfo.sqlExecPathBlrText]); const infoRet = new Uint8Array(65535); await this.statementHandle!.getInfoAsync(status, infoReq.byteLength, infoReq, infoRet.byteLength, infoRet); if (infoRet[0] == commonInfo.end) return undefined; else { if (infoRet[0] != statementInfo.sqlExecPathBlrText) throw new Error('Error retrieving statement execution path.'); const size = getPortableInteger(infoRet.subarray(1), 2); return new TextDecoder().decode(infoRet.subarray(3, 3 + size)); } }); } get columnLabels(): Promise<string[]> { const asyncFunc = async (): Promise<string[]> => { if (!this.outMetadata) return []; return await this.attachment.client.statusAction(async status => { const metaData = this.outMetadata!; const count = metaData.getCountSync(status); const array: string[] = []; for (let i = 0; i < count; ++i) array.push(metaData.getAliasSync(status, i)!); return array; }); }; return asyncFunc(); } }