Skip to content

Commit 8ba1ac9

Browse files
committed
Add Success With Info Warnings support
1 parent 5db0601 commit 8ba1ac9

File tree

14 files changed

+157
-11
lines changed

14 files changed

+157
-11
lines changed

doc/src/release_notes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ node-oracledb `v6.3.0 <https://github.com/oracle/node-oracledb/compare/v6.2.0...
1313
Common Changes
1414
++++++++++++++
1515

16+
#) Added a ``warning`` property to the ``result`` object for database
17+
warnings (such as PL/SQL compilation warnings) generated by calls to
18+
:meth:`connection.execute()` or :meth:`connection.executeMany()`.
19+
20+
#) Added property :attr:`connection.warning` for warnings (such as the
21+
password being in the grace period) generated during connection.
22+
1623
#) VARCHAR2 and LOB columns which contain JSON, and have the "IS JSON" check
1724
constraint enabled, can now be fetched in the same way as columns of type
1825
JSON. In node-oracledb :ref:`Thick mode <enablingthick>` this requires

lib/connection.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,11 @@ class Connection extends EventEmitter {
865865
throw err;
866866
}
867867

868+
// convert ORA errors to NJS
869+
if (result.warning) {
870+
result.warning = errors.transformErr(result.warning);
871+
}
872+
868873
// process queries; if a result set is not desired, fetch all of the rows
869874
// from the result set and then destroy the result set
870875
if (result.resultSet !== undefined) {
@@ -1107,6 +1112,21 @@ class Connection extends EventEmitter {
11071112
return this._impl && this._impl.getMaxOpenCursors();
11081113
}
11091114

1115+
//---------------------------------------------------------------------------
1116+
// warning
1117+
//
1118+
// Returns warningInfo.
1119+
//---------------------------------------------------------------------------
1120+
get warning() {
1121+
let warning = this._impl.getWarning();
1122+
if (warning) {
1123+
// Make sure that warning code attribute is populated and ORA error
1124+
// is converted to NJS, if required
1125+
warning = errors.transformErr(warning);
1126+
}
1127+
return this._impl && warning;
1128+
}
1129+
11101130
//---------------------------------------------------------------------------
11111131
// module
11121132
//

lib/errors.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ const ERR_INVALID_SID = 519;
162162
const ERR_TNS_NAMES_FILE_MISSING = 520;
163163
const ERR_CONNECTION_EOF = 521;
164164

165+
// Oracle SUCCESS_WITH_INFO warning start from 700
166+
const WRN_COMPILATION_CREATE = 700;
167+
165168
// define mapping for ODPI-C, OCI & ORA errors that need to be wrapped
166169
// with NJS errors
167170
const adjustErrorXref = new Map();
@@ -180,6 +183,7 @@ adjustErrorXref.set("ORA-00028", ERR_CONNECTION_CLOSED);
180183
adjustErrorXref.set("ORA-00600", ERR_CONNECTION_CLOSED);
181184
adjustErrorXref.set("ORA-24338", ERR_INVALID_REF_CURSOR);
182185
adjustErrorXref.set("ORA-25708", ERR_TOKEN_HAS_EXPIRED);
186+
adjustErrorXref.set("ORA-24344", WRN_COMPILATION_CREATE);
183187

184188
// define mapping for error messages
185189
const messages = new Map();
@@ -445,6 +449,11 @@ messages.set(ERR_TNS_NAMES_FILE_MISSING, // NJS-520
445449
messages.set(ERR_CONNECTION_EOF, // NJS-521
446450
'connection to host %s port %d received end-of-file on communication channel. (CONNECTION_ID=%s)');
447451

452+
// Oracle SUCCESS_WITH_INFO warning
453+
454+
messages.set(WRN_COMPILATION_CREATE, // NJS-700
455+
'creation succeeded with compilation errors');
456+
448457
//-----------------------------------------------------------------------------
449458
// assert()
450459
//
@@ -781,6 +790,7 @@ module.exports = {
781790
ERR_UNEXPECTED_XML_TYPE,
782791
ERR_WRONG_USER_FORMAT_EXTAUTH_PROXY,
783792
ERR_CONNECTION_CLOSED_CODE: `${ERR_PREFIX}-${ERR_CONNECTION_CLOSED}`,
793+
WRN_COMPILATION_CREATE,
784794
assert,
785795
assertArgCount,
786796
assertParamPropBool,

lib/impl/connection.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,15 @@ class ConnectionImpl {
354354
" on the connection");
355355
}
356356

357+
//---------------------------------------------------------------------------
358+
// getWarning()
359+
//
360+
// Returns a warning on a connection
361+
//---------------------------------------------------------------------------
362+
getWarning() {
363+
errors.throwNotImplemented("getting information about warning");
364+
}
365+
357366
//---------------------------------------------------------------------------
358367
// isHealthy()
359368
//

lib/thin/connection.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,9 @@ class ThinConnectionImpl extends ConnectionImpl {
206206

207207
// process results
208208
const result = {};
209+
if (message.warning) {
210+
result.warning = message.warning;
211+
}
209212
if (statement.numQueryVars > 0) {
210213
result.resultSet = message.resultSet;
211214
} else {
@@ -1120,5 +1123,12 @@ class ThinConnectionImpl extends ConnectionImpl {
11201123
getTransactionInProgress() {
11211124
return this._protocol.txnInProgress === constants.TNS_TXN_IN_PROGRESS;
11221125
}
1126+
1127+
//---------------------------------------------------------------------------
1128+
// Returns the warning object.
1129+
//---------------------------------------------------------------------------
1130+
getWarning() {
1131+
return this.warning;
1132+
}
11231133
}
11241134
module.exports = ThinConnectionImpl;

lib/thin/pool.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ class ThinPoolImpl extends PoolImpl {
541541

542542
// release connection to connection pool
543543
release(conn) {
544+
conn.warning = undefined;
544545
this._usedConnectionList.delete(conn);
545546
if (conn.nscon.connected) {
546547
conn._lastTimeUsed = Date.now();

lib/thin/protocol/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,9 @@ module.exports = {
757757
TNS_ERR_NO_DATA_FOUND: 1403,
758758
TNS_ERR_SESSION_SHUTDOWN: 12572,
759759

760+
// warnings
761+
TNS_WARN_COMPILATION_CREATE: 0x20,
762+
760763
// other constants
761764
PACKET_HEADER_SIZE: 8,
762765
NUMBER_AS_TEXT_CHARS: 172,

lib/thin/protocol/messages/auth.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ class AuthMessage extends Message {
327327
this.conn.instanceName = this.sessionData['AUTH_INSTANCENAME'];
328328
const fullVersionNum = Number(this.sessionData['AUTH_VERSION_NO']);
329329
const versionNum = (fullVersionNum >> 24) & 0xFF;
330+
this.conn.warning = this.warning;
330331
if (buf.caps.ttcFieldVersion >= constants.TNS_CCAP_FIELD_VERSION_18_1_EXT_1) {
331332
releaseNum = (fullVersionNum >> 16) & 0xFF;
332333
updateNum = (fullVersionNum >> 12) & 0x0F;

lib/thin/protocol/messages/base.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ class Message {
4444
this.flushOutBinds = false;
4545
this.endToEndSeqNum = 0;
4646
this.errorOccurred = false;
47-
this.isWarning = false;
4847
this.flushOutBinds = false;
4948
this.processedError = false;
49+
this.warning = undefined;
5050
}
5151

5252
preProcess() { }
@@ -75,7 +75,10 @@ class Message {
7575
buf.skipUB1(); // flags
7676
buf.skipUB1(); // user cursor options
7777
buf.skipUB1(); // UPI parameter
78-
buf.skipUB1(); // warning flag
78+
const warnFlag = buf.readUInt8(); // warning flag
79+
if (warnFlag & constants.TNS_WARN_COMPILATION_CREATE) {
80+
this.warning = errors.getErr(errors.WRN_COMPILATION_CREATE);
81+
}
7982
this.errorInfo.rowID = buf.readRowID(); // rowid
8083
buf.skipUB4(); // OS error
8184
buf.skipUB1(); // statement error
@@ -149,22 +152,23 @@ class Message {
149152
/*
150153
* Remove ending newline from ORA error message
151154
*/
152-
this.errorInfo.message = this.errorInfo.message.replace(/\n+$/, "");
155+
this.errorInfo.message = this.errorInfo.message.trim();
153156
}
154-
this.errorInfo.isWarning = false;
155157
this.processedError = true;
156158
}
157159

158160
processReturnParameter() { }
159161

160162
processWarningInfo(buf) {
161-
this.errorInfo.num = buf.readUB2(); // error number
162-
const numBytes = buf.readUB2(); // length of error message
163-
buf.skipUB2(); // flags
164-
if (this.errorInfo.num != 0 && numBytes > 0) {
165-
this.errorInfo.message = buf.readStr(constants.CSFRM_IMPLICIT);
163+
const errNum = buf.readUB2(); // warning number
164+
const numBytes = buf.readUB2(); // length of warning message
165+
buf.skipUB2(); // flags
166+
if (errNum != 0 && numBytes > 0) {
167+
// get message string and remove the ending newline.
168+
const message = buf.readStr(constants.CSFRM_IMPLICIT).trim();
169+
this.warning = new Error(message);
170+
this.warning.errorNum = errNum;
166171
}
167-
this.errorInfo.isWarning = true;
168172
}
169173

170174
hasMoreData(buf) {

src/njsConnection.c

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ NJS_NAPI_METHOD_DECL_ASYNC(njsConnection_getStatementInfo);
5858
NJS_NAPI_METHOD_DECL_SYNC(njsConnection_getStmtCacheSize);
5959
NJS_NAPI_METHOD_DECL_SYNC(njsConnection_getTag);
6060
NJS_NAPI_METHOD_DECL_SYNC(njsConnection_getTransactionInProgress);
61+
NJS_NAPI_METHOD_DECL_SYNC(njsConnection_getWarning);
6162
NJS_NAPI_METHOD_DECL_SYNC(njsConnection_isHealthy);
6263
NJS_NAPI_METHOD_DECL_ASYNC(njsConnection_ping);
6364
NJS_NAPI_METHOD_DECL_ASYNC(njsConnection_rollback);
@@ -175,6 +176,8 @@ static const napi_property_descriptor njsClassProperties[] = {
175176
NULL },
176177
{ "getTransactionInProgress", NULL, njsConnection_getTransactionInProgress,
177178
NULL, NULL, NULL, napi_default, NULL },
179+
{ "getWarning", NULL, njsConnection_getWarning, NULL, NULL, NULL,
180+
napi_default, NULL },
178181
{ "isHealthy", NULL, njsConnection_isHealthy, NULL, NULL, NULL,
179182
napi_default, NULL },
180183
{ "ping", NULL, njsConnection_ping, NULL, NULL, NULL, napi_default,
@@ -483,6 +486,9 @@ static bool njsConnection_connectAsync(njsBaton *baton)
483486
&baton->dpiConnHandle) < 0)
484487
return njsBaton_setErrorDPI(baton);
485488

489+
// handle warnings if any
490+
dpiContext_getError(baton->globals->context, &baton->warningInfo);
491+
486492
return true;
487493
}
488494

@@ -496,6 +502,11 @@ static bool njsConnection_connectPostAsync(njsBaton *baton, napi_env env,
496502
{
497503
njsConnection *conn = (njsConnection*) baton->callingInstance;
498504

505+
// process warnings if any
506+
if (baton->warningInfo.isWarning) {
507+
conn->warningInfo = baton->warningInfo;
508+
}
509+
499510
// transfer the ODPI-C connection handle to the new object
500511
conn->handle = baton->dpiConnHandle;
501512
baton->dpiConnHandle = NULL;
@@ -662,6 +673,9 @@ static bool njsConnection_executeAsync(njsBaton *baton)
662673
if (dpiStmt_execute(baton->dpiStmtHandle, mode, &baton->numQueryVars) < 0)
663674
return njsBaton_setErrorDPI(baton);
664675

676+
// handle warnings if any
677+
dpiContext_getError(baton->globals->context, &baton->warningInfo);
678+
665679
// for queries, initialize query variables
666680
if (baton->numQueryVars > 0) {
667681

@@ -698,7 +712,7 @@ static bool njsConnection_executeAsync(njsBaton *baton)
698712
static bool njsConnection_executePostAsync(njsBaton *baton, napi_env env,
699713
napi_value *result)
700714
{
701-
napi_value resultSet, rowsAffected, outBinds, lastRowid;
715+
napi_value resultSet, rowsAffected, outBinds, lastRowid, error;
702716
napi_value implicitResults;
703717
uint32_t rowidValueLength;
704718
const char *rowidValue;
@@ -711,6 +725,14 @@ static bool njsConnection_executePostAsync(njsBaton *baton, napi_env env,
711725
// create result object
712726
NJS_CHECK_NAPI(env, napi_create_object(env, result))
713727

728+
// process warnings if any
729+
if (baton->warningInfo.isWarning) {
730+
if (!njsUtils_getError(env, &baton->warningInfo, NULL, &error))
731+
return false;
732+
NJS_CHECK_NAPI(env, napi_set_named_property(env, *result, "warning",
733+
error))
734+
}
735+
714736
// handle queries
715737
if (baton->queryVars) {
716738

@@ -1700,6 +1722,25 @@ NJS_NAPI_METHOD_IMPL_SYNC(njsConnection_getTransactionInProgress, 0, NULL)
17001722
}
17011723

17021724

1725+
//-----------------------------------------------------------------------------
1726+
// njsConnection_getWarning()
1727+
// Get the warning set on connection object. This is set at connection
1728+
// creation time. The only warning expected is password-will-expire-soon
1729+
//-----------------------------------------------------------------------------
1730+
NJS_NAPI_METHOD_IMPL_SYNC(njsConnection_getWarning, 0, NULL)
1731+
{
1732+
njsConnection *conn = (njsConnection*) callingInstance;
1733+
1734+
if (conn->handle) {
1735+
if (conn->warningInfo.isWarning) {
1736+
if (!njsUtils_getError(env, &conn->warningInfo, NULL, returnValue))
1737+
return njsUtils_throwErrorDPI(env, globals);
1738+
}
1739+
}
1740+
return true;
1741+
}
1742+
1743+
17031744
//-----------------------------------------------------------------------------
17041745
// njsConnection_newFromBaton()
17051746
// Called when a connection is being created from the baton.
@@ -1727,6 +1768,10 @@ bool njsConnection_newFromBaton(njsBaton *baton, napi_env env,
17271768
baton->tagLength = 0;
17281769
}
17291770

1771+
// transfer any warning information to the connection
1772+
if (baton->warningInfo.isWarning) {
1773+
conn->warningInfo = baton->warningInfo;
1774+
}
17301775
return true;
17311776
}
17321777

0 commit comments

Comments
 (0)