From bae7dd19bdd84842457942034cd0fd945ff4fc72 Mon Sep 17 00:00:00 2001 From: Sharad Chandran R Date: Thu, 6 Jul 2023 13:10:16 +0530 Subject: [PATCH] Fixed bug that throws the NJS-111 internal error, on the second SELECT call issued after the first SELECT call on an empty table with LOBs --- doc/src/release_notes.rst | 4 +++ lib/thin/protocol/messages/execute.js | 10 +++---- lib/thin/protocol/messages/withData.js | 1 + lib/thin/statement.js | 1 + test/dataTypeClob.js | 40 ++++++++++++++++++++++++++ test/lobBind2.js | 35 ++++++++++++++++++++++ 6 files changed, 86 insertions(+), 5 deletions(-) diff --git a/doc/src/release_notes.rst b/doc/src/release_notes.rst index a9ea10194..bb368be51 100644 --- a/doc/src/release_notes.rst +++ b/doc/src/release_notes.rst @@ -11,6 +11,10 @@ node-oracledb `v6.0.3 `__. diff --git a/lib/thin/protocol/messages/execute.js b/lib/thin/protocol/messages/execute.js index 6f95bafd0..2d198a819 100644 --- a/lib/thin/protocol/messages/execute.js +++ b/lib/thin/protocol/messages/execute.js @@ -77,21 +77,21 @@ class ExecuteMessage extends MessageWithData { dmlOptions = constants.TNS_EXEC_OPTION_IMPLICIT_RESULTSET; options |= constants.TNS_EXEC_OPTION_EXECUTE; } - if (stmt.cursorId == 0 || stmt.isDdl) { + 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 (this.options.prefetchRows > 0) { - options |= constants.TNS_EXEC_OPTION_FETCH; - } 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) { @@ -276,7 +276,7 @@ class ExecuteMessage extends MessageWithData { if (this.currentRow === 0) { let stmt = this.statement; if (stmt.cursorId !== 0 && !stmt.requiresFullExecute && !this.parseOnly && !stmt.requiresDefine && !stmt.isDdl && !this.batchErrors) { - if (stmt.isQuery && !stmt.requiresDefine && this.options.prefetchRows > 0) { + if (stmt.isQuery && !stmt.requiresDefine && !stmt.noPrefetch && this.options.prefetchRows > 0) { this.functionCode = constants.TNS_FUNC_REEXECUTE_AND_FETCH; } else { this.functionCode = constants.TNS_FUNC_REEXECUTE; diff --git a/lib/thin/protocol/messages/withData.js b/lib/thin/protocol/messages/withData.js index d51b0ecfa..06640a1b2 100644 --- a/lib/thin/protocol/messages/withData.js +++ b/lib/thin/protocol/messages/withData.js @@ -547,6 +547,7 @@ class MessageWithData extends Message { variable.maxSize = constants.TNS_MAX_LONG_LENGTH; } resultSet.statement.requiresDefine = true; + resultSet.statement.noPrefetch = true; } } } diff --git a/lib/thin/statement.js b/lib/thin/statement.js index 2e5284028..9043df072 100644 --- a/lib/thin/statement.js +++ b/lib/thin/statement.js @@ -93,6 +93,7 @@ class Statement { this.queryVars = []; this.bindInfoDict = new Map(); this.requiresFullExecute = false; + this.noPrefetch = false; this.returnToCache = false; this.numColumns = 0; this.lastRowIndex; diff --git a/test/dataTypeClob.js b/test/dataTypeClob.js index 4d14378bf..c3b816bab 100644 --- a/test/dataTypeClob.js +++ b/test/dataTypeClob.js @@ -42,6 +42,7 @@ const fs = require('fs'); const assert = require('assert'); const dbConfig = require('./dbconfig.js'); const assist = require('./dataTypeAssist.js'); +const testsUtil = require('./testsUtil.js'); let inFileName = 'test/clobexample.txt'; // the file with text to be inserted into the database let outFileName = 'test/clobstreamout.txt'; // output file with the stream out data @@ -170,4 +171,43 @@ describe('40. dataTypeClob.js', function() { await assist.verifyNullValues(connection, tableName); }); }); + + describe('40.3 Read CLOB data on meta data change', function() { + let connection = null; + const tableNameCLOB = 'nodb_myclobs_re_create'; + const sqlCreateQuery = ` + CREATE TABLE ${tableNameCLOB} ( + num NUMBER, + content CLOB + )`; + const sqlDrop = testsUtil.sqlDropTable(tableNameCLOB); + const sqlCreate = testsUtil.sqlCreateTable(tableNameCLOB, sqlCreateQuery); + const insertSql = `INSERT INTO ${tableNameCLOB} (num, content) ` + + `VALUES (:n, 'CLOB')`; + const selectSql = `SELECT content FROM ${tableNameCLOB} WHERE num = 1`; + + before(async function() { + oracledb.fetchAsString = [oracledb.CLOB]; + connection = await oracledb.getConnection(dbConfig); + await connection.execute(sqlCreate); + await connection.execute(insertSql, { n: 1 }, { autoCommit: false }); + }); + + after(async function() { + oracledb.fetchAsString = []; + await connection.execute(sqlDrop); + await connection.close(); + }); + + it('40.3.1 Recreate table after CLOB column is read and statement is in statement cache', + async function() { + await connection.execute(selectSql, {}, { keepInStmtCache: true }); + await connection.execute(sqlDrop); + await connection.execute(sqlCreate); + await connection.execute(insertSql, { n: 1 }, { autoCommit: false }); + await connection.execute(selectSql); + }); + + }); + }); diff --git a/test/lobBind2.js b/test/lobBind2.js index edada3c2f..1e68a7369 100644 --- a/test/lobBind2.js +++ b/test/lobBind2.js @@ -36,6 +36,7 @@ const fs = require('fs'); const fsPromises = require('fs/promises'); const assert = require('assert'); const dbConfig = require('./dbconfig.js'); +const testsUtil = require('./testsUtil.js'); describe("72. lobBind2.js", function() { @@ -539,4 +540,38 @@ describe("72. lobBind2.js", function() { }); // 72.3 + describe('72.4 Create Table with CLOB and Number columns and do select with empty rows and select after rows insertion', function() { + let connection = null; + const tableNameCLOB = 'nodb_myclobs_num_table'; + const sqlCreateQuery = ` + CREATE TABLE ${tableNameCLOB} ( + F1 NUMBER, + F2 CLOB, + F3 CLOB + )`; + const sqlDrop = testsUtil.sqlDropTable(tableNameCLOB); + const sqlCreate = testsUtil.sqlCreateTable(tableNameCLOB, sqlCreateQuery); + const insertSql = `INSERT INTO ${tableNameCLOB} (F1, F2, F3) ` + + `VALUES (:1, :2, :3)`; + const selectSql = `SELECT * FROM ${tableNameCLOB} `; + + before(async function() { + oracledb.fetchAsString = [oracledb.CLOB]; + connection = await oracledb.getConnection(dbConfig); + await connection.execute(sqlCreate); + await connection.execute(selectSql, {}, {keepInStmtCache: true}); + await connection.execute(insertSql, [1, 'CLOB1', 'CLOB2']); + }); + + after(async function() { + oracledb.fetchAsString = []; + await connection.execute(sqlDrop); + await connection.close(); + }); + + it('72.4.1 Read both CLOB and Number with statement being in statement cache', async function() { + await connection.execute(selectSql, {}, {keepInStmtCache: true}); + }); + + }); });