node-firebird 1.1.8 → 1.1.10

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
@@ -63,28 +63,25 @@ options.database = 'database.fdb';
63
63
  options.user = 'SYSDBA';
64
64
  options.password = 'masterkey';
65
65
  options.lowercase_keys = false; // set to true to lowercase keys
66
- options.role = null; // default
67
- options.pageSize = 4096; // default when creating database
68
- options.pageSize = 4096; // default when creating database
66
+ options.role = null; // default
67
+ options.pageSize = 4096; // default when creating database
69
68
  options.retryConnectionInterval = 1000; // reconnect interval in case of connection drop
70
69
  options.blobAsText = false; // set to true to get blob as text, only affects blob subtype 1
71
- options.encoding = 'UTF-8'; // default encoding for connection is UTF-8
70
+ options.encoding = 'UTF8'; // default encoding for connection is UTF-8
71
+ options.wireCompression = false; // set to true for enable firebird compression on the wire (Work only on FB >= 3 and compression is enable on server (WireCompression = true in firebird.conf))
72
72
  ```
73
73
 
74
74
  ### Classic
75
75
 
76
76
  ```js
77
- Firebird.attach(options, function(err, db) {
78
-
79
- if (err)
80
- throw err;
81
-
82
- // db = DATABASE
83
- db.query('SELECT * FROM TABLE', function(err, result) {
84
- // IMPORTANT: close the connection
85
- db.detach();
86
- });
77
+ Firebird.attach(options, function (err, db) {
78
+ if (err) throw err;
87
79
 
80
+ // db = DATABASE
81
+ db.query('SELECT * FROM TABLE', function (err, result) {
82
+ // IMPORTANT: close the connection
83
+ db.detach();
84
+ });
88
85
  });
89
86
  ```
90
87
 
@@ -95,16 +92,14 @@ Firebird.attach(options, function(err, db) {
95
92
  var pool = Firebird.pool(5, options);
96
93
 
97
94
  // Get a free pool
98
- pool.get(function(err, db) {
99
-
100
- if (err)
101
- throw err;
95
+ pool.get(function (err, db) {
96
+ if (err) throw err;
102
97
 
103
- // db = DATABASE
104
- db.query('SELECT * FROM TABLE', function(err, result) {
105
- // IMPORTANT: release the pool connection
106
- db.detach();
107
- });
98
+ // db = DATABASE
99
+ db.query('SELECT * FROM TABLE', function (err, result) {
100
+ // IMPORTANT: release the pool connection
101
+ db.detach();
102
+ });
108
103
  });
109
104
 
110
105
  // Destroy pool
@@ -119,7 +114,21 @@ pool.destroy();
119
114
  - `db.execute(query, [params], function(err, result))` - classic query, returns Array of Array
120
115
  - `db.sequentially(query, [params], function(row, index), function(err))` - sequentially query
121
116
  - `db.detach(function(err))` detach a database
122
- - `db.transaction(isolation, function(err, transaction))` create transaction
117
+ - `db.transaction(options, function(err, transaction))` create transaction
118
+
119
+ ### Transaction options
120
+
121
+ ```js
122
+ const options = {
123
+ autoCommit: false,
124
+ autoUndo: true,
125
+ isolation: Firebird.ISOLATION_READ_COMMITTED,
126
+ ignoreLimbo: false,
127
+ readOnly: false,
128
+ wait: true,
129
+ waitTimeout: 0,
130
+ };
131
+ ```
123
132
 
124
133
  ### Transaction methods
125
134
 
@@ -135,187 +144,192 @@ pool.destroy();
135
144
  ### Parameters
136
145
 
137
146
  ```js
138
- Firebird.attach(options, function(err, db) {
139
-
140
- if (err)
141
- throw err;
142
-
143
- // db = DATABASE
144
- db.query('INSERT INTO USERS (ID, ALIAS, CREATED) VALUES(?, ?, ?) RETURNING ID', [1, 'Pe\'ter', new Date()], function(err, result) {
145
- console.log(result[0].id);
146
- db.query('SELECT * FROM USERS WHERE Alias=?', ['Peter'], function(err, result) {
147
- console.log(result);
148
- db.detach();
149
- });
150
- });
147
+ Firebird.attach(options, function (err, db) {
148
+ if (err) throw err;
149
+
150
+ // db = DATABASE
151
+ db.query(
152
+ 'INSERT INTO USERS (ID, ALIAS, CREATED) VALUES(?, ?, ?) RETURNING ID',
153
+ [1, "Pe'ter", new Date()],
154
+ function (err, result) {
155
+ console.log(result[0].id);
156
+ db.query(
157
+ 'SELECT * FROM USERS WHERE Alias=?',
158
+ ['Peter'],
159
+ function (err, result) {
160
+ console.log(result);
161
+ db.detach();
162
+ }
163
+ );
164
+ }
165
+ );
151
166
  });
152
167
  ```
153
168
 
154
169
  ### BLOB (stream)
155
170
 
156
171
  ```js
157
- Firebird.attach(options, function(err, db) {
158
-
159
- if (err)
160
- throw err;
161
-
162
- // db = DATABASE
163
- // INSERT STREAM as BLOB
164
- db.query('INSERT INTO USERS (ID, ALIAS, FILE) VALUES(?, ?, ?)', [1, 'Peter', fs.createReadStream('/users/image.jpg')], function(err, result) {
165
- // IMPORTANT: close the connection
166
- db.detach();
167
- });
172
+ Firebird.attach(options, function (err, db) {
173
+ if (err) throw err;
174
+
175
+ // db = DATABASE
176
+ // INSERT STREAM as BLOB
177
+ db.query(
178
+ 'INSERT INTO USERS (ID, ALIAS, FILE) VALUES(?, ?, ?)',
179
+ [1, 'Peter', fs.createReadStream('/users/image.jpg')],
180
+ function (err, result) {
181
+ // IMPORTANT: close the connection
182
+ db.detach();
183
+ }
184
+ );
168
185
  });
169
186
  ```
170
187
 
171
188
  ### BLOB (buffer)
172
189
 
173
190
  ```js
174
- Firebird.attach(options, function(err, db) {
175
-
176
- if (err)
177
- throw err;
178
-
179
- // db = DATABASE
180
- // INSERT BUFFER as BLOB
181
- db.query('INSERT INTO USERS (ID, ALIAS, FILE) VALUES(?, ?, ?)', [1, 'Peter', fs.readFileSync('/users/image.jpg')], function(err, result) {
182
- // IMPORTANT: close the connection
183
- db.detach();
184
- });
191
+ Firebird.attach(options, function (err, db) {
192
+ if (err) throw err;
193
+
194
+ // db = DATABASE
195
+ // INSERT BUFFER as BLOB
196
+ db.query(
197
+ 'INSERT INTO USERS (ID, ALIAS, FILE) VALUES(?, ?, ?)',
198
+ [1, 'Peter', fs.readFileSync('/users/image.jpg')],
199
+ function (err, result) {
200
+ // IMPORTANT: close the connection
201
+ db.detach();
202
+ }
203
+ );
185
204
  });
186
205
  ```
187
206
 
188
207
  ### Reading Blobs (Asynchronous)
189
208
 
190
209
  ```js
191
- Firebird.attach(options, function(err, db) {
192
-
193
- if (err)
194
- throw err;
210
+ Firebird.attach(options, function (err, db) {
211
+ if (err) throw err;
195
212
 
196
- // db = DATABASE
197
- db.query('SELECT ID, ALIAS, USERPICTURE FROM USER', function(err, rows) {
213
+ // db = DATABASE
214
+ db.query('SELECT ID, ALIAS, USERPICTURE FROM USER', function (err, rows) {
215
+ if (err) throw err;
198
216
 
199
- if (err)
200
- throw err;
217
+ // first row
218
+ rows[0].userpicture(function (err, name, e) {
219
+ if (err) throw err;
201
220
 
202
- // first row
203
- rows[0].userpicture(function(err, name, e) {
221
+ // +v0.2.4
222
+ // e.pipe(writeStream/Response);
204
223
 
205
- if (err)
206
- throw err;
207
-
208
- // +v0.2.4
209
- // e.pipe(writeStream/Response);
210
-
211
- // e === EventEmitter
212
- e.on('data', function(chunk) {
213
- // reading data
214
- });
215
-
216
- e.on('end', function() {
217
- // end reading
218
- // IMPORTANT: close the connection
219
- db.detach();
220
- });
221
- });
224
+ // e === EventEmitter
225
+ e.on('data', function (chunk) {
226
+ // reading data
227
+ });
222
228
 
229
+ e.on('end', function () {
230
+ // end reading
231
+ // IMPORTANT: close the connection
232
+ db.detach();
233
+ });
223
234
  });
235
+ });
224
236
  });
225
237
  ```
226
238
 
227
239
  ### Reading Multiples Blobs (Asynchronous)
240
+
228
241
  ```js
229
242
  Firebird.attach(options, (err, db) => {
230
- if (err)
231
- throw err;
232
-
233
- db.transaction(Firebird.ISOLATION_READ_COMMITTED, (err, transaction) => {
234
- if (err) {
235
- throw err;
236
- }
243
+ if (err) throw err;
237
244
 
238
- transaction.query('SELECT FIRST 10 * FROM JOB', (err, result) => {
239
- if (err) {
240
- transaction.rollback();
241
- return;
242
- }
245
+ db.transaction(Firebird.ISOLATION_READ_COMMITTED, (err, transaction) => {
246
+ if (err) {
247
+ throw err;
248
+ }
243
249
 
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;
250
+ transaction.query('SELECT FIRST 10 * FROM JOB', (err, result) => {
251
+ if (err) {
252
+ transaction.rollback();
253
+ return;
254
+ }
255
+
256
+ const arrBlob = [];
257
+ for (const item of result) {
258
+ const fields = Object.keys(item);
259
+ for (const key of fields) {
260
+ if (typeof item[key] === 'function') {
261
+ item[key] = new Promise((resolve, reject) => {
262
+ // the same transaction is used (better performance)
263
+ // this is optional
264
+ item[key](transaction, (error, name, event, row) => {
265
+ if (error) {
266
+ return reject(error);
275
267
  }
276
268
 
277
- transaction.commit((err) => {
278
- if (err) {
279
- transaction.rollback();
280
- return;
281
- }
282
-
283
- db.detach();
284
- console.log(result);
269
+ // reading data
270
+ let value = '';
271
+ event.on('data', (chunk) => {
272
+ value += chunk.toString('binary');
285
273
  });
286
- }).catch((err) => {
287
- transaction.rollback();
274
+ event.on('end', () => {
275
+ resolve({ value, column: name, row });
276
+ });
277
+ });
288
278
  });
279
+ arrBlob.push(item[key]);
280
+ }
281
+ }
282
+ }
283
+
284
+ Promise.all(arrBlob)
285
+ .then((blobs) => {
286
+ for (const blob of blobs) {
287
+ result[blob.row][blob.column] = blob.value;
288
+ }
289
+
290
+ transaction.commit((err) => {
291
+ if (err) {
292
+ transaction.rollback();
293
+ return;
294
+ }
295
+
296
+ db.detach();
297
+ console.log(result);
298
+ });
299
+ })
300
+ .catch((err) => {
301
+ transaction.rollback();
289
302
  });
290
303
  });
304
+ });
291
305
  });
292
306
  ```
293
307
 
294
308
  ### Streaming a big data
295
309
 
296
310
  ```js
297
- Firebird.attach(options, function(err, db) {
298
-
299
- if (err)
300
- throw err;
301
-
302
- // db = DATABASE
303
- db.sequentially('SELECT * FROM BIGTABLE', function(row, index) {
304
-
305
- // EXAMPLE
306
- stream.write(JSON.stringify(row));
307
-
308
- }, function(err) {
309
- // END
310
- // IMPORTANT: close the connection
311
- db.detach();
312
- });
311
+ Firebird.attach(options, function (err, db) {
312
+ if (err) throw err;
313
+
314
+ // db = DATABASE
315
+ db.sequentially(
316
+ 'SELECT * FROM BIGTABLE',
317
+ function (row, index) {
318
+ // EXAMPLE
319
+ stream.write(JSON.stringify(row));
320
+ },
321
+ function (err) {
322
+ // END
323
+ // IMPORTANT: close the connection
324
+ db.detach();
325
+ }
326
+ );
313
327
  });
314
328
  ```
315
329
 
316
330
  ### Transactions
317
331
 
318
- __Transaction types:__
332
+ **Transaction types:**
319
333
 
320
334
  - `Firebird.ISOLATION_READ_UNCOMMITTED`
321
335
  - `Firebird.ISOLATION_READ_COMMITTED`
@@ -324,77 +338,67 @@ __Transaction types:__
324
338
  - `Firebird.ISOLATION_READ_COMMITTED_READ_ONLY`
325
339
 
326
340
  ```js
327
- Firebird.attach(options, function(err, db) {
328
-
329
- if (err)
330
- throw err;
331
-
332
- // db = DATABASE
333
- db.transaction(Firebird.ISOLATION_READ_COMMITTED, function(err, transaction) {
334
- transaction.query('INSERT INTO users VALUE(?,?)', [1, 'Janko'], function(err, result) {
335
-
336
- if (err) {
337
- transaction.rollback();
338
- return;
339
- }
340
-
341
- transaction.commit(function(err) {
342
- if (err)
343
- transaction.rollback();
344
- else
345
- db.detach();
346
- });
347
- });
348
- });
341
+ Firebird.attach(options, function (err, db) {
342
+ if (err) throw err;
343
+
344
+ // db = DATABASE
345
+ db.transaction(
346
+ Firebird.ISOLATION_READ_COMMITTED,
347
+ function (err, transaction) {
348
+ transaction.query(
349
+ 'INSERT INTO users VALUE(?,?)',
350
+ [1, 'Janko'],
351
+ function (err, result) {
352
+ if (err) {
353
+ transaction.rollback();
354
+ return;
355
+ }
356
+
357
+ transaction.commit(function (err) {
358
+ if (err) transaction.rollback();
359
+ else db.detach();
360
+ });
361
+ }
362
+ );
363
+ }
364
+ );
349
365
  });
350
366
  ```
351
367
 
352
368
  ### Events
353
369
 
354
370
  ```js
355
- Firebird.attach(options, function(err, db) {
371
+ Firebird.attach(options, function (err, db) {
372
+ if (err) throw err;
356
373
 
357
- if (err)
358
- throw err;
374
+ db.on('row', function (row, index, isObject) {
375
+ // index === Number
376
+ // isObject === is row object or array?
377
+ });
359
378
 
360
- db.on('row', function(row, index, isObject) {
361
- // index === Number
362
- // isObject === is row object or array?
363
- });
379
+ db.on('result', function (result) {
380
+ // result === Array
381
+ });
364
382
 
365
- db.on('result', function(result) {
366
- // result === Array
367
- });
383
+ db.on('attach', function () {});
368
384
 
369
- db.on('attach', function() {
385
+ db.on('detach', function (isPoolConnection) {
386
+ // isPoolConnection == Boolean
387
+ });
370
388
 
371
- });
372
-
373
- db.on('detach', function(isPoolConnection) {
374
- // isPoolConnection == Boolean
375
- });
376
-
377
- db.on('reconnect', function() {
389
+ db.on('reconnect', function () {});
378
390
 
379
- });
380
-
381
- db.on('error', function(err) {
391
+ db.on('error', function (err) {});
382
392
 
383
- });
393
+ db.on('transaction', function (isolation) {
394
+ // isolation === Number
395
+ });
384
396
 
385
- db.on('transaction', function(isolation) {
386
- // isolation === Number
387
- });
397
+ db.on('commit', function () {});
388
398
 
389
- db.on('commit', function() {
399
+ db.on('rollback', function () {});
390
400
 
391
- });
392
-
393
- db.on('rollback', function() {
394
-
395
- });
396
-
397
- db.detach();
401
+ db.detach();
398
402
  });
399
403
  ```
400
404
 
@@ -402,8 +406,9 @@ Firebird.attach(options, function(err, db) {
402
406
 
403
407
  ```js
404
408
  var sql1 = 'SELECT * FROM TBL_USER WHERE ID>' + Firebird.escape(1);
405
- var sql2 = 'SELECT * FROM TBL_USER WHERE NAME=' + Firebird.escape('Pe\'er');
406
- var sql3 = 'SELECT * FROM TBL_USER WHERE CREATED<=' + Firebird.escape(new Date());
409
+ var sql2 = 'SELECT * FROM TBL_USER WHERE NAME=' + Firebird.escape("Pe'er");
410
+ var sql3 =
411
+ 'SELECT * FROM TBL_USER WHERE CREATED<=' + Firebird.escape(new Date());
407
412
  var sql4 = 'SELECT * FROM TBL_USER WHERE NEWSLETTER=' + Firebird.escape(true);
408
413
 
409
414
  // or db.escape()
@@ -419,19 +424,20 @@ console.log(sql4);
419
424
  ```js
420
425
  var { GDSCode } = require('node-firebird/lib/gdscodes');
421
426
  /*...*/
422
- db.query('insert into my_table(id, name) values (?, ?)', [1, 'John Doe'],
423
- function (err) {
424
- if(err.gdscode == GDSCode.UNIQUE_KEY_VIOLATION){
425
- console.log('constraint name:'+ err.gdsparams[0]);
426
- console.log('table name:'+ err.gdsparams[0]);
427
- /*...*/
428
- }
429
- /*...*/
430
- });
431
-
427
+ db.query(
428
+ 'insert into my_table(id, name) values (?, ?)',
429
+ [1, 'John Doe'],
430
+ function (err) {
431
+ if (err.gdscode == GDSCode.UNIQUE_KEY_VIOLATION) {
432
+ console.log('constraint name:' + err.gdsparams[0]);
433
+ console.log('table name:' + err.gdsparams[0]);
434
+ /*...*/
435
+ }
436
+ /*...*/
437
+ }
438
+ );
432
439
  ```
433
440
 
434
-
435
441
  ### Service Manager functions
436
442
 
437
443
  - backup
@@ -523,59 +529,62 @@ const RESTORE_OPTS = {
523
529
  Firebird.attach(config, (err, srv) => {
524
530
  srv.restore(RESTORE_OPTS, (err, data) => {
525
531
  data.on('data', () => {});
526
- data.on('end', () =>
527
- srv.detach();
532
+ data.on('end', () =>{
533
+ srv.detach();})
528
534
  });
529
535
  });
530
- });
531
536
  ```
532
537
 
533
538
  ### getLog and getFbserverInfos Service examples with use of stream and object return
534
539
 
535
540
  ```js
536
- fb.attach(_connection, function(err, svc) {
537
- if (err)
538
- return;
539
- // all function that return a stream take two optional parameter
540
- // optread => byline or buffer byline use isc_info_svc_line and buffer use isc_info_svc_to_eof
541
- // buffersize => is the buffer for service manager it can't exceed 8ko (i'm not sure)
542
-
543
- svc.getLog({optread:'buffer', buffersize:2048}, function (err, data) {
544
- // data is a readablestream that contain the firebird.log file
545
- console.log(err);
546
- data.on('data', function (data) {
547
- console.log(data.toString());
548
- });
549
- data.on('end', function() {
550
- console.log('finish');
551
- });
552
- });
553
-
554
- // an other exemple to use function that return object
555
- svc.getFbserverInfos(
556
- {
557
- "dbinfo" : true,
558
- "fbconfig" : true,
559
- "svcversion" : true,
560
- "fbversion" : true,
561
- "fbimplementation" : true,
562
- "fbcapatibilities" : true,
563
- "pathsecuritydb" : true,
564
- "fbenv" : true,
565
- "fbenvlock" : true,
566
- "fbenvmsg" : true
567
- }, {}, function (err, data) {
568
- console.log(err);
569
- console.log(data);
570
- });
541
+ fb.attach(_connection, function (err, svc) {
542
+ if (err) return;
543
+ // all function that return a stream take two optional parameter
544
+ // optread => byline or buffer byline use isc_info_svc_line and buffer use isc_info_svc_to_eof
545
+ // buffersize => is the buffer for service manager it can't exceed 8ko (i'm not sure)
546
+
547
+ svc.getLog({ optread: 'buffer', buffersize: 2048 }, function (err, data) {
548
+ // data is a readablestream that contain the firebird.log file
549
+ console.log(err);
550
+ data.on('data', function (data) {
551
+ console.log(data.toString());
552
+ });
553
+ data.on('end', function () {
554
+ console.log('finish');
555
+ });
556
+ });
557
+
558
+ // an other exemple to use function that return object
559
+ svc.getFbserverInfos(
560
+ {
561
+ dbinfo: true,
562
+ fbconfig: true,
563
+ svcversion: true,
564
+ fbversion: true,
565
+ fbimplementation: true,
566
+ fbcapatibilities: true,
567
+ pathsecuritydb: true,
568
+ fbenv: true,
569
+ fbenvlock: true,
570
+ fbenvmsg: true,
571
+ },
572
+ {},
573
+ function (err, data) {
574
+ console.log(err);
575
+ console.log(data);
576
+ }
577
+ );
571
578
  });
572
-
573
579
  ```
574
580
 
575
581
  ### Charset for database connection is always UTF-8
576
582
 
577
- node-firebird doesn't let you choose the charset connection, it will always use UTF-8.
578
- Node is unicode, no matter if your database is using another charset to store string or blob, Firebird will transliterate automatically.
583
+ Node Firebird uses UTF-8 as the default charset. If you want a different one, such as Latin1, you will need to go into the library and modify the default_encoding in the index.js file
584
+
585
+ ```js
586
+ const default_encoding = 'latin1';
587
+ ```
579
588
 
580
589
  This is why you should use **Firebird 2.5** server at least.
581
590
 
@@ -587,7 +596,7 @@ for Firebird 3.0 you need to add the following in firebird.conf according to Fir
587
596
 
588
597
  ```bash
589
598
  AuthServer = Srp, Legacy_Auth
590
- WireCrypt = Disabled
599
+ WireCrypt = Enabled
591
600
  UserManager = Legacy_UserManager
592
601
  ```
593
602
 
@@ -597,13 +606,25 @@ for Firebird 4.0 you need to add the following in firebird.conf according to Fir
597
606
 
598
607
  ```bash
599
608
  AuthServer = Srp256, Srp, Legacy_Auth
600
- WireCrypt = Disabled
609
+ WireCrypt = Enabled
601
610
  UserManager = Legacy_UserManager
602
611
  ```
603
612
 
604
613
  Please read also Authorization with Firebird 2.5 client library from Firebird 4 migration guide
605
614
  <https://ib-aid.com/download/docs/fb4migrationguide.html#_authorization_with_firebird_2_5_client_library_fbclient_dll>
606
615
 
616
+ Firebird 5 wire protocol is not supported yet so
617
+ for Firebird 5.0 you need to add the following in firebird.conf according to Firebird release notes
618
+ <https://firebirdsql.org/file/documentation/release_notes/html/en/4_0/rlsnotes40.html#rnfb40-config-srp256>
619
+
620
+ ```bash
621
+ AuthServer = Srp256, Srp, Legacy_Auth
622
+ WireCrypt = Enabled
623
+ UserManager = Legacy_UserManager
624
+ ```
625
+
626
+ Please read also Authorization with Firebird 2.5 client library from Firebird 5 migration guide
627
+ <https://ib-aid.com/download/docs/fb5migrationguide.html#_authorization_from_firebird_2_5_client_libraries>
607
628
 
608
629
 
609
630
  ## Contributors
@@ -614,7 +635,6 @@ Please read also Authorization with Firebird 2.5 client library from Firebird 4
614
635
 
615
636
  [license-image]: http://img.shields.io/badge/license-MOZILLA-blue.svg?style=flat
616
637
  [license-url]: LICENSE
617
-
618
638
  [npm-url]: https://npmjs.org/package/node-firebird
619
639
  [npm-version-image]: http://img.shields.io/npm/v/node-firebird.svg?style=flat
620
640
  [npm-downloads-image]: http://img.shields.io/npm/dm/node-firebird.svg?style=flat