Fixed issue which throws an error, when the same SELECT SQL statement is run for the second time with a different bind type. (Issue #1669)
This commit is contained in:
parent
04b4bcea0a
commit
d909a3afeb
|
@ -12,6 +12,9 @@ node-oracledb `v6.5.1 <https://github.com/oracle/node-oracledb/compare/v6.5.0...
|
|||
|
||||
Thin Mode Changes
|
||||
+++++++++++++++++
|
||||
#) Fixed issue which throws the `ORA-00932` error, when the same SELECT SQL
|
||||
statement is run for the second time with a different bind type.
|
||||
See `Issue #1669 <https://github.com/oracle/node-oracledb/issues/1669>`__.
|
||||
|
||||
#) Fixed exponent check condition for out-of-bounds number.
|
||||
See `Issue #1659 <https://github.com/oracle/node-oracledb/issues/1659>`__.
|
||||
|
|
|
@ -31,6 +31,17 @@ const util = require('util');
|
|||
// define error prefix for all messages
|
||||
const ERR_PREFIX = "NJS";
|
||||
|
||||
const ERR_INTEGRITY_ERROR_CODES = [
|
||||
1, // unique constraint violated
|
||||
1400, // cannot insert NULL
|
||||
1438, // value larger than specified precision
|
||||
2290, // check constraint violated
|
||||
2291, // integrity constraint violated - parent key not found
|
||||
2292, // integrity constraint violated - child record found
|
||||
21525, // attribute or collection element violated its constraints
|
||||
40479, // internal JSON serializer error
|
||||
];
|
||||
|
||||
// define error number constants (used in JavaScript library)
|
||||
const ERR_INVALID_POOL = 2;
|
||||
const ERR_INVALID_CONNECTION = 3;
|
||||
|
@ -695,6 +706,7 @@ function transformErr(err, fnOpt) {
|
|||
|
||||
// define exports
|
||||
module.exports = {
|
||||
ERR_INTEGRITY_ERROR_CODES,
|
||||
ERR_INVALID_POOL,
|
||||
ERR_INVALID_CONNECTION,
|
||||
ERR_INVALID_PROPERTY_VALUE,
|
||||
|
|
|
@ -724,6 +724,7 @@ module.exports = {
|
|||
TNS_XML_TYPE_FLAG_SKIP_NEXT_4: 0x100000,
|
||||
|
||||
// errors
|
||||
TNS_ERR_INCONSISTENT_DATA_TYPES: 932,
|
||||
TNS_ERR_VAR_NOT_IN_SELECT_LIST: 1007,
|
||||
TNS_ERR_INBAND_MESSAGE: 12573,
|
||||
TNS_ERR_INVALID_SERVICE_NAME: 12514,
|
||||
|
|
|
@ -113,9 +113,18 @@ class MessageWithData extends Message {
|
|||
this.errorInfo.num = 0;
|
||||
this.errorOccurred = false;
|
||||
this.statement.moreRowsToFetch = false;
|
||||
} else if (this.retry) {
|
||||
this.retry = false;
|
||||
} else if (this.statement.isQuery &&
|
||||
(this.errorInfo.num === constants.TNS_ERR_VAR_NOT_IN_SELECT_LIST
|
||||
|| this.errorInfo.num === constants.TNS_ERR_INCONSISTENT_DATA_TYPES)) {
|
||||
this.retry = true;
|
||||
this.connection.statementCache.clearCursor(this.statement);
|
||||
} else if (this.errorInfo.num !== 0 && this.errorInfo.cursorId !== 0) {
|
||||
this.connection.statementCache._cachedStatements.delete(this.statement.sql);
|
||||
this.statement.returnToCache = false;
|
||||
if (!errors.ERR_INTEGRITY_ERROR_CODES.includes(this.errorInfo.num)) {
|
||||
this.connection.statementCache.clearCursor(this.statement);
|
||||
this.statement.returnToCache = false;
|
||||
}
|
||||
}
|
||||
if (this.errorInfo.batchErrors) {
|
||||
this.errorOccurred = false;
|
||||
|
|
|
@ -174,6 +174,10 @@ class Protocol {
|
|||
if (callTimeoutExpired) {
|
||||
errors.throwErr(errors.ERR_CALL_TIMEOUT_EXCEEDED, this.callTimeout);
|
||||
}
|
||||
if (message.retry) {
|
||||
message.errorOccurred = false;
|
||||
return await this._processMessage(message);
|
||||
}
|
||||
let err = new Error(message.errorInfo.message);
|
||||
err.offset = message.errorInfo.pos;
|
||||
err.errorNum = message.errorInfo.num;
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
const { Buffer } = require('buffer');
|
||||
const constants = require('../constants');
|
||||
const errors = require('../errors');
|
||||
const protoConstants = require('./protocol/constants');
|
||||
|
||||
/**
|
||||
* It is used to cache the metadata about bind information
|
||||
|
@ -519,6 +520,9 @@ class Statement {
|
|||
// cursor also requires a full execute.
|
||||
//---------------------------------------------------------------------------
|
||||
_setVariable(bindInfo, variable) {
|
||||
if (variable.type._oraTypeNum === protoConstants.TNS_DATA_TYPE_CURSOR) {
|
||||
this.requiresFullExecute = true;
|
||||
}
|
||||
if (variable.maxSize !== bindInfo.maxSize
|
||||
|| variable.dir !== bindInfo.dir
|
||||
|| variable.isArray !== bindInfo.isArray
|
||||
|
|
|
@ -146,6 +146,11 @@ class StatementCache {
|
|||
return stmt;
|
||||
}
|
||||
|
||||
clearCursor(statement) {
|
||||
this._addCursorToClose(statement);
|
||||
statement.cursorId = 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// returnStatement()
|
||||
// Return the statement to the statement cache, if applicable. If the
|
||||
|
|
|
@ -1119,4 +1119,72 @@ describe('4. binding.js', function() {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('4.15 binding different data types for same sql', function() {
|
||||
let connection;
|
||||
let sysDBAConn;
|
||||
let sid;
|
||||
const numIters = 40;
|
||||
|
||||
before(async function() {
|
||||
if (!dbConfig.test.DBA_PRIVILEGE) this.skip();
|
||||
const dbaConfig = {
|
||||
user: dbConfig.test.DBA_user,
|
||||
password: dbConfig.test.DBA_password,
|
||||
connectionString: dbConfig.connectString,
|
||||
privilege: oracledb.SYSDBA
|
||||
};
|
||||
sysDBAConn = await oracledb.getConnection(dbaConfig);
|
||||
connection = await oracledb.getConnection(dbConfig);
|
||||
sid = await testsUtil.getSid(connection);
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
if (connection) {
|
||||
await connection.close();
|
||||
}
|
||||
if (sysDBAConn) {
|
||||
await sysDBAConn.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('4.15.1 change bindtypes using bindByPosition for queries',
|
||||
async function() {
|
||||
const sql = 'SELECT :1 FROM DUAL';
|
||||
const dt = new Date();
|
||||
const openCount = await testsUtil.getOpenCursorCount(sysDBAConn, sid);
|
||||
for (let i = 0; i < numIters; i++) {
|
||||
let result = await connection.execute(sql, [1]);
|
||||
assert.strictEqual(result.rows[0][0], 1);
|
||||
result = await connection.execute(sql, [dt]);
|
||||
assert.deepStrictEqual(result.rows[0][0], dt);
|
||||
result = await connection.execute(sql, [2]);
|
||||
assert.strictEqual(result.rows[0][0], 2);
|
||||
}
|
||||
const newOpenCount = await testsUtil.
|
||||
getOpenCursorCount(sysDBAConn, sid);
|
||||
|
||||
// ensure cursors are not linearly opened as numIters causing leak.
|
||||
assert(newOpenCount - openCount < 4);
|
||||
});
|
||||
|
||||
it('4.15.2 change bindtypes using bindByName for queries',
|
||||
async function() {
|
||||
const sql = 'SELECT :x FROM DUAL';
|
||||
const openCount = await testsUtil.getOpenCursorCount(sysDBAConn, sid);
|
||||
const dt = new Date();
|
||||
for (let i = 0; i < numIters; i++) {
|
||||
let result = await connection.execute(sql, {x: 1});
|
||||
assert.strictEqual(result.rows[0][0], 1);
|
||||
result = await connection.execute(sql, {x: dt});
|
||||
assert.deepStrictEqual(result.rows[0][0], dt);
|
||||
result = await connection.execute(sql, {x: 2});
|
||||
assert.strictEqual(result.rows[0][0], 2);
|
||||
}
|
||||
const newOpenCount = await testsUtil.getOpenCursorCount(sysDBAConn, sid);
|
||||
|
||||
// ensure cursors are not linearly opened as numIters causing leak.
|
||||
assert(newOpenCount - openCount < 4);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -515,5 +515,29 @@ describe('6. dmlReturning.js', function() {
|
|||
// NJS-011: encountered bind value and type mismatch
|
||||
});
|
||||
|
||||
it('6.2.10 INSERT statement, check re-execute by changing the out bindtype', async function() {
|
||||
let id = 50;
|
||||
let sql = `INSERT INTO ${tableName} VALUES (${id}, TO_DATE('2015-01-11','YYYY-DD-MM'))
|
||||
RETURNING num, content INTO :rnum, :rcontent`;
|
||||
const dt = new Date('2015-11-01 00:00:00');
|
||||
const bindVar =
|
||||
{
|
||||
rnum: { type: oracledb.NUMBER, dir: oracledb.BIND_OUT },
|
||||
rcontent: { type: oracledb.DATE, dir: oracledb.BIND_OUT }
|
||||
};
|
||||
|
||||
// outbind as date.
|
||||
let result = await connection.execute(sql, bindVar);
|
||||
assert.strictEqual(result.outBinds.rcontent[0].getTime(), dt.getTime());
|
||||
|
||||
// Change outbind type to string
|
||||
bindVar.rcontent.type = oracledb.STRING;
|
||||
id = 51;
|
||||
sql = `INSERT INTO ${tableName} VALUES (${id}, TO_DATE('2015-01-11','YYYY-DD-MM'))
|
||||
RETURNING num, content INTO :rnum, :rcontent`;
|
||||
result = await connection.execute(sql, bindVar);
|
||||
assert.strictEqual(result.outBinds.rcontent[0], '01-NOV-15');
|
||||
});
|
||||
|
||||
}); // 6.2
|
||||
});
|
||||
|
|
|
@ -222,6 +222,7 @@ Overview of node-oracledb functional tests
|
|||
6.2.7 DELETE statement, single row matched, Object binding format
|
||||
6.2.8 DELETE statement, multiple rows matched, Array binding format
|
||||
6.2.9 Negative test - bind value and type mismatch
|
||||
6.2.10 INSERT statement, check re-execute by changing the out bindtype
|
||||
|
||||
7. autoCommit.js
|
||||
7.1 autoCommit takes effect when setting oracledb.autoCommit before connecting
|
||||
|
@ -4804,6 +4805,7 @@ oracledb.OUT_FORMAT_OBJECT and resultSet = true
|
|||
239.4 implicit binding type
|
||||
239.5 check REF CURSOR round-trips with no prefetching
|
||||
239.6 check REF CURSOR round-trips with prefetching
|
||||
239.7 check REF CURSOR bind with re-execute
|
||||
|
||||
240. errorOffset.js
|
||||
240.1 checks the offset value of the error
|
||||
|
|
|
@ -263,4 +263,36 @@ describe('239. plsqlBindCursorsIN.js', () => {
|
|||
((oracledb.thin) ? assert.strictEqual(rt, 3) : assert.strictEqual(rt, 2));
|
||||
}); // 239.6
|
||||
|
||||
|
||||
it('239.7 check REF CURSOR bind with re-execute ', async () => {
|
||||
const refCursorOptions = {
|
||||
resultSet: true,
|
||||
};
|
||||
let result = await conn.execute(sqlRefCursor, [], refCursorOptions);
|
||||
|
||||
const plsql = `begin ${procName1}(:bv); end;`;
|
||||
await conn.execute(
|
||||
plsql,
|
||||
{
|
||||
bv: {val: result.resultSet, type: oracledb.CURSOR, dir: oracledb.BIND_IN, maxSize: 10 }
|
||||
}
|
||||
);
|
||||
result = await conn.execute(sqlRefCursor, [], refCursorOptions);
|
||||
|
||||
// Check Re-execute.
|
||||
await conn.execute(
|
||||
plsql,
|
||||
{
|
||||
bv: {val: result.resultSet, type: oracledb.CURSOR, dir: oracledb.BIND_IN, maxSize: 10 }
|
||||
}
|
||||
);
|
||||
|
||||
await result.resultSet.close();
|
||||
|
||||
const sqlQuery = `select * from ${tableName}`;
|
||||
const queryResult = await conn.execute(sqlQuery);
|
||||
|
||||
assert.strictEqual(queryResult.rows.length, 3);
|
||||
}); // 239.7
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue