node-firebird 1.0.0 → 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/README.md CHANGED
@@ -68,6 +68,7 @@ options.role = null; // default
68
68
  options.pageSize = 4096; // default when creating database
69
69
  options.pageSize = 4096; // default when creating database
70
70
  options.retryConnectionInterval = 1000; // reconnect interval in case of connection drop
71
+ options.blobAsText = false; // set to true to get blob as text, only affects blob subtype 1
71
72
  ```
72
73
 
73
74
  ### Classic
@@ -223,6 +224,73 @@ Firebird.attach(options, function(err, db) {
223
224
  });
224
225
  ```
225
226
 
227
+ ### Reading Multiples Blobs (Asynchronous)
228
+ ```js
229
+ Firebird.attach(options, (err, db) => {
230
+ if (err)
231
+ throw err;
232
+
233
+ db.transaction(Firebird.ISOLATION_READ_COMMITED, (err, transaction) => {
234
+ if (err) {
235
+ throw err;
236
+ }
237
+
238
+ transaction.query('SELECT FIRST 10 * FROM JOB', (err, result) => {
239
+ if (err) {
240
+ transaction.rollback();
241
+ return;
242
+ }
243
+
244
+ const arrBlob = [];
245
+ for (const item of result) {
246
+ const fields = Object.keys(item);
247
+ for (const key of fields) {
248
+ if (typeof item[key] === 'function') {
249
+ item[key] = new Promise((resolve, reject) => {
250
+ // the same transaction is used (better performance)
251
+ // this is optional
252
+ item[key](transaction, (error, name, event, row) => {
253
+ if (error) {
254
+ return reject(error);
255
+ }
256
+
257
+ // reading data
258
+ let value = '';
259
+ event.on('data', (chunk) => {
260
+ value += chunk.toString('binary');
261
+ });
262
+ event.on('end', () => {
263
+ resolve({ value, column: name, row });
264
+ });
265
+ });
266
+ });
267
+ arrBlob.push(item[key]);
268
+ }
269
+ }
270
+ }
271
+
272
+ Promise.all(arrBlob).then((blobs) => {
273
+ for (const blob of blobs) {
274
+ result[blob.row][blob.column] = blob.value;
275
+ }
276
+
277
+ transaction.commit((err) => {
278
+ if (err) {
279
+ transaction.rollback();
280
+ return;
281
+ }
282
+
283
+ db.detach();
284
+ console.log(result);
285
+ });
286
+ }).catch((err) => {
287
+ transaction.rollback();
288
+ });
289
+ });
290
+ });
291
+ });
292
+ ```
293
+
226
294
  ### Streaming a big data
227
295
 
