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.
Files changed (52) hide show
  1. package/CHANGELOG.md +174 -54
  2. package/LICENSE.md +52 -18
  3. package/README.md +35 -68
  4. package/binding.gyp +5 -4
  5. package/lib/connection.js +164 -14
  6. package/lib/oracledb.js +160 -11
  7. package/lib/pool.js +33 -22
  8. package/lib/querystream.js +3 -2
  9. package/lib/rel.txt +1 -1
  10. package/odpi/LICENSE.md +1 -1
  11. package/odpi/README.md +25 -84
  12. package/odpi/embed/README.md +1 -1
  13. package/odpi/include/dpi.h +51 -7
  14. package/odpi/src/dpiConn.c +78 -9
  15. package/odpi/src/dpiContext.c +24 -10
  16. package/odpi/src/dpiEnv.c +5 -1
  17. package/odpi/src/dpiError.c +4 -0
  18. package/odpi/src/dpiErrorMessages.h +6 -4
  19. package/odpi/src/dpiImpl.h +125 -68
  20. package/odpi/src/dpiMsgProps.c +33 -0
  21. package/odpi/src/dpiObject.c +17 -10
  22. package/odpi/src/dpiObjectType.c +1 -1
  23. package/odpi/src/dpiOci.c +55 -40
  24. package/odpi/src/dpiOracleType.c +2 -2
  25. package/odpi/src/dpiPool.c +88 -18
  26. package/odpi/src/dpiStmt.c +57 -36
  27. package/odpi/src/dpiSubscr.c +117 -9
  28. package/odpi/src/dpiUtils.c +20 -9
  29. package/odpi/src/dpiVar.c +504 -430
  30. package/package/MAKEPKG.BAT +64 -1
  31. package/package/README.md +9 -5
  32. package/package/createpackage.js +2 -2
  33. package/package/extractpackage.js +2 -1
  34. package/package/oracledbinstall.js +19 -19
  35. package/package/package.json +4 -4
  36. package/package.json +4 -4
  37. package/src/njsCommon.cpp +94 -10
  38. package/src/njsCommon.h +73 -21
  39. package/src/njsConnection.cpp +1425 -458
  40. package/src/njsConnection.h +68 -19
  41. package/src/njsIntLob.cpp +5 -1
  42. package/src/njsIntLob.h +1 -1
  43. package/src/njsMessages.cpp +9 -2
  44. package/src/njsMessages.h +9 -2
  45. package/src/njsOracle.cpp +213 -23
  46. package/src/njsOracle.h +18 -2
  47. package/src/njsPool.cpp +32 -14
  48. package/src/njsPool.h +4 -4
  49. package/src/njsResultSet.cpp +4 -3
  50. package/src/njsResultSet.h +1 -1
  51. package/src/njsSubscription.cpp +388 -0
  52. package/src/njsSubscription.h +111 -0
@@ -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(Handle<Object> target)
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, "release", Release);
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 baseNumElements)
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
- njsDataType dataType;
252
- switch (var->varTypeNum) {
253
- case DPI_ORACLE_TYPE_CLOB:
254
- case DPI_ORACLE_TYPE_NCLOB:
255
- dataType = NJS_DATATYPE_CLOB;
256
- break;
257
- case DPI_ORACLE_TYPE_BLOB:
258
- dataType = NJS_DATATYPE_BLOB;
259
- break;
260
- case DPI_ORACLE_TYPE_STMT:
261
- stmt = var->dpiVarData->value.asStmt;
262
- if (dpiStmt_getNumQueryColumns(stmt, &var->numQueryVars) < 0) {
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
- var->queryVars = new njsVariable[var->numQueryVars];
267
- if (!ProcessQueryVars(baton, stmt, var->queryVars,
268
- var->numQueryVars))
288
+ if (!ProcessVarBuffer(baton, var, buffer))
269
289
  return false;
270
- continue;
271
- default:
272
- continue;
290
+ }
273
291
  }
274
292
 
