UNPKG

oracledb

Version:

A Node.js module for Oracle Database access from JavaScript and TypeScript

317 lines (298 loc) 12.6 kB
// Copyright (c) 2022, 2023, Oracle and/or its affiliates. //----------------------------------------------------------------------------- // // This software is dual-licensed to you under the Universal Permissive License // (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License // 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose // either license. // // If you elect to accept the software under the Apache License, Version 2.0, // the following applies: // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //----------------------------------------------------------------------------- 'use strict'; const MessageWithData = require("./withData.js"); const constants = require("../constants.js"); const errors = require("../../../errors.js"); /** * * Executes OALL8 RPC function * * @class ExecuteMessage * @extends {MessageWithData} */ class ExecuteMessage extends MessageWithData { /** * * @param {object} statement * @param {object} options */ constructor(connection, statement, options, resultSet) { super(connection, statement, options); if (!resultSet && statement.isQuery) { resultSet = connection._createResultSet(options, statement); } this.resultSet = resultSet; this.functionCode = constants.TNS_FUNC_EXECUTE; this.bindParams = undefined; this.currentRow = 0; } //------------------------------------------------------------------------- // writeReExecuteMessage() // // Write the message for a full execute. //------------------------------------------------------------------------- writeExecuteMessage(buf) { let options = 0x0; let dmlOptions = 0; let numParams = 0; let numIters = 1; // Configuring the options field thats send to the server const stmt = this.statement; const params = stmt.bindInfoList; if (this.noImplicitRelease) { dmlOptions |= constants.TNS_EXEC_OPTION_NO_IMPL_REL; } if (!stmt.requiresDefine && !this.parseOnly && params) { numParams = params.length; } if (stmt.requiresDefine) { options |= constants.TNS_EXEC_OPTION_DEFINE; } else if (!this.parseOnly && stmt.sql) { dmlOptions |= constants.TNS_EXEC_OPTION_IMPLICIT_RESULTSET; options |= constants.TNS_EXEC_OPTION_EXECUTE; } if (stmt.cursorId === 0 || stmt.isDdl) { options |= constants.TNS_EXEC_OPTION_PARSE; } if (stmt.isQuery) { if (this.parseOnly) { options |= constants.TNS_EXEC_OPTION_DESCRIBE; } else { if (stmt.cursorId === 0 || stmt.requiresDefine) { numIters = this.options.prefetchRows; } else { numIters = this.options.fetchArraySize; } if (numIters > 0 && !stmt.noPrefetch) { options |= constants.TNS_EXEC_OPTION_FETCH; } } } if (!stmt.isPlSql && !this.parseOnly) { options |= constants.TNS_EXEC_OPTION_NOT_PLSQL; } else if (stmt.isPlSql && numParams > 0) { options |= constants.TNS_EXEC_OPTION_PLSQL_BIND; } if (numParams > 0) { options |= constants.TNS_EXEC_OPTION_BIND; } if (this.batchErrors) { options |= constants.TNS_EXEC_OPTION_BATCH_ERRORS; } if (this.arrayDmlRowCounts) { dmlOptions = constants.TNS_EXEC_OPTION_DML_ROWCOUNTS; } if (this.options.autoCommit) { options |= constants.TNS_EXEC_OPTION_COMMIT; } this.writePiggybacks(buf); this.writeFunctionHeader(buf); buf.writeUB4(options); // execute options buf.writeUB4(stmt.cursorId); // cursor id if (stmt.cursorId === 0 || stmt.isDdl) { buf.writeUInt8(1); // pointer (cursor id) buf.writeUB4(stmt.sqlLength); } else { buf.writeUInt8(0); // pointer (cursor id) buf.writeUB4(0); } buf.writeUInt8(1); // pointer (vector) buf.writeUB4(13); // al8i4 array length buf.writeUInt8(0); // pointer (al8o4) buf.writeUInt8(0); // pointer (al8o4l) buf.writeUInt8(0); // prefetc buffer size buf.writeUB4(numIters); // prefetch num rows buf.writeUB4(constants.TNS_MAX_LONG_LENGTH); // maximum long size if (numParams === 0) { buf.writeUInt8(0); // pointer (binds) buf.writeUB4(0); // number of binds } else { buf.writeUInt8(1); // pointer (binds) buf.writeUB4(numParams); // number of binds } buf.writeUInt8(0); // pointer (al8pp) buf.writeUInt8(0); // pointer (al8txn) buf.writeUInt8(0); // pointer (al8txl) buf.writeUInt8(0); // pointer (al8kv) buf.writeUInt8(0); // pointer (al8kvl) if (stmt.requiresDefine) { buf.writeUInt8(1); // pointer (al8doac) buf.writeUB4(this.statement.queryVars.length); // number of defines } else { buf.writeUInt8(0); buf.writeUB4(0); } buf.writeUB4(0); // registration id buf.writeUInt8(0); // pointer (al8objlist) buf.writeUInt8(1); // pointer (al8objlen) buf.writeUInt8(0); // pointer (al8blv) buf.writeUB4(0); // al8blv buf.writeUInt8(0); // pointer (al8dnam) buf.writeUB4(0); // al8dnaml buf.writeUB4(0); // al8regid_msb if (this.arrayDmlRowCounts) { buf.writeUInt8(1); // pointer (al8pidmlrc) buf.writeUB4(this.numExecs); // al8pidmlrcbl buf.writeUInt8(1); // pointer (al8pidmlrcl) } else { buf.writeUInt8(0); // pointer (al8pidmlrc) buf.writeUB4(0); // al8pidmlrcbl buf.writeUInt8(0); // pointer (al8pidmlrcl) } if (buf.caps.ttcFieldVersion >= constants.TNS_CCAP_FIELD_VERSION_12_2) { buf.writeUInt8(0); // pointer (al8sqlsig) buf.writeUB4(0); // SQL signature length buf.writeUInt8(0); // pointer (SQL ID) buf.writeUB4(0); // allocated size of SQL ID buf.writeUInt8(0); // pointer (length of SQL ID) if (buf.caps.ttcFieldVersion >= constants.TNS_CCAP_FIELD_VERSION_12_2_EXT1) { buf.writeUInt8(0); // pointer (chunk ids) buf.writeUB4(0); // number of chunk ids } } if (stmt.cursorId === 0 || stmt.isDdl) { if (stmt.sql) { buf.writeBytesWithLength(stmt.sqlBytes); buf.writeUB4(1); // al8i4[0] parse } else { errors.throwErr(errors.ERR_INVALID_REF_CURSOR); } } else { buf.writeUB4(0); // al8i4[0] parse } if (stmt.isQuery) { if (stmt.cursorId === 0) { buf.writeUB4(0); // al8i4[1] execution count } else { buf.writeUB4(numIters); } } else { buf.writeUB4(this.numExecs); // al8i4[1] execution count } buf.writeUB4(0); // al8i4[2] buf.writeUB4(0); // al8i4[3] buf.writeUB4(0); // al8i4[4] buf.writeUB4(0); // al8i4[5] SCN (part 1) buf.writeUB4(0); // al8i4[6] SCN (part 2) buf.writeUB4((stmt.isQuery) ? 1 : 0); // al8i4[7] is query buf.writeUB4(0); // al8i4[8] buf.writeUB4(dmlOptions); // al8i4[9] DML row counts/implicit buf.writeUB4(0); // al8i4[10] buf.writeUB4(0); // al8i4[11] buf.writeUB4(0); // al8i4[12] /* * write column metadata and bind params */ if (stmt.requiresDefine) { this.writeColumnMetadata(buf, this.statement.queryVars); } else if (numParams > 0) { return this.processBindParams(buf, params); } } //------------------------------------------------------------------------- // writeReExecuteMessage() // // Write the message header for a re-execute and return the bind parameters. //------------------------------------------------------------------------- writeReExecuteMessage(buf) { const stmt = this.statement; let params = stmt.bindInfoList; let execFlag1 = 0, execFlag2 = 0, numIters; if (params !== undefined) { if (!stmt.isQuery) { this.outVariables = []; params.forEach(info => { if (info.bindDir !== constants.TNS_BIND_DIR_INPUT) { this.outVariables.push(info.bindVar); } }); } const tmpparams = []; params.forEach(info => { if (info.bindDir !== constants.TNS_BIND_DIR_OUTPUT && !info.isReturnBind) { tmpparams.push(info); } }); params = tmpparams; } if (this.functionCode === constants.TNS_FUNC_REEXECUTE_AND_FETCH) { execFlag1 |= constants.TNS_EXEC_OPTION_EXECUTE; numIters = this.options.prefetchRows; } else { if (this.options.autoCommit) { execFlag2 |= constants.TNS_EXEC_OPTION_COMMIT_REEXECUTE; } numIters = this.numExecs; } this.writePiggybacks(buf); this.writeFunctionHeader(buf); buf.writeUB4(stmt.cursorId); buf.writeUB4(numIters); buf.writeUB4(execFlag1); buf.writeUB4(execFlag2); return params; } //------------------------------------------------------------------------- // encode() // // Write the execute message to the buffer. Two types of execute messages // are possible: one for a full execute and the second, simpler message, // for when an existing cursor is being re-executed. //------------------------------------------------------------------------- encode(buf) { // no rows have yet been sent so the header information needs to be sent if (this.currentRow === 0) { const stmt = this.statement; if (stmt.cursorId !== 0 && !stmt.requiresFullExecute && !this.parseOnly && !stmt.requiresDefine && !stmt.noPrefetch && !stmt.isDdl && !this.batchErrors) { if (stmt.isQuery && this.options.prefetchRows > 0) { this.functionCode = constants.TNS_FUNC_REEXECUTE_AND_FETCH; } else { this.functionCode = constants.TNS_FUNC_REEXECUTE; } this.bindParams = this.writeReExecuteMessage(buf); } else { this.functionCode = constants.TNS_FUNC_EXECUTE; this.bindParams = this.writeExecuteMessage(buf); } } // if any bind parameters need to be sent, do that // after each row is sent, check to see whether a pause should be performed if (this.bindParams && this.bindParams.length > 0) { const adapter = buf.nsi.ntAdapter; while (this.currentRow < this.numExecs) { buf.writeUInt8(constants.TNS_MSG_TYPE_ROW_DATA); this.writeBindParamsRow(buf, this.bindParams, this.currentRow); this.currentRow++; if (this.currentRow < this.numExecs && adapter.shouldPauseWrite()) return true; } } // reset state in case message is resent this.currentRow = 0; this.bindParams = undefined; } } module.exports = ExecuteMessage;