Fix code to handle edge cases for pool and statement handling
This commit is contained in:
parent
af85a36c85
commit
cbd914a6db
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
message.numExecs = numIters - 1;
|
||||
}
|
||||
} else {
|
||||
statement.requiresFullExecute = false;
|
||||
message.numExecs = numIters - 1;
|
||||
message.offset = 1;
|
||||
}
|
||||
if (message.numExecs > 0) {
|
||||
await this._protocol._processMessage(message);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
this.bindInfoList.push(info);
|
||||
|
||||
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]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
15
test/pool.js
15
test/pool.js
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue