oracledb 2.1.0 → 2.3.0
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 +174 -54
- package/LICENSE.md +52 -18
- package/README.md +35 -68
- package/binding.gyp +5 -4
- package/lib/connection.js +164 -14
- package/lib/oracledb.js +160 -11
- package/lib/pool.js +33 -22
- package/lib/querystream.js +3 -2
- package/lib/rel.txt +1 -1
- package/odpi/LICENSE.md +1 -1
- package/odpi/README.md +25 -84
- package/odpi/embed/README.md +1 -1
- package/odpi/include/dpi.h +51 -7
- package/odpi/src/dpiConn.c +78 -9
- package/odpi/src/dpiContext.c +24 -10
- package/odpi/src/dpiEnv.c +5 -1
- package/odpi/src/dpiError.c +4 -0
- package/odpi/src/dpiErrorMessages.h +6 -4
- package/odpi/src/dpiImpl.h +125 -68
- package/odpi/src/dpiMsgProps.c +33 -0
- package/odpi/src/dpiObject.c +17 -10
- package/odpi/src/dpiObjectType.c +1 -1
- package/odpi/src/dpiOci.c +55 -40
- package/odpi/src/dpiOracleType.c +2 -2
- package/odpi/src/dpiPool.c +88 -18
- package/odpi/src/dpiStmt.c +57 -36
- package/odpi/src/dpiSubscr.c +117 -9
- package/odpi/src/dpiUtils.c +20 -9
- package/odpi/src/dpiVar.c +504 -430
- package/package/MAKEPKG.BAT +64 -1
- package/package/README.md +9 -5
- package/package/createpackage.js +2 -2
- package/package/extractpackage.js +2 -1
- package/package/oracledbinstall.js +19 -19
- package/package/package.json +4 -4
- package/package.json +4 -4
- package/src/njsCommon.cpp +94 -10
- package/src/njsCommon.h +73 -21
- package/src/njsConnection.cpp +1425 -458
- package/src/njsConnection.h +68 -19
- package/src/njsIntLob.cpp +5 -1
- package/src/njsIntLob.h +1 -1
- package/src/njsMessages.cpp +9 -2
- package/src/njsMessages.h +9 -2
- package/src/njsOracle.cpp +213 -23
- package/src/njsOracle.h +18 -2
- package/src/njsPool.cpp +32 -14
- package/src/njsPool.h +4 -4
- package/src/njsResultSet.cpp +4 -3
- package/src/njsResultSet.h +1 -1
- package/src/njsSubscription.cpp +388 -0
- package/src/njsSubscription.h +111 -0
package/src/njsConnection.cpp
CHANGED
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
|
|
52
52
|
#include "njsConnection.h"
|
|
53
53
|
#include "njsResultSet.h"
|
|
54
|
+
#include "njsSubscription.h"
|
|
54
55
|
#include "njsIntLob.h"
|
|
55
56
|
#include <stdlib.h>
|
|
56
57
|
#include <limits>
|
|
@@ -71,7 +72,7 @@ Nan::Persistent<FunctionTemplate> njsConnection::connectionTemplate_s;
|
|
|
71
72
|
// Initialization function of Connection class. Maps functions and properties
|
|
72
73
|
// from JS to C++.
|
|
73
74
|
//-----------------------------------------------------------------------------
|
|
74
|
-
void njsConnection::Init(
|
|
75
|
+
void njsConnection::Init(Local<Object> target)
|
|
75
76
|
{
|
|
76
77
|
Nan::HandleScope scope;
|
|
77
78
|
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
|
|
@@ -80,11 +81,17 @@ void njsConnection::Init(Handle<Object> target)
|
|
|
80
81
|
tpl->SetClassName(Nan::New<v8::String>("Connection").ToLocalChecked());
|
|
81
82
|
|
|
82
83
|
Nan::SetPrototypeMethod(tpl, "execute", Execute);
|
|
83
|
-
Nan::SetPrototypeMethod(tpl, "
|
|
84
|
+
Nan::SetPrototypeMethod(tpl, "executeMany", ExecuteMany);
|
|
85
|
+
Nan::SetPrototypeMethod(tpl, "getStatementInfo", GetStatementInfo);
|
|
86
|
+
Nan::SetPrototypeMethod(tpl, "close", Close);
|
|
84
87
|
Nan::SetPrototypeMethod(tpl, "commit", Commit);
|
|
85
88
|
Nan::SetPrototypeMethod(tpl, "rollback", Rollback);
|
|
86
89
|
Nan::SetPrototypeMethod(tpl, "break", Break);
|
|
87
90
|
Nan::SetPrototypeMethod(tpl, "createLob", CreateLob);
|
|
91
|
+
Nan::SetPrototypeMethod(tpl, "changePassword", ChangePassword);
|
|
92
|
+
Nan::SetPrototypeMethod(tpl, "ping", Ping);
|
|
93
|
+
Nan::SetPrototypeMethod(tpl, "subscribe", Subscribe);
|
|
94
|
+
Nan::SetPrototypeMethod(tpl, "unsubscribe", Unsubscribe);
|
|
88
95
|
|
|
89
96
|
Nan::SetAccessor(tpl->InstanceTemplate(),
|
|
90
97
|
Nan::New<v8::String>("stmtCacheSize").ToLocalChecked(),
|
|
@@ -102,6 +109,10 @@ void njsConnection::Init(Handle<Object> target)
|
|
|
102
109
|
Nan::New<v8::String>("oracleServerVersion").ToLocalChecked(),
|
|
103
110
|
njsConnection::GetOracleServerVersion,
|
|
104
111
|
njsConnection::SetOracleServerVersion);
|
|
112
|
+
Nan::SetAccessor(tpl->InstanceTemplate(),
|
|
113
|
+
Nan::New<v8::String>("oracleServerVersionString").ToLocalChecked(),
|
|
114
|
+
njsConnection::GetOracleServerVersionString,
|
|
115
|
+
njsConnection::SetOracleServerVersionString);
|
|
105
116
|
|
|
106
117
|
connectionTemplate_s.Reset(tpl);
|
|
107
118
|
Nan::Set(target, Nan::New<v8::String>("Connection").ToLocalChecked(),
|
|
@@ -189,6 +200,9 @@ bool njsConnection::ProcessQueryVars(njsBaton *baton, dpiStmt *dpiStmtHandle,
|
|
|
189
200
|
case DPI_ORACLE_TYPE_NCHAR:
|
|
190
201
|
case DPI_ORACLE_TYPE_RAW:
|
|
191
202
|
vars[i].maxSize = queryInfo.typeInfo.clientSizeInBytes;
|
|
203
|
+
if (queryInfo.typeInfo.oracleTypeNum == DPI_ORACLE_TYPE_RAW &&
|
|
204
|
+
vars[i].varTypeNum == DPI_ORACLE_TYPE_VARCHAR)
|
|
205
|
+
vars[i].maxSize *= 2;
|
|
192
206
|
break;
|
|
193
207
|
case DPI_ORACLE_TYPE_DATE:
|
|
194
208
|
case DPI_ORACLE_TYPE_TIMESTAMP:
|
|
@@ -239,77 +253,97 @@ bool njsConnection::ProcessQueryVars(njsBaton *baton, dpiStmt *dpiStmtHandle,
|
|
|
239
253
|
// round trips.
|
|
240
254
|
//-----------------------------------------------------------------------------
|
|
241
255
|
bool njsConnection::ProcessVars(njsBaton *baton, njsVariable *vars,
|
|
242
|
-
uint32_t numVars, uint32_t
|
|
256
|
+
uint32_t numVars, uint32_t numRows)
|
|
243
257
|
{
|
|
244
|
-
uint32_t numElements;
|
|
245
|
-
dpiStmt *stmt;
|
|
246
|
-
|
|
247
258
|
for (uint32_t col = 0; col < numVars; col++) {
|
|
248
259
|
njsVariable *var = &vars[col];
|
|
260
|
+
var->buffer.numElements = numRows;
|
|
249
261
|
if (var->bindDir == NJS_BIND_IN)
|
|
250
262
|
continue;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
+
if (var->dmlReturningBuffers) {
|
|
264
|
+
delete [] var->dmlReturningBuffers;
|
|
265
|
+
var->dmlReturningBuffers = NULL;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// for arrays, determine the number of elements in the array
|
|
269
|
+
if (var->isArray) {
|
|
270
|
+
if (dpiVar_getNumElementsInArray(var->dpiVarHandle,
|
|
271
|
+
&var->buffer.numElements) < 0) {
|
|
272
|
+
baton->GetDPIError();
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// for DML returning statements, each row has its own set of rows, so
|
|
277
|
+
// acquire those from ODPI-C and store them in variable buffers for
|
|
278
|
+
// later processing
|
|
279
|
+
} else if (baton->isReturning && var->bindDir == NJS_BIND_OUT) {
|
|
280
|
+
var->dmlReturningBuffers = new njsVariableBuffer[numRows];
|
|
281
|
+
for (uint32_t row = 0; row < numRows; row++) {
|
|
282
|
+
njsVariableBuffer *buffer = &var->dmlReturningBuffers[row];
|
|
283
|
+
if (dpiVar_getReturnedData(var->dpiVarHandle, row,
|
|
284
|
+
&buffer->numElements, &buffer->dpiVarData) < 0) {
|
|
263
285
|
baton->GetDPIError();
|
|
264
286
|
return false;
|
|
265
287
|
}
|
|
266
|
-
|
|
267
|
-
if (!ProcessQueryVars(baton, stmt, var->queryVars,
|
|
268
|
-
var->numQueryVars))
|
|
288
|
+
if (!ProcessVarBuffer(baton, var, buffer))
|
|
269
289
|
return false;
|
|
270
|
-
|
|
271
|
-
default:
|
|
272
|
-
continue;
|
|
290
|
+
}
|
|
273
291
|
}
|
|
274
292
|
|
|
275
|
-
//
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// has taken place
|
|
280
|
-
if (baton->isReturning && var->bindDir == NJS_BIND_OUT) {
|
|
281
|
-
if (dpiVar_getData(var->dpiVarHandle, &var->maxArraySize,
|
|
282
|
-
&var->dpiVarData) < 0) {
|
|
283
|
-
baton->GetDPIError();
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
numElements = (uint32_t) baton->rowsAffected;
|
|
293
|
+
// process the main buffer if DML returning is not in effect
|
|
294
|
+
if (!var->dmlReturningBuffers &&
|
|
295
|
+
!ProcessVarBuffer(baton, var, &var->buffer))
|
|
296
|
+
return false;
|
|
287
297
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
//-----------------------------------------------------------------------------
|
|
305
|
+
// njsConnection::ProcessVarBuffer()
|
|
306
|
+
// Process a variable buffer. REF cursors must have their query variables
|
|
307
|
+
// defined and LOBs must be initially processed in order to have as much work
|
|
308
|
+
// as possible done in the worker thread and avoid any round trips.
|
|
309
|
+
//-----------------------------------------------------------------------------
|
|
310
|
+
bool njsConnection::ProcessVarBuffer(njsBaton *baton, njsVariable *var,
|
|
311
|
+
njsVariableBuffer *buffer)
|
|
312
|
+
{
|
|
313
|
+
dpiStmt *stmt;
|
|
314
|
+
|
|
315
|
+
switch (var->varTypeNum) {
|
|
316
|
+
case DPI_ORACLE_TYPE_CLOB:
|
|
317
|
+
case DPI_ORACLE_TYPE_NCLOB:
|
|
318
|
+
case DPI_ORACLE_TYPE_BLOB:
|
|
319
|
+
buffer->lobs = new njsProtoILob[buffer->numElements];
|
|
320
|
+
for (uint32_t i = 0; i < buffer->numElements; i++) {
|
|
321
|
+
njsProtoILob *lob = &buffer->lobs[i];
|
|
322
|
+
lob->dataType = (var->varTypeNum == DPI_ORACLE_TYPE_BLOB) ?
|
|
323
|
+
NJS_DATATYPE_BLOB : NJS_DATATYPE_CLOB;
|
|
324
|
+
lob->isAutoClose = true;
|
|
325
|
+
uint32_t elementIndex = baton->bufferRowIndex + i;
|
|
326
|
+
dpiData *data = &buffer->dpiVarData[elementIndex];
|
|
327
|
+
if (data->isNull)
|
|
328
|
+
continue;
|
|
329
|
+
if (!lob->PopulateFromDPI(baton, data->value.asLOB, true))
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
break;
|
|
333
|
+
case DPI_ORACLE_TYPE_STMT:
|
|
334
|
+
stmt = buffer->dpiVarData->value.asStmt;
|
|
335
|
+
if (dpiStmt_getNumQueryColumns(stmt, &var->numQueryVars) < 0) {
|
|
293
336
|
baton->GetDPIError();
|
|
294
337
|
return false;
|
|
295
338
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
var->lobs = new njsProtoILob[numElements];
|
|
300
|
-
for (uint32_t row = 0; row < numElements; row++) {
|
|
301
|
-
njsProtoILob *lob = &var->lobs[row];
|
|
302
|
-
lob->dataType = dataType;
|
|
303
|
-
lob->isAutoClose = true;
|
|
304
|
-
uint32_t elementIndex = baton->bufferRowIndex + row;
|
|
305
|
-
dpiData *data = &var->dpiVarData[elementIndex];
|
|
306
|
-
if (data->isNull)
|
|
307
|
-
continue;
|
|
308
|
-
if (!lob->PopulateFromDPI(baton, data->value.asLOB, true))
|
|
339
|
+
var->queryVars = new njsVariable[var->numQueryVars];
|
|
340
|
+
if (!ProcessQueryVars(baton, stmt, var->queryVars,
|
|
341
|
+
var->numQueryVars))
|
|
309
342
|
return false;
|
|
310
|
-
|
|
343
|
+
break;
|
|
344
|
+
default:
|
|
345
|
+
break;
|
|
311
346
|
}
|
|
312
|
-
|
|
313
347
|
return true;
|
|
314
348
|
}
|
|
315
349
|
|
|
@@ -348,51 +382,56 @@ bool njsConnection::MapByName(njsBaton *baton, dpiQueryInfo *queryInfo,
|
|
|
348
382
|
bool njsConnection::MapByType(njsBaton *baton, dpiQueryInfo *queryInfo,
|
|
349
383
|
dpiOracleTypeNum &targetType)
|
|
350
384
|
{
|
|
351
|
-
|
|
385
|
+
uint32_t i;
|
|
386
|
+
|
|
387
|
+
// handle fetchAsString
|
|
388
|
+
for (i = 0; i < baton->numFetchAsStringTypes; i++) {
|
|
352
389
|
switch (queryInfo->typeInfo.oracleTypeNum) {
|
|
353
390
|
case DPI_ORACLE_TYPE_NUMBER:
|
|
354
391
|
case DPI_ORACLE_TYPE_NATIVE_FLOAT:
|
|
355
392
|
case DPI_ORACLE_TYPE_NATIVE_DOUBLE:
|
|
356
393
|
case DPI_ORACLE_TYPE_NATIVE_INT:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
394
|
+
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_NUM) {
|
|
395
|
+
targetType = DPI_ORACLE_TYPE_VARCHAR;
|
|
396
|
+
return true;
|
|
362
397
|
}
|
|
363
398
|
break;
|
|
364
399
|
case DPI_ORACLE_TYPE_DATE:
|
|
365
400
|
case DPI_ORACLE_TYPE_TIMESTAMP:
|
|
366
401
|
case DPI_ORACLE_TYPE_TIMESTAMP_TZ:
|
|
367
402
|
case DPI_ORACLE_TYPE_TIMESTAMP_LTZ:
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
return true;
|
|
372
|
-
}
|
|
403
|
+
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_DATE) {
|
|
404
|
+
targetType = DPI_ORACLE_TYPE_VARCHAR;
|
|
405
|
+
return true;
|
|
373
406
|
}
|
|
374
407
|
break;
|
|
375
408
|
case DPI_ORACLE_TYPE_CLOB:
|
|
376
409
|
case DPI_ORACLE_TYPE_NCLOB:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return true;
|
|
381
|
-
}
|
|
410
|
+
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_CLOB) {
|
|
411
|
+
targetType = DPI_ORACLE_TYPE_VARCHAR;
|
|
412
|
+
return true;
|
|
382
413
|
}
|
|
383
414
|
break;
|
|
384
|
-
case
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return true;
|
|
389
|
-
}
|
|
415
|
+
case DPI_ORACLE_TYPE_RAW:
|
|
416
|
+
if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_BUFFER) {
|
|
417
|
+
targetType = DPI_ORACLE_TYPE_VARCHAR;
|
|
418
|
+
return true;
|
|
390
419
|
}
|
|
391
420
|
break;
|
|
392
421
|
default:
|
|
393
422
|
break;
|
|
394
423
|
}
|
|
395
424
|
}
|
|
425
|
+
|
|
426
|
+
// handle fetchAsBuffer
|
|
427
|
+
for (i = 0; i < baton->numFetchAsBufferTypes; i++) {
|
|
428
|
+
if (queryInfo->typeInfo.oracleTypeNum == DPI_ORACLE_TYPE_BLOB &&
|
|
429
|
+
baton->fetchAsBufferTypes[i] == NJS_DATATYPE_BLOB) {
|
|
430
|
+
targetType = DPI_ORACLE_TYPE_RAW;
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
396
435
|
return false;
|
|
397
436
|
}
|
|
398
437
|
|
|
@@ -431,12 +470,6 @@ bool njsConnection::PrepareAndBind(njsBaton *baton)
|
|
|
431
470
|
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
432
471
|
int status;
|
|
433
472
|
njsVariable *var = &baton->bindVars[i];
|
|
434
|
-
if (stmtInfo.isReturning && var->bindDir == NJS_BIND_OUT &&
|
|
435
|
-
var->varTypeNum == DPI_ORACLE_TYPE_RAW) {
|
|
436
|
-
baton->error = njsMessages::Get(errBufferReturningInvalid);
|
|
437
|
-
baton->ClearAsyncData();
|
|
438
|
-
return false;
|
|
439
|
-
}
|
|
440
473
|
if (var->name.empty())
|
|
441
474
|
status = dpiStmt_bindByPos(baton->dpiStmtHandle, var->pos,
|
|
442
475
|
var->dpiVarHandle);
|
|
@@ -515,166 +548,24 @@ Local<Value> njsConnection::GetMetaData(njsVariable *vars, uint32_t numVars,
|
|
|
515
548
|
|
|
516
549
|
|
|
517
550
|
//-----------------------------------------------------------------------------
|
|
518
|
-
// njsConnection::
|
|
519
|
-
//
|
|
520
|
-
// each one and added to the binds array for processing during the actual
|
|
521
|
-
// execution.
|
|
522
|
-
//-----------------------------------------------------------------------------
|
|
523
|
-
bool njsConnection::ProcessBinds(Nan::NAN_METHOD_ARGS_TYPE args,
|
|
524
|
-
unsigned int index, njsBaton *baton)
|
|
525
|
-
{
|
|
526
|
-
Nan::HandleScope scope;
|
|
527
|
-
if (args[static_cast<int>(index)]->IsArray()) {
|
|
528
|
-
Local<Array> bindsArray = Local<Array>::Cast(
|
|
529
|
-
args[static_cast<int>(index)]);
|
|
530
|
-
return ProcessBindsByPos(bindsArray, baton);
|
|
531
|
-
}
|
|
532
|
-
if (args[static_cast<int>(index)]->IsObject() &&
|
|
533
|
-
!args[static_cast<int>(index)]->IsFunction()) {
|
|
534
|
-
Local<Object> bindsObject = args[static_cast<int>(index)]->ToObject();
|
|
535
|
-
return ProcessBindsByName(bindsObject, baton);
|
|
536
|
-
}
|
|
537
|
-
baton->error = njsMessages::Get(errInvalidParameterType, index);
|
|
538
|
-
return false;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
//-----------------------------------------------------------------------------
|
|
543
|
-
// njsConnection::ProcessBindsByName()
|
|
544
|
-
// Get bind variables from JS (by name).
|
|
545
|
-
//-----------------------------------------------------------------------------
|
|
546
|
-
bool njsConnection::ProcessBindsByName(Handle<Object> bindObj, njsBaton *baton)
|
|
547
|
-
{
|
|
548
|
-
Nan::HandleScope scope;
|
|
549
|
-
Local<Array> array = bindObj->GetOwnPropertyNames();
|
|
550
|
-
|
|
551
|
-
baton->numBindVars = array->Length();
|
|
552
|
-
baton->bindVars = new njsVariable[baton->numBindVars];
|
|
553
|
-
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
554
|
-
njsVariable *var = &baton->bindVars[i];
|
|
555
|
-
Local<Value> temp = Nan::Get(array, i).ToLocalChecked();
|
|
556
|
-
v8::String::Utf8Value v8str(temp->ToString());
|
|
557
|
-
std::string str = std::string(*v8str,
|
|
558
|
-
static_cast<size_t>(v8str.length()));
|
|
559
|
-
var->name = ":" + str;
|
|
560
|
-
var->pos = i + 1;
|
|
561
|
-
MaybeLocal<Value> mval = Nan::Get (bindObj,
|
|
562
|
-
Nan::New(str).ToLocalChecked());
|
|
563
|
-
Local<Value> val;
|
|
564
|
-
if (!mval.ToLocal (&val))
|
|
565
|
-
return false;
|
|
566
|
-
|
|
567
|
-
if (!ProcessBind(val, var, false, baton))
|
|
568
|
-
return false;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
return true;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
//-----------------------------------------------------------------------------
|
|
576
|
-
// njsConnection::ProcessBindsByPos()
|
|
577
|
-
// Get bind variables from JS (by position).
|
|
578
|
-
//-----------------------------------------------------------------------------
|
|
579
|
-
bool njsConnection::ProcessBindsByPos(Handle<Array> binds, njsBaton *baton)
|
|
580
|
-
{
|
|
581
|
-
Nan::HandleScope scope;
|
|
582
|
-
|
|
583
|
-
baton->numBindVars = binds->Length();
|
|
584
|
-
baton->bindVars = new njsVariable[baton->numBindVars];
|
|
585
|
-
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
586
|
-
njsVariable *var = &baton->bindVars[i];
|
|
587
|
-
var->pos = i + 1;
|
|
588
|
-
Local<Value> val = Nan::Get(binds,i).ToLocalChecked ();
|
|
589
|
-
if (!ProcessBind(val, var, true, baton))
|
|
590
|
-
return false;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
return true;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
//-----------------------------------------------------------------------------
|
|
598
|
-
// njsConnection::ProcessBind()
|
|
599
|
-
// Process bind variable from JS.
|
|
551
|
+
// njsConnection::CreateVarBuffer()
|
|
552
|
+
// Create ODPI-C variables used to hold the bind data.
|
|
600
553
|
//-----------------------------------------------------------------------------
|
|
601
|
-
bool njsConnection::
|
|
602
|
-
bool byPosition, njsBaton *baton)
|
|
554
|
+
bool njsConnection::CreateVarBuffer(njsVariable *var, njsBaton *baton)
|
|
603
555
|
{
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
// default values
|
|
609
|
-
var->bindDir = NJS_BIND_IN;
|
|
610
|
-
var->maxSize = 0;
|
|
611
|
-
var->maxArraySize = 0;
|
|
612
|
-
var->isArray = false;
|
|
613
|
-
bindType = 0;
|
|
614
|
-
|
|
615
|
-
// value is an object, get information on the bind variable from it
|
|
616
|
-
if (val->IsObject() && !val->IsDate() && !Buffer::HasInstance(val) &&
|
|
617
|
-
!njsILob::HasInstance(val)) {
|
|
618
|
-
Local<Object> bindUnit = val->ToObject();
|
|
619
|
-
|
|
620
|
-
// In case of bind-by-position, JSON objects are expected to be
|
|
621
|
-
// unnamed. If JSON object is provided, we look for "dir", "type",
|
|
622
|
-
// "maxSize" and "val" key names. If not found, an error is reported.
|
|
623
|
-
// Array (positional) binds syntax
|
|
624
|
-
// [ id, name, {type : oracledb.STRING, dir : oracledb.BIND_OUT}]
|
|
625
|
-
// the 3rd parameter is unnamed JSON object.
|
|
626
|
-
// [ id, n, { a: { type : oracledb.STRING, dir : oracledb.BIND_OUT} }]
|
|
627
|
-
// will fail now. In this example the third parameter JSON object has
|
|
628
|
-
// a key name "a" and value as another JSON. This is incorrect syntax.
|
|
629
|
-
if (byPosition) {
|
|
630
|
-
Local<Array> keys = bindUnit->GetOwnPropertyNames();
|
|
631
|
-
bool valid = false;
|
|
632
|
-
for (uint32_t i = 0; i < keys->Length(); i++) {
|
|
633
|
-
Local<String> temp = Nan::Get(keys,
|
|
634
|
-
i).ToLocalChecked().As<String>();
|
|
635
|
-
|
|
636
|
-
v8::String::Utf8Value utf8str(temp->ToString());
|
|
637
|
-
std::string key = std::string(*utf8str,
|
|
638
|
-
static_cast<size_t>(utf8str.length()));
|
|
639
|
-
if (key.compare("dir") == 0 ||
|
|
640
|
-
key.compare("type") == 0 ||
|
|
641
|
-
key.compare("maxSize") == 0 ||
|
|
642
|
-
key.compare("val") == 0) {
|
|
643
|
-
valid = true;
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
if (!valid) {
|
|
648
|
-
baton->error = njsMessages::Get(errNamedJSON);
|
|
649
|
-
return false;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
if (!baton->GetUnsignedIntFromJSON(bindUnit, "dir", 1, &var->bindDir))
|
|
654
|
-
return false;
|
|
655
|
-
if (!baton->GetUnsignedIntFromJSON(bindUnit, "type", 1, &bindType))
|
|
656
|
-
return false;
|
|
657
|
-
if (var->bindDir != NJS_BIND_IN) {
|
|
658
|
-
var->maxSize = NJS_MAX_OUT_BIND_SIZE;
|
|
659
|
-
if (!baton->GetUnsignedIntFromJSON(bindUnit, "maxSize", 1,
|
|
660
|
-
&var->maxSize))
|
|
661
|
-
return false;
|
|
662
|
-
}
|
|
663
|
-
if (!baton->GetUnsignedIntFromJSON(bindUnit, "maxArraySize", 1,
|
|
664
|
-
&var->maxArraySize))
|
|
665
|
-
return false;
|
|
666
|
-
if (var->maxArraySize > 0)
|
|
667
|
-
var->isArray = true;
|
|
668
|
-
bindValue = Nan::Get(bindUnit,
|
|
669
|
-
Nan::New<v8::String>("val").ToLocalChecked()).ToLocalChecked ();
|
|
556
|
+
// if the variable is not an array use the bind array size
|
|
557
|
+
if (!var->isArray)
|
|
558
|
+
var->maxArraySize = baton->bindArraySize;
|
|
670
559
|
|
|
671
|
-
//
|
|
672
|
-
|
|
673
|
-
|
|
560
|
+
// if the variable has no data type assume string of size 1
|
|
561
|
+
if (var->bindDataType == NJS_DATATYPE_DEFAULT) {
|
|
562
|
+
var->bindDataType = NJS_DATATYPE_STR;
|
|
563
|
+
var->maxSize = 1;
|
|
674
564
|
}
|
|
675
565
|
|
|
676
566
|
// REF cursors are only supported as out binds currently
|
|
677
|
-
if (
|
|
567
|
+
if (var->bindDataType == NJS_DATATYPE_CURSOR &&
|
|
568
|
+
var->bindDir != NJS_BIND_OUT) {
|
|
678
569
|
baton->error = njsMessages::Get(errInvalidPropertyValueInParam,
|
|
679
570
|
"type", 1);
|
|
680
571
|
return false;
|
|
@@ -687,42 +578,8 @@ bool njsConnection::ProcessBind(Local<Value> val, njsVariable *var,
|
|
|
687
578
|
return false;
|
|
688
579
|
}
|
|
689
580
|
|
|
690
|
-
//
|
|
691
|
-
|
|
692
|
-
uint32_t defaultMaxSize = 0, defaultBindType = 0;
|
|
693
|
-
if (!GetBindTypeAndSizeFromValue(var, bindValue, &defaultBindType,
|
|
694
|
-
&defaultMaxSize, baton))
|
|
695
|
-
return false;
|
|
696
|
-
if (!bindType)
|
|
697
|
-
bindType = defaultBindType;
|
|
698
|
-
if (defaultMaxSize > var->maxSize)
|
|
699
|
-
var->maxSize = defaultMaxSize;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// for IN binds, maxArraySize is ignored and obtained from the actual
|
|
703
|
-
// array size; for INOUT binds, maxArraySize does need to be specified
|
|
704
|
-
// by the application; for OUT binds, the value from the application
|
|
705
|
-
// must be accepted as is as there is no way to validate it
|
|
706
|
-
if (bindValue->IsArray()) {
|
|
707
|
-
var->isArray = true;
|
|
708
|
-
Local<Array> arrayVal = Local<Array>::Cast(bindValue);
|
|
709
|
-
if (var->bindDir == NJS_BIND_IN) {
|
|
710
|
-
var->maxArraySize = arrayVal->Length();
|
|
711
|
-
if (var->maxArraySize == 0)
|
|
712
|
-
var->maxArraySize = 1;
|
|
713
|
-
} else if (!var->maxArraySize) {
|
|
714
|
-
baton->error = njsMessages::Get(errReqdMaxArraySize);
|
|
715
|
-
return false;
|
|
716
|
-
}
|
|
717
|
-
if (var->bindDir == NJS_BIND_INOUT &&
|
|
718
|
-
arrayVal->Length() > var->maxArraySize) {
|
|
719
|
-
baton->error = njsMessages::Get(errInvalidArraySize);
|
|
720
|
-
return false;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// validate bind type and determine variable type
|
|
725
|
-
switch (bindType) {
|
|
581
|
+
// determine ODPI-C Oracle type and native type to use
|
|
582
|
+
switch (var->bindDataType) {
|
|
726
583
|
case NJS_DATATYPE_STR:
|
|
727
584
|
var->varTypeNum = DPI_ORACLE_TYPE_VARCHAR;
|
|
728
585
|
var->nativeTypeNum = DPI_NATIVE_TYPE_BYTES;
|
|
@@ -760,28 +617,130 @@ bool njsConnection::ProcessBind(Local<Value> val, njsVariable *var,
|
|
|
760
617
|
return false;
|
|
761
618
|
}
|
|
762
619
|
|
|
763
|
-
// create
|
|
764
|
-
if (!var->isArray)
|
|
765
|
-
var->maxArraySize = 1;
|
|
620
|
+
// create ODPI-C variable
|
|
766
621
|
if (dpiConn_newVar(baton->dpiConnHandle, var->varTypeNum,
|
|
767
622
|
var->nativeTypeNum, var->maxArraySize, var->maxSize, 1,
|
|
768
|
-
var->isArray, NULL, &var->dpiVarHandle,
|
|
623
|
+
var->isArray, NULL, &var->dpiVarHandle,
|
|
624
|
+
&var->buffer.dpiVarData) < 0) {
|
|
769
625
|
baton->GetDPIError();
|
|
770
626
|
return false;
|
|
771
627
|
}
|
|
772
628
|
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
//-----------------------------------------------------------------------------
|
|
634
|
+
// njsConnection::ProcessExecuteBinds()
|
|
635
|
+
// Process binds passed through to Execute() call.
|
|
636
|
+
//-----------------------------------------------------------------------------
|
|
637
|
+
bool njsConnection::ProcessExecuteBinds(Local<Object> binds, njsBaton *baton)
|
|
638
|
+
{
|
|
639
|
+
Nan::HandleScope scope;
|
|
640
|
+
Local<Array> bindNames;
|
|
641
|
+
|
|
642
|
+
// determine bind names (if binding by name)
|
|
643
|
+
baton->bindArraySize = 1;
|
|
644
|
+
if (!binds->IsUndefined() && !binds->IsArray())
|
|
645
|
+
bindNames = binds->GetOwnPropertyNames();
|
|
646
|
+
|
|
647
|
+
// initialize variables; if there are no variables, nothing further to do!
|
|
648
|
+
if (!InitBindVars(binds, bindNames, baton))
|
|
649
|
+
return false;
|
|
650
|
+
if (baton->numBindVars == 0)
|
|
651
|
+
return true;
|
|
652
|
+
|
|
653
|
+
// scan the execute binds and populate the bind variables
|
|
654
|
+
return ScanExecuteBinds(binds, bindNames, baton);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
//-----------------------------------------------------------------------------
|
|
659
|
+
// njsConnection::ScanExecuteBinds()
|
|
660
|
+
// Scan the binds passed through to Execute() and determine the bind
|
|
661
|
+
// type and maximum size (for strings/buffers).
|
|
662
|
+
//-----------------------------------------------------------------------------
|
|
663
|
+
bool njsConnection::ScanExecuteBinds(Local<Object> binds,
|
|
664
|
+
Local<Array> bindNames, njsBaton *baton)
|
|
665
|
+
{
|
|
666
|
+
Nan::HandleScope scope;
|
|
667
|
+
Local<Value> bindName, bindUnit, bindValue;
|
|
668
|
+
Local<Array> byPositionValues;
|
|
669
|
+
njsVariable *var;
|
|
670
|
+
bool byPosition;
|
|
671
|
+
|
|
672
|
+
// determine if binding is by position or by name
|
|
673
|
+
byPosition = (baton->bindVars[0].pos > 0);
|
|
674
|
+
if (byPosition)
|
|
675
|
+
byPositionValues = binds.As<Array>();
|
|
676
|
+
|
|
677
|
+
// scan each column
|
|
678
|
+
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
679
|
+
var = &baton->bindVars[i];
|
|
680
|
+
|
|
681
|
+
// determine bind information and value
|
|
682
|
+
if (byPosition)
|
|
683
|
+
bindUnit = Nan::Get(byPositionValues, i).ToLocalChecked();
|
|
684
|
+
else {
|
|
685
|
+
bindName = Nan::Get(bindNames, i).ToLocalChecked();
|
|
686
|
+
if (!Nan::Get(binds, bindName).ToLocal(&bindUnit))
|
|
780
687
|
return false;
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
688
|
+
}
|
|
689
|
+
if (bindUnit->IsObject() && !bindUnit->IsDate() &&
|
|
690
|
+
!Buffer::HasInstance(bindUnit) &&
|
|
691
|
+
!njsILob::HasInstance(bindUnit)) {
|
|
692
|
+
if (!ScanExecuteBindUnit(bindUnit.As<Object>(), var, false, baton))
|
|
693
|
+
return false;
|
|
694
|
+
Local<String> key = Nan::New<String>("val").ToLocalChecked();
|
|
695
|
+
bindValue = Nan::Get(bindUnit.As<Object>(), key).ToLocalChecked();
|
|
696
|
+
} else bindValue = bindUnit;
|
|
697
|
+
|
|
698
|
+
// get bind information from value if it has not already been specified
|
|
699
|
+
if (var->bindDataType == NJS_DATATYPE_DEFAULT || !var->maxSize ||
|
|
700
|
+
var->maxSize == NJS_MAX_OUT_BIND_SIZE) {
|
|
701
|
+
njsDataType defaultBindType = NJS_DATATYPE_DEFAULT;
|
|
702
|
+
uint32_t defaultMaxSize = 0;
|
|
703
|
+
if (!GetBindTypeAndSizeFromValue(var, bindValue, &defaultBindType,
|
|
704
|
+
&defaultMaxSize, baton))
|
|
705
|
+
return false;
|
|
706
|
+
if (var->bindDataType == NJS_DATATYPE_DEFAULT)
|
|
707
|
+
var->bindDataType = defaultBindType;
|
|
708
|
+
if (defaultMaxSize > var->maxSize)
|
|
709
|
+
var->maxSize = defaultMaxSize;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// for IN binds, maxArraySize is ignored and obtained from the actual
|
|
713
|
+
// array size; for INOUT binds, maxArraySize does need to be specified
|
|
714
|
+
// by the application; for OUT binds, the value from the application
|
|
715
|
+
// must be accepted as is as there is no way to validate it
|
|
716
|
+
if (bindValue->IsArray()) {
|
|
717
|
+
var->isArray = true;
|
|
718
|
+
Local<Array> arrayVal = Local<Array>::Cast(bindValue);
|
|
719
|
+
if (var->bindDir == NJS_BIND_IN) {
|
|
720
|
+
var->maxArraySize = arrayVal->Length();
|
|
721
|
+
if (var->maxArraySize == 0)
|
|
722
|
+
var->maxArraySize = 1;
|
|
723
|
+
} else if (var->maxArraySize == 0) {
|
|
724
|
+
baton->error = njsMessages::Get(errReqdMaxArraySize);
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
if (var->bindDir == NJS_BIND_INOUT &&
|
|
728
|
+
arrayVal->Length() > var->maxArraySize) {
|
|
729
|
+
baton->error = njsMessages::Get(errInvalidArraySize);
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// create buffer for variable
|
|
735
|
+
if (!CreateVarBuffer(var, baton))
|
|
784
736
|
return false;
|
|
737
|
+
|
|
738
|
+
// process bind value (for all except OUT)
|
|
739
|
+
if (var->bindDir != NJS_BIND_OUT) {
|
|
740
|
+
if (!ProcessBindValue(bindValue, var, baton))
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
|
|
785
744
|
}
|
|
786
745
|
|
|
787
746
|
return true;
|
|
@@ -789,44 +748,90 @@ bool njsConnection::ProcessBind(Local<Value> val, njsVariable *var,
|
|
|
789
748
|
|
|
790
749
|
|
|
791
750
|
//-----------------------------------------------------------------------------
|
|
792
|
-
// njsConnection::
|
|
793
|
-
//
|
|
751
|
+
// njsConnection::ScanExecuteBindUnit()
|
|
752
|
+
// Scan the execute bind unit for bind information.
|
|
794
753
|
//-----------------------------------------------------------------------------
|
|
795
|
-
bool njsConnection::
|
|
796
|
-
njsBaton *baton)
|
|
754
|
+
bool njsConnection::ScanExecuteBindUnit(Local<Object> bindUnit,
|
|
755
|
+
njsVariable *var, bool inExecuteMany, njsBaton *baton)
|
|
797
756
|
{
|
|
798
|
-
|
|
799
|
-
if (!var->isArray)
|
|
800
|
-
return ProcessScalarBindValue(value, var, 0, baton);
|
|
757
|
+
Nan::HandleScope scope;
|
|
801
758
|
|
|
802
|
-
//
|
|
803
|
-
if
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
759
|
+
// scan all keys and verify that one of "dir", "type", "maxSize" or "val"
|
|
760
|
+
// is found; if not, the bind information is considered invalid
|
|
761
|
+
Local<Array> keys = bindUnit->GetOwnPropertyNames();
|
|
762
|
+
bool valid = false;
|
|
763
|
+
for (uint32_t i = 0; i < keys->Length(); i++) {
|
|
764
|
+
Local<String> temp =
|
|
765
|
+
Nan::Get(keys, i).ToLocalChecked().As<String>();
|
|
766
|
+
Nan::Utf8String utf8str(temp);
|
|
767
|
+
std::string key =
|
|
768
|
+
std::string(*utf8str, static_cast<size_t>(utf8str.length()));
|
|
769
|
+
if (key.compare("dir") == 0 || key.compare("type") == 0 ||
|
|
770
|
+
key.compare("maxSize") == 0 || key.compare("val") == 0) {
|
|
771
|
+
valid = true;
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
if (!valid) {
|
|
776
|
+
baton->error = njsMessages::Get(errNamedJSON);
|
|
807
777
|
return false;
|
|
808
778
|
}
|
|
809
779
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
780
|
+
// get and validate bind direction
|
|
781
|
+
uint32_t temp = (uint32_t) var->bindDir;
|
|
782
|
+
if (!baton->GetUnsignedIntFromJSON(bindUnit, "dir", 1, &temp))
|
|
813
783
|
return false;
|
|
784
|
+
var->bindDir = (njsBindDir) temp;
|
|
785
|
+
switch (var->bindDir) {
|
|
786
|
+
case NJS_BIND_OUT:
|
|
787
|
+
case NJS_BIND_IN:
|
|
788
|
+
case NJS_BIND_INOUT:
|
|
789
|
+
break;
|
|
790
|
+
default:
|
|
791
|
+
baton->error = njsMessages::Get(errInvalidBindDirection);
|
|
792
|
+
return false;
|
|
814
793
|
}
|
|
815
794
|
|
|
816
|
-
//
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
if (dpiVar_setNumElementsInArray(var->dpiVarHandle,
|
|
820
|
-
arrayVal->Length()) < 0) {
|
|
821
|
-
baton->GetDPIError();
|
|
795
|
+
// get data type
|
|
796
|
+
temp = (uint32_t) var->bindDataType;
|
|
797
|
+
if (!baton->GetUnsignedIntFromJSON(bindUnit, "type", 1, &temp))
|
|
822
798
|
return false;
|
|
799
|
+
if (!temp && inExecuteMany) {
|
|
800
|
+
if (var->pos > 0)
|
|
801
|
+
baton->error = njsMessages::Get(errMissingTypeByPos, var->pos);
|
|
802
|
+
else baton->error = njsMessages::Get(errMissingTypeByName,
|
|
803
|
+
var->name.c_str());
|
|
823
804
|
}
|
|
805
|
+
var->bindDataType = (njsDataType) temp;
|
|
824
806
|
|
|
825
|
-
//
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (
|
|
807
|
+
// get maximum size for strings/buffers; this value is only used for
|
|
808
|
+
// IN/OUT and OUT binds in execute() and at all times for executeMany()
|
|
809
|
+
if (var->bindDir != NJS_BIND_IN || inExecuteMany) {
|
|
810
|
+
if (var->bindDir != NJS_BIND_IN)
|
|
811
|
+
var->maxSize = NJS_MAX_OUT_BIND_SIZE;
|
|
812
|
+
if (!baton->GetUnsignedIntFromJSON(bindUnit, "maxSize", 1,
|
|
813
|
+
&var->maxSize))
|
|
814
|
+
return false;
|
|
815
|
+
if (inExecuteMany && var->maxSize == 0) {
|
|
816
|
+
if (var->bindDataType == NJS_DATATYPE_STR ||
|
|
817
|
+
var->bindDataType == NJS_DATATYPE_BUFFER) {
|
|
818
|
+
if (var->pos > 0)
|
|
819
|
+
baton->error = njsMessages::Get(errMissingMaxSizeByPos,
|
|
820
|
+
var->pos);
|
|
821
|
+
else baton->error = njsMessages::Get(errMissingMaxSizeByName,
|
|
822
|
+
var->name.c_str());
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// get max array size (for array binds)
|
|
829
|
+
if (!inExecuteMany) {
|
|
830
|
+
if (!baton->GetUnsignedIntFromJSON(bindUnit, "maxArraySize", 1,
|
|
831
|
+
&var->maxArraySize))
|
|
829
832
|
return false;
|
|
833
|
+
if (var->maxArraySize > 0)
|
|
834
|
+
var->isArray = true;
|
|
830
835
|
}
|
|
831
836
|
|
|
832
837
|
return true;
|
|
@@ -834,45 +839,316 @@ bool njsConnection::ProcessBindValue(Local<Value> value, njsVariable *var,
|
|
|
834
839
|
|
|
835
840
|
|
|
836
841
|
//-----------------------------------------------------------------------------
|
|
837
|
-
// njsConnection::
|
|
838
|
-
// Process
|
|
839
|
-
// position.
|
|
842
|
+
// njsConnection::ProcessExecuteManyBinds()
|
|
843
|
+
// Process binds passed through to ExecuteMany() call.
|
|
840
844
|
//-----------------------------------------------------------------------------
|
|
841
|
-
bool njsConnection::
|
|
842
|
-
|
|
845
|
+
bool njsConnection::ProcessExecuteManyBinds(Local<Array> binds,
|
|
846
|
+
Local<Object> options, njsBaton *baton)
|
|
843
847
|
{
|
|
844
848
|
Nan::HandleScope scope;
|
|
845
|
-
|
|
846
|
-
|
|
849
|
+
Local<Value> bindDefs, bindName, bindUnit;
|
|
850
|
+
Local<Array> bindNames;
|
|
851
|
+
bool scanRequired;
|
|
847
852
|
|
|
848
|
-
//
|
|
849
|
-
|
|
850
|
-
data->isNull = 0;
|
|
853
|
+
// all rows are bound at one time
|
|
854
|
+
baton->bindArraySize = binds->Length();
|
|
851
855
|
|
|
852
|
-
//
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
+
// determine if bind definitions have been specified
|
|
857
|
+
Local<String> key = Nan::New<v8::String>("bindDefs").ToLocalChecked();
|
|
858
|
+
if (!Nan::Get(options, key).ToLocal(&bindDefs))
|
|
859
|
+
return false;
|
|
860
|
+
scanRequired = bindDefs->IsUndefined();
|
|
856
861
|
|
|
857
|
-
//
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
if (utf8str.length() == 0)
|
|
864
|
-
data->isNull = 1;
|
|
865
|
-
else if (dpiVar_setFromBytes(var->dpiVarHandle, pos, *utf8str,
|
|
866
|
-
static_cast<uint32_t>( utf8str.length())) < 0) {
|
|
867
|
-
baton->GetDPIError();
|
|
868
|
-
return false;
|
|
869
|
-
}
|
|
870
|
-
}
|
|
862
|
+
// if no bind definitions are specified, the first row is used to determine
|
|
863
|
+
// the number of bind variables and types
|
|
864
|
+
if (scanRequired)
|
|
865
|
+
bindDefs = Nan::Get(binds, 0).ToLocalChecked();
|
|
866
|
+
if (!bindDefs->IsUndefined() && !bindDefs->IsArray())
|
|
867
|
+
bindNames = bindDefs.As<Object>()->GetOwnPropertyNames();
|
|
871
868
|
|
|
872
|
-
//
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
869
|
+
// initialize variables; if there are no variables, nothing further to do!
|
|
870
|
+
if (!InitBindVars(bindDefs.As<Object>(), bindNames, baton))
|
|
871
|
+
return false;
|
|
872
|
+
if (baton->numBindVars == 0)
|
|
873
|
+
return true;
|
|
874
|
+
|
|
875
|
+
// if no bind definitions are specified, scan the binds to determine type
|
|
876
|
+
// and size
|
|
877
|
+
if (scanRequired) {
|
|
878
|
+
if (!ScanExecuteManyBinds(binds, bindNames, baton))
|
|
879
|
+
return false;
|
|
880
|
+
|
|
881
|
+
// otherwise, use the bind definitions to determine type and size
|
|
882
|
+
} else {
|
|
883
|
+
bool byPosition = (baton->bindVars[0].pos > 0);
|
|
884
|
+
Local<Array> byPositionValues;
|
|
885
|
+
if (byPosition)
|
|
886
|
+
byPositionValues = bindDefs.As<Array>();
|
|
887
|
+
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
888
|
+
njsVariable *var = &baton->bindVars[i];
|
|
889
|
+
if (byPosition)
|
|
890
|
+
bindUnit = Nan::Get(byPositionValues, i).ToLocalChecked();
|
|
891
|
+
else {
|
|
892
|
+
bindName = Nan::Get(bindNames, i).ToLocalChecked();
|
|
893
|
+
if (!Nan::Get(bindDefs.As<Object>(),
|
|
894
|
+
bindName).ToLocal(&bindUnit))
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
if (!ScanExecuteBindUnit(bindUnit.As<Object>(), var, true, baton))
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// create the ODPI-C variables used to hold the data
|
|
903
|
+
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
904
|
+
if (!CreateVarBuffer(&baton->bindVars[i], baton))
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// populate the ODPI-C variables with the data from JavaScript binds
|
|
909
|
+
if (!TransferExecuteManyBinds(binds, bindNames, baton))
|
|
910
|
+
return false;
|
|
911
|
+
|
|
912
|
+
return true;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
//-----------------------------------------------------------------------------
|
|
917
|
+
// njsConnection::ScanExecuteManyBinds()
|
|
918
|
+
// Scan the binds passed through to ExecuteMany() and determine the bind
|
|
919
|
+
// type and maximum size (for strings/buffers).
|
|
920
|
+
//-----------------------------------------------------------------------------
|
|
921
|
+
bool njsConnection::ScanExecuteManyBinds(Local<Array> binds,
|
|
922
|
+
Local<Array> bindNames, njsBaton *baton)
|
|
923
|
+
{
|
|
924
|
+
njsDataType defaultBindType;
|
|
925
|
+
uint32_t defaultMaxSize;
|
|
926
|
+
bool byPosition;
|
|
927
|
+
Nan::HandleScope scope;
|
|
928
|
+
Local<Array> byPositionValues;
|
|
929
|
+
Local<Value> bindName, val;
|
|
930
|
+
Local<Object> row;
|
|
931
|
+
njsVariable *var;
|
|
932
|
+
|
|
933
|
+
byPosition = (baton->bindVars[0].pos > 0);
|
|
934
|
+
for (uint32_t i = 0; i < baton->bindArraySize; i++) {
|
|
935
|
+
row = Nan::Get(binds, i).ToLocalChecked().As<Object>();
|
|
936
|
+
|
|
937
|
+
// verify that all rows are by position (array) or by name (object)
|
|
938
|
+
if ((byPosition && !row->IsArray()) ||
|
|
939
|
+
(!byPosition && row->IsArray())) {
|
|
940
|
+
baton->error = njsMessages::Get(errMixedBind);
|
|
941
|
+
return false;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// scan each of the columns in the row and determine the bind type and
|
|
945
|
+
// maximum size of the input data
|
|
946
|
+
if (byPosition)
|
|
947
|
+
byPositionValues = row.As<Array>();
|
|
948
|
+
for (uint32_t j = 0; j < baton->numBindVars; j++) {
|
|
949
|
+
var = &baton->bindVars[j];
|
|
950
|
+
if (byPosition)
|
|
951
|
+
val = Nan::Get(byPositionValues, j).ToLocalChecked();
|
|
952
|
+
else {
|
|
953
|
+
bindName = Nan::Get(bindNames, j).ToLocalChecked();
|
|
954
|
+
if (!Nan::Get(row, bindName).ToLocal(&val))
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
if (val->IsUndefined() || val->IsNull())
|
|
958
|
+
continue;
|
|
959
|
+
if (!GetBindTypeAndSizeFromValue(var, val, &defaultBindType,
|
|
960
|
+
&defaultMaxSize, baton, true))
|
|
961
|
+
return false;
|
|
962
|
+
if (var->bindDataType == 0)
|
|
963
|
+
var->bindDataType = defaultBindType;
|
|
964
|
+
if (defaultMaxSize > var->maxSize)
|
|
965
|
+
var->maxSize = defaultMaxSize;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
//-----------------------------------------------------------------------------
|
|
975
|
+
// njsConnection::TransferExecuteManyBinds()
|
|
976
|
+
// Transfer the binds from JavaScript to the ODPI-C variable buffers already
|
|
977
|
+
// created.
|
|
978
|
+
//-----------------------------------------------------------------------------
|
|
979
|
+
bool njsConnection::TransferExecuteManyBinds(Local<Array> binds,
|
|
980
|
+
Local<Array> bindNames, njsBaton *baton)
|
|
981
|
+
{
|
|
982
|
+
bool byPosition;
|
|
983
|
+
Nan::HandleScope scope;
|
|
984
|
+
Local<Array> byPositionValues;
|
|
985
|
+
Local<Value> bindName, val;
|
|
986
|
+
Local<Object> row;
|
|
987
|
+
njsVariable *var;
|
|
988
|
+
|
|
989
|
+
// determine if we are binding by position or by name
|
|
990
|
+
byPosition = (baton->bindVars[0].pos > 0);
|
|
991
|
+
|
|
992
|
+
// process each row
|
|
993
|
+
for (uint32_t i = 0; i < baton->bindArraySize; i++) {
|
|
994
|
+
row = Nan::Get(binds, i).ToLocalChecked().As<Object>();
|
|
995
|
+
|
|
996
|
+
// verify that all rows are by position (array) or by name (object)
|
|
997
|
+
if ((byPosition && !row->IsArray()) ||
|
|
998
|
+
(!byPosition && row->IsArray())) {
|
|
999
|
+
baton->error = njsMessages::Get(errMixedBind);
|
|
1000
|
+
return false;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// process each column
|
|
1004
|
+
if (byPosition)
|
|
1005
|
+
byPositionValues = row.As<Array>();
|
|
1006
|
+
for (uint32_t j = 0; j < baton->numBindVars; j++) {
|
|
1007
|
+
var = &baton->bindVars[j];
|
|
1008
|
+
if (byPosition)
|
|
1009
|
+
val = Nan::Get(byPositionValues, j).ToLocalChecked();
|
|
1010
|
+
else {
|
|
1011
|
+
bindName = Nan::Get(bindNames, j).ToLocalChecked();
|
|
1012
|
+
if (!Nan::Get(row, bindName).ToLocal(&val))
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
if (!ProcessScalarBindValue(val, var, i, true, baton))
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
return true;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
//-----------------------------------------------------------------------------
|
|
1026
|
+
// njsConnection::InitBindVars()
|
|
1027
|
+
// Initialize bind variables using the given bind objectd/array as a
|
|
1028
|
+
// template. For binds performed by name, return the bind names as an array.
|
|
1029
|
+
//-----------------------------------------------------------------------------
|
|
1030
|
+
bool njsConnection::InitBindVars(Local<Object> bindObj,
|
|
1031
|
+
Local<Array> bindNames, njsBaton *baton)
|
|
1032
|
+
{
|
|
1033
|
+
bool byPosition = bindObj->IsArray();
|
|
1034
|
+
|
|
1035
|
+
// create bind variables (one for each element of the bind array or each
|
|
1036
|
+
// property of the bind object)
|
|
1037
|
+
if (bindObj->IsUndefined())
|
|
1038
|
+
baton->numBindVars = 0;
|
|
1039
|
+
else if (byPosition)
|
|
1040
|
+
baton->numBindVars = bindObj.As<Array>()->Length();
|
|
1041
|
+
else baton->numBindVars = bindNames->Length();
|
|
1042
|
+
baton->bindVars = new njsVariable[baton->numBindVars];
|
|
1043
|
+
|
|
1044
|
+
// initialize bind variables (set position or name)
|
|
1045
|
+
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
1046
|
+
njsVariable *var = &baton->bindVars[i];
|
|
1047
|
+
if (byPosition)
|
|
1048
|
+
var->pos = i + 1;
|
|
1049
|
+
else {
|
|
1050
|
+
Local<Value> temp = Nan::Get(bindNames, i).ToLocalChecked();
|
|
1051
|
+
Nan::Utf8String v8str(temp->ToString());
|
|
1052
|
+
std::string str = std::string(*v8str,
|
|
1053
|
+
static_cast<size_t>(v8str.length()));
|
|
1054
|
+
var->name = ":" + str;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
return true;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
//-----------------------------------------------------------------------------
|
|
1063
|
+
// njsConnection::ProcessBindValue()
|
|
1064
|
+
// Process bind value from JS and copy it to the variable that was created.
|
|
1065
|
+
//-----------------------------------------------------------------------------
|
|
1066
|
+
bool njsConnection::ProcessBindValue(Local<Value> value, njsVariable *var,
|
|
1067
|
+
njsBaton *baton)
|
|
1068
|
+
{
|
|
1069
|
+
// scalar values can be handled directly
|
|
1070
|
+
if (!var->isArray)
|
|
1071
|
+
return ProcessScalarBindValue(value, var, 0, false, baton);
|
|
1072
|
+
|
|
1073
|
+
// only strings and numbers are currently allowed
|
|
1074
|
+
if (var->varTypeNum != DPI_ORACLE_TYPE_VARCHAR &&
|
|
1075
|
+
var->varTypeNum != DPI_ORACLE_TYPE_NUMBER &&
|
|
1076
|
+
var->varTypeNum != DPI_ORACLE_TYPE_NATIVE_INT) {
|
|
1077
|
+
baton->error = njsMessages::Get(errInvalidTypeForArrayBind);
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/* Expecting an array value here */
|
|
1082
|
+
if ( !value->IsArray ()) {
|
|
1083
|
+
baton->error = njsMessages::Get ( errNonArrayProvided ) ;
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// set the number of actual elements in the variable
|
|
1088
|
+
Nan::HandleScope scope;
|
|
1089
|
+
Local<Array> arrayVal = Local<Array>::Cast(value);
|
|
1090
|
+
if (dpiVar_setNumElementsInArray(var->dpiVarHandle,
|
|
1091
|
+
arrayVal->Length()) < 0) {
|
|
1092
|
+
baton->GetDPIError();
|
|
1093
|
+
return false;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// process each element in the array
|
|
1097
|
+
for (uint32_t i = 0; i < arrayVal->Length(); i++) {
|
|
1098
|
+
Local<Value> elementValue = Nan::Get (arrayVal, i).ToLocalChecked ();
|
|
1099
|
+
if (!ProcessScalarBindValue(elementValue, var, i, false, baton))
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
//-----------------------------------------------------------------------------
|
|
1108
|
+
// njsConnection::ProcessScalarBindValue()
|
|
1109
|
+
// Process scalar bind value from JS and copy it to the variable at the given
|
|
1110
|
+
// position.
|
|
1111
|
+
//-----------------------------------------------------------------------------
|
|
1112
|
+
bool njsConnection::ProcessScalarBindValue(Local<Value> value,
|
|
1113
|
+
njsVariable *var, uint32_t pos, bool inExecuteMany, njsBaton *baton)
|
|
1114
|
+
{
|
|
1115
|
+
Nan::HandleScope scope;
|
|
1116
|
+
bool bindOk = false;
|
|
1117
|
+
dpiData *data;
|
|
1118
|
+
|
|
1119
|
+
// initialization
|
|
1120
|
+
data = &var->buffer.dpiVarData[pos];
|
|
1121
|
+
data->isNull = 0;
|
|
1122
|
+
|
|
1123
|
+
// nulls and undefined in JS are mapped to NULL in Oracle; no checks needed
|
|
1124
|
+
if (value->IsUndefined() || value->IsNull()) {
|
|
1125
|
+
data->isNull = 1;
|
|
1126
|
+
bindOk = true;
|
|
1127
|
+
|
|
1128
|
+
// value is a string, variable type should be a string
|
|
1129
|
+
} else if (value->IsString()) {
|
|
1130
|
+
bindOk = (var->varTypeNum == DPI_ORACLE_TYPE_VARCHAR ||
|
|
1131
|
+
var->varTypeNum == DPI_ORACLE_TYPE_CLOB);
|
|
1132
|
+
if (bindOk) {
|
|
1133
|
+
Nan::Utf8String utf8str(value);
|
|
1134
|
+
if (utf8str.length() == 0)
|
|
1135
|
+
data->isNull = 1;
|
|
1136
|
+
else if (inExecuteMany &&
|
|
1137
|
+
(uint32_t) utf8str.length() > var->maxSize) {
|
|
1138
|
+
baton->error = njsMessages::Get(errMaxSizeTooSmall,
|
|
1139
|
+
var->maxSize, utf8str.length(), pos);
|
|
1140
|
+
return false;
|
|
1141
|
+
} else if (dpiVar_setFromBytes(var->dpiVarHandle, pos, *utf8str,
|
|
1142
|
+
static_cast<uint32_t>( utf8str.length())) < 0) {
|
|
1143
|
+
baton->GetDPIError();
|
|
1144
|
+
return false;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// value is an integer
|
|
1149
|
+
} else if (value->IsInt32() || value->IsUint32()) {
|
|
1150
|
+
bindOk = (var->varTypeNum == DPI_ORACLE_TYPE_NUMBER);
|
|
1151
|
+
if (bindOk) {
|
|
876
1152
|
if (var->nativeTypeNum == DPI_NATIVE_TYPE_INT64) {
|
|
877
1153
|
if (value->IsInt32())
|
|
878
1154
|
data->value.asInt64 = Nan::To<int32_t>(value).FromJust();
|
|
@@ -905,8 +1181,12 @@ bool njsConnection::ProcessScalarBindValue(Local<Value> value,
|
|
|
905
1181
|
var->varTypeNum == DPI_ORACLE_TYPE_BLOB);
|
|
906
1182
|
if (bindOk) {
|
|
907
1183
|
Local<Object> obj = value->ToObject();
|
|
908
|
-
if (
|
|
909
|
-
|
|
1184
|
+
if (inExecuteMany && Buffer::Length(obj) > var->maxSize) {
|
|
1185
|
+
baton->error = njsMessages::Get(errMaxSizeTooSmall,
|
|
1186
|
+
var->maxSize, Buffer::Length(obj), pos);
|
|
1187
|
+
return false;
|
|
1188
|
+
} else if (dpiVar_setFromBytes(var->dpiVarHandle, pos,
|
|
1189
|
+
Buffer::Data(obj), (uint32_t) Buffer::Length(obj)) < 0) {
|
|
910
1190
|
baton->GetDPIError();
|
|
911
1191
|
return false;
|
|
912
1192
|
}
|
|
@@ -967,7 +1247,7 @@ bool njsConnection::ProcessScalarBindValue(Local<Value> value,
|
|
|
967
1247
|
// is supported at this point, 0 is returned.
|
|
968
1248
|
//-----------------------------------------------------------------------------
|
|
969
1249
|
bool njsConnection::GetBindTypeAndSizeFromValue(njsVariable *var,
|
|
970
|
-
Local<Value> value,
|
|
1250
|
+
Local<Value> value, njsDataType *bindType, uint32_t *maxSize,
|
|
971
1251
|
njsBaton *baton, bool scalarOnly)
|
|
972
1252
|
{
|
|
973
1253
|
if (value->IsUndefined() || value->IsNull()) {
|
|
@@ -975,7 +1255,7 @@ bool njsConnection::GetBindTypeAndSizeFromValue(njsVariable *var,
|
|
|
975
1255
|
*maxSize = 1;
|
|
976
1256
|
} else if (value->IsString()) {
|
|
977
1257
|
*bindType = NJS_DATATYPE_STR;
|
|
978
|
-
|
|
1258
|
+
Nan::Utf8String utf8str(value->ToString());
|
|
979
1259
|
*maxSize = static_cast<uint32_t>(utf8str.length());
|
|
980
1260
|
} else if (value->IsInt32() || value->IsUint32()) {
|
|
981
1261
|
*bindType = NJS_DATATYPE_INT;
|
|
@@ -987,7 +1267,8 @@ bool njsConnection::GetBindTypeAndSizeFromValue(njsVariable *var,
|
|
|
987
1267
|
Nan::HandleScope scope;
|
|
988
1268
|
Local<Array> arrayVal = Local<Array>::Cast(value);
|
|
989
1269
|
Local<Value> element;
|
|
990
|
-
|
|
1270
|
+
njsDataType elementBindType;
|
|
1271
|
+
uint32_t elementMaxSize;
|
|
991
1272
|
for (uint32_t i = 0; i < arrayVal->Length(); i++) {
|
|
992
1273
|
element = Nan::Get (arrayVal, i).ToLocalChecked ();
|
|
993
1274
|
if (element->IsUndefined() || element->IsNull())
|
|
@@ -1007,7 +1288,7 @@ bool njsConnection::GetBindTypeAndSizeFromValue(njsVariable *var,
|
|
|
1007
1288
|
Buffer::Length(value->ToObject()));
|
|
1008
1289
|
} else if (njsILob::HasInstance(value)) {
|
|
1009
1290
|
njsILob *lob = njsILob::GetInstance(value);
|
|
1010
|
-
*bindType =
|
|
1291
|
+
*bindType = lob->GetDataType();
|
|
1011
1292
|
}
|
|
1012
1293
|
} else {
|
|
1013
1294
|
baton->error= njsMessages::Get(errInvalidBindDataType, 2);
|
|
@@ -1018,24 +1299,16 @@ bool njsConnection::GetBindTypeAndSizeFromValue(njsVariable *var,
|
|
|
1018
1299
|
|
|
1019
1300
|
|
|
1020
1301
|
//-----------------------------------------------------------------------------
|
|
1021
|
-
// njsConnection::
|
|
1022
|
-
// Processing of options. If an error is detected,
|
|
1023
|
-
// populated and false is returned.
|
|
1302
|
+
// njsConnection::ProcessExecuteOptions()
|
|
1303
|
+
// Processing of options for connection.execute(). If an error is detected,
|
|
1304
|
+
// the baton error is populated and false is returned.
|
|
1024
1305
|
//-----------------------------------------------------------------------------
|
|
1025
|
-
bool njsConnection::
|
|
1026
|
-
|
|
1306
|
+
bool njsConnection::ProcessExecuteOptions(Local<Object> options,
|
|
1307
|
+
njsBaton *baton)
|
|
1027
1308
|
{
|
|
1028
1309
|
Nan::HandleScope scope;
|
|
1029
1310
|
|
|
1030
|
-
// an object is expected, not an array
|
|
1031
|
-
if (!args[static_cast<int>(index)]->IsObject() ||
|
|
1032
|
-
args[static_cast<int>(index)]->IsArray()) {
|
|
1033
|
-
baton->error = njsMessages::Get(errInvalidParameterType, index);
|
|
1034
|
-
return false;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
1311
|
// process the basic options
|
|
1038
|
-
Local<Object> options = args[static_cast<int>(index)]->ToObject();
|
|
1039
1312
|
if (!baton->GetUnsignedIntFromJSON(options, "maxRows", 2, &baton->maxRows))
|
|
1040
1313
|
return false;
|
|
1041
1314
|
if (!baton->GetPositiveIntFromJSON(options, "fetchArraySize", 2,
|
|
@@ -1059,33 +1332,22 @@ bool njsConnection::ProcessOptions(Nan::NAN_METHOD_ARGS_TYPE args,
|
|
|
1059
1332
|
|
|
1060
1333
|
// process the fetchAs specifications, if applicable
|
|
1061
1334
|
Local<Value> key = Nan::New<v8::String>("fetchInfo").ToLocalChecked();
|
|
1062
|
-
MaybeLocal<Value> mval = Nan::Get(options, key);
|
|
1063
1335
|
Local<Value> val;
|
|
1064
|
-
|
|
1065
|
-
if (!mval.ToLocal(&val))
|
|
1336
|
+
if (!Nan::Get(options, key).ToLocal(&val))
|
|
1066
1337
|
return false;
|
|
1067
|
-
|
|
1068
1338
|
if (!val->IsUndefined() && !val->IsNull()) {
|
|
1069
1339
|
Local<Object> jsFetchInfo = val->ToObject();
|
|
1070
1340
|
Local<Array> keys = jsFetchInfo->GetOwnPropertyNames();
|
|
1071
|
-
if (keys->Length() == 0) {
|
|
1072
|
-
baton->error = njsMessages::Get(errEmptyArrayForFetchAs, index);
|
|
1073
|
-
return false;
|
|
1074
|
-
}
|
|
1075
1341
|
baton->numFetchInfo = keys->Length();
|
|
1076
|
-
baton->
|
|
1342
|
+
if (baton->numFetchInfo > 0)
|
|
1343
|
+
baton->fetchInfo = new njsFetchInfo[baton->numFetchInfo];
|
|
1077
1344
|
for (uint32_t i = 0; i < baton->numFetchInfo; i++) {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
v8::String::Utf8Value utf8str(temp->ToString());
|
|
1345
|
+
Local<Value> temp = Nan::Get(keys, i).ToLocalChecked();
|
|
1346
|
+
Nan::Utf8String utf8str(temp->ToString());
|
|
1082
1347
|
baton->fetchInfo[i].name = std::string(*utf8str,
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
static_cast<size_t>(v8str.length()));
|
|
1087
|
-
Local<Object> colInfo = Nan::Get(jsFetchInfo,
|
|
1088
|
-
Nan::New(str).ToLocalChecked()).ToLocalChecked()->ToObject();
|
|
1348
|
+
static_cast<size_t>(utf8str.length()));
|
|
1349
|
+
Local<Object> colInfo =
|
|
1350
|
+
Nan::Get(jsFetchInfo, temp).ToLocalChecked()->ToObject();
|
|
1089
1351
|
uint32_t tempType = static_cast<uint32_t>(NJS_DATATYPE_UNKNOWN);
|
|
1090
1352
|
if (!baton->GetUnsignedIntFromJSON(colInfo, "type", 2, &tempType))
|
|
1091
1353
|
return false;
|
|
@@ -1106,6 +1368,96 @@ bool njsConnection::ProcessOptions(Nan::NAN_METHOD_ARGS_TYPE args,
|
|
|
1106
1368
|
}
|
|
1107
1369
|
|
|
1108
1370
|
|
|
1371
|
+
//-----------------------------------------------------------------------------
|
|
1372
|
+
// njsConnection::ProcessExecuteManyOptions()
|
|
1373
|
+
// Processing of options for connection.executeMany(). If an error is
|
|
1374
|
+
// detected, the baton error is populated and false is returned.
|
|
1375
|
+
//-----------------------------------------------------------------------------
|
|
1376
|
+
bool njsConnection::ProcessExecuteManyOptions(Local<Object> options,
|
|
1377
|
+
njsBaton *baton)
|
|
1378
|
+
{
|
|
1379
|
+
if (!baton->GetBoolFromJSON(options, "autoCommit", 2, &baton->autoCommit))
|
|
1380
|
+
return false;
|
|
1381
|
+
if (!baton->GetBoolFromJSON(options, "batchErrors", 2,
|
|
1382
|
+
&baton->batchErrors))
|
|
1383
|
+
return false;
|
|
1384
|
+
if (!baton->GetBoolFromJSON(options, "dmlRowCounts", 2,
|
|
1385
|
+
&baton->dmlRowCounts))
|
|
1386
|
+
return false;
|
|
1387
|
+
|
|
1388
|
+
return true;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
//-----------------------------------------------------------------------------
|
|
1393
|
+
// njsConnection::ProcessSubscriptionOptions()
|
|
1394
|
+
// Processing of options for connection.subscribe(). If an error is detected,
|
|
1395
|
+
// the baton error is populated and false is returned.
|
|
1396
|
+
//-----------------------------------------------------------------------------
|
|
1397
|
+
bool njsConnection::ProcessSubscriptionOptions(Local<Object> options,
|
|
1398
|
+
njsBaton *baton)
|
|
1399
|
+
{
|
|
1400
|
+
Nan::HandleScope scope;
|
|
1401
|
+
uint32_t temp;
|
|
1402
|
+
|
|
1403
|
+
// if subscription doesn't exist, get options for creating subscription
|
|
1404
|
+
if (!baton->dpiSubscrHandle) {
|
|
1405
|
+
temp = DPI_SUBSCR_NAMESPACE_DBCHANGE;
|
|
1406
|
+
if (!baton->GetUnsignedIntFromJSON(options, "namespace", 1,
|
|
1407
|
+
&temp))
|
|
1408
|
+
return false;
|
|
1409
|
+
baton->subscription->SetNamespace((dpiSubscrNamespace) temp);
|
|
1410
|
+
if (!baton->GetStringFromJSON(options, "ipAddress", 1,
|
|
1411
|
+
baton->ipAddress))
|
|
1412
|
+
return false;
|
|
1413
|
+
if (!baton->GetUnsignedIntFromJSON(options, "port", 1,
|
|
1414
|
+
&baton->portNumber))
|
|
1415
|
+
return false;
|
|
1416
|
+
if (!baton->GetUnsignedIntFromJSON(options, "timeout", 1,
|
|
1417
|
+
&baton->timeout))
|
|
1418
|
+
return false;
|
|
1419
|
+
if (!baton->GetUnsignedIntFromJSON(options, "operations", 1,
|
|
1420
|
+
&baton->operations))
|
|
1421
|
+
return false;
|
|
1422
|
+
if (!baton->GetUnsignedIntFromJSON(options, "qos", 1, &baton->qos))
|
|
1423
|
+
return false;
|
|
1424
|
+
if (!baton->GetUnsignedIntFromJSON(options, "groupingClass", 1,
|
|
1425
|
+
&baton->subscrGroupingClass))
|
|
1426
|
+
return false;
|
|
1427
|
+
if (!baton->GetUnsignedIntFromJSON(options, "groupingValue", 1,
|
|
1428
|
+
&baton->subscrGroupingValue))
|
|
1429
|
+
return false;
|
|
1430
|
+
if (!baton->GetUnsignedIntFromJSON(options, "groupingType", 1,
|
|
1431
|
+
&baton->subscrGroupingType))
|
|
1432
|
+
return false;
|
|
1433
|
+
Local<Function> callback;
|
|
1434
|
+
if (!baton->GetFunctionFromJSON(options, "callback", 1, &callback))
|
|
1435
|
+
return false;
|
|
1436
|
+
if (callback.IsEmpty())
|
|
1437
|
+
baton->error = njsMessages::Get(errMissingSubscrCallback);
|
|
1438
|
+
else baton->subscription->SetCallback(callback);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// get options that are used for registering queries
|
|
1442
|
+
if (baton->subscription->GetNamespace() == DPI_SUBSCR_NAMESPACE_DBCHANGE) {
|
|
1443
|
+
if (!baton->GetStringFromJSON(options, "sql", 1, baton->sql))
|
|
1444
|
+
return false;
|
|
1445
|
+
if (baton->sql.empty()) {
|
|
1446
|
+
baton->error = njsMessages::Get(errMissingSubscrSql);
|
|
1447
|
+
return false;
|
|
1448
|
+
}
|
|
1449
|
+
Local<Value> binds;
|
|
1450
|
+
Local<String> key = Nan::New("binds").ToLocalChecked();
|
|
1451
|
+
if (!Nan::Get(options, key).ToLocal(&binds))
|
|
1452
|
+
return false;
|
|
1453
|
+
if (!ProcessExecuteBinds(binds.As<Object>(), baton))
|
|
1454
|
+
return false;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
return true;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
|
|
1109
1461
|
//-----------------------------------------------------------------------------
|
|
1110
1462
|
// njsConnection::GetScalarValueFromVar()
|
|
1111
1463
|
// Get the value from the DPI variable of the specified type at the specified
|
|
@@ -1113,14 +1465,14 @@ bool njsConnection::ProcessOptions(Nan::NAN_METHOD_ARGS_TYPE args,
|
|
|
1113
1465
|
// baton error has been populated with the error that was received from DPI.
|
|
1114
1466
|
//-----------------------------------------------------------------------------
|
|
1115
1467
|
bool njsConnection::GetScalarValueFromVar(njsBaton *baton, njsVariable *var,
|
|
1116
|
-
uint32_t pos, Local<Value> &value)
|
|
1468
|
+
njsVariableBuffer *buffer, uint32_t pos, Local<Value> &value)
|
|
1117
1469
|
{
|
|
1118
1470
|
Nan::EscapableHandleScope scope;
|
|
1119
1471
|
Local<Value> temp;
|
|
1120
1472
|
|
|
1121
1473
|
// LOBs make use of the njsProtoILob objects created in the worker thread
|
|
1122
|
-
if (
|
|
1123
|
-
njsProtoILob *protoLob = &
|
|
1474
|
+
if (buffer->lobs) {
|
|
1475
|
+
njsProtoILob *protoLob = &buffer->lobs[pos];
|
|
1124
1476
|
if (!protoLob->dpiLobHandle)
|
|
1125
1477
|
temp = Nan::Null();
|
|
1126
1478
|
else {
|
|
@@ -1136,9 +1488,9 @@ bool njsConnection::GetScalarValueFromVar(njsBaton *baton, njsVariable *var,
|
|
|
1136
1488
|
return true;
|
|
1137
1489
|
}
|
|
1138
1490
|
|
|
1139
|
-
// get the value from
|
|
1491
|
+
// get the value from ODPI-C
|
|
1140
1492
|
uint32_t bufferRowIndex = baton->bufferRowIndex + pos;
|
|
1141
|
-
dpiData *data = &
|
|
1493
|
+
dpiData *data = &buffer->dpiVarData[bufferRowIndex];
|
|
1142
1494
|
|
|
1143
1495
|
// transform it to a JS value
|
|
1144
1496
|
if (data->isNull) {
|
|
@@ -1147,7 +1499,7 @@ bool njsConnection::GetScalarValueFromVar(njsBaton *baton, njsVariable *var,
|
|
|
1147
1499
|
}
|
|
1148
1500
|
switch (var->nativeTypeNum) {
|
|
1149
1501
|
case DPI_NATIVE_TYPE_INT64:
|
|
1150
|
-
temp = Nan::New<Number>(data->value.asInt64);
|
|
1502
|
+
temp = Nan::New<Number>((double) data->value.asInt64);
|
|
1151
1503
|
break;
|
|
1152
1504
|
case DPI_NATIVE_TYPE_FLOAT:
|
|
1153
1505
|
temp = Nan::New<Number>(data->value.asFloat);
|
|
@@ -1198,39 +1550,21 @@ bool njsConnection::GetScalarValueFromVar(njsBaton *baton, njsVariable *var,
|
|
|
1198
1550
|
|
|
1199
1551
|
|
|
1200
1552
|
//-----------------------------------------------------------------------------
|
|
1201
|
-
// njsConnection::
|
|
1202
|
-
// Get the value from the DPI variable of the specified type.
|
|
1553
|
+
// njsConnection::GetArrayValueFromVar()
|
|
1554
|
+
// Get the value from the DPI variable of the specified type as an array.
|
|
1203
1555
|
//-----------------------------------------------------------------------------
|
|
1204
|
-
bool njsConnection::
|
|
1205
|
-
Local<Value> &value)
|
|
1556
|
+
bool njsConnection::GetArrayValueFromVar(njsBaton *baton, njsVariable *var,
|
|
1557
|
+
uint32_t pos, Local<Value> &value)
|
|
1206
1558
|
{
|
|
1207
|
-
|
|
1208
|
-
if (!var->isArray && !baton->isReturning)
|
|
1209
|
-
return GetScalarValueFromVar(baton, var, 0, value);
|
|
1210
|
-
|
|
1211
|
-
// determine number of elements in output array
|
|
1212
|
-
uint32_t numElements;
|
|
1213
|
-
if (baton->isReturning) {
|
|
1214
|
-
if (dpiVar_getData(var->dpiVarHandle, &var->maxArraySize,
|
|
1215
|
-
&var->dpiVarData) < 0) {
|
|
1216
|
-
baton->GetDPIError();
|
|
1217
|
-
return false;
|
|
1218
|
-
}
|
|
1219
|
-
numElements = (uint32_t) baton->rowsAffected;
|
|
1220
|
-
} else {
|
|
1221
|
-
if (dpiVar_getNumElementsInArray(var->dpiVarHandle,
|
|
1222
|
-
&numElements) < 0) {
|
|
1223
|
-
baton->GetDPIError();
|
|
1224
|
-
return false;
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1559
|
+
njsVariableBuffer *buffer = &var->buffer;
|
|
1227
1560
|
|
|
1228
|
-
|
|
1561
|
+
if (var->dmlReturningBuffers)
|
|
1562
|
+
buffer = &var->dmlReturningBuffers[pos];
|
|
1229
1563
|
Nan::EscapableHandleScope scope;
|
|
1230
|
-
Local<Array> arrayVal = Nan::New<Array>(numElements);
|
|
1231
|
-
for (uint32_t i = 0; i < numElements; i++) {
|
|
1564
|
+
Local<Array> arrayVal = Nan::New<Array>(buffer->numElements);
|
|
1565
|
+
for (uint32_t i = 0; i < buffer->numElements; i++) {
|
|
1232
1566
|
Local<Value> elementValue;
|
|
1233
|
-
if (!GetScalarValueFromVar(baton, var, i, elementValue))
|
|
1567
|
+
if (!GetScalarValueFromVar(baton, var, buffer, i, elementValue))
|
|
1234
1568
|
return false;
|
|
1235
1569
|
Nan::Set(arrayVal, i, elementValue);
|
|
1236
1570
|
}
|
|
@@ -1240,54 +1574,81 @@ bool njsConnection::GetValueFromVar(njsBaton *baton, njsVariable *var,
|
|
|
1240
1574
|
|
|
1241
1575
|
|
|
1242
1576
|
//-----------------------------------------------------------------------------
|
|
1243
|
-
// njsConnection::
|
|
1577
|
+
// njsConnection::GetExecuteOutBinds()
|
|
1578
|
+
// Get the out binds as an object/array.
|
|
1579
|
+
//-----------------------------------------------------------------------------
|
|
1580
|
+
bool njsConnection::GetExecuteOutBinds(Local<Value> &outBinds, njsBaton *baton)
|
|
1581
|
+
{
|
|
1582
|
+
uint32_t numOutBinds = baton->GetNumOutBinds();
|
|
1583
|
+
|
|
1584
|
+
if (numOutBinds > 0)
|
|
1585
|
+
return GetOutBinds(outBinds, numOutBinds, 0, baton);
|
|
1586
|
+
return true;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
|
|
1590
|
+
//-----------------------------------------------------------------------------
|
|
1591
|
+
// njsConnection::GetExecuteManyOutBinds()
|
|
1244
1592
|
// Get the out binds as an object/array.
|
|
1245
1593
|
//-----------------------------------------------------------------------------
|
|
1246
|
-
Local<Value>
|
|
1594
|
+
bool njsConnection::GetExecuteManyOutBinds(Local<Value> &outBinds,
|
|
1595
|
+
uint32_t numOutBinds, njsBaton *baton)
|
|
1247
1596
|
{
|
|
1248
1597
|
Nan::EscapableHandleScope scope;
|
|
1249
|
-
|
|
1598
|
+
Local<Array> rows = Nan::New<Array>(baton->bindArraySize);
|
|
1599
|
+
Local<Value> row;
|
|
1250
1600
|
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
numOutBinds++;
|
|
1601
|
+
for (uint32_t i = 0; i < baton->bindArraySize; i++) {
|
|
1602
|
+
if (!GetOutBinds(row, numOutBinds, i, baton))
|
|
1603
|
+
return false;
|
|
1604
|
+
Nan::Set(rows, i, row);
|
|
1256
1605
|
}
|
|
1606
|
+
outBinds = scope.Escape(rows);
|
|
1607
|
+
return true;
|
|
1608
|
+
}
|
|
1257
1609
|
|
|
1258
|
-
// for 0 we return undefined
|
|
1259
|
-
if (numOutBinds == 0)
|
|
1260
|
-
return scope.Escape(Nan::Undefined());
|
|
1261
1610
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1611
|
+
//-----------------------------------------------------------------------------
|
|
1612
|
+
// njsConnection::GetOutBinds()
|
|
1613
|
+
// Get the out binds as an object/array.
|
|
1614
|
+
//-----------------------------------------------------------------------------
|
|
1615
|
+
bool njsConnection::GetOutBinds(Local<Value> &outBinds, uint32_t numOutBinds,
|
|
1616
|
+
uint32_t pos, njsBaton *baton)
|
|
1617
|
+
{
|
|
1618
|
+
Nan::EscapableHandleScope scope;
|
|
1264
1619
|
Local<Array> bindArray;
|
|
1265
1620
|
Local<Object> bindObj;
|
|
1266
1621
|
Local<Value> val;
|
|
1622
|
+
bool bindByPos, ok;
|
|
1623
|
+
njsVariable *var;
|
|
1624
|
+
|
|
1625
|
+
bindByPos = baton->bindVars[0].name.empty();
|
|
1267
1626
|
if (bindByPos)
|
|
1268
1627
|
bindArray = Nan::New<Array>(numOutBinds);
|
|
1269
1628
|
else bindObj = Nan::New<Object>();
|
|
1270
|
-
njsVariable *var;
|
|
1271
1629
|
uint32_t arrayPos = 0;
|
|
1272
1630
|
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
1273
1631
|
var = &baton->bindVars[i];
|
|
1274
1632
|
if (var->bindDir == NJS_BIND_IN)
|
|
1275
1633
|
continue;
|
|
1276
|
-
if (
|
|
1277
|
-
|
|
1634
|
+
if (var->isArray || baton->isReturning)
|
|
1635
|
+
ok = GetArrayValueFromVar(baton, var, pos, val);
|
|
1636
|
+
else ok = GetScalarValueFromVar(baton, var, &var->buffer, pos, val);
|
|
1637
|
+
if (!ok)
|
|
1638
|
+
return false;
|
|
1278
1639
|
if (bindByPos)
|
|
1279
|
-
Nan::Set(bindArray, arrayPos
|
|
1640
|
+
Nan::Set(bindArray, arrayPos++, val);
|
|
1280
1641
|
else {
|
|
1281
1642
|
Local<String> key = Nan::New<String>(var->name.c_str() + 1,
|
|
1282
1643
|
(int) var->name.length() - 1).ToLocalChecked();
|
|
1283
1644
|
Nan::Set(bindObj, key, val);
|
|
1284
1645
|
}
|
|
1285
|
-
arrayPos++;
|
|
1286
1646
|
}
|
|
1287
|
-
|
|
1288
1647
|
if (bindByPos)
|
|
1289
|
-
|
|
1290
|
-
|
|
1648
|
+
outBinds = scope.Escape(bindArray);
|
|
1649
|
+
else outBinds = scope.Escape(bindObj);
|
|
1650
|
+
|
|
1651
|
+
return true;
|
|
1291
1652
|
}
|
|
1292
1653
|
|
|
1293
1654
|
|
|
@@ -1309,9 +1670,9 @@ void njsConnection::SetTextAttribute(Nan::NAN_SETTER_ARGS_TYPE args,
|
|
|
1309
1670
|
Nan::ThrowError(errMsg.c_str());
|
|
1310
1671
|
return;
|
|
1311
1672
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1673
|
+
Nan::Utf8String utfstr(value->ToString());
|
|
1674
|
+
uint32_t valueLength = static_cast<uint32_t>(utfstr.length());
|
|
1675
|
+
if ((*setter)(connection->dpiConnHandle, *utfstr, valueLength) < 0)
|
|
1315
1676
|
njsOracledb::ThrowDPIError();
|
|
1316
1677
|
}
|
|
1317
1678
|
|
|
@@ -1370,9 +1731,9 @@ NAN_METHOD(njsConnection::Execute)
|
|
|
1370
1731
|
baton->extendedMetaData = oracledb->getExtendedMetaData();
|
|
1371
1732
|
}
|
|
1372
1733
|
if (ok)
|
|
1373
|
-
ok =
|
|
1734
|
+
ok = ProcessExecuteBinds(info[1].As<Object>(), baton);
|
|
1374
1735
|
if (ok)
|
|
1375
|
-
|
|
1736
|
+
ProcessExecuteOptions(info[2].As<Object>(), baton);
|
|
1376
1737
|
baton->CheckJSException(&tryCatch);
|
|
1377
1738
|
baton->QueueWork("Execute", Async_Execute, Async_AfterExecute, 2);
|
|
1378
1739
|
}
|
|
@@ -1454,9 +1815,10 @@ void njsConnection::Async_AfterExecute(njsBaton *baton, Local<Value> argv[])
|
|
|
1454
1815
|
resultSet);
|
|
1455
1816
|
|
|
1456
1817
|
} else {
|
|
1457
|
-
Nan::
|
|
1458
|
-
|
|
1459
|
-
|
|
1818
|
+
Local<Value> outBinds = Nan::Undefined();
|
|
1819
|
+
GetExecuteOutBinds(outBinds, baton);
|
|
1820
|
+
Local<String> key = Nan::New<v8::String>("outBinds").ToLocalChecked();
|
|
1821
|
+
Nan::DefineOwnProperty(result, key, outBinds);
|
|
1460
1822
|
if (baton->isPLSQL)
|
|
1461
1823
|
Nan::Set(result,
|
|
1462
1824
|
Nan::New<v8::String>("rowsAffected").ToLocalChecked(),
|
|
@@ -1476,9 +1838,319 @@ void njsConnection::Async_AfterExecute(njsBaton *baton, Local<Value> argv[])
|
|
|
1476
1838
|
|
|
1477
1839
|
|
|
1478
1840
|
//-----------------------------------------------------------------------------
|
|
1479
|
-
// njsConnection::
|
|
1480
|
-
//
|
|
1481
|
-
//
|
|
1841
|
+
// njsConnection::ExecuteMany()
|
|
1842
|
+
// Executes a statement on the connection multiple times, once for each row
|
|
1843
|
+
// of data that is passed.
|
|
1844
|
+
//
|
|
1845
|
+
// PARAMETERS
|
|
1846
|
+
// - SQL Statement
|
|
1847
|
+
// - Array of Binds
|
|
1848
|
+
// - Options Object (Optional)
|
|
1849
|
+
// - JS callback which will receive (error, result)
|
|
1850
|
+
//-----------------------------------------------------------------------------
|
|
1851
|
+
NAN_METHOD(njsConnection::ExecuteMany)
|
|
1852
|
+
{
|
|
1853
|
+
njsConnection *connection;
|
|
1854
|
+
njsOracledb *oracledb;
|
|
1855
|
+
std::string sql;
|
|
1856
|
+
njsBaton *baton;
|
|
1857
|
+
|
|
1858
|
+
connection = (njsConnection*) ValidateArgs(info, 4, 4);
|
|
1859
|
+
if (!connection)
|
|
1860
|
+
return;
|
|
1861
|
+
if (!connection->GetStringArg(info, 0, sql))
|
|
1862
|
+
return;
|
|
1863
|
+
baton = connection->CreateBaton(info);
|
|
1864
|
+
if (!baton)
|
|
1865
|
+
return;
|
|
1866
|
+
Nan::TryCatch tryCatch;
|
|
1867
|
+
bool ok = baton->error.empty();
|
|
1868
|
+
if (ok) {
|
|
1869
|
+
baton->sql = sql;
|
|
1870
|
+
baton->SetDPIConnHandle(connection->dpiConnHandle);
|
|
1871
|
+
baton->jsOracledb.Reset(connection->jsOracledb);
|
|
1872
|
+
oracledb = baton->GetOracledb();
|
|
1873
|
+
baton->autoCommit = oracledb->getAutoCommit();
|
|
1874
|
+
}
|
|
1875
|
+
if (ok)
|
|
1876
|
+
ok = ProcessExecuteManyOptions(info[2].As<Object>(), baton);
|
|
1877
|
+
if (ok)
|
|
1878
|
+
ProcessExecuteManyBinds(info[1].As<Array>(), info[2].As<Object>(),
|
|
1879
|
+
baton);
|
|
1880
|
+
baton->CheckJSException(&tryCatch);
|
|
1881
|
+
baton->QueueWork("ExecuteMany", Async_ExecuteMany,
|
|
1882
|
+
Async_AfterExecuteMany, 2);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
|
|
1886
|
+
//-----------------------------------------------------------------------------
|
|
1887
|
+
// njsConnection::Async_ExecuteMany()
|
|
1888
|
+
// Worker function for njsConnection::ExecuteMany() method.
|
|
1889
|
+
//-----------------------------------------------------------------------------
|
|
1890
|
+
void njsConnection::Async_ExecuteMany(njsBaton *baton)
|
|
1891
|
+
{
|
|
1892
|
+
uint32_t mode;
|
|
1893
|
+
|
|
1894
|
+
// prepare statement and perform any binds that are needed
|
|
1895
|
+
if (!PrepareAndBind(baton))
|
|
1896
|
+
return;
|
|
1897
|
+
|
|
1898
|
+
// execute statement
|
|
1899
|
+
mode = (baton->autoCommit) ? DPI_MODE_EXEC_COMMIT_ON_SUCCESS :
|
|
1900
|
+
DPI_MODE_EXEC_DEFAULT;
|
|
1901
|
+
if (baton->batchErrors)
|
|
1902
|
+
mode |= DPI_MODE_EXEC_BATCH_ERRORS;
|
|
1903
|
+
if (baton->dmlRowCounts)
|
|
1904
|
+
mode |= DPI_MODE_EXEC_ARRAY_DML_ROWCOUNTS;
|
|
1905
|
+
if (dpiStmt_executeMany(baton->dpiStmtHandle, (dpiExecMode) mode,
|
|
1906
|
+
baton->bindArraySize) < 0) {
|
|
1907
|
+
baton->GetDPIError();
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
// process any LOBS for out binds, as needed
|
|
1912
|
+
if (dpiStmt_getRowCount(baton->dpiStmtHandle,
|
|
1913
|
+
&baton->rowsAffected) < 0) {
|
|
1914
|
+
baton->GetDPIError();
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
baton->bufferRowIndex = 0;
|
|
1918
|
+
if (!ProcessVars(baton, baton->bindVars, baton->numBindVars,
|
|
1919
|
+
baton->bindArraySize))
|
|
1920
|
+
return;
|
|
1921
|
+
|
|
1922
|
+
// get DML row counts if option was enabled
|
|
1923
|
+
if (baton->dmlRowCounts) {
|
|
1924
|
+
if (dpiStmt_getRowCounts(baton->dpiStmtHandle, &baton->numRowCounts,
|
|
1925
|
+
&baton->rowCounts) < 0) {
|
|
1926
|
+
baton->GetDPIError();
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
// get batch errors, if option was enabled
|
|
1932
|
+
if (baton->batchErrors) {
|
|
1933
|
+
if (dpiStmt_getBatchErrorCount(baton->dpiStmtHandle,
|
|
1934
|
+
&baton->numBatchErrorInfos) < 0) {
|
|
1935
|
+
baton->GetDPIError();
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1938
|
+
if (baton->numBatchErrorInfos > 0) {
|
|
1939
|
+
baton->batchErrorInfos =
|
|
1940
|
+
new dpiErrorInfo[baton->numBatchErrorInfos];
|
|
1941
|
+
if (dpiStmt_getBatchErrors(baton->dpiStmtHandle,
|
|
1942
|
+
baton->numBatchErrorInfos, baton->batchErrorInfos) < 0) {
|
|
1943
|
+
baton->GetDPIError();
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
|
|
1952
|
+
//-----------------------------------------------------------------------------
|
|
1953
|
+
// njsConnection::Async_AfterExecuteMany()
|
|
1954
|
+
// Returns result to JS by invoking JS callback.
|
|
1955
|
+
//-----------------------------------------------------------------------------
|
|
1956
|
+
void njsConnection::Async_AfterExecuteMany(njsBaton *baton,
|
|
1957
|
+
Local<Value> argv[])
|
|
1958
|
+
{
|
|
1959
|
+
Nan::EscapableHandleScope scope;
|
|
1960
|
+
Local<Object> result = Nan::New<v8::Object>();
|
|
1961
|
+
|
|
1962
|
+
// get out binds
|
|
1963
|
+
uint32_t numOutBinds = baton->GetNumOutBinds();
|
|
1964
|
+
Local<Value> outBinds;
|
|
1965
|
+
if (numOutBinds > 0) {
|
|
1966
|
+
if (GetExecuteManyOutBinds(outBinds, numOutBinds, baton)) {
|
|
1967
|
+
Local<String> key = Nan::New<String>("outBinds").ToLocalChecked();
|
|
1968
|
+
Nan::DefineOwnProperty(result, key, outBinds, v8::ReadOnly);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// get total number of rows affected
|
|
1973
|
+
if (!baton->isPLSQL)
|
|
1974
|
+
Nan::DefineOwnProperty(result,
|
|
1975
|
+
Nan::New<v8::String>("rowsAffected").ToLocalChecked(),
|
|
1976
|
+
Nan::New<v8::Integer>( (unsigned int) baton->rowsAffected),
|
|
1977
|
+
v8::ReadOnly);
|
|
1978
|
+
|
|
1979
|
+
// get DML row counts if option was enabled
|
|
1980
|
+
if (baton->dmlRowCounts && baton->numRowCounts > 0) {
|
|
1981
|
+
Local<Array> rowCountsObj = Nan::New<Array>(baton->numRowCounts);
|
|
1982
|
+
for (uint32_t i = 0; i < baton->numRowCounts; i++) {
|
|
1983
|
+
Local<Value> rowCountObj =
|
|
1984
|
+
Nan::New<Integer>((unsigned int) baton->rowCounts[i]);
|
|
1985
|
+
Nan::Set(rowCountsObj, i, rowCountObj);
|
|
1986
|
+
}
|
|
1987
|
+
Nan::DefineOwnProperty(result,
|
|
1988
|
+
Nan::New<v8::String>("dmlRowCounts").ToLocalChecked(),
|
|
1989
|
+
rowCountsObj, v8::ReadOnly);
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
// get batch errors, if option was enabled
|
|
1993
|
+
if (baton->batchErrors && baton->numBatchErrorInfos > 0) {
|
|
1994
|
+
Local<Array> batchErrorsObj =
|
|
1995
|
+
Nan::New<Array>(baton->numBatchErrorInfos);
|
|
1996
|
+
for (uint32_t i = 0; i < baton->numBatchErrorInfos; i++) {
|
|
1997
|
+
dpiErrorInfo *info = &baton->batchErrorInfos[i];
|
|
1998
|
+
std::string errorStr =
|
|
1999
|
+
std::string(info->message, info->messageLength);
|
|
2000
|
+
Local<String> errorStrObj =
|
|
2001
|
+
Nan::New<String>(errorStr).ToLocalChecked();
|
|
2002
|
+
Local<Object> errorObj =
|
|
2003
|
+
v8::Exception::Error(errorStrObj).As<Object>();
|
|
2004
|
+
Nan::Set(errorObj,
|
|
2005
|
+
Nan::New<v8::String>("errorNum").ToLocalChecked(),
|
|
2006
|
+
Nan::New<v8::Number>(info->code));
|
|
2007
|
+
Nan::Set(errorObj,
|
|
2008
|
+
Nan::New<v8::String>("offset").ToLocalChecked(),
|
|
2009
|
+
Nan::New<v8::Number>(info->offset));
|
|
2010
|
+
Nan::Set(batchErrorsObj, i, errorObj);
|
|
2011
|
+
}
|
|
2012
|
+
Nan::DefineOwnProperty(result,
|
|
2013
|
+
Nan::New<v8::String>("batchErrors").ToLocalChecked(),
|
|
2014
|
+
batchErrorsObj, v8::ReadOnly);
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
argv[1] = scope.Escape(result);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
|
|
2021
|
+
//-----------------------------------------------------------------------------
|
|
2022
|
+
// njsConnection::GetStatementInfo()
|
|
2023
|
+
// Parses a statement on the connection and returns information about the
|
|
2024
|
+
// statement.
|
|
2025
|
+
//
|
|
2026
|
+
// PARAMETERS
|
|
2027
|
+
// - SQL Statement
|
|
2028
|
+
// - JS callback which will receive (error, result)
|
|
2029
|
+
//-----------------------------------------------------------------------------
|
|
2030
|
+
NAN_METHOD(njsConnection::GetStatementInfo)
|
|
2031
|
+
{
|
|
2032
|
+
njsConnection *connection;
|
|
2033
|
+
std::string sql;
|
|
2034
|
+
njsBaton *baton;
|
|
2035
|
+
|
|
2036
|
+
connection = (njsConnection*) ValidateArgs(info, 2, 2);
|
|
2037
|
+
if (!connection)
|
|
2038
|
+
return;
|
|
2039
|
+
if (!connection->GetStringArg(info, 0, sql))
|
|
2040
|
+
return;
|
|
2041
|
+
baton = connection->CreateBaton(info);
|
|
2042
|
+
if (!baton)
|
|
2043
|
+
return;
|
|
2044
|
+
if (baton->error.empty()) {
|
|
2045
|
+
baton->sql = sql;
|
|
2046
|
+
baton->extendedMetaData = true;
|
|
2047
|
+
baton->SetDPIConnHandle(connection->dpiConnHandle);
|
|
2048
|
+
baton->jsOracledb.Reset(connection->jsOracledb);
|
|
2049
|
+
}
|
|
2050
|
+
baton->QueueWork("GetStatementInfo", Async_GetStatementInfo,
|
|
2051
|
+
Async_AfterGetStatementInfo, 2);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
|
|
2055
|
+
//-----------------------------------------------------------------------------
|
|
2056
|
+
// njsConnection::Async_GetStatementInfo()
|
|
2057
|
+
// Worker function for njsConnection::GetStatementInfo() method.
|
|
2058
|
+
//-----------------------------------------------------------------------------
|
|
2059
|
+
void njsConnection::Async_GetStatementInfo(njsBaton *baton)
|
|
2060
|
+
{
|
|
2061
|
+
dpiExecMode mode;
|
|
2062
|
+
|
|
2063
|
+
// prepare DPI statement for use
|
|
2064
|
+
if (dpiConn_prepareStmt(baton->dpiConnHandle, 0, baton->sql.c_str(),
|
|
2065
|
+
(uint32_t) baton->sql.length(), NULL, 0,
|
|
2066
|
+
&baton->dpiStmtHandle) < 0) {
|
|
2067
|
+
baton->GetDPIError();
|
|
2068
|
+
return;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
// parse the statement
|
|
2072
|
+
if (dpiStmt_getInfo(baton->dpiStmtHandle, &baton->stmtInfo) < 0) {
|
|
2073
|
+
baton->GetDPIError();
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
if (!baton->stmtInfo.isDDL) {
|
|
2077
|
+
if (baton->stmtInfo.isQuery)
|
|
2078
|
+
mode = DPI_MODE_EXEC_DESCRIBE_ONLY;
|
|
2079
|
+
else mode = DPI_MODE_EXEC_PARSE_ONLY;
|
|
2080
|
+
if (dpiStmt_execute(baton->dpiStmtHandle, mode,
|
|
2081
|
+
&baton->numQueryVars) < 0) {
|
|
2082
|
+
baton->GetDPIError();
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
// for queries, process query variables to get metadata
|
|
2088
|
+
if (baton->numQueryVars > 0) {
|
|
2089
|
+
baton->queryVars = new njsVariable[baton->numQueryVars];
|
|
2090
|
+
if (!ProcessQueryVars(baton, baton->dpiStmtHandle, baton->queryVars,
|
|
2091
|
+
baton->numQueryVars))
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
|
|
2097
|
+
//-----------------------------------------------------------------------------
|
|
2098
|
+
// njsConnection::Async_AfterGetStatementInfo()
|
|
2099
|
+
// Returns result to JS by invoking JS callback.
|
|
2100
|
+
//-----------------------------------------------------------------------------
|
|
2101
|
+
void njsConnection::Async_AfterGetStatementInfo(njsBaton *baton,
|
|
2102
|
+
Local<Value> argv[])
|
|
2103
|
+
{
|
|
2104
|
+
Nan::EscapableHandleScope scope;
|
|
2105
|
+
Local<Object> result = Nan::New<v8::Object>();
|
|
2106
|
+
Local<Object> callingObj, rows;
|
|
2107
|
+
Local<Function> callback;
|
|
2108
|
+
uint32_t numBinds;
|
|
2109
|
+
|
|
2110
|
+
// add metadata (queries only)
|
|
2111
|
+
if (baton->queryVars) {
|
|
2112
|
+
Nan::Set(result, Nan::New<v8::String>("metaData").ToLocalChecked(),
|
|
2113
|
+
GetMetaData(baton->queryVars, baton->numQueryVars,
|
|
2114
|
+
baton->extendedMetaData));
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
// add list of bind variable names
|
|
2118
|
+
if (dpiStmt_getBindCount(baton->dpiStmtHandle, &numBinds) < 0) {
|
|
2119
|
+
baton->GetDPIError();
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
uint32_t *bindNameLengths = new uint32_t[numBinds];
|
|
2123
|
+
const char **bindNames = new const char *[numBinds];
|
|
2124
|
+
if (dpiStmt_getBindNames(baton->dpiStmtHandle, &numBinds, bindNames,
|
|
2125
|
+
bindNameLengths) < 0) {
|
|
2126
|
+
baton->GetDPIError();
|
|
2127
|
+
delete [] bindNames;
|
|
2128
|
+
delete [] bindNameLengths;
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
Local<Array> bindNamesArray = Nan::New<Array>(numBinds);
|
|
2132
|
+
for (uint32_t i = 0; i < numBinds; i++) {
|
|
2133
|
+
Local<String> bindName = Nan::New<String>(bindNames[i],
|
|
2134
|
+
(int) bindNameLengths[i]).ToLocalChecked();
|
|
2135
|
+
Nan::Set(bindNamesArray, i, bindName);
|
|
2136
|
+
}
|
|
2137
|
+
delete [] bindNames;
|
|
2138
|
+
delete [] bindNameLengths;
|
|
2139
|
+
Nan::Set(result, Nan::New<v8::String>("bindNames").ToLocalChecked(),
|
|
2140
|
+
bindNamesArray);
|
|
2141
|
+
|
|
2142
|
+
// add statement type
|
|
2143
|
+
Nan::Set(result, Nan::New<v8::String>("statementType").ToLocalChecked(),
|
|
2144
|
+
Nan::New(static_cast<int>(baton->stmtInfo.statementType)));
|
|
2145
|
+
|
|
2146
|
+
argv[1] = scope.Escape(result);
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
|
|
2150
|
+
//-----------------------------------------------------------------------------
|
|
2151
|
+
// njsConnection::Close()
|
|
2152
|
+
// Releases the connection from use by JS. This releases the connection back
|
|
2153
|
+
// to the pool or closes the standalone connection so further use is not
|
|
1482
2154
|
// possible. The reference to the DPI handle is transferred to the baton so
|
|
1483
2155
|
// that it will be cleared automatically upon success and so that the
|
|
1484
2156
|
// connection is marked as invalid immediately.
|
|
@@ -1486,7 +2158,7 @@ void njsConnection::Async_AfterExecute(njsBaton *baton, Local<Value> argv[])
|
|
|
1486
2158
|
// PARAMETERS
|
|
1487
2159
|
// - JS callback which will receive (error)
|
|
1488
2160
|
//-----------------------------------------------------------------------------
|
|
1489
|
-
NAN_METHOD(njsConnection::
|
|
2161
|
+
NAN_METHOD(njsConnection::Close)
|
|
1490
2162
|
{
|
|
1491
2163
|
njsConnection *connection;
|
|
1492
2164
|
njsBaton *baton;
|
|
@@ -1499,17 +2171,17 @@ NAN_METHOD(njsConnection::Release)
|
|
|
1499
2171
|
return;
|
|
1500
2172
|
baton->dpiConnHandle = connection->dpiConnHandle;
|
|
1501
2173
|
connection->dpiConnHandle = NULL;
|
|
1502
|
-
baton->QueueWork("
|
|
2174
|
+
baton->QueueWork("Close", Async_Close, NULL, 1);
|
|
1503
2175
|
}
|
|
1504
2176
|
|
|
1505
2177
|
|
|
1506
2178
|
//-----------------------------------------------------------------------------
|
|
1507
|
-
// njsConnection::
|
|
1508
|
-
// Worker function for njsConnection::
|
|
2179
|
+
// njsConnection::Async_Close()
|
|
2180
|
+
// Worker function for njsConnection::Close() method. If the attempt to
|
|
1509
2181
|
// close fails, the reference to the DPI handle is transferred back from the
|
|
1510
2182
|
// baton to the connection.
|
|
1511
2183
|
//-----------------------------------------------------------------------------
|
|
1512
|
-
void njsConnection::
|
|
2184
|
+
void njsConnection::Async_Close(njsBaton *baton)
|
|
1513
2185
|
{
|
|
1514
2186
|
if (dpiConn_close(baton->dpiConnHandle, DPI_MODE_CONN_CLOSE_DEFAULT, NULL,
|
|
1515
2187
|
0) < 0) {
|
|
@@ -1522,10 +2194,10 @@ void njsConnection::Async_Release(njsBaton *baton)
|
|
|
1522
2194
|
|
|
1523
2195
|
|
|
1524
2196
|
//-----------------------------------------------------------------------------
|
|
1525
|
-
// njsConnection::
|
|
1526
|
-
// Finishes
|
|
2197
|
+
// njsConnection::Async_AfterClose()
|
|
2198
|
+
// Finishes close by cleaning up references.
|
|
1527
2199
|
//-----------------------------------------------------------------------------
|
|
1528
|
-
void njsConnection::
|
|
2200
|
+
void njsConnection::Async_AfterClose(njsBaton *baton, Local<Value> argv[])
|
|
1529
2201
|
{
|
|
1530
2202
|
njsConnection *connection = (njsConnection*) baton->callingObj;
|
|
1531
2203
|
connection->jsOracledb.Reset();
|
|
@@ -1568,7 +2240,7 @@ void njsConnection::Async_Commit(njsBaton *baton)
|
|
|
1568
2240
|
|
|
1569
2241
|
|
|
1570
2242
|
//-----------------------------------------------------------------------------
|
|
1571
|
-
// njsConnection::
|
|
2243
|
+
// njsConnection::Rollback()
|
|
1572
2244
|
// Rolls back the active transaction.
|
|
1573
2245
|
//
|
|
1574
2246
|
// PARAMETERS
|
|
@@ -1710,6 +2382,261 @@ void njsConnection::Async_AfterCreateLob(njsBaton *baton, Local<Value> argv[])
|
|
|
1710
2382
|
}
|
|
1711
2383
|
|
|
1712
2384
|
|
|
2385
|
+
//-----------------------------------------------------------------------------
|
|
2386
|
+
// njsConnection::ChangePassword()
|
|
2387
|
+
// Change the password on the connection.
|
|
2388
|
+
//
|
|
2389
|
+
// PARAMETERS
|
|
2390
|
+
// - JS callback which will receive (error)
|
|
2391
|
+
//-----------------------------------------------------------------------------
|
|
2392
|
+
NAN_METHOD(njsConnection::ChangePassword)
|
|
2393
|
+
{
|
|
2394
|
+
std::string user, password, newPassword;
|
|
2395
|
+
njsConnection *connection;
|
|
2396
|
+
njsBaton *baton;
|
|
2397
|
+
|
|
2398
|
+
connection = (njsConnection*) ValidateArgs(info, 4, 4);
|
|
2399
|
+
if (!connection)
|
|
2400
|
+
return;
|
|
2401
|
+
if (!connection->GetStringArg(info, 0, user))
|
|
2402
|
+
return;
|
|
2403
|
+
if (!connection->GetStringArg(info, 1, password))
|
|
2404
|
+
return;
|
|
2405
|
+
if (!connection->GetStringArg(info, 2, newPassword))
|
|
2406
|
+
return;
|
|
2407
|
+
baton = connection->CreateBaton(info);
|
|
2408
|
+
if (!baton)
|
|
2409
|
+
return;
|
|
2410
|
+
if (baton->error.empty()) {
|
|
2411
|
+
baton->SetDPIConnHandle(connection->dpiConnHandle);
|
|
2412
|
+
baton->user = user;
|
|
2413
|
+
baton->password = password;
|
|
2414
|
+
baton->newPassword = newPassword;
|
|
2415
|
+
}
|
|
2416
|
+
baton->QueueWork("ChangePassword", Async_ChangePassword, NULL, 1);
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
|
|
2420
|
+
//-----------------------------------------------------------------------------
|
|
2421
|
+
// njsConnection::Async_ChangePassword()
|
|
2422
|
+
// Worker function for njsConnection::ChangePassword() method.
|
|
2423
|
+
//-----------------------------------------------------------------------------
|
|
2424
|
+
void njsConnection::Async_ChangePassword(njsBaton *baton)
|
|
2425
|
+
{
|
|
2426
|
+
if (dpiConn_changePassword(baton->dpiConnHandle, baton->user.c_str(),
|
|
2427
|
+
(uint32_t) baton->user.length(), baton->password.c_str(),
|
|
2428
|
+
(uint32_t) baton->password.length(), baton->newPassword.c_str(),
|
|
2429
|
+
(uint32_t) baton->newPassword.length()) < 0)
|
|
2430
|
+
baton->GetDPIError();
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
|
|
2434
|
+
//-----------------------------------------------------------------------------
|
|
2435
|
+
// njsConnection::Ping()
|
|
2436
|
+
// Ping the database to see if it is "alive".
|
|
2437
|
+
//
|
|
2438
|
+
// PARAMETERS
|
|
2439
|
+
// - JS callback which will receive (error)
|
|
2440
|
+
//-----------------------------------------------------------------------------
|
|
2441
|
+
NAN_METHOD(njsConnection::Ping)
|
|
2442
|
+
{
|
|
2443
|
+
njsConnection *connection;
|
|
2444
|
+
njsBaton *baton;
|
|
2445
|
+
|
|
2446
|
+
connection = (njsConnection*) ValidateArgs(info, 1, 1);
|
|
2447
|
+
if (!connection)
|
|
2448
|
+
return;
|
|
2449
|
+
baton = connection->CreateBaton(info);
|
|
2450
|
+
if (!baton)
|
|
2451
|
+
return;
|
|
2452
|
+
if (baton->error.empty())
|
|
2453
|
+
baton->SetDPIConnHandle(connection->dpiConnHandle);
|
|
2454
|
+
baton->QueueWork("Ping", Async_Ping, NULL, 1);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
|
|
2458
|
+
//-----------------------------------------------------------------------------
|
|
2459
|
+
// njsConnection::Async_Ping()
|
|
2460
|
+
// Worker function for njsConnection::Ping() method.
|
|
2461
|
+
//-----------------------------------------------------------------------------
|
|
2462
|
+
void njsConnection::Async_Ping(njsBaton *baton)
|
|
2463
|
+
{
|
|
2464
|
+
if (dpiConn_ping(baton->dpiConnHandle) < 0)
|
|
2465
|
+
baton->GetDPIError();
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
|
|
2469
|
+
//-----------------------------------------------------------------------------
|
|
2470
|
+
// njsConnection::Subscribe()
|
|
2471
|
+
// Subscribe to events from the database. The provided callback will be
|
|
2472
|
+
// invoked each time a notification is received. The name is used to uniquely
|
|
2473
|
+
// identify a subscription and a reference is stored on the oracledb instance
|
|
2474
|
+
// for use by subsequent calls to subscribe() or unsubscribe().
|
|
2475
|
+
//
|
|
2476
|
+
// PARAMETERS
|
|
2477
|
+
// - JS callback which will receive (error)
|
|
2478
|
+
//-----------------------------------------------------------------------------
|
|
2479
|
+
NAN_METHOD(njsConnection::Subscribe)
|
|
2480
|
+
{
|
|
2481
|
+
njsConnection *connection;
|
|
2482
|
+
std::string name;
|
|
2483
|
+
njsBaton *baton;
|
|
2484
|
+
|
|
2485
|
+
connection = (njsConnection*) ValidateArgs(info, 3, 3);
|
|
2486
|
+
if (!connection)
|
|
2487
|
+
return;
|
|
2488
|
+
if (!connection->GetStringArg(info, 0, name))
|
|
2489
|
+
return;
|
|
2490
|
+
baton = connection->CreateBaton(info);
|
|
2491
|
+
if (!baton)
|
|
2492
|
+
return;
|
|
2493
|
+
baton->name = name;
|
|
2494
|
+
baton->SetDPIConnHandle(connection->dpiConnHandle);
|
|
2495
|
+
baton->subscription = njsOracledb::GetSubscription(name);
|
|
2496
|
+
if (baton->subscription)
|
|
2497
|
+
baton->subscription->SetDPISubscrHandle(baton);
|
|
2498
|
+
else {
|
|
2499
|
+
Local<Object> obj = njsSubscription::Create();
|
|
2500
|
+
baton->subscription = Nan::ObjectWrap::Unwrap<njsSubscription>(obj);
|
|
2501
|
+
baton->jsSubscription.Reset(obj);
|
|
2502
|
+
}
|
|
2503
|
+
Local<Object> options = info[1].As<Object>();
|
|
2504
|
+
ProcessSubscriptionOptions(options, baton);
|
|
2505
|
+
baton->QueueWork("Subscribe", Async_Subscribe, Async_AfterSubscribe, 1);
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
|
|
2509
|
+
//-----------------------------------------------------------------------------
|
|
2510
|
+
// njsConnection::Async_Subscribe()
|
|
2511
|
+
// Worker function for njsConnection::Subscribe() method.
|
|
2512
|
+
//-----------------------------------------------------------------------------
|
|
2513
|
+
void njsConnection::Async_Subscribe(njsBaton *baton)
|
|
2514
|
+
{
|
|
2515
|
+
// create subscription, if necessary
|
|
2516
|
+
if (!baton->dpiSubscrHandle) {
|
|
2517
|
+
dpiSubscrCreateParams params;
|
|
2518
|
+
dpiContext *context = njsOracledb::GetDPIContext();
|
|
2519
|
+
if (dpiContext_initSubscrCreateParams(context, ¶ms) < 0) {
|
|
2520
|
+
baton->GetDPIError();
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
params.subscrNamespace = baton->subscription->GetNamespace();
|
|
2524
|
+
params.name = baton->name.c_str();
|
|
2525
|
+
params.nameLength = baton->name.length();
|
|
2526
|
+
params.protocol = DPI_SUBSCR_PROTO_CALLBACK;
|
|
2527
|
+
params.callback = (dpiSubscrCallback) njsSubscription::EventHandler;
|
|
2528
|
+
params.callbackContext = baton->subscription;
|
|
2529
|
+
params.ipAddress = baton->ipAddress.c_str();
|
|
2530
|
+
params.ipAddressLength = baton->ipAddress.length();
|
|
2531
|
+
params.portNumber = baton->portNumber;
|
|
2532
|
+
params.timeout = baton->timeout;
|
|
2533
|
+
params.qos = (dpiSubscrQOS) baton->qos;
|
|
2534
|
+
params.operations = (dpiOpCode) baton->operations;
|
|
2535
|
+
params.groupingClass = (uint8_t) baton->subscrGroupingClass;
|
|
2536
|
+
params.groupingValue = baton->subscrGroupingValue;
|
|
2537
|
+
params.groupingType = (uint8_t) baton->subscrGroupingType;
|
|
2538
|
+
if (dpiConn_subscribe(baton->dpiConnHandle, ¶ms,
|
|
2539
|
+
&baton->dpiSubscrHandle) < 0) {
|
|
2540
|
+
baton->GetDPIError();
|
|
2541
|
+
return;
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
// register query if applicable
|
|
2546
|
+
if (!baton->sql.empty()) {
|
|
2547
|
+
|
|
2548
|
+
// prepare statement for registration
|
|
2549
|
+
if (dpiSubscr_prepareStmt(baton->dpiSubscrHandle, baton->sql.c_str(),
|
|
2550
|
+
(uint32_t) baton->sql.length(), &baton->dpiStmtHandle) < 0) {
|
|
2551
|
+
baton->GetDPIError();
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
// perform any binds necessary
|
|
2556
|
+
for (uint32_t i = 0; i < baton->numBindVars; i++) {
|
|
2557
|
+
int status;
|
|
2558
|
+
njsVariable *var = &baton->bindVars[i];
|
|
2559
|
+
if (var->name.empty())
|
|
2560
|
+
status = dpiStmt_bindByPos(baton->dpiStmtHandle, var->pos,
|
|
2561
|
+
var->dpiVarHandle);
|
|
2562
|
+
else status = dpiStmt_bindByName(baton->dpiStmtHandle,
|
|
2563
|
+
var->name.c_str(), (uint32_t) var->name.length(),
|
|
2564
|
+
var->dpiVarHandle);
|
|
2565
|
+
if (status < 0) {
|
|
2566
|
+
baton->GetDPIError();
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
// perform execute (which registers the query)
|
|
2572
|
+
if (dpiStmt_execute(baton->dpiStmtHandle, DPI_MODE_EXEC_DEFAULT,
|
|
2573
|
+
NULL) < 0)
|
|
2574
|
+
baton->GetDPIError();
|
|
2575
|
+
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
|
|
2580
|
+
//-----------------------------------------------------------------------------
|
|
2581
|
+
// njsConnection::Async_AfterSubscribe()
|
|
2582
|
+
// Returns result to JS by invoking JS callback.
|
|
2583
|
+
//-----------------------------------------------------------------------------
|
|
2584
|
+
void njsConnection::Async_AfterSubscribe(njsBaton *baton, Local<Value> argv[])
|
|
2585
|
+
{
|
|
2586
|
+
baton->subscription->StartNotifications(baton);
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
|
|
2590
|
+
//-----------------------------------------------------------------------------
|
|
2591
|
+
// njsConnection::Unsubscribe()
|
|
2592
|
+
// Unsubscribe from events in the database that were originally subscribed
|
|
2593
|
+
// with a call to connection.subscribe().
|
|
2594
|
+
//
|
|
2595
|
+
// PARAMETERS
|
|
2596
|
+
// - JS callback which will receive (error)
|
|
2597
|
+
//-----------------------------------------------------------------------------
|
|
2598
|
+
NAN_METHOD(njsConnection::Unsubscribe)
|
|
2599
|
+
{
|
|
2600
|
+
njsConnection *connection;
|
|
2601
|
+
std::string name;
|
|
2602
|
+
njsBaton *baton;
|
|
2603
|
+
|
|
2604
|
+
connection = (njsConnection*) ValidateArgs(info, 2, 2);
|
|
2605
|
+
if (!connection)
|
|
2606
|
+
return;
|
|
2607
|
+
if (!connection->GetStringArg(info, 0, name))
|
|
2608
|
+
return;
|
|
2609
|
+
baton = connection->CreateBaton(info);
|
|
2610
|
+
if (!baton)
|
|
2611
|
+
return;
|
|
2612
|
+
baton->name = name;
|
|
2613
|
+
baton->subscription = njsOracledb::GetSubscription(name);
|
|
2614
|
+
if (baton->subscription) {
|
|
2615
|
+
baton->subscription->SetDPISubscrHandle(baton);
|
|
2616
|
+
baton->SetDPIConnHandle(connection->dpiConnHandle);
|
|
2617
|
+
} else {
|
|
2618
|
+
baton->error = njsMessages::Get(errInvalidSubscription);
|
|
2619
|
+
}
|
|
2620
|
+
baton->QueueWork("Unsubscribe", Async_Unsubscribe, NULL, 1);
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
|
|
2624
|
+
//-----------------------------------------------------------------------------
|
|
2625
|
+
// njsConnection::Async_Unsubscribe()
|
|
2626
|
+
// Worker function for njsConnection::Unsubscribe() method.
|
|
2627
|
+
//-----------------------------------------------------------------------------
|
|
2628
|
+
void njsConnection::Async_Unsubscribe(njsBaton *baton)
|
|
2629
|
+
{
|
|
2630
|
+
if (dpiConn_unsubscribe(baton->dpiConnHandle, baton->dpiSubscrHandle) < 0)
|
|
2631
|
+
baton->GetDPIError();
|
|
2632
|
+
else {
|
|
2633
|
+
baton->dpiSubscrHandle = NULL;
|
|
2634
|
+
baton->subscription->StopNotifications();
|
|
2635
|
+
}
|
|
2636
|
+
baton->subscription = NULL;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
|
|
1713
2640
|
//-----------------------------------------------------------------------------
|
|
1714
2641
|
// njsConnection::GetStmtCacheSize()
|
|
1715
2642
|
// Get accessor of "stmtCacheSize" property.
|
|
@@ -1843,3 +2770,43 @@ NAN_SETTER(njsConnection::SetOracleServerVersion)
|
|
|
1843
2770
|
PropertyIsReadOnly("oracleServerVersion");
|
|
1844
2771
|
}
|
|
1845
2772
|
|
|
2773
|
+
|
|
2774
|
+
//-----------------------------------------------------------------------------
|
|
2775
|
+
// njsConnection::GetOracleServerVersionString()
|
|
2776
|
+
// Get accessor of "oracleServerVersionString" property.
|
|
2777
|
+
//-----------------------------------------------------------------------------
|
|
2778
|
+
NAN_GETTER(njsConnection::GetOracleServerVersionString)
|
|
2779
|
+
{
|
|
2780
|
+
njsConnection *connection = (njsConnection*) ValidateGetter(info);
|
|
2781
|
+
if (!connection)
|
|
2782
|
+
return;
|
|
2783
|
+
if (!connection->IsValid()) {
|
|
2784
|
+
info.GetReturnValue().Set(Nan::Undefined());
|
|
2785
|
+
return;
|
|
2786
|
+
}
|
|
2787
|
+
dpiVersionInfo versionInfo;
|
|
2788
|
+
uint32_t releaseStringLength;
|
|
2789
|
+
const char *releaseString;
|
|
2790
|
+
if (dpiConn_getServerVersion(connection->dpiConnHandle, &releaseString,
|
|
2791
|
+
&releaseStringLength, &versionInfo) < 0) {
|
|
2792
|
+
njsOracledb::ThrowDPIError();
|
|
2793
|
+
return;
|
|
2794
|
+
}
|
|
2795
|
+
char versionString[40];
|
|
2796
|
+
(void) sprintf(versionString, "%d.%d.%d.%d.%d", versionInfo.versionNum,
|
|
2797
|
+
versionInfo.releaseNum, versionInfo.updateNum,
|
|
2798
|
+
versionInfo.portReleaseNum, versionInfo.portUpdateNum);
|
|
2799
|
+
Local<String> value = Nan::New<v8::String>(versionString).ToLocalChecked();
|
|
2800
|
+
info.GetReturnValue().Set(value);
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
|
|
2804
|
+
//-----------------------------------------------------------------------------
|
|
2805
|
+
// njsConnection::SetOracleServerVersionString()
|
|
2806
|
+
// Set accessor of "oracleServerVersionString" property.
|
|
2807
|
+
//-----------------------------------------------------------------------------
|
|
2808
|
+
NAN_SETTER(njsConnection::SetOracleServerVersionString)
|
|
2809
|
+
{
|
|
2810
|
+
PropertyIsReadOnly("oracleServerVersionString");
|
|
2811
|
+
}
|
|
2812
|
+
|