node-firebird 0.9.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -11,7 +11,9 @@ var
11
11
  BitSet = serialize.BitSet,
12
12
  messages = require('./messages.js'),
13
13
  crypt = require('./unix-crypt.js'),
14
- path = require('path');
14
+ path = require('path'),
15
+ srp = require('./srp'),
16
+ BigInt = require('big-integer');
15
17
 
16
18
  if (typeof(setImmediate) === 'undefined') {
17
19
  global.setImmediate = function(cb) {
@@ -274,7 +276,9 @@ const
274
276
  ptype_rpc = 2, // Simple remote procedure call
275
277
  ptype_batch_send = 3, // Batch sends, no asynchrony
276
278
  ptype_out_of_band = 4, // Batch sends w/ out of band notification
277
- ptype_lazy_send = 5; // Deferred packets delivery;
279
+ ptype_lazy_send = 5, // Deferred packets delivery;
280
+ ptype_mask = 0xFF, // Mask - up to 255 types of protocol
281
+ pflag_compress = 0x100; // Turn on compression if possible
278
282
 
279
283
  const SUPPORTED_PROTOCOL = [
280
284
  [PROTOCOL_VERSION10, ARCHITECTURE_GENERIC, ptype_rpc, ptype_batch_send, 1],
@@ -790,11 +794,25 @@ const
790
794
  DEFAULT_PAGE_SIZE = 4096,
791
795
  DEFAULT_SVC_NAME = 'service_mgr';
792
796
 
793
- const AUTH_PLUGIN_LEGACY = 'Legacy_Auth';
797
+ const AUTH_PLUGIN_LEGACY = 'Legacy_Auth',
798
+ AUTH_PLUGIN_SRP = 'Srp';
799
+ // AUTH_PLUGIN_SRP256 = 'Srp256';
794
800
 
795
801
  const
796
- AUTH_PLUGIN_LIST = [AUTH_PLUGIN_LEGACY],
797
- LEGACY_AUTH_SALT = '9z';
802
+ // AUTH_PLUGIN_LIST = [AUTH_PLUGIN_SRP256, AUTH_PLUGIN_SRP, AUTH_PLUGIN_LEGACY],
803
+ AUTH_PLUGIN_LIST = [AUTH_PLUGIN_SRP, AUTH_PLUGIN_LEGACY],
804
+ // AUTH_PLUGIN_SRP_LIST = [AUTH_PLUGIN_SRP256, AUTH_PLUGIN_SRP],
805
+ AUTH_PLUGIN_SRP_LIST = [AUTH_PLUGIN_SRP],
806
+ LEGACY_AUTH_SALT = '9z',
807
+ WIRE_CRYPT_DISABLE = 0,
808
+ WIRE_CRYPT_ENABLE = 1;
809
+
810
+ exports.AUTH_PLUGIN_LEGACY = AUTH_PLUGIN_LEGACY;
811
+ exports.AUTH_PLUGIN_SRP = AUTH_PLUGIN_SRP;
812
+ // exports.AUTH_PLUGIN_SRP256 = AUTH_PLUGIN_SRP256;
813
+
814
+ exports.WIRE_CRYPT_DISABLE = WIRE_CRYPT_DISABLE;
815
+ exports.WIRE_CRYPT_ENABLE = WIRE_CRYPT_ENABLE;
798
816
 
799
817
  exports.ISOLATION_READ_UNCOMMITTED = ISOLATION_READ_UNCOMMITTED;
800
818
  exports.ISOLATION_READ_COMMITED = ISOLATION_READ_COMMITED;
@@ -1284,7 +1302,9 @@ SQLParamBool.prototype.calcBlr = function(blr) {
1284
1302
  ***************************************/
1285
1303
 
1286
1304
  function isError(obj) {
1287
- return (obj instanceof Object && obj.status);
1305
+ return Boolean(
1306
+ obj != null && typeof obj === "object" && !Array.isArray(obj) && obj.status
1307
+ );
1288
1308
  }
1289
1309
 
1290
1310
  function doCallback(obj, callback) {
@@ -2885,17 +2905,40 @@ Pool.prototype.check = function() {
2885
2905
  return self;
2886
2906
  };
2887
2907
 
2888
- Pool.prototype.destroy = function() {
2908
+ Pool.prototype.destroy = function(callback) {
2889
2909
  var self = this;
2910
+
2911
+ var connectionCount = this.internaldb.length;
2912
+
2913
+ if (connectionCount === 0 && callback) {
2914
+ callback();
2915
+ }
2916
+
2917
+ function detachCallback(err) {
2918
+ if (err) {
2919
+ if (callback) {
2920
+ callback(err);
2921
+ }
2922
+ return;
2923
+ }
2924
+
2925
+ connectionCount--;
2926
+ if (connectionCount === 0 && callback) {
2927
+ callback();
2928
+ }
2929
+ }
2930
+
2890
2931
  this.internaldb.forEach(function(db) {
2891
- if (db.connection._pooled === false)
2932
+ if (db.connection._pooled === false) {
2933
+ detachCallback();
2892
2934
  return;
2935
+ }
2893
2936
  // check if the db is not free into the pool otherwise user should manual detach it
2894
2937
  var _db_in_pool = self.pooldb.indexOf(db);
2895
2938
  if (_db_in_pool !== -1) {
2896
2939
  self.pooldb.splice(_db_in_pool, 1);
2897
2940
  db.connection._pooled = false;
2898
- db.detach();
2941
+ db.detach(detachCallback);
2899
2942
  }
2900
2943
  });
2901
2944
  };
@@ -2918,6 +2961,7 @@ var Connection = exports.Connection = function (host, port, callback, options, d
2918
2961
  this._detachAuto;
2919
2962
  this._socket = net.createConnection(port, host);
2920
2963
  this._pending = [];
2964
+ this._isOpened = false;
2921
2965
  this._isClosed = false;
2922
2966
  this._isDetach = false;
2923
2967
  this._isUsed = false;
@@ -2925,6 +2969,8 @@ var Connection = exports.Connection = function (host, port, callback, options, d
2925
2969
  this.options = options;
2926
2970
  this._bind_events(host, port, callback);
2927
2971
  this.error;
2972
+ this._retry_connection_id;
2973
+ this._retry_connection_interval = options.retryConnectionInterval || 1000;
2928
2974
  this._max_cached_query = options.maxCachedQuery || -1;
2929
2975
  this._cache_query = options.cacheQuery?{}:null;
2930
2976
  this._messageFile = options.messageFile || path.join(__dirname, 'firebird.msg');
@@ -2950,10 +2996,11 @@ exports.Connection.prototype._bind_events = function(host, port, callback) {
2950
2996
 
2951
2997
  self._socket.on('close', function() {
2952
2998
 
2953
- self._isClosed = true;
2954
-
2955
- if (self._isDetach)
2999
+ if (!self._isOpened || self._isDetach) {
2956
3000
  return;
3001
+ }
3002
+
3003
+ self._isOpened = false;
2957
3004
 
2958
3005
  if (!self.db) {
2959
3006
  if (callback)
@@ -2961,10 +3008,9 @@ exports.Connection.prototype._bind_events = function(host, port, callback) {
2961
3008
  return;
2962
3009
  }
2963
3010
 
2964
- setImmediate(function() {
3011
+ self._retry_connection_id = setTimeout(function() {
3012
+ self._socket.removeAllListeners();
2965
3013
  self._socket = null;
2966
- self._msg = null;
2967
- self._blr = null;
2968
3014
 
2969
3015
  var ctx = new Connection(host, port, function(err) {
2970
3016
  ctx.connect(self.options, function(err) {
@@ -2988,8 +3034,10 @@ exports.Connection.prototype._bind_events = function(host, port, callback) {
2988
3034
  }, self.db);
2989
3035
  });
2990
3036
 
3037
+ Object.assign(self, ctx);
3038
+
2991
3039
  }, self.options, self.db);
2992
- });
3040
+ }, self._retry_connection_interval);
2993
3041
 
2994
3042
  });
2995
3043
 
@@ -3152,6 +3200,8 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3152
3200
  }
3153
3201
  }
3154
3202
 
3203
+ const arrBlob = [];
3204
+
3155
3205
  while (data.fcount && (data.fstatus !== 100)) {
3156
3206
  var lowerV13 = statement.connection.accept.protocolVersion < PROTOCOL_VERSION13;
3157
3207
 
@@ -3161,10 +3211,16 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3161
3211
 
3162
3212
  try {
3163
3213
  _xdrpos = data.pos;
3164
- let key = custom.asObject ? data.fcols[data.fcolumn] : data.fcolumn;
3214
+ const key = custom.asObject ? data.fcols[data.fcolumn] : data.fcolumn;
3215
+ const row = data.frows.length;
3165
3216
  let value = item.decode(data, lowerV13);
3166
- if (item.type === SQL_BLOB) {
3167
- value = fetch_blob_async(statement, value, key);
3217
+ if (item.type === SQL_BLOB && value !== null) {
3218
+ if (item.subType === isc_blob_text && cnx.options.blobAsText) {
3219
+ value = fetch_blob_async_transaction(statement, value, key, row);
3220
+ arrBlob.push(value);
3221
+ } else {
3222
+ value = fetch_blob_async(statement, value, key, row);
3223
+ }
3168
3224
  }
3169
3225
  data.frow[key] = value;
3170
3226
  } catch (e) {
@@ -3197,8 +3253,13 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3197
3253
  _xdrpos = data.pos;
3198
3254
  var key = custom.asObject ? data.fcols[data.fcolumn] : data.fcolumn;
3199
3255
  var value = item.decode(data, lowerV13);
3200
- if (item.type === SQL_BLOB) {
3201
- value = fetch_blob_async(statement, value, key);
3256
+ if (item.type === SQL_BLOB && value !== null) {
3257
+ if (item.subType === isc_blob_text && cnx.options.blobAsText) {
3258
+ value = fetch_blob_async_transaction(statement, value, key, data);
3259
+ arrBlob.push(value);
3260
+ } else {
3261
+ value = fetch_blob_async(statement, value, key);
3262
+ }
3202
3263
  }
3203
3264
  data.frow[key] = value;
3204
3265
  } catch (e) {
@@ -3211,6 +3272,9 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3211
3272
  }
3212
3273
 
3213
3274
  data.fcolumn = 0;
3275
+ // ToDo: emit "row" with blob subtype string decoded
3276
+ // use: data.frow['fieldBlob'](transaction?).then(({ value }) => console.log(value))
3277
+ // arg "transaction" is optional
3214
3278
  statement.connection.db.emit('row', data.frow, statement.nbrowsfetched, custom.asObject);
3215
3279
  data.frows.push(data.frow);
3216
3280
  data.frow = custom.asObject ? {} : new Array(output.length);
@@ -3245,8 +3309,9 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3245
3309
  statement.nbrowsfetched++;
3246
3310
  }
3247
3311
 
3248
- statement.connection.db.emit('result', data.frows);
3249
- return cb(null, {data: data.frows, fetched: Boolean(!isOpFetch || data.fstatus === 100)});
3312
+ // ToDo: emit "result" with blob subtype string decoded
3313
+ statement.connection.db.emit('result', data.frows, arrBlob);
3314
+ return cb(null, {data: data.frows, fetched: Boolean(!isOpFetch || data.fstatus === 100), arrBlob});
3250
3315
  case op_accept:
3251
3316
  case op_cond_accept:
3252
3317
  case op_accept_data:
@@ -3255,22 +3320,77 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3255
3320
  protocolArchitecture: data.readInt(),
3256
3321
  protocolMinimumType: data.readInt(),
3257
3322
  pluginName: '',
3258
- authData: ''
3323
+ authData: '',
3324
+ sessionKey: ''
3259
3325
  };
3260
3326
 
3261
3327
  accept.protocolMinimumType = accept.protocolMinimumType & 0xFF;
3328
+ //accept.compress = (accept.acceptType & pflag_compress) !== 0; // TODO Handle zlib compression
3262
3329
  if (accept.protocolVersion < 0) {
3263
3330
  accept.protocolVersion = (accept.protocolVersion & FB_PROTOCOL_MASK) | FB_PROTOCOL_FLAG;
3264
3331
  }
3265
3332
 
3266
3333
  if (r === op_cond_accept || r === op_accept_data) {
3267
- var d = data.readString(DEFAULT_ENCODING);
3334
+ var d = new BlrReader(data.readArray());
3268
3335
  accept.pluginName = data.readString(DEFAULT_ENCODING);
3269
3336
  var is_authenticated = data.readInt();
3270
3337
  var keys = data.readString(DEFAULT_ENCODING); // keys
3271
3338
 
3272
3339
  if (is_authenticated === 0) {
3273
- if (accept.pluginName === AUTH_PLUGIN_LEGACY) {
3340
+ if (cnx.options.pluginName && cnx.options.pluginName !== accept.pluginName) {
3341
+ doError(new Error('Server don\'t accept plugin : ' + cnx.options.pluginName + ', but support : ' + accept.pluginName), callback);
3342
+ }
3343
+
3344
+ if (AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1) {
3345
+ var crypto = {
3346
+ Srp: 'sha1',
3347
+ Srp256: 'sha256'
3348
+ };
3349
+ accept.srpAlgo = crypto[accept.pluginName];
3350
+
3351
+ // TODO : Fallback Srp256 to Srp ?
3352
+ /*if (!d.buffer) {
3353
+ cnx.sendOpContAuth(
3354
+ cnx.clientKeys.public.toString(16),
3355
+ DEFAULT_ENCODING,
3356
+ accept.pluginName
3357
+ );
3358
+
3359
+ return cb(new Error('login'));
3360
+ }*/
3361
+
3362
+ // Check buffer contains salt
3363
+ var saltLen = d.buffer.readUInt16LE(0);
3364
+ if (saltLen > 32 * 2) {
3365
+ console.log('salt to long'); // TODO : Throw error
3366
+ }
3367
+
3368
+ // Check buffer contains key
3369
+ var keyLen = d.buffer.readUInt16LE(saltLen + 2);
3370
+ var keyStart = saltLen + 4;
3371
+ if (d.buffer.length - keyStart !== keyLen) {
3372
+ console.log('key error'); // TODO : Throw error
3373
+ }
3374
+
3375
+ // Server keys
3376
+ cnx.serverKeys = {
3377
+ salt: d.buffer.slice(2, saltLen + 2).toString('utf8'),
3378
+ public: BigInt(d.buffer.slice(keyStart, d.buffer.length).toString('utf8'), 16)
3379
+ };
3380
+
3381
+ var proof = srp.clientProof(
3382
+ cnx.options.user.toUpperCase(),
3383
+ cnx.options.password,
3384
+ cnx.serverKeys.salt,
3385
+ cnx.clientKeys.public,
3386
+ cnx.serverKeys.public,
3387
+ cnx.clientKeys.private,
3388
+ accept.srpAlgo
3389
+ );
3390
+
3391
+ accept.authData = proof.authData.toString(16);
3392
+ accept.sessionKey = proof.clientSessionKey;
3393
+ } else if (accept.pluginName === AUTH_PLUGIN_LEGACY) {
3274
3394
  accept.authData = crypt.crypt(cnx.options.password, LEGACY_AUTH_SALT).substring(2);
3275
3395
  } else {
3276
3396
  return cb(new Error('Unknow auth plugin : ' + accept.pluginName));
@@ -3280,7 +3400,35 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3280
3400
  accept.sessionKey = '';
3281
3401
  }
3282
3402
  }
3403
+
3283
3404
  return cb(undefined, accept);
3405
+ case op_cont_auth:
3406
+ var d = new BlrReader(data.readArray());
3407
+ var pluginName = data.readString(DEFAULT_ENCODING);
3408
+ data.readString(DEFAULT_ENCODING); // plist
3409
+ data.readString(DEFAULT_ENCODING); // pkey
3410
+
3411
+ if (!cnx.options.pluginName) {
3412
+ if (cnx.accept.pluginName === pluginName) {
3413
+ // Erreur plugin not able to connect
3414
+ return cb(new Error("Unable to connect with plugin " + cnx.accept.pluginName));
3415
+ }
3416
+
3417
+ if (pluginName === AUTH_PLUGIN_LEGACY) { // Fallback to LegacyAuth
3418
+ cnx.accept.pluginName = pluginName;
3419
+ cnx.accept.authData = crypt.crypt(cnx.options.password, LEGACY_AUTH_SALT).substring(2);
3420
+
3421
+ cnx.sendOpContAuth(
3422
+ cnx.accept.authData,
3423
+ DEFAULT_ENCODING,
3424
+ pluginName
3425
+ );
3426
+
3427
+ return {error: new Error('login')};
3428
+ }
3429
+ }
3430
+
3431
+ return data.accept;
3284
3432
  default:
3285
3433
  return cb(new Error('Unexpected:' + r));
3286
3434
  }
@@ -3366,6 +3514,20 @@ function parseOpResponse(data, response, cb) {
3366
3514
  }
3367
3515
  }
3368
3516
 
3517
+ Connection.prototype.sendOpContAuth = function(authData, authDataEnc, pluginName) {
3518
+ var msg = this._msg;
3519
+ msg.pos = 0;
3520
+
3521
+ msg.addInt(op_cont_auth);
3522
+ msg.addString(authData, authDataEnc);
3523
+ msg.addString(pluginName, DEFAULT_ENCODING)
3524
+ msg.addString(AUTH_PLUGIN_LIST.join(','), DEFAULT_ENCODING);
3525
+ // msg.addInt(0); // p_list
3526
+ msg.addInt(0); // keys
3527
+
3528
+ this._socket.write(msg.getData());
3529
+ }
3530
+
3369
3531
  Connection.prototype._queueEvent = function(callback){
3370
3532
  var self = this;
3371
3533
 
@@ -3380,7 +3542,7 @@ Connection.prototype._queueEvent = function(callback){
3380
3542
  };
3381
3543
 
3382
3544
  Connection.prototype.connect = function (options, callback) {
3383
- var pluginName = AUTH_PLUGIN_LEGACY;
3545
+ var pluginName = options.manager ? AUTH_PLUGIN_LEGACY : options.pluginName || AUTH_PLUGIN_LIST[0]; // TODO Srp for service
3384
3546
  var msg = this._msg;
3385
3547
  var blr = this._blr;
3386
3548
 
@@ -3393,13 +3555,19 @@ Connection.prototype.connect = function (options, callback) {
3393
3555
  blr.addString(CNCT_plugin_name, pluginName, DEFAULT_ENCODING);
3394
3556
  blr.addString(CNCT_plugin_list, AUTH_PLUGIN_LIST.join(','), DEFAULT_ENCODING);
3395
3557
 
3396
- if (pluginName === AUTH_PLUGIN_LEGACY) {
3397
- var specificData = crypt.crypt(options.password, LEGACY_AUTH_SALT).substring(2);
3558
+ var specificData = '';
3559
+ if (AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) > -1) {
3560
+ this.clientKeys = srp.clientSeed();
3561
+ specificData = this.clientKeys.public.toString(16);
3562
+ blr.addMultiblockPart(CNCT_specific_data, specificData, DEFAULT_ENCODING);
3563
+ } else if (pluginName === AUTH_PLUGIN_LEGACY) {
3564
+ specificData = crypt.crypt(options.password, LEGACY_AUTH_SALT).substring(2);
3398
3565
  blr.addMultiblockPart(CNCT_specific_data, specificData, DEFAULT_ENCODING);
3399
3566
  } else {
3400
- throw new Error('Invalide auth plugin');
3567
+ doError(new Error('Invalide auth plugin \'' + pluginName + '\''), callback);
3568
+ return;
3401
3569
  }
3402
- blr.addBytes([CNCT_client_crypt, 4, 0, 0, 0, 0]); // WireCrypt = Disabled
3570
+ blr.addBytes([CNCT_client_crypt, 4, WIRE_CRYPT_DISABLE, 0, 0, 0]); // WireCrypt = Disabled
3403
3571
  blr.addString(CNCT_user, os.userInfo().username || 'Unknown', DEFAULT_ENCODING);
3404
3572
  blr.addString(CNCT_host, os.hostname(), DEFAULT_ENCODING);
3405
3573
  blr.addBytes([CNCT_user_verification, 0]);
@@ -3454,15 +3622,18 @@ Connection.prototype.attach = function (options, callback, db) {
3454
3622
  blr.pos = 0;
3455
3623
 
3456
3624
  blr.addByte(isc_dpb_version1);
3457
- blr.addString(isc_dpb_lc_ctype, 'UTF8', DEFAULT_ENCODING);
3625
+ blr.addString(isc_dpb_lc_ctype, options.encoding || 'UTF8', DEFAULT_ENCODING);
3458
3626
  blr.addString(isc_dpb_user_name, user, DEFAULT_ENCODING);
3459
- if (this.accept.protocolVersion < PROTOCOL_VERSION13) {
3460
- if (this.accept.protocolVersion === PROTOCOL_VERSION10) {
3461
- blr.addString(isc_dpb_password, password, DEFAULT_ENCODING);
3462
- } else {
3463
- blr.addString(isc_dpb_password_enc, crypt.crypt(password, LEGACY_AUTH_SALT).substring(2), DEFAULT_ENCODING);
3627
+ if (options.password && !this.accept.authData) {
3628
+ if (this.accept.protocolVersion < PROTOCOL_VERSION13) {
3629
+ if (this.accept.protocolVersion === PROTOCOL_VERSION10) {
3630
+ blr.addString(isc_dpb_password, password, DEFAULT_ENCODING);
3631
+ } else {
3632
+ blr.addString(isc_dpb_password_enc, crypt.crypt(password, LEGACY_AUTH_SALT).substring(2), DEFAULT_ENCODING);
3633
+ }
3464
3634
  }
3465
3635
  }
3636
+
3466
3637
  if (role)
3467
3638
  blr.addString(isc_dpb_sql_role_name, role, DEFAULT_ENCODING);
3468
3639
 
@@ -3522,6 +3693,7 @@ Connection.prototype.detach = function (callback) {
3522
3693
  msg.addInt(0); // Database Object ID
3523
3694
 
3524
3695
  self._queueEvent(function(err, ret) {
3696
+ clearTimeout(self._retry_connection_id);
3525
3697
  delete(self.dbhandle);
3526
3698
  if (callback)
3527
3699
  callback(err, ret);
@@ -4258,63 +4430,129 @@ Connection.prototype.sendExecute = function (op, statement, transaction, callbac
4258
4430
  this._queueEvent(callback);
4259
4431
  }
4260
4432
 
4261
- function fetch_blob_async(statement, id, name) {
4262
-
4263
- if (!id)
4264
- return null;
4433
+ function fetch_blob_async_transaction(statement, id, name, row) {
4434
+ const infoValue = {
4435
+ row,
4436
+ column: name,
4437
+ value: ''
4438
+ };
4265
4439
 
4266
- return function(callback) {
4267
- // callback(err, buffer, name);
4268
- statement.connection.startTransaction(ISOLATION_READ_UNCOMMITTED, function(err, transaction) {
4440
+ return (transactionArg) => {
4441
+ const singleTransaction = transactionArg === undefined;
4269
4442
 
4270
- if (err) {
4271
- callback(err);
4272
- return;
4273
- }
4443
+ let promiseTransaction;
4444
+ if (singleTransaction) {
4445
+ promiseTransaction = new Promise((resolve, reject) => {
4446
+ statement.connection.startTransaction(ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
4447
+ if (err) {
4448
+ return reject(err);
4449
+ }
4450
+ resolve(transaction);
4451
+ });
4452
+ });
4453
+ } else {
4454
+ promiseTransaction = Promise.resolve(transactionArg);
4455
+ }
4274
4456
 
4275
- statement.connection._pending.push('openBlob');
4276
- statement.connection.openBlob(id, transaction, function(err, blob) {
4457
+ return promiseTransaction.then((transaction) => {
4458
+ return new Promise((resolve, reject) => {
4459
+ statement.connection._pending.push('openBlob');
4460
+ statement.connection.openBlob(id, transaction, (err, blob) => {
4461
+
4462
+ if (err) {
4463
+ reject(err);
4464
+ return;
4465
+ }
4466
+
4467
+ const read = () => {
4468
+ statement.connection.getSegment(blob, (err, ret) => {
4469
+
4470
+ if (err) {
4471
+ if (singleTransaction) {
4472
+ transaction.rollback(() => reject(err));
4473
+ } else {
4474
+ reject(err);
4475
+ }
4476
+ return;
4477
+ }
4478
+
4479
+ if (ret.buffer) {
4480
+ const blr = new BlrReader(ret.buffer);
4481
+ const data = blr.readSegment();
4482
+ infoValue.value += data.toString(DEFAULT_ENCODING);
4483
+ }
4484
+
4485
+ if (ret.handle !== 2) {
4486
+ read();
4487
+ return;
4488
+ }
4489
+
4490
+ statement.connection.closeBlob(blob);
4491
+ if (singleTransaction) {
4492
+ transaction.commit((err) => {
4493
+ if (err) {
4494
+ reject(err);
4495
+ } else {
4496
+ resolve(infoValue);
4497
+ }
4498
+ });
4499
+ } else {
4500
+ resolve(infoValue);
4501
+ }
4502
+ });
4503
+ };
4504
+
4505
+ read();
4506
+ });
4507
+ });
4508
+ });
4509
+ };
4510
+ }
4277
4511
 
4278
- var e = new Events.EventEmitter();
4512
+ function fetch_blob_async(statement, id, name, row) {
4513
+ const cbTransaction = (transaction, close, callback) => {
4514
+ statement.connection._pending.push('openBlob');
4515
+ statement.connection.openBlob(id, transaction, (err, blob) => {
4516
+ let e = new Events.EventEmitter();
4279
4517
 
4280
- e.pipe = function(stream) {
4281
- e.on('data', function(chunk) {
4282
- stream.write(chunk);
4283
- });
4284
- e.on('end', function() {
4285
- stream.end();
4286
- });
4287
- };
4518
+ e.pipe = (stream) => {
4519
+ e.on('data', (chunk) => {
4520
+ stream.write(chunk);
4521
+ });
4522
+ e.on('end', () => {
4523
+ stream.end();
4524
+ });
4525
+ };
4288
4526
 
4289
- if (err) {
4290
- callback(err, name, e);
4291
- return;
4292
- }
4527
+ if (err) {
4528
+ return callback(err, name, e, row);
4529
+ }
4293
4530
 
4294
- function read() {
4295
- statement.connection.getSegment(blob, function(err, ret) {
4531
+ const read = () => {
4532
+ statement.connection.getSegment(blob, (err, ret) => {
4296
4533
 
4297
- if (err) {
4298
- transaction.rollback(function() {
4299
- e.emit('error', err);
4300
- });
4301
- return;
4302
- }
4534
+ if (err) {
4535
+ transaction.rollback(() => {
4536
+ e.emit('error', err);
4537
+ });
4538
+ return;
4539
+ }
4303
4540
 
4304
- if (ret.buffer) {
4305
- var blr = new BlrReader(ret.buffer);
4306
- var data = blr.readSegment();
4541
+ if (ret.buffer) {
4542
+ const blr = new BlrReader(ret.buffer);
4543
+ const data = blr.readSegment();
4307
4544
 
4308
- e.emit('data', data);
4309
- }
4545
+ e.emit('data', data);
4546
+ }
4310
4547
 
4311
- if (ret.handle !== 2) {
4312
- read();
4313
- return;
4314
- }
4548
+ if (ret.handle !== 2) {
4549
+ read();
4550
+ return;
4551
+ }
4315
4552
 
4316
- statement.connection.closeBlob(blob);
4317
- transaction.commit(function(err) {
4553
+ statement.connection.closeBlob(blob);
4554
+ if (close) {
4555
+ transaction.commit((err) => {
4318
4556
  if (err) {
4319
4557
  e.emit('error', err);
4320
4558
  } else {
@@ -4322,15 +4560,33 @@ function fetch_blob_async(statement, id, name) {
4322
4560
  }
4323
4561
  e = null;
4324
4562
  });
4563
+ } else {
4564
+ e.emit('end');
4565
+ e = null;
4566
+ }
4567
+ });
4568
+ };
4325
4569
 
4326
- });
4327
- }
4328
-
4329
- callback(err, name, e);
4330
- read();
4570
+ callback(err, name, e, row);
4571
+ read();
4572
+ });
4573
+ };
4331
4574
 
4575
+ return (transaction, callback) => {
4576
+ // callback(error, nameField, eventEmitter, row)
4577
+ const singleTransaction = callback === undefined;
4578
+ if (singleTransaction) {
4579
+ callback = transaction;
4580
+ statement.connection.startTransaction(ISOLATION_READ_UNCOMMITTED, (err, transaction) => {
4581
+ if (err) {
4582
+ callback(err);
4583
+ return;
4584
+ }
4585
+ cbTransaction(transaction, singleTransaction, callback);
4332
4586
  });
4333
- });
4587
+ } else {
4588
+ cbTransaction(transaction, singleTransaction, callback);
4589
+ }
4334
4590
  };
4335
4591
  }
4336
4592
 
@@ -4359,24 +4615,34 @@ Connection.prototype.fetch = function(statement, transaction, count, callback) {
4359
4615
  };
4360
4616
 
4361
4617
  Connection.prototype.fetchAll = function (statement, transaction, callback) {
4362
- var self = this, data = [];
4363
- var loop = function (err, ret) {
4618
+ const self = this, data = [];
4619
+ const loop = (err, ret) => {
4364
4620
  if (err) {
4365
4621
  return callback(err);
4366
4622
  } else if (ret && ret.data && ret.data.length) {
4367
- for (var i = 0; i < ret.data.length; i++) {
4368
- var pos = data.push(ret.data[i]);
4369
- if (statement.custom && statement.custom.asStream && statement.custom.on) {
4370
- statement.custom.on(ret.data[i], pos - 1);
4371
- }
4372
- if (i === ret.data.length - 1) {
4373
- if (ret.fetched) {
4374
- return callback(undefined, data);
4375
- } else {
4376
- self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4377
- }
4378
- }
4379
- }
4623
+ const arrPromise = (ret.arrBlob || []).map(value => value(transaction));
4624
+
4625
+ Promise.all(arrPromise).then((arrBlob) => {
4626
+ for (let i = 0; i < arrBlob.length; i++) {
4627
+ const blob = arrBlob[i];
4628
+ ret.data[blob.row][blob.column] = blob.value;
4629
+ }
4630
+
4631
+ const lastIndex = ret.data.length - 1;
4632
+ for (let i = 0; i < ret.data.length; i++) {
4633
+ const pos = data.push(ret.data[i]);
4634
+ if (statement.custom && statement.custom.asStream && statement.custom.on) {
4635
+ statement.custom.on(ret.data[i], pos - 1);
4636
+ }
4637
+ if (i === lastIndex) {
4638
+ if (ret.fetched) {
4639
+ return callback(undefined, data);
4640
+ } else {
4641
+ self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4642
+ }
4643
+ }
4644
+ }
4645
+ }).catch(callback);
4380
4646
  } else if (ret.fetched) {
4381
4647
  callback(undefined, data);
4382
4648
  } else {