228
296
  ```js
package/lib/index.d.ts CHANGED
@@ -51,6 +51,37 @@ declare module 'node-firebird' {
51
51
  rollbackRetaining(callback?: SimpleCallback): void;
52
52
  }
53
53
 
54
+ export type SupportedCharacterSet = |
55
+ 'NONE' |
56
+ 'CP943C' |
57
+ 'DOS737' |
58
+ 'DOS775' |
59
+ 'DOS858' |
60
+ 'DOS862' |
61
+ 'DOS864' |
62
+ 'DOS866' |
63
+ 'DOS869' |
64
+ 'GB18030' |
65
+ 'GBK' |
66
+ 'ISO8859_2' |
67
+ 'ISO8859_3' |
68
+ 'ISO8859_4' |
69
+ 'ISO8859_5' |
70
+ 'ISO8859_6' |
71
+ 'ISO8859_7' |
72
+ 'ISO8859_8' |
73
+ 'ISO8859_9' |
74
+ 'ISO8859_13' |
75
+ 'KOI8R' |
76
+ 'KOI8U' |
77
+ 'TIS620' |
78
+ 'UTF8' |
79
+ 'WIN1255' |
80
+ 'WIN1256' |
81
+ 'WIN1257' |
82
+ 'WIN1258' |
83
+ 'WIN_1258';
84
+
54
85
  export interface Options {
55
86
  host?: string;
56
87
  port?: number;
@@ -61,6 +92,8 @@ declare module 'node-firebird' {
61
92
  role?: string;
62
93
  pageSize?: number;
63
94
  retryConnectionInterval?: number;
95
+ encoding?: SupportedCharacterSet;
96
+ blobAsText?: boolean; // only affects for blob subtype 1
64
97
  }
65
98
 
66
99
  export interface SvcMgrOptions extends Options {
package/lib/index.js CHANGED
@@ -3200,6 +3200,8 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3200
3200
  }
3201
3201
  }
3202
3202
 
3203
+ const arrBlob = [];
3204
+
3203
3205
  while (data.fcount && (data.fstatus !== 100)) {
3204
3206
  var lowerV13 = statement.connection.accept.protocolVersion < PROTOCOL_VERSION13;
3205
3207
 
@@ -3209,10 +3211,16 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3209
3211
 
3210
3212
  try {
3211
3213
  _xdrpos = data.pos;
3212
- 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;
3213
3216
  let value = item.decode(data, lowerV13);
3214
- if (item.type === SQL_BLOB) {
3215
- 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
+ }
3216
3224
  }
3217
3225
  data.frow[key] = value;
3218
3226
  } catch (e) {
@@ -3245,8 +3253,13 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3245
3253
  _xdrpos = data.pos;
3246
3254
  var key = custom.asObject ? data.fcols[data.fcolumn] : data.fcolumn;
3247
3255
  var value = item.decode(data, lowerV13);
3248
- if (item.type === SQL_BLOB) {
3249
- 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
+ }
3250
3263
  }
3251
3264
  data.frow[key] = value;
3252
3265
  } catch (e) {
@@ -3259,6 +3272,9 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3259
3272
  }
3260
3273
 
3261
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
3262
3278
  statement.connection.db.emit('row', data.frow, statement.nbrowsfetched, custom.asObject);
3263
3279
  data.frows.push(data.frow);
3264
3280
  data.frow = custom.asObject ? {} : new Array(output.length);
@@ -3293,8 +3309,9 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3293
3309
  statement.nbrowsfetched++;
3294
3310
  }
3295
3311
 
3296
- statement.connection.db.emit('result', data.frows);
3297
- 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});
3298
3315
  case op_accept:
3299
3316
  case op_cond_accept:
3300
3317
  case op_accept_data:
@@ -3605,7 +3622,7 @@ Connection.prototype.attach = function (options, callback, db) {
3605
3622
  blr.pos = 0;
3606
3623
 
3607
3624
  blr.addByte(isc_dpb_version1);
3608
- blr.addString(isc_dpb_lc_ctype, 'UTF8', DEFAULT_ENCODING);
3625
+ blr.addString(isc_dpb_lc_ctype, options.encoding || 'UTF8', DEFAULT_ENCODING);
3609
3626
  blr.addString(isc_dpb_user_name, user, DEFAULT_ENCODING);
3610
3627
  if (options.password && !this.accept.authData) {
3611
3628
  if (this.accept.protocolVersion < PROTOCOL_VERSION13) {
@@ -4413,63 +4430,129 @@ Connection.prototype.sendExecute = function (op, statement, transaction, callbac
4413
4430
  this._queueEvent(callback);
4414
4431
  }
4415
4432
 
4416
- function fetch_blob_async(statement, id, name) {
4417
-
4418
- if (!id)
4419
- return null;
4433
+ function fetch_blob_async_transaction(statement, id, name, row) {
4434
+ const infoValue = {
4435
+ row,
4436
+ column: name,
4437
+ value: ''
4438
+ };
4420
4439
 
4421
- return function(callback) {
4422
- // callback(err, buffer, name);
4423
- statement.connection.startTransaction(ISOLATION_READ_UNCOMMITTED, function(err, transaction) {
4440
+ return (transactionArg) => {
4441
+ const singleTransaction = transactionArg === undefined;
4424
4442
 
4425
- if (err) {
4426
- callback(err);
4427
- return;
4428
- }
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
+ }
4429
4456
 
4430
- statement.connection._pending.push('openBlob');
4431
- 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
+ }
4432
4511
 
4433
- 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();
4434
4517
 
4435
- e.pipe = function(stream) {
4436
- e.on('data', function(chunk) {
4437
- stream.write(chunk);
4438
- });
4439
- e.on('end', function() {
4440
- stream.end();
4441
- });
4442
- };
4518
+ e.pipe = (stream) => {
4519
+ e.on('data', (chunk) => {
4520
+ stream.write(chunk);
4521
+ });
4522
+ e.on('end', () => {
4523
+ stream.end();
4524
+ });
4525
+ };
4443
4526
 
4444
- if (err) {
4445
- callback(err, name, e);
4446
- return;
4447
- }
4527
+ if (err) {
4528
+ return callback(err, name, e, row);
4529
+ }
4448
4530
 
4449
- function read() {
4450
- statement.connection.getSegment(blob, function(err, ret) {
4531
+ const read = () => {
4532
+ statement.connection.getSegment(blob, (err, ret) => {
4451
4533
 
4452
- if (err) {
4453
- transaction.rollback(function() {
4454
- e.emit('error', err);
4455
- });
4456
- return;
4457
- }
4534
+ if (err) {
4535
+ transaction.rollback(() => {
4536
+ e.emit('error', err);
4537
+ });
4538
+ return;
4539
+ }
4458
4540
 
4459
- if (ret.buffer) {
4460
- var blr = new BlrReader(ret.buffer);
4461
- var data = blr.readSegment();
4541
+ if (ret.buffer) {
4542
+ const blr = new BlrReader(ret.buffer);
4543
+ const data = blr.readSegment();
4462
4544
 
4463
- e.emit('data', data);
4464
- }
4545
+ e.emit('data', data);
4546
+ }
4465
4547
 
4466
- if (ret.handle !== 2) {
4467
- read();
4468
- return;
4469
- }
4548
+ if (ret.handle !== 2) {
4549
+ read();
4550
+ return;
4551
+ }
4470
4552
 
4471
- statement.connection.closeBlob(blob);
4472
- transaction.commit(function(err) {
4553
+ statement.connection.closeBlob(blob);
4554
+ if (close) {
4555
+ transaction.commit((err) => {
4473
4556
  if (err) {
4474
4557
  e.emit('error', err);
4475
4558
  } else {
@@ -4477,15 +4560,33 @@ function fetch_blob_async(statement, id, name) {
4477
4560
  }
4478
4561
  e = null;
4479
4562
  });
4563
+ } else {
4564
+ e.emit('end');
4565
+ e = null;
4566
+ }
4567
+ });
4568
+ };
4480
4569
 
4481
- });
4482
- }
4483
-
4484
- callback(err, name, e);
4485
- read();
4570
+ callback(err, name, e, row);
4571
+ read();
4572
+ });
4573
+ };
4486
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);
4487
4586
  });
4488
- });
4587
+ } else {
4588
+ cbTransaction(transaction, singleTransaction, callback);
4589
+ }
4489
4590
  };
4490
4591
  }
4491
4592
 
@@ -4514,24 +4615,34 @@ Connection.prototype.fetch = function(statement, transaction, count, callback) {
4514
4615
  };
4515
4616
 
4516
4617
  Connection.prototype.fetchAll = function (statement, transaction, callback) {
4517
- var self = this, data = [];
4518
- var loop = function (err, ret) {
4618
+ const self = this, data = [];
4619
+ const loop = (err, ret) => {
4519
4620
  if (err) {
4520
4621
  return callback(err);
4521
4622
  } else if (ret && ret.data && ret.data.length) {
4522
- for (var i = 0; i < ret.data.length; i++) {
4523
- var pos = data.push(ret.data[i]);
4524
- if (statement.custom && statement.custom.asStream && statement.custom.on) {
4525
- statement.custom.on(ret.data[i], pos - 1);
4526
- }
4527
- if (i === ret.data.length - 1) {
4528
- if (ret.fetched) {
4529
- return callback(undefined, data);
4530
- } else {
4531
- self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4532
- }
4533
- }
4534
- }
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);
4535
4646
  } else if (ret.fetched) {
4536
4647
  callback(undefined, data);
4537
4648
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-firebird",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
5
5
  "keywords": [
6
6
  "firebird",