Skip to content

Commit 0255b70

Browse files
committed
Fixed bug throwing unexpected errors during LOB column fetch after table recreation for Issue #1565
1 parent 08f62bc commit 0255b70

File tree

4 files changed

+110
-20
lines changed

4 files changed

+110
-20
lines changed

doc/src/release_notes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ Common Changes
2323
Thin Mode Changes
2424
+++++++++++++++++
2525

26+
#) Fixed bug giving 'ORA-01002 and NJS-112 errors' during LOB columns
27+
fetch after table recreation.
28+
`Issue #1589 <https://github.com/oracle/node-oracledb/issues/1565>`__.
29+
2630
#) Fixed bug in handling unexpected pool growth which is crossing pool max limit
2731
due to improper handling of parallel connection request.
2832
`Issue #1591 <https://github.com/oracle/node-oracledb/issues/1591>`__.

lib/thin/protocol/messages/execute.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,8 @@ class ExecuteMessage extends MessageWithData {
275275
// no rows have yet been sent so the header information needs to be sent
276276
if (this.currentRow === 0) {
277277
const stmt = this.statement;
278-
if (stmt.cursorId !== 0 && !stmt.requiresFullExecute && !this.parseOnly && !stmt.requiresDefine && !stmt.isDdl && !this.batchErrors) {
279-
if (stmt.isQuery && !stmt.requiresDefine && !stmt.noPrefetch && this.options.prefetchRows > 0) {
278+
if (stmt.cursorId !== 0 && !stmt.requiresFullExecute && !this.parseOnly && !stmt.requiresDefine && !stmt.noPrefetch && !stmt.isDdl && !this.batchErrors) {
279+
if (stmt.isQuery && this.options.prefetchRows > 0) {
280280
this.functionCode = constants.TNS_FUNC_REEXECUTE_AND_FETCH;
281281
} else {
282282
this.functionCode = constants.TNS_FUNC_REEXECUTE;

lib/thin/protocol/messages/withData.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,12 @@ class MessageWithData extends Message {
6565
processMessage(buf, messageType) {
6666
if (messageType === constants.TNS_MSG_TYPE_DESCRIBE_INFO) {
6767
buf.skipBytesChunked();
68+
const prevQueryVars = this.statement.queryVars;
6869
this.statement.queryVars = [];
6970
this.statement.numQueryVars = 0;
7071
this.statement.bufferRowCount = 0;
7172
this.statement.bufferRowIndex = 0;
72-
this.processDescribeInfo(buf, this.resultSet);
73+
this.processDescribeInfo(buf, this.resultSet, prevQueryVars);
7374
this.outVariables = this.statement.queryVars;
7475
} else if (messageType === constants.TNS_MSG_TYPE_ROW_HEADER) {
7576
this.processRowHeader(buf);
@@ -123,7 +124,22 @@ class MessageWithData extends Message {
123124
}
124125
}
125126

126-
processDescribeInfo(buf, resultSet) {
127+
//---------------------------------------------------------------------------
128+
// If we have fetched this column earlier, we set that
129+
// fetch type for the describe info variable received
130+
// assuming the returned column order is same as previous.
131+
//---------------------------------------------------------------------------
132+
_adjustFetchType(pVar, cVar) {
133+
if ((cVar.fetchInfo.dbType._oraTypeNum === constants.TNS_DATA_TYPE_CLOB
134+
&& pVar.fetchInfo.fetchType._oraTypeNum === constants.TNS_DATA_TYPE_LONG)
135+
|| (cVar.fetchInfo.dbType._oraTypeNum === constants.TNS_DATA_TYPE_BLOB
136+
&& pVar.fetchInfo.fetchType._oraTypeNum === constants.TNS_DATA_TYPE_LONG_RAW)) {
137+
cVar.type = pVar.fetchInfo.fetchType;
138+
cVar.maxSize = pVar.maxSize;
139+
}
140+
}
141+
142+
processDescribeInfo(buf, resultSet, prevQueryVars) {
127143
const statement = resultSet.statement;
128144
buf.skipUB4(); // max row size
129145
statement.numQueryVars = buf.readUB4();
@@ -133,6 +149,9 @@ class MessageWithData extends Message {
133149
resultSet.metadata = [];
134150
for (let i = 0; i < statement.numQueryVars; i++) {
135151
const variable = this.processColumnInfo(buf, i + 1);
152+
if (prevQueryVars && i < prevQueryVars.length) {
153+
this._adjustFetchType(prevQueryVars[i], variable);
154+
}
136155
statement.queryVars.push(variable);
137156
resultSet.metadata.push(variable.fetchInfo);
138157
}
@@ -535,8 +554,10 @@ class MessageWithData extends Message {
535554
variable.type = variable.fetchInfo.fetchType;
536555
variable.maxSize = constants.TNS_MAX_LONG_LENGTH;
537556
}
538-
resultSet.statement.requiresDefine = true;
539-
resultSet.statement.noPrefetch = true;
557+
if (!resultSet.statement.noPrefetch) {
558+
resultSet.statement.requiresDefine = true;
559+
resultSet.statement.noPrefetch = true;
560+
}
540561
}
541562
}
542563
}

test/dataTypeClob.js

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,34 +178,99 @@ describe('40. dataTypeClob.js', function() {
178178
const sqlCreateQuery = `
179179
CREATE TABLE ${tableNameCLOB} (
180180
num NUMBER,
181+
value varchar2(255),
181182
content CLOB
182183
)`;
183184
const sqlDrop = testsUtil.sqlDropTable(tableNameCLOB);
184185
const sqlCreate = testsUtil.sqlCreateTable(tableNameCLOB, sqlCreateQuery);
185-
const insertSql = `INSERT INTO ${tableNameCLOB} (num, content) ` +
186-
`VALUES (:n, 'CLOB')`;
187-
const selectSql = `SELECT content FROM ${tableNameCLOB} WHERE num = 1`;
188-
189-
before(async function() {
190-
oracledb.fetchAsString = [oracledb.CLOB];
186+
const insertSql = `INSERT INTO ${tableNameCLOB} (num, value, content) ` +
187+
`VALUES (:n, :val, 'CLOB')`;
188+
const selectSqlCharBind = `SELECT * FROM ${tableNameCLOB} WHERE value = :val`;
189+
const binds = [
190+
{ n: 1, val: 'GEN_COL' },
191+
{ n: 2, val: 'GEN_COL_NEW' }
192+
];
193+
194+
async function setupConnAndTable() {
191195
connection = await oracledb.getConnection(dbConfig);
192196
await connection.execute(sqlCreate);
193-
await connection.execute(insertSql, { n: 1 }, { autoCommit: false });
197+
await connection.executeMany(insertSql, binds);
198+
}
199+
200+
async function doFirstSelect() {
201+
await connection.execute(selectSqlCharBind, {val:'GEN_COL'}, { keepInStmtCache: true });
202+
await connection.execute(sqlDrop);
203+
await connection.execute(sqlCreate);
204+
await connection.executeMany(insertSql, binds);
205+
}
206+
207+
afterEach(async function() {
208+
if (connection) {
209+
await connection.execute(sqlDrop);
210+
await connection.close();
211+
connection = null;
212+
}
194213
});
195214

196215
after(async function() {
197216
oracledb.fetchAsString = [];
198-
await connection.execute(sqlDrop);
199-
await connection.close();
217+
if (connection) {
218+
await connection.execute(sqlDrop);
219+
await connection.close();
220+
}
200221
});
201222

202-
it('40.3.1 Recreate table after CLOB column is read and statement is in statement cache',
223+
it('40.3.1 Recreate table after CLOB column as CLOB is read and statement is in statement cache',
224+
async function() {
225+
oracledb.fetchAsString = [];
226+
await setupConnAndTable();
227+
await doFirstSelect();
228+
await connection.execute(selectSqlCharBind, {val:'GEN_COL'});
229+
});
230+
231+
it('40.3.2 Recreate table after CLOB column as String is read and statement is in statement cache',
203232
async function() {
204-
await connection.execute(selectSql, {}, { keepInStmtCache: true });
233+
oracledb.fetchAsString = [oracledb.CLOB];
234+
await setupConnAndTable();
235+
await doFirstSelect();
236+
await connection.execute(selectSqlCharBind, {val:'GEN_COL'});
237+
});
238+
239+
it('40.3.3 select with large bindvalue than previous select bindvalue after Recreate table ',
240+
async function() {
241+
oracledb.fetchAsString = [oracledb.CLOB];
242+
await setupConnAndTable();
243+
await doFirstSelect();
244+
// provide bind value 'GEN_COL_NEW' larger than earlier bind value 'GEN_COL'.
245+
await connection.execute(selectSqlCharBind, {val:'GEN_COL_NEW'});
246+
});
247+
248+
it('40.3.4 select using fetchAsCLOB with large bindvalue than previous select bindvalue after Recreate table ',
249+
async function() {
250+
oracledb.fetchAsString = [];
251+
await setupConnAndTable();
252+
await doFirstSelect();
253+
// provide bind value 'GEN_COL_NEW' larger than earlier bind value 'GEN_COL'.
254+
await connection.execute(selectSqlCharBind, {val:'GEN_COL_NEW'});
255+
});
256+
257+
it('40.3.5 select using fetchAsCLOB with large bindvalue than previous select bindvalue after Recreate table twice',
258+
async function() {
259+
oracledb.fetchAsString = [oracledb.CLOB];
260+
await setupConnAndTable();
261+
await doFirstSelect();
262+
// provide bind value 'GEN_COL_NEW' larger than earlier bind value 'GEN_COL'.
263+
await connection.execute(selectSqlCharBind, {val:'GEN_COL_NEW'});
264+
265+
// cleanup the connection
205266
await connection.execute(sqlDrop);
206-
await connection.execute(sqlCreate);
207-
await connection.execute(insertSql, { n: 1 }, { autoCommit: false });
208-
await connection.execute(selectSql);
267+
await connection.close();
268+
connection = null;
269+
270+
await setupConnAndTable();
271+
await doFirstSelect();
272+
// provide bind value 'GEN_COL_NEW' larger than earlier bind value 'GEN_COL'.
273+
await connection.execute(selectSqlCharBind, {val:'GEN_COL_NEW'});
209274
});
210275

211276
});

0 commit comments

Comments
 (0)