oracledb 5.5.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1875
- package/LICENSE.txt +8 -222
- package/NOTICE.txt +1 -1
- package/README.md +38 -30
- package/SECURITY.md +38 -0
- package/build/Release/oracledb-6.0.1-darwin-x64.node +0 -0
- package/build/Release/oracledb-6.0.1-darwin-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-js-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-linux-arm64.node +0 -0
- package/build/Release/oracledb-6.0.1-linux-arm64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-linux-x64.node +0 -0
- package/build/Release/oracledb-6.0.1-linux-x64.node-buildinfo.txt +1 -0
- package/build/Release/oracledb-6.0.1-win32-x64.node +0 -0
- package/build/Release/oracledb-6.0.1-win32-x64.node-buildinfo.txt +1 -0
- package/examples/README.md +113 -0
- package/examples/dbconfig.js +100 -0
- package/examples/example.js +156 -0
- package/index.js +26 -0
- package/lib/aqDeqOptions.js +148 -8
- package/lib/aqEnqOptions.js +59 -7
- package/lib/aqMessage.js +118 -7
- package/lib/aqQueue.js +243 -53
- package/lib/connection.js +1326 -465
- package/lib/constants.js +203 -0
- package/lib/dbObject.js +346 -32
- package/lib/errors.js +785 -0
- package/lib/impl/aqDeqOptions.js +197 -0
- package/lib/impl/aqEnqOptions.js +89 -0
- package/lib/impl/aqMessage.js +134 -0
- package/lib/impl/aqQueue.js +81 -0
- package/lib/impl/connection.js +529 -0
- package/lib/impl/dbObject.js +159 -0
- package/lib/impl/index.js +59 -0
- package/lib/impl/lob.js +85 -0
- package/lib/impl/pool.js +195 -0
- package/lib/impl/resultset.js +223 -0
- package/lib/impl/sodaCollection.js +174 -0
- package/lib/impl/sodaDatabase.js +81 -0
- package/lib/impl/sodaDocCursor.js +63 -0
- package/lib/impl/sodaDocument.js +98 -0
- package/lib/impl/sodaOperation.js +109 -0
- package/lib/lob.js +157 -68
- package/lib/oracledb.js +1047 -345
- package/lib/pool.js +654 -465
- package/lib/poolStatistics.js +24 -9
- package/lib/queryStream.js +16 -8
- package/lib/resultset.js +291 -183
- package/lib/settings.js +191 -0
- package/lib/sodaCollection.js +214 -201
- package/lib/sodaDatabase.js +108 -71
- package/lib/sodaDocCursor.js +44 -32
- package/lib/sodaDocument.js +76 -14
- package/lib/sodaOperation.js +131 -118
- package/lib/thin/connection.js +1043 -0
- package/lib/thin/dbObject.js +630 -0
- package/lib/thin/index.js +40 -0
- package/lib/thin/lob.js +189 -0
- package/lib/thin/pool.js +481 -0
- package/lib/thin/protocol/buffer.js +1043 -0
- package/lib/thin/protocol/capabilities.js +121 -0
- package/lib/thin/protocol/constants.js +765 -0
- package/lib/thin/protocol/encryptDecrypt.js +185 -0
- package/lib/thin/protocol/messages/auth.js +349 -0
- package/lib/thin/protocol/messages/base.js +476 -0
- package/lib/thin/protocol/messages/commit.js +56 -0
- package/lib/thin/protocol/messages/dataType.js +391 -0
- package/lib/thin/protocol/messages/execute.js +312 -0
- package/lib/thin/protocol/messages/fetch.js +60 -0
- package/lib/thin/protocol/messages/index.js +53 -0
- package/lib/thin/protocol/messages/lobOp.js +201 -0
- package/lib/thin/protocol/messages/logOff.js +53 -0
- package/lib/thin/protocol/messages/ping.js +56 -0
- package/lib/thin/protocol/messages/protocol.js +78 -0
- package/lib/thin/protocol/messages/rollback.js +56 -0
- package/lib/thin/protocol/messages/sessionRelease.js +60 -0
- package/lib/thin/protocol/messages/withData.js +812 -0
- package/lib/thin/protocol/oson.js +661 -0
- package/lib/thin/protocol/packet.js +519 -0
- package/lib/thin/protocol/protocol.js +202 -0
- package/lib/thin/protocol/utils.js +89 -0
- package/lib/thin/resultSet.js +122 -0
- package/lib/thin/sqlnet/connStrategy.js +175 -0
- package/lib/thin/sqlnet/constants.js +199 -0
- package/lib/thin/sqlnet/ezConnectResolver.js +488 -0
- package/lib/thin/sqlnet/navNodes.js +870 -0
- package/lib/thin/sqlnet/networkSession.js +621 -0
- package/lib/thin/sqlnet/ntTcp.js +527 -0
- package/lib/thin/sqlnet/nvStrToNvPair.js +783 -0
- package/lib/thin/sqlnet/packet.js +405 -0
- package/lib/thin/sqlnet/paramParser.js +206 -0
- package/lib/thin/sqlnet/sessionAtts.js +160 -0
- package/lib/thin/statement.js +300 -0
- package/lib/thin/util.js +289 -0
- package/lib/transformer.js +252 -0
- package/lib/types.js +357 -0
- package/lib/util.js +196 -110
- package/lib/version.js +36 -0
- package/package/README.md +33 -30
- package/package/install.js +89 -64
- package/package/prunebinaries.js +60 -12
- package/package.json +3 -3
- package/build/Release/oracledb-5.5.0-darwin-x64.node +0 -0
- package/build/Release/oracledb-5.5.0-darwin-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-5.5.0-linux-x64.node +0 -0
- package/build/Release/oracledb-5.5.0-linux-x64.node-buildinfo.txt +0 -1
- package/build/Release/oracledb-5.5.0-win32-x64.node +0 -0
- package/build/Release/oracledb-5.5.0-win32-x64.node-buildinfo.txt +0 -1
|
@@ -0,0 +1,1043 @@
|
|
|
1
|
+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
|
|
2
|
+
|
|
3
|
+
//-----------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// This software is dual-licensed to you under the Universal Permissive License
|
|
6
|
+
// (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License
|
|
7
|
+
// 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose
|
|
8
|
+
// either license.
|
|
9
|
+
//
|
|
10
|
+
// If you elect to accept the software under the Apache License, Version 2.0,
|
|
11
|
+
// the following applies:
|
|
12
|
+
//
|
|
13
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
// you may not use this file except in compliance with the License.
|
|
15
|
+
// You may obtain a copy of the License at
|
|
16
|
+
//
|
|
17
|
+
// https://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
//
|
|
19
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
// See the License for the specific language governing permissions and
|
|
23
|
+
// limitations under the License.
|
|
24
|
+
//
|
|
25
|
+
//-----------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const { Buffer } = require('buffer');
|
|
30
|
+
const ConnectionImpl = require('../impl/connection.js');
|
|
31
|
+
const ThinResultSetImpl = require('./resultSet.js');
|
|
32
|
+
const ThinLobImpl = require("./lob.js");
|
|
33
|
+
const Protocol = require("./protocol/protocol.js");
|
|
34
|
+
const { BaseBuffer } = require('./protocol/buffer.js');
|
|
35
|
+
const {NetworkSession:nsi, getConnectionInfo} = require("./sqlnet/networkSession.js");
|
|
36
|
+
const { Statement } = require("./statement");
|
|
37
|
+
const thinUtil = require('./util');
|
|
38
|
+
const sqlNetConstants = require('./sqlnet/constants.js');
|
|
39
|
+
const constants = require('./protocol/constants.js');
|
|
40
|
+
const process = require('process');
|
|
41
|
+
const types = require('../types.js');
|
|
42
|
+
const errors = require("../errors.js");
|
|
43
|
+
const messages = require('./protocol/messages');
|
|
44
|
+
|
|
45
|
+
const finalizationRegistry = new global.FinalizationRegistry((heldValue) => {
|
|
46
|
+
heldValue.disconnect();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
class TDSBuffer extends BaseBuffer {
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class ThinConnectionImpl extends ConnectionImpl {
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Terminates the connection
|
|
56
|
+
*
|
|
57
|
+
* @return {Promise}
|
|
58
|
+
*/
|
|
59
|
+
async close() {
|
|
60
|
+
if (this._protocol.txnInProgress) {
|
|
61
|
+
await this.rollback();
|
|
62
|
+
}
|
|
63
|
+
this._protocol.callTimeout = 0; // not applicable for close
|
|
64
|
+
if (this._drcpEnabled) {
|
|
65
|
+
await this._sessRelease();
|
|
66
|
+
this._drcpEstablishSession = true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (this._pool && !this._dropSess) {
|
|
70
|
+
await this._pool.release(this);
|
|
71
|
+
} else {
|
|
72
|
+
if (!this._drcpEnabled) {
|
|
73
|
+
const message = new messages.LogOffMessage(this);
|
|
74
|
+
await this._protocol._processMessage(message);
|
|
75
|
+
}
|
|
76
|
+
this.nscon.disconnect();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async _sessRelease() {
|
|
81
|
+
const message = new messages.SessionReleaseMessage(this);
|
|
82
|
+
if (!this.isPooled()) {
|
|
83
|
+
message.sessReleaseMode = constants.DRCP_DEAUTHENTICATE;
|
|
84
|
+
}
|
|
85
|
+
await this._protocol._processMessage(message);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//---------------------------------------------------------------------------
|
|
89
|
+
// _determineElementObjType()
|
|
90
|
+
//
|
|
91
|
+
// Determine the element type's object type. This is needed when processing
|
|
92
|
+
// collections with an object as the element type since this information is
|
|
93
|
+
// not available in the TDS.
|
|
94
|
+
//---------------------------------------------------------------------------
|
|
95
|
+
async _determineElementObjType(info) {
|
|
96
|
+
const binds = [
|
|
97
|
+
{
|
|
98
|
+
name: "owner",
|
|
99
|
+
type: types.DB_TYPE_VARCHAR,
|
|
100
|
+
dir: constants.BIND_IN,
|
|
101
|
+
maxSize: 128,
|
|
102
|
+
values: [info.schema]
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "name",
|
|
106
|
+
type: types.DB_TYPE_VARCHAR,
|
|
107
|
+
dir: constants.BIND_IN,
|
|
108
|
+
maxSize: 128,
|
|
109
|
+
values: [info.name]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "package_name",
|
|
113
|
+
type: types.DB_TYPE_VARCHAR,
|
|
114
|
+
dir: constants.BIND_IN,
|
|
115
|
+
maxSize: 128,
|
|
116
|
+
values: [info.packageName]
|
|
117
|
+
}
|
|
118
|
+
];
|
|
119
|
+
let sql;
|
|
120
|
+
if (info.packageName) {
|
|
121
|
+
sql = `
|
|
122
|
+
select
|
|
123
|
+
elem_type_owner,
|
|
124
|
+
elem_type_name,
|
|
125
|
+
elem_type_package
|
|
126
|
+
from all_plsql_coll_types
|
|
127
|
+
where owner = :owner
|
|
128
|
+
and type_name = :name
|
|
129
|
+
and package_name = :package_name`;
|
|
130
|
+
} else {
|
|
131
|
+
binds.pop();
|
|
132
|
+
sql = `
|
|
133
|
+
select
|
|
134
|
+
elem_type_owner,
|
|
135
|
+
elem_type_name
|
|
136
|
+
from all_coll_types
|
|
137
|
+
where owner = :owner
|
|
138
|
+
and type_name = :name`;
|
|
139
|
+
}
|
|
140
|
+
const options = {
|
|
141
|
+
connection: { _impl: this },
|
|
142
|
+
prefetchRows: 2
|
|
143
|
+
};
|
|
144
|
+
const result = await this.execute(sql, 1, binds, options, false);
|
|
145
|
+
const rows = await result.resultSet.getRows(1, options);
|
|
146
|
+
await result.resultSet.close();
|
|
147
|
+
const row = rows[0];
|
|
148
|
+
info.elementTypeClass = this._getDbObjectType(row[0], row[1], row[2]);
|
|
149
|
+
if (info.elementTypeClass.partial) {
|
|
150
|
+
this._partialDbObjectTypes.push(info.elementTypeClass);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
//---------------------------------------------------------------------------
|
|
155
|
+
// _parseElementType()
|
|
156
|
+
//
|
|
157
|
+
// Parses the element type from the TDS buffer.
|
|
158
|
+
//---------------------------------------------------------------------------
|
|
159
|
+
async _parseElementType(buf, info) {
|
|
160
|
+
let oraTypeNum, csfrm;
|
|
161
|
+
const attrType = buf.readUInt8();
|
|
162
|
+
switch (attrType) {
|
|
163
|
+
case constants.TNS_OBJ_TDS_TYPE_NUMBER:
|
|
164
|
+
case constants.TNS_OBJ_TDS_TYPE_FLOAT:
|
|
165
|
+
info.elementType = types.DB_TYPE_NUMBER;
|
|
166
|
+
break;
|
|
167
|
+
case constants.TNS_OBJ_TDS_TYPE_VARCHAR:
|
|
168
|
+
case constants.TNS_OBJ_TDS_TYPE_CHAR:
|
|
169
|
+
info.maxSize = buf.readUInt16BE();
|
|
170
|
+
oraTypeNum = (attrType === constants.TNS_OBJ_TDS_TYPE_VARCHAR) ?
|
|
171
|
+
constants.TNS_DATA_TYPE_VARCHAR : constants.TNS_DATA_TYPE_CHAR;
|
|
172
|
+
csfrm = buf.readUInt8();
|
|
173
|
+
info.elementType = types.getTypeByOraTypeNum(oraTypeNum, csfrm);
|
|
174
|
+
break;
|
|
175
|
+
case constants.TNS_OBJ_TDS_TYPE_RAW:
|
|
176
|
+
info.elementType = types.DB_TYPE_RAW;
|
|
177
|
+
break;
|
|
178
|
+
case constants.TNS_OBJ_TDS_TYPE_BINARY_FLOAT:
|
|
179
|
+
info.elementType = types.DB_TYPE_BINARY_FLOAT;
|
|
180
|
+
break;
|
|
181
|
+
case constants.TNS_OBJ_TDS_TYPE_BINARY_DOUBLE:
|
|
182
|
+
info.elementType = types.DB_TYPE_BINARY_DOUBLE;
|
|
183
|
+
break;
|
|
184
|
+
case constants.TNS_OBJ_TDS_TYPE_DATE:
|
|
185
|
+
info.elementType = types.DB_TYPE_DATE;
|
|
186
|
+
break;
|
|
187
|
+
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP:
|
|
188
|
+
info.elementType = types.DB_TYPE_TIMESTAMP;
|
|
189
|
+
break;
|
|
190
|
+
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_LTZ:
|
|
191
|
+
info.elementType = types.DB_TYPE_TIMESTAMP_LTZ;
|
|
192
|
+
break;
|
|
193
|
+
case constants.TNS_OBJ_TDS_TYPE_TIMESTAMP_TZ:
|
|
194
|
+
info.elementType = types.DB_TYPE_TIMESTAMP_TZ;
|
|
195
|
+
break;
|
|
196
|
+
case constants.TNS_OBJ_TDS_TYPE_BOOLEAN:
|
|
197
|
+
info.elementType = types.DB_TYPE_BOOLEAN;
|
|
198
|
+
break;
|
|
199
|
+
case constants.TNS_OBJ_TDS_TYPE_CLOB:
|
|
200
|
+
this._determineElementTypeCharsetForm(info);
|
|
201
|
+
break;
|
|
202
|
+
case constants.TNS_OBJ_TDS_TYPE_BLOB:
|
|
203
|
+
info.elementType = types.DB_TYPE_BLOB;
|
|
204
|
+
break;
|
|
205
|
+
case constants.TNS_OBJ_TDS_TYPE_OBJ:
|
|
206
|
+
info.elementType = types.DB_TYPE_OBJECT;
|
|
207
|
+
await this._determineElementObjType(info);
|
|
208
|
+
break;
|
|
209
|
+
default:
|
|
210
|
+
errors.throwErr(errors.ERR_TDS_TYPE_NOT_SUPPORTED, attrType);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
//---------------------------------------------------------------------------
|
|
215
|
+
// _parseTDS()
|
|
216
|
+
//
|
|
217
|
+
// Parses the TDS for the type. This is only needed for collection types, so
|
|
218
|
+
// if the TDS is determined to be for an object type, the remaining
|
|
219
|
+
// information is ignored.
|
|
220
|
+
//---------------------------------------------------------------------------
|
|
221
|
+
async _parseTDS(tds, info) {
|
|
222
|
+
|
|
223
|
+
// parse initial TDS bytes
|
|
224
|
+
const buf = new TDSBuffer(tds);
|
|
225
|
+
buf.skipBytes(4); // end offset
|
|
226
|
+
buf.skipBytes(2); // version op code and version
|
|
227
|
+
buf.skipBytes(2); // unknown
|
|
228
|
+
|
|
229
|
+
// if the number of attributes exceeds 1, the type cannot refer to a
|
|
230
|
+
// collection, so nothing further needs to be done
|
|
231
|
+
const numAttrs = buf.readUInt16BE();
|
|
232
|
+
if (numAttrs > 1) {
|
|
233
|
+
info.isCollection = false;
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// continue parsing TDS bytes to discover if type refers to a collection
|
|
238
|
+
buf.skipBytes(1); // TDS attributes?
|
|
239
|
+
buf.skipBytes(1); // start ADT op code
|
|
240
|
+
buf.skipBytes(2); // ADT number (always zero)
|
|
241
|
+
buf.skipBytes(4); // offset to index table
|
|
242
|
+
|
|
243
|
+
// if type of first attribute is not a collection, nothing further needs
|
|
244
|
+
// to be done
|
|
245
|
+
const attrType = buf.readUInt8();
|
|
246
|
+
info.isCollection = (attrType === constants.TNS_OBJ_TDS_TYPE_COLL);
|
|
247
|
+
if (!info.isCollection)
|
|
248
|
+
return;
|
|
249
|
+
|
|
250
|
+
// continue parsing TDS to determine element type
|
|
251
|
+
const elementPos = buf.readUInt32BE();
|
|
252
|
+
info.maxNumElements = buf.readUInt32BE();
|
|
253
|
+
info.collectionType = buf.readUInt8();
|
|
254
|
+
if (info.collectionType === constants.TNS_OBJ_PLSQL_INDEX_TABLE) {
|
|
255
|
+
info.collectionFlags = constants.TNS_OBJ_HAS_INDEXES;
|
|
256
|
+
}
|
|
257
|
+
buf.pos = elementPos;
|
|
258
|
+
|
|
259
|
+
await this._parseElementType(buf, info);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
//---------------------------------------------------------------------------
|
|
263
|
+
// _populateDbObjectTypeInfo()
|
|
264
|
+
//
|
|
265
|
+
// Poplates type information given the name of the type.
|
|
266
|
+
//---------------------------------------------------------------------------
|
|
267
|
+
async _populateDbObjectTypeInfo(name) {
|
|
268
|
+
|
|
269
|
+
// get type information from the database
|
|
270
|
+
const sql = `
|
|
271
|
+
declare
|
|
272
|
+
t_Instantiable varchar2(3);
|
|
273
|
+
t_SuperTypeOwner varchar2(128);
|
|
274
|
+
t_SuperTypeName varchar2(128);
|
|
275
|
+
t_SubTypeRefCursor sys_refcursor;
|
|
276
|
+
t_Pos pls_integer;
|
|
277
|
+
begin
|
|
278
|
+
:ret_val := dbms_pickler.get_type_shape(:full_name, :oid,
|
|
279
|
+
:version, :tds, t_Instantiable, t_SuperTypeOwner,
|
|
280
|
+
t_SuperTypeName, :attrs_rc, t_SubTypeRefCursor);
|
|
281
|
+
:package_name := null;
|
|
282
|
+
if substr(:full_name, length(:full_name) - 7) = '%ROWTYPE' then
|
|
283
|
+
t_Pos := instr(:full_name, '.');
|
|
284
|
+
:schema := substr(:full_name, 1, t_Pos - 1);
|
|
285
|
+
:name := substr(:full_name, t_Pos + 1);
|
|
286
|
+
else
|
|
287
|
+
begin
|
|
288
|
+
select owner, type_name
|
|
289
|
+
into :schema, :name
|
|
290
|
+
from all_types
|
|
291
|
+
where type_oid = :oid;
|
|
292
|
+
exception
|
|
293
|
+
when no_data_found then
|
|
294
|
+
begin
|
|
295
|
+
select owner, package_name, type_name
|
|
296
|
+
into :schema, :package_name, :name
|
|
297
|
+
from all_plsql_types
|
|
298
|
+
where type_oid = :oid;
|
|
299
|
+
exception
|
|
300
|
+
when no_data_found then
|
|
301
|
+
null;
|
|
302
|
+
end;
|
|
303
|
+
end;
|
|
304
|
+
end if;
|
|
305
|
+
end;`;
|
|
306
|
+
const binds = [
|
|
307
|
+
{
|
|
308
|
+
name: "full_name",
|
|
309
|
+
type: types.DB_TYPE_VARCHAR,
|
|
310
|
+
dir: constants.BIND_INOUT,
|
|
311
|
+
maxSize: 500,
|
|
312
|
+
values: [name]
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "ret_val",
|
|
316
|
+
type: types.DB_TYPE_BINARY_INTEGER,
|
|
317
|
+
dir: constants.BIND_OUT,
|
|
318
|
+
values: []
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: "oid",
|
|
322
|
+
type: types.DB_TYPE_RAW,
|
|
323
|
+
maxSize: 16,
|
|
324
|
+
dir: constants.BIND_OUT,
|
|
325
|
+
values: []
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: "version",
|
|
329
|
+
type: types.DB_TYPE_BINARY_INTEGER,
|
|
330
|
+
dir: constants.BIND_OUT,
|
|
331
|
+
values: []
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "tds",
|
|
335
|
+
type: types.DB_TYPE_RAW,
|
|
336
|
+
maxSize: 2000,
|
|
337
|
+
dir: constants.BIND_OUT,
|
|
338
|
+
values: []
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "attrs_rc",
|
|
342
|
+
type: types.DB_TYPE_CURSOR,
|
|
343
|
+
dir: constants.BIND_OUT,
|
|
344
|
+
values: []
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: "package_name",
|
|
348
|
+
type: types.DB_TYPE_VARCHAR,
|
|
349
|
+
maxSize: 128,
|
|
350
|
+
dir: constants.BIND_OUT,
|
|
351
|
+
values: []
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "schema",
|
|
355
|
+
type: types.DB_TYPE_VARCHAR,
|
|
356
|
+
maxSize: 128,
|
|
357
|
+
dir: constants.BIND_OUT,
|
|
358
|
+
values: []
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: "name",
|
|
362
|
+
type: types.DB_TYPE_VARCHAR,
|
|
363
|
+
maxSize: 128,
|
|
364
|
+
dir: constants.BIND_OUT,
|
|
365
|
+
values: []
|
|
366
|
+
}
|
|
367
|
+
];
|
|
368
|
+
const options = {
|
|
369
|
+
connection: { _impl: this },
|
|
370
|
+
nullifyInvalidCursor: true
|
|
371
|
+
};
|
|
372
|
+
const result = await this.execute(sql, 1, binds, options, false);
|
|
373
|
+
if (result.outBinds.ret_val !== 0) {
|
|
374
|
+
errors.throwErr(errors.ERR_INVALID_OBJECT_TYPE_NAME, name);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// check cache; if already present, nothing more to do!
|
|
378
|
+
const info = this._getDbObjectType(result.outBinds.schema,
|
|
379
|
+
result.outBinds.name, result.outBinds.package_name, result.outBinds.oid);
|
|
380
|
+
if (!info.partial)
|
|
381
|
+
return info;
|
|
382
|
+
|
|
383
|
+
// process TDS and attributes cursor
|
|
384
|
+
info.version = result.outBinds.version;
|
|
385
|
+
await this._parseTDS(result.outBinds.tds, info);
|
|
386
|
+
const attrRows = await result.outBinds.attrs_rc.getRows(1000, {});
|
|
387
|
+
if (attrRows.length > 0) {
|
|
388
|
+
info.attributes = [];
|
|
389
|
+
for (const row of attrRows) {
|
|
390
|
+
const attr = { name: row[1] };
|
|
391
|
+
if (row[4]) {
|
|
392
|
+
attr.type = types.DB_TYPE_OBJECT;
|
|
393
|
+
attr.typeClass = this._getDbObjectType(row[4], row[3], row[5], row[6]);
|
|
394
|
+
if (attr.typeClass.partial) {
|
|
395
|
+
this._partialDbObjectTypes.push(attr.typeClass);
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
attr.type = types.getTypeByColumnTypeName(row[3]);
|
|
399
|
+
}
|
|
400
|
+
info.attributes.push(attr);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
info.partial = false;
|
|
404
|
+
|
|
405
|
+
return info;
|
|
406
|
+
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
//---------------------------------------------------------------------------
|
|
410
|
+
// _populatePartialDbObjectTypes()
|
|
411
|
+
//
|
|
412
|
+
// Populates partial types that were discovered earlier. Since populating an
|
|
413
|
+
// object type might result in additional object types being discovered,
|
|
414
|
+
// object types are popped from the partial types list until the list is
|
|
415
|
+
// empty.
|
|
416
|
+
//---------------------------------------------------------------------------
|
|
417
|
+
async _populatePartialDbObjectTypes() {
|
|
418
|
+
while (this._partialDbObjectTypes.length > 0) {
|
|
419
|
+
const info = this._partialDbObjectTypes.pop();
|
|
420
|
+
let suffix = "%ROWTYPE";
|
|
421
|
+
let name = info.name;
|
|
422
|
+
if (name.endsWith(suffix)) {
|
|
423
|
+
name = name.substring(0, name.length - suffix.length);
|
|
424
|
+
} else {
|
|
425
|
+
suffix = "";
|
|
426
|
+
}
|
|
427
|
+
let fullName;
|
|
428
|
+
if (info.packageName) {
|
|
429
|
+
fullName = `"${info.schema}"."${info.packageName}"."${name}"${suffix}`;
|
|
430
|
+
} else {
|
|
431
|
+
fullName = `"${info.schema}"."${name}"${suffix}`;
|
|
432
|
+
}
|
|
433
|
+
await this._populateDbObjectTypeInfo(fullName);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async commit() {
|
|
438
|
+
const message = new messages.CommitMessage(this);
|
|
439
|
+
await this._protocol._processMessage(message);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async breakExecution() {
|
|
443
|
+
await this._protocol.breakMessage();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
_healthStatus() {
|
|
447
|
+
return this.nscon.recvInbandNotif();
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
isHealthy() {
|
|
451
|
+
try {
|
|
452
|
+
if (this.nscon.recvInbandNotif() === 0)
|
|
453
|
+
return true;
|
|
454
|
+
return false;
|
|
455
|
+
} catch {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
isPooled() {
|
|
461
|
+
return (this._pool) ? true : false;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
*
|
|
466
|
+
* @param {object} params Configuration of the connection
|
|
467
|
+
*
|
|
468
|
+
* @return {Promise}
|
|
469
|
+
*/
|
|
470
|
+
async connect(params) {
|
|
471
|
+
if (!params.connectString) {
|
|
472
|
+
errors.throwErr(errors.ERR_EMPTY_CONNECT_STRING);
|
|
473
|
+
}
|
|
474
|
+
thinUtil.checkCredentials(params);
|
|
475
|
+
|
|
476
|
+
this.sessionID = 0;
|
|
477
|
+
this.serialNum = 0;
|
|
478
|
+
this.autoCommit = false;
|
|
479
|
+
this.serverVersion = "";
|
|
480
|
+
this.statementCache = null;
|
|
481
|
+
this.currentSchema = "";
|
|
482
|
+
this.invokeSessionCallback = true;
|
|
483
|
+
this.statementCache = new Map();
|
|
484
|
+
this.statementCacheSize = params.stmtCacheSize;
|
|
485
|
+
this._numCursorsToClose = 0;
|
|
486
|
+
this._currentSchemaModified = false;
|
|
487
|
+
this._cursorsToClose = new Set();
|
|
488
|
+
this._tempLobsToClose = [];
|
|
489
|
+
this._tempLobsTotalSize = 0;
|
|
490
|
+
this._drcpEstablishSession = false;
|
|
491
|
+
this._cclass = null;
|
|
492
|
+
this._clientIdentifier = "";
|
|
493
|
+
this._clientIdentifierModified = false;
|
|
494
|
+
this._action = "";
|
|
495
|
+
this._actionModified = false;
|
|
496
|
+
this._dbOp = "";
|
|
497
|
+
this._dbOpModified = false;
|
|
498
|
+
this._clientInfo = "";
|
|
499
|
+
this._clientInfoModified = false;
|
|
500
|
+
this._module = "";
|
|
501
|
+
this._moduleModified = false;
|
|
502
|
+
this.serviceName = '';
|
|
503
|
+
this.remoteAddress = '';
|
|
504
|
+
this.privilege = params.privilege;
|
|
505
|
+
this.comboKey = null; // used in changePassword API
|
|
506
|
+
|
|
507
|
+
this.nscon = new nsi();
|
|
508
|
+
finalizationRegistry.register(this, this.nscon);
|
|
509
|
+
await this.nscon.connect(params);
|
|
510
|
+
|
|
511
|
+
this._connInfo = (this.isPooled()) ? params._connInfo.map((x) => x) :
|
|
512
|
+
await getConnectionInfo(params);
|
|
513
|
+
this._drcpEnabled = String(this._connInfo[0]).toLowerCase() === 'pooled';
|
|
514
|
+
this.serviceName = this._connInfo[2];
|
|
515
|
+
this.purity = this._connInfo[3] | constants.PURITY_DEFAULT;
|
|
516
|
+
this.remoteAddress = this.nscon.getOption(sqlNetConstants.REMOTEADDR);
|
|
517
|
+
this.connClass = params.connectionClass;
|
|
518
|
+
|
|
519
|
+
/*
|
|
520
|
+
* if drcp is used, use purity = NEW as the default purity for
|
|
521
|
+
* standalone connections and purity = SELF for connections that belong
|
|
522
|
+
* to a pool
|
|
523
|
+
*/
|
|
524
|
+
if (this.purity === constants.PURITY_DEFAULT && this._drcpEnabled) {
|
|
525
|
+
if (this.isPooled()) {
|
|
526
|
+
this.purity = constants.PURITY_SELF;
|
|
527
|
+
} else {
|
|
528
|
+
this.purity = constants.PURITY_NEW;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
this._protocol = new Protocol(this);
|
|
533
|
+
|
|
534
|
+
// check if the protocol version supported by the database is high
|
|
535
|
+
// enough; if not, reject the connection immediately
|
|
536
|
+
if (this._protocol.caps.protocolVersion < constants.TNS_VERSION_MIN_ACCEPTED) {
|
|
537
|
+
errors.throwErr(errors.ERR_SERVER_VERSION_NOT_SUPPORTED);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
try {
|
|
541
|
+
let message = new messages.ProtocolMessage(this);
|
|
542
|
+
await this._protocol._processMessage(message);
|
|
543
|
+
message = new messages.DataTypeMessage(this);
|
|
544
|
+
await this._protocol._processMessage(message);
|
|
545
|
+
message = new messages.AuthMessage(this, params);
|
|
546
|
+
await this._protocol._processMessage(message); // OSESSKEY
|
|
547
|
+
await this._protocol._processMessage(message); // OAUTH
|
|
548
|
+
} catch (err) {
|
|
549
|
+
this.nscon.disconnect();
|
|
550
|
+
throw err;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// maintain a list of partially populated database object types
|
|
554
|
+
this._partialDbObjectTypes = [];
|
|
555
|
+
|
|
556
|
+
if (params.debugJDWP) {
|
|
557
|
+
this.jdwpData = Buffer.from(params.debugJDWP);
|
|
558
|
+
} else if (process.env.ORA_DEBUG_JDWP) {
|
|
559
|
+
this.jdwpData = Buffer.from(process.env.ORA_DEBUG_JDWP);
|
|
560
|
+
}
|
|
561
|
+
this._protocol.connInProgress = false;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
//---------------------------------------------------------------------------
|
|
565
|
+
// Sets that a statement is no longer in use
|
|
566
|
+
//---------------------------------------------------------------------------
|
|
567
|
+
_returnStatement(statement) {
|
|
568
|
+
if (statement.bindInfoList) {
|
|
569
|
+
statement.bindInfoList.forEach(bindInfo => {
|
|
570
|
+
bindInfo.bindVar = null;
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
if (statement.queryVars) {
|
|
574
|
+
statement.queryVars.forEach(queryVar => {
|
|
575
|
+
queryVar.values.fill(null);
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
if (statement.returnToCache) {
|
|
579
|
+
statement.inUse = false;
|
|
580
|
+
} else if (statement.cursorId !== 0) {
|
|
581
|
+
this._addCursorToClose(statement.cursorId);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
//---------------------------------------------------------------------------
|
|
586
|
+
// Adds the cursors that needs to be closed to the _cursorsToClose set
|
|
587
|
+
//---------------------------------------------------------------------------
|
|
588
|
+
_addCursorToClose(cursorId) {
|
|
589
|
+
if (this._cursorsToClose.has(cursorId)) {
|
|
590
|
+
const reason = `attempt to close cursor ${cursorId} twice`;
|
|
591
|
+
errors.throwErr(errors.ERR_INTERNAL, reason);
|
|
592
|
+
}
|
|
593
|
+
this._cursorsToClose.add(cursorId);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
//---------------------------------------------------------------------------
|
|
597
|
+
// Adjusts the statement cache to remove least recently used statements
|
|
598
|
+
//---------------------------------------------------------------------------
|
|
599
|
+
_adjustStatementCache() {
|
|
600
|
+
while (this.statementCache.size > this.statementCacheSize) {
|
|
601
|
+
const sql = this.statementCache.keys().next().value;
|
|
602
|
+
const stmt = this.statementCache.get(sql);
|
|
603
|
+
this.statementCache.delete(sql);
|
|
604
|
+
if (stmt.inUse) {
|
|
605
|
+
stmt.returnToCache = false;
|
|
606
|
+
} else if (stmt.cursorId !== 0) {
|
|
607
|
+
this._addCursorToClose(stmt.cursorId);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
//---------------------------------------------------------------------------
|
|
613
|
+
// Parses the sql statement and puts it into cache if keepInStmtCache
|
|
614
|
+
// option is true
|
|
615
|
+
//---------------------------------------------------------------------------
|
|
616
|
+
_prepare(sql, options) {
|
|
617
|
+
let statement = this._getStatement(sql, options.keepInStmtCache);
|
|
618
|
+
statement.bufferRowIndex = 0;
|
|
619
|
+
statement.bufferRowCount = 0;
|
|
620
|
+
statement.lastRowIndex = 0;
|
|
621
|
+
statement.moreRowsToFetch = true;
|
|
622
|
+
return statement;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
//---------------------------------------------------------------------------
|
|
626
|
+
// Binds the values by user to the statement object
|
|
627
|
+
//---------------------------------------------------------------------------
|
|
628
|
+
async _bind(stmt, variable, pos = 0) {
|
|
629
|
+
let bindInfoDict = stmt.bindInfoDict;
|
|
630
|
+
let bindInfoList = stmt.bindInfoList;
|
|
631
|
+
|
|
632
|
+
/*
|
|
633
|
+
* For PL/SQL blocks, if the size of a string or bytes object exceeds
|
|
634
|
+
* 32,767 bytes it is converted to a BLOB/CLOB; and conversion
|
|
635
|
+
* needs to be established as well to return the string in the way that
|
|
636
|
+
* the user expects to get it
|
|
637
|
+
*/
|
|
638
|
+
if (stmt.isPlSql && variable.maxSize > 32767) {
|
|
639
|
+
if (variable.type === types.DB_TYPE_RAW ||
|
|
640
|
+
variable.type === types.DB_TYPE_LONG_RAW) {
|
|
641
|
+
variable.type = types.DB_TYPE_BLOB;
|
|
642
|
+
} else if (variable.type._csfrm === constants.CSFRM_NCHAR) {
|
|
643
|
+
variable.type = types.DB_TYPE_NCLOB;
|
|
644
|
+
} else {
|
|
645
|
+
variable.type = types.DB_TYPE_CLOB;
|
|
646
|
+
}
|
|
647
|
+
const maxSize = variable.maxSize;
|
|
648
|
+
delete variable.maxSize;
|
|
649
|
+
variable.outConverter = async function(val) {
|
|
650
|
+
if (val === null) {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
let data = await val.getData();
|
|
654
|
+
let len = val._length;
|
|
655
|
+
if (data && len > maxSize) {
|
|
656
|
+
errors.throwErr(errors.ERR_INSUFFICIENT_BUFFER_FOR_BINDS);
|
|
657
|
+
}
|
|
658
|
+
return data;
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (variable.type === types.DB_TYPE_CLOB ||
|
|
663
|
+
variable.type === types.DB_TYPE_NCLOB ||
|
|
664
|
+
variable.type === types.DB_TYPE_BLOB) {
|
|
665
|
+
for (let [index, val] of variable.values.entries()) {
|
|
666
|
+
if (!(val instanceof ThinLobImpl)) {
|
|
667
|
+
if (val && val.length > 0) {
|
|
668
|
+
const lobImpl = new ThinLobImpl();
|
|
669
|
+
await lobImpl.create(this, variable.type);
|
|
670
|
+
await lobImpl.write(1, val);
|
|
671
|
+
variable.values[index] = lobImpl;
|
|
672
|
+
} else {
|
|
673
|
+
variable.values[index] = null;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (variable.name) {
|
|
680
|
+
let normalizedName;
|
|
681
|
+
if (variable.name.startsWith('"') && variable.name.endsWith('"')) {
|
|
682
|
+
normalizedName = variable.name.substring(1, variable.name.length - 1);
|
|
683
|
+
} else {
|
|
684
|
+
normalizedName = variable.name.toUpperCase();
|
|
685
|
+
}
|
|
686
|
+
if (normalizedName.startsWith(':')) {
|
|
687
|
+
normalizedName = variable.name.substring(1);
|
|
688
|
+
}
|
|
689
|
+
if (!bindInfoDict.has(normalizedName)) {
|
|
690
|
+
errors.throwErr(errors.ERR_INVALID_BIND_NAME, normalizedName);
|
|
691
|
+
}
|
|
692
|
+
bindInfoDict.get(normalizedName).forEach((bindInfo) => {
|
|
693
|
+
stmt._setVariable(bindInfo, variable);
|
|
694
|
+
});
|
|
695
|
+
} else {
|
|
696
|
+
let bindInfo = bindInfoList[pos - 1];
|
|
697
|
+
stmt._setVariable(bindInfo, variable);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
//---------------------------------------------------------------------------
|
|
702
|
+
// _createResultSet()
|
|
703
|
+
//
|
|
704
|
+
// Creates a result set and performs any necessary initialization.
|
|
705
|
+
//---------------------------------------------------------------------------
|
|
706
|
+
_createResultSet(options, statement) {
|
|
707
|
+
const resultSet = new ThinResultSetImpl();
|
|
708
|
+
if (!statement) {
|
|
709
|
+
statement = new Statement();
|
|
710
|
+
}
|
|
711
|
+
resultSet._resultSetNew(this, statement, options);
|
|
712
|
+
if (statement.queryVars.length > 0) {
|
|
713
|
+
const metadata = thinUtil.getMetadataMany(statement.queryVars);
|
|
714
|
+
resultSet._setup(options, metadata);
|
|
715
|
+
}
|
|
716
|
+
return resultSet;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
//---------------------------------------------------------------------------
|
|
720
|
+
// Prepares the sql given by user and binds the value to the statement object
|
|
721
|
+
//---------------------------------------------------------------------------
|
|
722
|
+
async _prepareAndBind(sql, binds, options, isParse = false) {
|
|
723
|
+
const stmt = this._prepare(sql, options);
|
|
724
|
+
let position = 0;
|
|
725
|
+
if (!isParse) {
|
|
726
|
+
const numBinds = stmt.bindInfoList.length;
|
|
727
|
+
const numVars = binds.length;
|
|
728
|
+
if (numBinds !== numVars && (numVars === 0 || !binds[0].name)) {
|
|
729
|
+
errors.throwErr(errors.ERR_WRONG_NUMBER_OF_POSITIONAL_BINDS, numBinds, numVars);
|
|
730
|
+
}
|
|
731
|
+
for (const variable of binds) {
|
|
732
|
+
await this._bind(stmt, variable, position + 1);
|
|
733
|
+
position += 1;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return stmt;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
//---------------------------------------------------------------------------
|
|
740
|
+
// Clears the statement cache for the connection
|
|
741
|
+
//---------------------------------------------------------------------------
|
|
742
|
+
resetStatmentCache() {
|
|
743
|
+
this.statementCache.clear();
|
|
744
|
+
this._cursorsToClose.clear();
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
//---------------------------------------------------------------------------
|
|
748
|
+
// getDbObjectClass()
|
|
749
|
+
//
|
|
750
|
+
// Returns a database object class given its name.
|
|
751
|
+
//---------------------------------------------------------------------------
|
|
752
|
+
async getDbObjectClass(name) {
|
|
753
|
+
const info = await this._populateDbObjectTypeInfo(name);
|
|
754
|
+
await this._populatePartialDbObjectTypes();
|
|
755
|
+
return info;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
//---------------------------------------------------------------------------
|
|
759
|
+
// Parses the sql given by User
|
|
760
|
+
// calls the OAL8 RPC that parses the SQL statement and returns the metadata
|
|
761
|
+
// information for a statment.
|
|
762
|
+
//---------------------------------------------------------------------------
|
|
763
|
+
async getStatementInfo(sql) {
|
|
764
|
+
let options = {};
|
|
765
|
+
let result = {};
|
|
766
|
+
let statement = await this._prepareAndBind(sql, null, options, true);
|
|
767
|
+
options.connection = this;
|
|
768
|
+
// parse the statement (but not for DDL which doesn't support it)
|
|
769
|
+
if (!statement.isDdl) {
|
|
770
|
+
const message = new messages.ExecuteMessage(this, statement, options);
|
|
771
|
+
message.parseOnly = true;
|
|
772
|
+
await this._protocol._processMessage(message);
|
|
773
|
+
}
|
|
774
|
+
if (statement.numQueryVars > 0) {
|
|
775
|
+
result.metaData = thinUtil.getMetadataMany(statement.queryVars);
|
|
776
|
+
}
|
|
777
|
+
result.bindNames = Array.from(statement.bindInfoDict.keys());
|
|
778
|
+
result.statementType = statement.statementType;
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
//---------------------------------------------------------------------------
|
|
783
|
+
// Prepares the sql given by the user,
|
|
784
|
+
// calls the OAL8 RPC that executes a SQL statement and returns the results.
|
|
785
|
+
//---------------------------------------------------------------------------
|
|
786
|
+
async execute(sql, numIters, binds, options, executeManyFlag) {
|
|
787
|
+
const result = {};
|
|
788
|
+
if (executeManyFlag) {
|
|
789
|
+
return await this.executeMany(sql, numIters, binds, options);
|
|
790
|
+
}
|
|
791
|
+
const statement = await this._prepareAndBind(sql, binds, options);
|
|
792
|
+
|
|
793
|
+
// send the initial request to the database
|
|
794
|
+
const message = new messages.ExecuteMessage(this, statement, options);
|
|
795
|
+
message.numExecs = 1;
|
|
796
|
+
await this._protocol._processMessage(message);
|
|
797
|
+
statement.requiresFullExecute = false;
|
|
798
|
+
|
|
799
|
+
// if a define is required, send an additional request to the database
|
|
800
|
+
if (statement.requiresDefine && statement.sql) {
|
|
801
|
+
statement.requiresFullExecute = true;
|
|
802
|
+
await this._protocol._processMessage(message);
|
|
803
|
+
statement.requiresFullExecute = false;
|
|
804
|
+
statement.requiresDefine = false;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// process message results
|
|
808
|
+
if (statement.numQueryVars > 0) {
|
|
809
|
+
result.resultSet = message.resultSet;
|
|
810
|
+
} else {
|
|
811
|
+
statement.bufferRowIndex = 0;
|
|
812
|
+
const bindVars = thinUtil.getBindVars(statement);
|
|
813
|
+
const outBinds = thinUtil.getExecuteOutBinds(bindVars);
|
|
814
|
+
if (outBinds) {
|
|
815
|
+
result.outBinds = outBinds;
|
|
816
|
+
}
|
|
817
|
+
if (statement.isPlSql) {
|
|
818
|
+
if (options.implicitResultSet) {
|
|
819
|
+
result.implicitResults = options.implicitResultSet;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (statement.lastRowid) {
|
|
823
|
+
result.lastRowid = statement.lastRowid;
|
|
824
|
+
delete statement.lastRowid;
|
|
825
|
+
}
|
|
826
|
+
if (statement.isPlSql) {
|
|
827
|
+
if (statement.rowCount) {
|
|
828
|
+
result.rowsAffected = statement.rowCount;
|
|
829
|
+
}
|
|
830
|
+
} else {
|
|
831
|
+
result.rowsAffected = statement.rowCount || 0;
|
|
832
|
+
}
|
|
833
|
+
if (statement.rowCount) {
|
|
834
|
+
delete statement.rowCount;
|
|
835
|
+
}
|
|
836
|
+
this._returnStatement(statement);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return result;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
//---------------------------------------------------------------------------
|
|
843
|
+
// executeMany()
|
|
844
|
+
//
|
|
845
|
+
// Prepares the sql given by the user, calls the OAL8 RPC that executes a SQL
|
|
846
|
+
// statement multiple times and returns the results.
|
|
847
|
+
//---------------------------------------------------------------------------
|
|
848
|
+
async executeMany(sql, numIters, binds, options) {
|
|
849
|
+
const statement = await this._prepareAndBind(sql, binds, options);
|
|
850
|
+
if (statement.isPlSql && (options.batchErrors || options.dmlRowCounts)) {
|
|
851
|
+
errors.throwErr(errors.ERR_EXEC_MODE_ONLY_FOR_DML);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// send database request
|
|
855
|
+
const message = new messages.ExecuteMessage(this, statement, options);
|
|
856
|
+
message.numExecs = numIters;
|
|
857
|
+
message.arrayDmlRowCounts = options.dmlRowCounts;
|
|
858
|
+
message.batchErrors = options.batchErrors;
|
|
859
|
+
if (statement.isPlSql && statement.requiresFullExecute) {
|
|
860
|
+
message.numExecs = 1;
|
|
861
|
+
await this._protocol._processMessage(message);
|
|
862
|
+
statement.requiresFullExecute = false;
|
|
863
|
+
message.numExecs = numIters - 1;
|
|
864
|
+
message.offset = 1;
|
|
865
|
+
}
|
|
866
|
+
if (message.numExecs > 0) {
|
|
867
|
+
await this._protocol._processMessage(message);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// process results
|
|
871
|
+
const returnObj = {};
|
|
872
|
+
statement.bufferRowIndex = 0;
|
|
873
|
+
const bindVars = thinUtil.getBindVars(statement);
|
|
874
|
+
const outBinds = thinUtil.getExecuteManyOutBinds(bindVars, numIters);
|
|
875
|
+
if (outBinds) {
|
|
876
|
+
returnObj.outBinds = outBinds;
|
|
877
|
+
}
|
|
878
|
+
const rowsAffected = !statement.isPlSql ? statement.rowCount : undefined;
|
|
879
|
+
if (rowsAffected) {
|
|
880
|
+
returnObj.rowsAffected = rowsAffected;
|
|
881
|
+
delete statement.rowCount;
|
|
882
|
+
}
|
|
883
|
+
if (options.dmlRowCounts) {
|
|
884
|
+
returnObj.dmlRowCounts = options.dmlRowCounts;
|
|
885
|
+
}
|
|
886
|
+
if (options.batchErrors) {
|
|
887
|
+
returnObj.batchErrors = options.batchErrors;
|
|
888
|
+
}
|
|
889
|
+
this._returnStatement(statement);
|
|
890
|
+
|
|
891
|
+
return returnObj;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
//---------------------------------------------------------------------------
|
|
895
|
+
// Get the statement object from the statement cache for the SQL if it exists
|
|
896
|
+
// else prepare a new statement object for the SQL. If a statement is already
|
|
897
|
+
// in use a copy will be made and returned (and will not be returned to the
|
|
898
|
+
// cache). If a statement is being executed for the first time after releasing
|
|
899
|
+
// a DRCP session, a copy will also be made (and will not be returned to the
|
|
900
|
+
// cache) since it is unknown at this point whether the original session or a
|
|
901
|
+
// new session is going to be used.
|
|
902
|
+
//---------------------------------------------------------------------------
|
|
903
|
+
_getStatement(sql, cacheStatement = false) {
|
|
904
|
+
let statement = this.statementCache.get(sql);
|
|
905
|
+
if (!statement) {
|
|
906
|
+
statement = new Statement();
|
|
907
|
+
statement._prepare(sql);
|
|
908
|
+
if (cacheStatement && !this._drcpEstablishSession && !statement.isDdl &&
|
|
909
|
+
this.statementCacheSize > 0) {
|
|
910
|
+
statement.returnToCache = true;
|
|
911
|
+
this.statementCache.set(sql, statement);
|
|
912
|
+
this._adjustStatementCache();
|
|
913
|
+
}
|
|
914
|
+
} else if (statement.inUse || !cacheStatement ||
|
|
915
|
+
this._drcpEstablishSession) {
|
|
916
|
+
if (!cacheStatement) {
|
|
917
|
+
this.statementCache.delete(sql);
|
|
918
|
+
statement.returnToCache = false;
|
|
919
|
+
}
|
|
920
|
+
if (statement.inUse || this._drcpEstablishSession) {
|
|
921
|
+
statement = statement._copy();
|
|
922
|
+
}
|
|
923
|
+
} else {
|
|
924
|
+
this.statementCache.delete(sql);
|
|
925
|
+
this.statementCache.set(sql, statement);
|
|
926
|
+
}
|
|
927
|
+
statement.inUse = true;
|
|
928
|
+
return statement;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
//---------------------------------------------------------------------------
|
|
932
|
+
// Calls the ping RPC for Oracle Database
|
|
933
|
+
//---------------------------------------------------------------------------
|
|
934
|
+
async ping() {
|
|
935
|
+
const message = new messages.PingMessage(this);
|
|
936
|
+
await this._protocol._processMessage(message);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
//---------------------------------------------------------------------------
|
|
940
|
+
// Calls the Rollback RPC for Oracle Database
|
|
941
|
+
//---------------------------------------------------------------------------
|
|
942
|
+
async rollback() {
|
|
943
|
+
const message = new messages.RollbackMessage(this);
|
|
944
|
+
await this._protocol._processMessage(message);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
//---------------------------------------------------------------------------
|
|
948
|
+
// Returns the Oracle Server version
|
|
949
|
+
//---------------------------------------------------------------------------
|
|
950
|
+
getOracleServerVersion() {
|
|
951
|
+
return this.serverVersion;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
//---------------------------------------------------------------------------
|
|
955
|
+
// Returns the Oracle Server version string
|
|
956
|
+
//---------------------------------------------------------------------------
|
|
957
|
+
getOracleServerVersionString() {
|
|
958
|
+
return this.serverVersionString;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
setCurrentSchema(schema) {
|
|
962
|
+
this._currentSchemaModified = true;
|
|
963
|
+
this.currentSchema = schema;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
getCurrentSchema() {
|
|
967
|
+
return this.currentSchema;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
setClientId(clientId) {
|
|
971
|
+
this._clientIdentifierModified = true;
|
|
972
|
+
this._clientIdentifier = clientId;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
setDbOp(dbOp) {
|
|
976
|
+
this._dbOpModified = true;
|
|
977
|
+
this._dbOp = dbOp;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
setClientInfo(clientInfo) {
|
|
981
|
+
this._clientInfoModified = true;
|
|
982
|
+
this._clientInfo = clientInfo;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
setModule(module) {
|
|
986
|
+
this._moduleModified = true;
|
|
987
|
+
this._module = module;
|
|
988
|
+
|
|
989
|
+
/*
|
|
990
|
+
* setting the module by itself results in an error so always force
|
|
991
|
+
* action to be set as well (which eliminates this error)
|
|
992
|
+
*/
|
|
993
|
+
this._actionModified = true;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
setAction(action) {
|
|
997
|
+
this._actionModified = true;
|
|
998
|
+
this._action = action;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
async changePassword(user, password, newPassword) {
|
|
1002
|
+
const config = {
|
|
1003
|
+
user: user,
|
|
1004
|
+
newPassword: newPassword,
|
|
1005
|
+
password: password,
|
|
1006
|
+
changePassword: true
|
|
1007
|
+
};
|
|
1008
|
+
const message = new messages.AuthMessage(this, config);
|
|
1009
|
+
await this._protocol._processMessage(message); // OAUTH
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
async createLob(dbType) {
|
|
1013
|
+
const lobImpl = new ThinLobImpl();
|
|
1014
|
+
await lobImpl.create(this, dbType);
|
|
1015
|
+
return lobImpl;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
//---------------------------------------------------------------------------
|
|
1019
|
+
// Returns the statement cache size for the statement cache maintained by
|
|
1020
|
+
// the connection object
|
|
1021
|
+
//---------------------------------------------------------------------------
|
|
1022
|
+
getStmtCacheSize() {
|
|
1023
|
+
return this.statementCacheSize;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
setCallTimeout(timeout) {
|
|
1027
|
+
this._protocol.callTimeout = timeout;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
getCallTimeout() {
|
|
1031
|
+
return this._protocol.callTimeout;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
//---------------------------------------------------------------------------
|
|
1035
|
+
// Returns getTag. Actual tag returned by db must be a string.
|
|
1036
|
+
//---------------------------------------------------------------------------
|
|
1037
|
+
getTag() {
|
|
1038
|
+
return '';
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
module.exports = ThinConnectionImpl;
|