Fix code to handle edge cases for pool and statement handling

This commit is contained in:
Sharad Chandran R 2023-05-24 00:27:26 +05:30
parent af85a36c85
commit cbd914a6db
9 changed files with 74 additions and 97 deletions

View File

@ -135,6 +135,7 @@ const ERR_DELETE_ELEMENTS_OF_VARRAY = 133;
const ERR_WRONG_VALUE_FOR_DBOBJECT_ATTR = 134;
const ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM = 135;
const ERR_WRONG_CRED_FOR_EXTAUTH = 136;
const ERR_MISSING_BIND_VALUE = 137;
// Oracle Net layer errors start from 500
const ERR_CONNECTION_CLOSED = 500;
@ -390,6 +391,8 @@ messages.set(ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM, // NJS-135
'value is of wrong type for an element of object %s');
messages.set(ERR_WRONG_CRED_FOR_EXTAUTH, // NJS-136
'user name and password cannot be set when using external authentication');
messages.set(ERR_MISSING_BIND_VALUE, // NJS-137
'a bind variable replacement value for placeholder ":%s" was not provided');
// Oracle Net layer errors
@ -759,6 +762,7 @@ module.exports = {
ERR_WRONG_VALUE_FOR_DBOBJECT_ATTR,
ERR_WRONG_VALUE_FOR_DBOBJECT_ELEM,
ERR_WRONG_CRED_FOR_EXTAUTH,
ERR_MISSING_BIND_VALUE,
ERR_CONNECTION_CLOSED_CODE: `${ERR_PREFIX}-${ERR_CONNECTION_CLOSED}`,
assert,
assertArgCount,

View File

@ -373,10 +373,10 @@ async function _verifyOptions(options, inCreatePool) {
outOptions.queueTimeout = options.queueTimeout;
}
// queueMax must be an integer >= -1
// queueMax must be an integer
if (options.queueMax !== undefined) {
errors.assertParamPropValue(Number.isInteger(options.queueMax) &&
options.queueMax >= -1, 1, "queueMax");
errors.assertParamPropValue(Number.isInteger(options.queueMax), 1,
"queueMax");
outOptions.queueMax = options.queueMax;
}

View File

@ -683,10 +683,10 @@ class ThinConnectionImpl extends ConnectionImpl {
if (normalizedName.startsWith(':')) {
normalizedName = variable.name.substring(1);
}
if (bindInfoDict[normalizedName] === undefined) {
if (!bindInfoDict.has(normalizedName)) {
errors.throwErr(errors.ERR_INVALID_BIND_NAME, normalizedName);
}
bindInfoDict[normalizedName].forEach((bindInfo) => {
bindInfoDict.get(normalizedName).forEach((bindInfo) => {
stmt._setVariable(bindInfo, variable);
});
} else {
@ -722,7 +722,7 @@ class ThinConnectionImpl extends ConnectionImpl {
if (!isParse) {
const numBinds = stmt.bindInfoList.length;
const numVars = binds.length;
if (numBinds !== numVars) {
if (numBinds !== numVars && (numVars === 0 || !binds[0].name)) {
errors.throwErr(errors.ERR_WRONG_NUMBER_OF_POSITIONAL_BINDS, numBinds, numVars);
}
for (const variable of binds) {
@ -771,7 +771,7 @@ class ThinConnectionImpl extends ConnectionImpl {
if (statement.numQueryVars > 0) {
result.metaData = thinUtil.getMetadataMany(statement.queryVars);
}
result.bindNames = Object.keys(statement.bindInfoDict);
result.bindNames = Array.from(statement.bindInfoDict.keys());
result.statementType = statement.statementType;
return result;
}
@ -806,8 +806,8 @@ class ThinConnectionImpl extends ConnectionImpl {
result.resultSet = message.resultSet;
} else {
statement.bufferRowIndex = 0;
let bindVars = thinUtil.getBindVars(statement);
let outBinds = thinUtil.getExecuteOutBinds(bindVars);
const bindVars = thinUtil.getBindVars(statement);
const outBinds = thinUtil.getExecuteOutBinds(bindVars);
if (outBinds) {
result.outBinds = outBinds;
}
@ -856,16 +856,11 @@ class ThinConnectionImpl extends ConnectionImpl {
if (statement.isPlSql && statement.requiresFullExecute) {
message.numExecs = 1;
await this._protocol._processMessage(message);
if (statement.plsqlMultipleExecs) {
for (let i = 0; i < numIters - 1; i++) {
message.offset = i + 1;
await this._protocol._processMessage(message);
}
} else {
message.offset = 1;
statement.requiresFullExecute = false;
message.numExecs = numIters - 1;
message.offset = 1;
}
} else {
if (message.numExecs > 0) {
await this._protocol._processMessage(message);
}

View File

@ -213,9 +213,7 @@ class ExecuteMessage extends MessageWithData {
if (stmt.requiresDefine) {
this.writeColumnMetadata(buf, this.statement.queryVars);
} else if (numParams > 0) {
const returningOnly = this.processBindParams(buf, params);
if (!returningOnly)
return params;
return this.processBindParams(buf, params);
}
}

View File

@ -302,7 +302,7 @@ class MessageWithData extends Message {
variable.values[this.rowIndex] = value;
} else {
value = this.processColumnData(buf, variable, this.rowIndex);
variable.values[this.rowIndex + this.offset] = value;
variable.values[this.rowIndex] = value;
}
}
this.rowIndex++;
@ -337,10 +337,6 @@ class MessageWithData extends Message {
}
this.outVariables.push(bindInfo.bindVar);
}
if (this.statement.isPlSql && this.outVariables.length > 0) {
this.statement.plsqlMultipleExecs = true;
}
}
processColumnData(buf, variable) {
@ -588,34 +584,16 @@ class MessageWithData extends Message {
}
processBindParams(buf, params) {
let returningOnly = true;
let allValuesNull = true;
let bindVars = [];
let bindVals = [];
const bindVars = [];
const nonReturningParams = [];
for (const bindInfo of params) {
if (!bindInfo.isReturnBind) {
returningOnly = false;
}
bindVals = bindInfo.bindVar.values;
if (allValuesNull) {
for (let i = 0; i < bindVals.length; i++) {
if (bindVals[i] !== null) {
allValuesNull = false;
break;
}
}
nonReturningParams.push(bindInfo);
}
bindVars.push(bindInfo.bindVar);
}
this.writeColumnMetadata(buf, bindVars);
// plsql batch executions without bind values
if (this.statement.isPlsql && this.numExecs > 1 && !allValuesNull) {
buf.writeUInt8(constants.TNS_MSG_TYPE_ROW_DATA);
buf.writeUInt8(constants.TNS_ESCAPE_CHAR);
buf.writeUInt8(1);
}
return returningOnly;
return nonReturningParams;
}
writeColumnMetadata(buf, bindVars) {

View File

@ -127,10 +127,10 @@ class Statement {
copiedStatement.bindInfoDict = new Map();
bindInfoDict = copiedStatement.bindInfoDict;
for (let bindInfoObj of this.bindInfoList) {
if (copiedStatement.bindInfoDict.has(bindInfoObj.bindName)) {
bindInfoDict[bindInfoObj.bindName].push(bindInfoObj);
if (bindInfoDict.has(bindInfoObj.bindName)) {
bindInfoDict.get(bindInfoObj.bindName).push(bindInfoObj);
} else {
bindInfoDict[bindInfoObj.bindName] = [bindInfoObj];
bindInfoDict.set(bindInfoObj.bindName, [bindInfoObj]);
}
}
copiedStatement.returnToCache = false;
@ -213,7 +213,7 @@ class Statement {
let returningSql;
if (this.isQuery || this.isDml || this.isPlSql) {
let inputSql = sql;
if (!this.isPlSql) {
if (this.isDml) {
/*
* get starting index after DML_RETURNING_KEY from begining of sql and starting index
* after DML_RETURNING_INTO_KEY from the end of sql
@ -260,10 +260,12 @@ class Statement {
return;
}
let info = new BindInfo(name, isReturnBind);
if (!this.bindInfoDict.hasOwnProperty(info.bindName)) { // eslint-disable-line
this.bindInfoDict[info.bindName] = [info];
this.bindInfoList.push(info);
if (this.bindInfoDict.has(info.bindName)) {
this.bindInfoDict.get(info.bindName).push(info);
} else {
this.bindInfoDict.set(info.bindName, [info]);
}
});
}

View File

@ -58,57 +58,59 @@ describe('226. dbType01.js', function() {
const strInVal = "Node-oracledb";
const dateInVal = new Date();
const numInVal = 12;
const SQL = `SELECT :1, DUMP(:1) FROM dual`;
const SQL = `SELECT :BIND1, DUMP(:BIND1) FROM dual`;
it('226.1 DB_TYPE_VARCHAR', async () => {
const bindVal = { BIND1: {val: strInVal, type: oracledb.DB_TYPE_VARCHAR }};
const result = await conn.execute(SQL,
[{ val: strInVal, type: oracledb.DB_TYPE_VARCHAR }]);
bindVal);
assert.strictEqual(strInVal, result.rows[0][0]);
assert.match(result.rows[0][1], /Typ=1 Len=13/);
}); // 226.1
it('226.2 DB_TYPE_CHAR', async () => {
const bindVal = { BIND1: {val: strInVal, type: oracledb.DB_TYPE_CHAR }};
const result = await conn.execute(SQL,
[{ val: strInVal, type: oracledb.DB_TYPE_CHAR }]);
bindVal);
assert.strictEqual(strInVal, result.rows[0][0]);
assert.match(result.rows[0][1], /Typ=96 Len=13/);
}); // 226.2
it('226.3 DB_TYPE_NVARCHAR', async () => {
const result = await conn.execute(SQL,
[{ val: strInVal, type: oracledb.DB_TYPE_NVARCHAR }]);
const bindVal = { BIND1: {val: strInVal, type: oracledb.DB_TYPE_NVARCHAR }};
const result = await conn.execute(SQL, bindVal);
assert.strictEqual(strInVal, result.rows[0][0]);
assert.match(result.rows[0][1], /Typ=1 Len=26/);
}); // 226.3
it('226.4 DB_TYPE_NCHAR', async () => {
const result = await conn.execute(SQL,
[{ val: strInVal, type: oracledb.DB_TYPE_NCHAR }]);
const bindVal = { BIND1: {val: strInVal, type: oracledb.DB_TYPE_NCHAR }};
const result = await conn.execute(SQL, bindVal);
assert.strictEqual(strInVal, result.rows[0][0]);
assert.match(result.rows[0][1], /Typ=96 Len=26/);
}); // 226.4
it('226.5 DB_TYPE_DATE', async () => {
const result = await conn.execute(SQL,
[{ val: dateInVal, type: oracledb.DB_TYPE_DATE }]);
const bindVal = { BIND1: {val: dateInVal, type: oracledb.DB_TYPE_DATE }};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=12 Len=7/);
}); // 226.5
it('226.6 DB_TYPE_TIMESTAMP_LTZ', async () => {
const result = await conn.execute(SQL,
[{ val: dateInVal, type: oracledb.DB_TYPE_TIMESTAMP_LTZ }]);
const bindVal = { BIND1: {val: dateInVal, type: oracledb.DB_TYPE_TIMESTAMP_LTZ}};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=231 Len=11/);
}); // 226.6
it('226.7 DB_TYPE_TIMESTAMP', async () => {
const result = await conn.execute(SQL,
[{ val: dateInVal, type: oracledb.DB_TYPE_TIMESTAMP }]);
const bindVal = { BIND1: {val: dateInVal, type: oracledb.DB_TYPE_TIMESTAMP}};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=180 Len=11/);
});
it('226.8 DB_TYPE_TIMESTAMP_TZ', async () => {
const result = await conn.execute(SQL,
[{ val: dateInVal, type: oracledb.DB_TYPE_TIMESTAMP_TZ }]);
const bindVal = { BIND1: {val: dateInVal, type: oracledb.DB_TYPE_TIMESTAMP_TZ}};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=181 Len=13/);
});
@ -120,28 +122,38 @@ describe('226. dbType01.js', function() {
});
it('226.10 DB_TYPE_BINARY_FLOAT', async () => {
const result = await conn.execute(SQL,
[{ val: numInVal, type: oracledb.DB_TYPE_BINARY_FLOAT }]);
const bindVal = { BIND1: {val: numInVal, type: oracledb.DB_TYPE_BINARY_FLOAT}};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=100 Len=4/);
});
it('226.11 DB_TYPE_BINARY_DOUBLE', async () => {
const result = await conn.execute(SQL,
[{ val: numInVal, type: oracledb.DB_TYPE_BINARY_DOUBLE }]);
const bindVal = { BIND1: {val: numInVal, type: oracledb.DB_TYPE_BINARY_DOUBLE}};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=101 Len=8/);
});
it('226.12 Infinity, DB_TYPE_BINARY_FLOAT', async () => {
const num = 1 / 0;
const result = await conn.execute(SQL,
[{ val: num, type: oracledb.DB_TYPE_BINARY_FLOAT }]);
const bindVal = { BIND1: {val: num, type: oracledb.DB_TYPE_BINARY_FLOAT}};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=100 Len=4/);
});
it('226.13 Infinity, DB_TYPE_BINARY_DOUBLE', async () => {
const num = 1 / 0;
const result = await conn.execute(SQL,
[{ val: num, type: oracledb.DB_TYPE_BINARY_DOUBLE }]);
const bindVal = { BIND1: {val: num, type: oracledb.DB_TYPE_BINARY_DOUBLE}};
const result = await conn.execute(SQL, bindVal);
assert.match(result.rows[0][1], /Typ=101 Len=8/);
});
it('226.14 DB_TYPE_VARCHAR Repeating using bindByPos', async () => {
const SQL = `SELECT :1, DUMP(:1) FROM dual`;
const bindVal = {val: strInVal, type: oracledb.DB_TYPE_VARCHAR};
const result = await conn.execute(SQL,
[bindVal, bindVal]);
assert.strictEqual(strInVal, result.rows[0][0]);
assert.match(result.rows[0][1], /Typ=1 Len=13/);
}); // 226.14
});

View File

@ -65,10 +65,11 @@ Overview of node-oracledb functional tests
2.8 connection queueing
2.8.1 basic case
2.8.2 generates NJS-040 if request is queued and queueTimeout expires
2.8.3 does not generate NJS-040 if request is queued for less time than queueTimeout
2.8.4 generates NJS-076 if request exceeds queueMax
2.8.5 generates NJS-076 if request exceeds queueMax 0
2.8.6 request queue never terminate for queueTimeout set to 0
2.8.3 generates NJS-076 if request exceeds queueMax
2.8.4 generates NJS-076 if request exceeds queueMax 0
2.8.5 request queue never terminate for queueTimeout set to 0
2.8.6 queueMax range check, queueMax -1
2.8.7 queueMax range check, queueMax -0.5 not an integer
2.9 _enableStats & _logStats functionality
2.9.1 does not work after the pool has been terminated
2.10 Close method

View File

@ -515,20 +515,7 @@ describe('2. pool.js', function() {
await pool.close(0);
});
it('2.8.7 queueMax range check, queueMax -2 out of range', async function() {
const config = {...dbConfig,
poolMin : 1,
poolMax : 1,
poolIncrement : 0,
queueMax : -2
};
await assert.rejects(
async () => await oracledb.createPool(config),
/NJS-007:/
);
});
it('2.8.8 queueMax range check, queueMax -0.5 not an integer', async function() {
it('2.8.7 queueMax range check, queueMax -0.5 not an integer', async function() {
const config = {...dbConfig,
poolMin : 1,
poolMax : 1,