275
- // for DML returning statements, the number of rows returned may have
276
- // exceeded the maxArraySize of the variable, requiring ODPI-C to
277
- // free the original buffers and allocate new ones; this call gets the
278
- // buffers and maxArraySize value for the variable just in case that
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
- } else if (!var->isArray) {
289
- numElements = baseNumElements;
290
- } else {
291
- if (dpiVar_getNumElementsInArray(var->dpiVarHandle,
292
- &numElements) < 0) {
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
- if (var->lobs)
298
- delete [] var->lobs;
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
- if (baton->fetchAsStringTypes || baton->fetchAsBufferTypes) {
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
- for (uint32_t i = 0; i < baton->numFetchAsStringTypes; i++) {
358
- if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_NUM) {
359
- targetType = DPI_ORACLE_TYPE_VARCHAR;
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
- for (uint32_t i = 0; i < baton->numFetchAsStringTypes; i++) {
369
- if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_DATE) {
370
- targetType = DPI_ORACLE_TYPE_VARCHAR;
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
- for (uint32_t i = 0; i < baton->numFetchAsStringTypes; i++) {
378
- if (baton->fetchAsStringTypes[i] == NJS_DATATYPE_CLOB) {
379
- targetType = DPI_ORACLE_TYPE_VARCHAR;
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 DPI_ORACLE_TYPE_BLOB:
385
- for (uint32_t i = 0; i < baton->numFetchAsBufferTypes; i++) {
386
- if (baton->fetchAsBufferTypes[i] == NJS_DATATYPE_BLOB) {
387
- targetType = DPI_ORACLE_TYPE_RAW;
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::ProcessBinds()
519
- // Process binds passed through to execute call. A variable is created for
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::ProcessBind(Local<Value> val, njsVariable *var,
602
- bool byPosition, njsBaton *baton)
554
+ bool njsConnection::CreateVarBuffer(njsVariable *var, njsBaton *baton)
603
555
  {
604
- Nan::HandleScope scope;
605
- Local<Value> bindValue;
606
- uint32_t bindType;
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
- // otherwise, bind value is directly passed and other values are defaults
672
- } else {
673
- bindValue = val;
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 (bindType == NJS_DATATYPE_CURSOR && var->bindDir != NJS_BIND_OUT) {
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
- // validate bind type
691
- if (!bindType || !var->maxSize || var->maxSize == NJS_MAX_OUT_BIND_SIZE) {
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 DPI variable to hold data
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, &var->dpiVarData) < 0) {
623
+ var->isArray, NULL, &var->dpiVarHandle,
624
+ &var->buffer.dpiVarData) < 0) {
769
625
  baton->GetDPIError();
770
626
  return false;
771
627
  }
772
628
 
773
- // for in and in/out binds, copy value into buffers
774
- switch (var->bindDir) {
775
- case NJS_BIND_OUT:
776
- break;
777
- case NJS_BIND_IN:
778
- case NJS_BIND_INOUT:
779
- if (!ProcessBindValue(bindValue, var, baton))
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
- break;
782
- default:
783
- baton->error = njsMessages::Get(errInvalidBindDirection);
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::ProcessBindValue()
793
- // Process bind value from JS and copy it to the variable that was created.
751
+ // njsConnection::ScanExecuteBindUnit()
752
+ // Scan the execute bind unit for bind information.
794
753
  //-----------------------------------------------------------------------------
795
- bool njsConnection::ProcessBindValue(Local<Value> value, njsVariable *var,
796
- njsBaton *baton)
754
+ bool njsConnection::ScanExecuteBindUnit(Local<Object> bindUnit,
755
+ njsVariable *var, bool inExecuteMany, njsBaton *baton)
797
756
  {
798
- // scalar values can be handled directly
799
- if (!var->isArray)
800
- return ProcessScalarBindValue(value, var, 0, baton);
757
+ Nan::HandleScope scope;
801
758
 
802
- // only strings and numbers are currently allowed
803
- if (var->varTypeNum != DPI_ORACLE_TYPE_VARCHAR &&
804
- var->varTypeNum != DPI_ORACLE_TYPE_NUMBER &&
805
- var->varTypeNum != DPI_ORACLE_TYPE_NATIVE_INT) {
806
- baton->error = njsMessages::Get(errInvalidTypeForArrayBind);
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
- /* Expecting an array value here */
811
- if ( !value->IsArray ()) {
812
- baton->error = njsMessages::Get ( errNonArrayProvided ) ;
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
- // set the number of actual elements in the variable
817
- Nan::HandleScope scope;
818
- Local<Array> arrayVal = Local<Array>::Cast(value);
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
- // process each element in the array
826
- for (uint32_t i = 0; i < arrayVal->Length(); i++) {
827
- Local<Value> elementValue = Nan::Get (arrayVal, i).ToLocalChecked ();
828
- if (!ProcessScalarBindValue(elementValue, var, i, baton))
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::ProcessScalarBindValue()
838
- // Process scalar bind value from JS and copy it to the variable at the given
839
- // position.
842
+ // njsConnection::ProcessExecuteManyBinds()
843
+ // Process binds passed through to ExecuteMany() call.
840
844
  //-----------------------------------------------------------------------------
841
- bool njsConnection::ProcessScalarBindValue(Local<Value> value,
842
- njsVariable *var, uint32_t pos, njsBaton *baton)
845
+ bool njsConnection::ProcessExecuteManyBinds(Local<Array> binds,
846
+ Local<Object> options, njsBaton *baton)
843
847
  {
844
848
  Nan::HandleScope scope;
845
- bool bindOk = false;
846
- dpiData *data;
849
+ Local<Value> bindDefs, bindName, bindUnit;
850
+ Local<Array> bindNames;
851
+ bool scanRequired;
847
852
 
848
- // initialization
849
- data = &var->dpiVarData[pos];
850
- data->isNull = 0;
853
+ // all rows are bound at one time
854
+ baton->bindArraySize = binds->Length();
851
855
 
852
- // nulls and undefined in JS are mapped to NULL in Oracle; no checks needed
853
- if (value->IsUndefined() || value->IsNull()) {
854
- data->isNull = 1;
855
- bindOk = true;
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
- // value is a string, variable type should be a string
858
- } else if (value->IsString()) {
859
- bindOk = (var->varTypeNum == DPI_ORACLE_TYPE_VARCHAR ||
860
- var->varTypeNum == DPI_ORACLE_TYPE_CLOB);
861
- if (bindOk) {
862
- v8::String::Utf8Value utf8str(value);
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
- // value is an integer
873
- } else if (value->IsInt32() || value->IsUint32()) {
874
- bindOk = (var->varTypeNum == DPI_ORACLE_TYPE_NUMBER);
875
- if (bindOk) {
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 (dpiVar_setFromBytes(var->dpiVarHandle, pos, Buffer::Data(obj),
909
- (uint32_t) Buffer::Length(obj)) < 0) {
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, uint32_t *bindType, uint32_t *maxSize,
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
- v8::String::Utf8Value utf8str(value->ToString());
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
- uint32_t elementBindType, elementMaxSize;
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 = static_cast<uint32_t>(lob->GetDataType());
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::ProcessOptions()
1022
- // Processing of options. If an error is detected, the baton error is
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::ProcessOptions(Nan::NAN_METHOD_ARGS_TYPE args,
1026
- unsigned int index, njsBaton *baton)
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->fetchInfo = new njsFetchInfo[baton->numFetchInfo];
1342
+ if (baton->numFetchInfo > 0)
1343
+ baton->fetchInfo = new njsFetchInfo[baton->numFetchInfo];
1077
1344
  for (uint32_t i = 0; i < baton->numFetchInfo; i++) {
1078
- Local<String> temp = Nan::Get (keys,
1079
- i).ToLocalChecked ().As<String>();
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
- static_cast<size_t>(utf8str.length()));
1084
- v8::String::Utf8Value v8str(temp->ToString());
1085
- std::string str = std::string(*v8str,
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 (var->lobs) {
1123
- njsProtoILob *protoLob = &var->lobs[pos];
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 DPI
1491
+ // get the value from ODPI-C
1140
1492
  uint32_t bufferRowIndex = baton->bufferRowIndex + pos;
1141
- dpiData *data = &var->dpiVarData[bufferRowIndex];
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::GetValueFromVar()
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::GetValueFromVar(njsBaton *baton, njsVariable *var,
1205
- Local<Value> &value)
1556
+ bool njsConnection::GetArrayValueFromVar(njsBaton *baton, njsVariable *var,
1557
+ uint32_t pos, Local<Value> &value)
1206
1558
  {
1207
- // scalar values can be handled directly
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
- // process each element in the array
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::GetOutBinds()
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> njsConnection::GetOutBinds(njsBaton *baton)
1594
+ bool njsConnection::GetExecuteManyOutBinds(Local<Value> &outBinds,
1595
+ uint32_t numOutBinds, njsBaton *baton)
1247
1596
  {
1248
1597
  Nan::EscapableHandleScope scope;
1249
- uint32_t numOutBinds;
1598
+ Local<Array> rows = Nan::New<Array>(baton->bindArraySize);
1599
+ Local<Value> row;
1250
1600
 
1251
- // determine the number of out bind variables
1252
- numOutBinds = 0;
1253
- for (uint32_t i = 0; i < baton->numBindVars; i++) {
1254
- if (baton->bindVars[i].bindDir != NJS_BIND_IN)
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
- // process each of the out bind variables
1263
- bool bindByPos = baton->bindVars[0].name.empty();
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 (!GetValueFromVar(baton, var, val))
1277
- return scope.Escape(Nan::Undefined());
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, val);
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
- return scope.Escape(bindArray);
1290
- return scope.Escape(bindObj);
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
- v8::String::Utf8Value utfstr(value->ToString());
1313
- if ((*setter)(connection->dpiConnHandle, *utfstr,
1314
- static_cast<int32_t> (utfstr.length()) < 0))
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 = ProcessBinds(info, 1, baton);
1734
+ ok = ProcessExecuteBinds(info[1].As<Object>(), baton);
1374
1735
  if (ok)
1375
- ProcessOptions(info, 2, baton);
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::DefineOwnProperty (result,
1458
- Nan::New<v8::String>("outBinds").ToLocalChecked(),
1459
- GetOutBinds(baton), v8::ReadOnly);
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::Release()
1480
- // Releases the connection from use by JS. This releases the connection back
1481
- // to the pool or closes the standalone connection so further use is not
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::Release)
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("Release", Async_Release, NULL, 1);
2174
+ baton->QueueWork("Close", Async_Close, NULL, 1);
1503
2175
  }
1504
2176
 
1505
2177
 
1506
2178
  //-----------------------------------------------------------------------------
1507
- // njsConnection::Async_Release()
1508
- // Worker function for njsConnection::Release() method. If the attempt to
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::Async_Release(njsBaton *baton)
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::Async_AfterRelease()
1526
- // Finishes release by cleaning up references.
2197
+ // njsConnection::Async_AfterClose()
2198
+ // Finishes close by cleaning up references.
1527
2199
  //-----------------------------------------------------------------------------
1528
- void njsConnection::Async_AfterRelease(njsBaton *baton, Local<Value> argv[])
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::Commit()
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, &params) < 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, &params,
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
+