node-firebird 2.4.1 → 2.5.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
@@ -6,15 +6,26 @@
6
6
 
7
7
  [![NPM](https://nodei.co/npm/node-firebird.png?downloads=true&downloadRank=true)](https://nodei.co/npm/node-firebird/)
8
8
 
9
- [Firebird forum](https://groups.google.com/forum/#!forum/node-firebird) on Google Groups.
10
-
11
- ## Firebird database on social networks
12
-
13
- - [Firebird on Twitter](https://twitter.com/firebirdsql/)
14
- - [Firebird on Facebook](https://www.facebook.com/FirebirdSQL)
15
-
16
- - [Firebird documentation](https://firebirdsql.org/en/documentation/)
17
- - [Firebird limits and data types](https://firebirdsql.org/en/firebird-technical-specifications/)
9
+ ## Table of contents
10
+
11
+ - [Installation](#installation)
12
+ - [Usage](#usage) — including [developing the driver](#developing-the-driver)
13
+ - [Promises and async/await](#promises-and-asyncawait) — the `*Async` API plus `withConnection` / `withTransaction` helpers
14
+ - [Connection types](#connection-types) — connection options, classic connections, pooling
15
+ - [Database object (db)](#database-object-db) — database, transaction and statement methods/options
16
+ - [Examples](#examples) — parametrized queries, BLOBs, streaming big data, transactions, driver events, database events (POST_EVENT), service manager, charsets/encoding, Firebird 3.0–6.0 features
17
+ - [Extensive Examples](#extensive-examples) — DECFLOAT/INT128, statement timeouts, scrollable cursors, RETURNING multiple rows, SKIP LOCKED, advanced pooling
18
+ - [Using node-firebird with Express.js](#using-node-firebird-with-expressjs)
19
+ - [FAQ](#faq)
20
+ - [Contributing](#contributing) · [Contributors](#contributors)
21
+
22
+ ## Community & resources
23
+
24
+ - [Firebird forum](https://groups.google.com/forum/#!forum/node-firebird) on Google Groups — questions and discussion
25
+ - [GitHub issues](https://github.com/hgourvest/node-firebird/issues) — bug reports and feature requests
26
+ - [ROADMAP](ROADMAP.md) — planned work and protocol implementation status
27
+ - Driver internals: [SRP authentication protocol](docs/SRP_PROTOCOL.md) · [database encryption key callback](docs/ENCRYPTION_CALLBACK.md)
28
+ - Firebird itself: [documentation](https://firebirdsql.org/en/documentation/) · [limits and data types](https://firebirdsql.org/en/firebird-technical-specifications/) · [Twitter/X](https://twitter.com/firebirdsql/) · [Facebook](https://www.facebook.com/FirebirdSQL)
18
29
 
19
30
  ## Installation
20
31
 
@@ -88,6 +99,55 @@ npm test # build + run the vitest suite (unit + integration)
88
99
  - `Firebird.create(options, function(err, db))` create a database
89
100
  - `Firebird.attachOrCreate(options, function(err, db))` attach or create database
90
101
  - `Firebird.pool(max, options) -> return {Object}` create a connection pooling
102
+ - `Firebird.attachAsync(options) -> Promise<Database>`, `createAsync`, `attachOrCreateAsync`, `dropAsync` — promise counterparts, see [Promises and async/await](#promises-and-asyncawait)
103
+
104
+ ## Promises and async/await
105
+
106
+ Every callback API has a promise-returning counterpart with an `Async`
107
+ suffix, plus two higher-level helpers: `pool.withConnection()` and
108
+ `db.withTransaction()`. The callback API is unchanged and the two styles can
109
+ be mixed freely, though sticking to one per project keeps code readable.
110
+
111
+ ```js
112
+ const Firebird = require('node-firebird');
113
+
114
+ const pool = Firebird.pool(5, options);
115
+
116
+ // acquire → work → always release, even when `work` throws
117
+ const users = await pool.withConnection((db) =>
118
+ db.queryAsync('SELECT id, name FROM users WHERE plan = ?', ['pro'])
119
+ );
120
+
121
+ // commit on success, rollback on error
122
+ await pool.withConnection((db) =>
123
+ db.withTransaction(async (transaction) => {
124
+ await transaction.executeAsync('INSERT INTO audit (msg) VALUES (?)', ['signup']);
125
+ await transaction.executeAsync('UPDATE stats SET signups = signups + 1');
126
+ })
127
+ );
128
+
129
+ await pool.destroyAsync();
130
+ ```
131
+
132
+ Available wrappers:
133
+
134
+ - **module** — `Firebird.attachAsync(options)`, `createAsync`, `attachOrCreateAsync`, `dropAsync`; resolve with a `Database` (or a `ServiceManager` when `options.manager` is `true`)
135
+ - **pool** — `pool.getAsync()`, `pool.destroyAsync()`, `pool.withConnection(work)`
136
+ - **database** — `db.queryAsync(sql, params?, options?)`, `executeAsync`, `sequentiallyAsync(sql, params?, onRow, options?)`, `transactionAsync(options?)`, `newStatementAsync(sql)`, `attachEventAsync()`, `detachAsync()`, `dropAsync()`, `db.withTransaction(work, options?)`
137
+ - **transaction** — `queryAsync`, `executeAsync`, `sequentiallyAsync`, `newStatementAsync`, `commitAsync`, `rollbackAsync`, `commitRetainingAsync`, `rollbackRetainingAsync`
138
+ - **statement** — `executeAsync(transaction, params?, options?)`, `fetchAsync`, `fetchScrollAsync`, `fetchAllAsync`, `closeAsync`, `dropAsync`, `releaseAsync`
139
+
140
+ Notes:
141
+
142
+ - Rejections are always `Error` instances carrying the usual Firebird
143
+ properties (`err.gdscode`, `err.gdsparams`) — see [Using GDS codes](#using-gds-codes).
144
+ - `queryAsync` / `executeAsync` resolve with the rows only; column metadata
145
+ is currently available through the callback API only.
146
+ - An un-`await`ed rejected promise becomes an unhandled rejection instead of
147
+ a callback error — prefer the `withConnection` / `withTransaction` helpers,
148
+ which guarantee cleanup on every path.
149
+ - TypeScript: the async methods accept a row-shape generic, e.g.
150
+ `db.queryAsync<User>(sql, params)` returns `Promise<User[]>`.
91
151
 
92
152
  ## Connection types
93
153
 
@@ -1220,6 +1280,8 @@ process.on('SIGTERM', function() {
1220
1280
 
1221
1281
  node-firebird works well with Express, but because the driver is connection/pool based rather than an ORM with automatic connection management, a few request-lifecycle patterns keep connections from leaking under load. This section documents the recommended architecture, referenced from the [roadmap](ROADMAP.md#2-expressjs-support-first-class-integration).
1222
1282
 
1283
+ > The driver now ships native `pool.withConnection()` and `db.withTransaction()` helpers (see [Promises and async/await](#promises-and-asyncawait)) that supersede the hand-rolled `withConnection` / `transactional` helpers shown in the examples below. The examples remain valid and show what the helpers do under the hood.
1284
+
1223
1285
  ### Recommended architecture: one pool per app, created at startup
1224
1286
 
1225
1287
  Create a single `Firebird.pool(...)` when the app boots and reuse it for the lifetime of the process. Do **not** call `Firebird.pool()` or `Firebird.attach()` inside a request handler — that opens a new socket (or an entire new pool) on every request and will exhaust server and database resources under load.
@@ -1522,11 +1584,27 @@ db.query('SELECT * FROM ACTORS WHERE NAME LIKE ?', ['James Wick%'], function (er
1522
1584
  });
1523
1585
  ```
1524
1586
 
1587
+ ## Contributing
1588
+
1589
+ Contributions are welcome — code, documentation, and bug reports alike.
1590
+
1591
+ - **Found a bug or have a question?** Open a [GitHub issue](https://github.com/hgourvest/node-firebird/issues). Please include your Node.js version, your Firebird server version, and a minimal reproduction (SQL plus code).
1592
+ - **Want to write code?** See [Developing the driver](#developing-the-driver) for the build and test setup, and the [ROADMAP](ROADMAP.md) for ideas on what to pick up. Pull requests against `master` should come with tests and pass `npm test`, `npm run typecheck` and `npm run lint`.
1593
+ - **Improving the documentation** is just as valuable — clearer examples and FAQ entries help everyone.
1594
+
1525
1595
  ## Contributors
1526
1596
 
1527
- - Henri Gourvest, <https://github.com/hgourvest>
1528
- - Popa Marius Adrian, <https://github.com/mariuz>
1529
- - Peter Širka, <https://github.com/petersirka>
1597
+ `node-firebird` was created by **Henri Gourvest** and is maintained by **Popa Marius Adrian**, with major early contributions from **Peter Širka** (pooling, sequential selects, driver events).
1598
+
1599
+ - Henri Gourvest — author, <https://github.com/hgourvest>
1600
+ - Popa Marius Adrian — maintainer, <https://github.com/mariuz>
1601
+ - Peter Širka — <https://github.com/petersirka>
1602
+
1603
+ …and many more — see the full [list of contributors](https://github.com/hgourvest/node-firebird/graphs/contributors).
1604
+
1605
+ <a href="https://github.com/hgourvest/node-firebird/graphs/contributors">
1606
+ <img src="https://contrib.rocks/image?repo=hgourvest/node-firebird" alt="Contributors" />
1607
+ </a>
1530
1608
 
1531
1609
  [license-image]: http://img.shields.io/badge/license-MOZILLA-blue.svg?style=flat
1532
1610
  [license-url]: LICENSE
package/lib/callback.d.ts CHANGED
@@ -16,5 +16,18 @@ export interface FbError extends Error {
16
16
  }
17
17
  export type SimpleCallback = (err?: any) => void;
18
18
  export type Callback<T = any> = (err?: any, result?: T) => void;
19
+ /**
20
+ * Normalize the values the driver passes as the callback error argument.
21
+ * Most code paths already deliver Error instances, but a few older ones
22
+ * pass plain objects (status vectors, `{error, message}` wrappers). A
23
+ * Promise must reject with an Error, so wrap those while preserving all
24
+ * their properties (gdscode, gdsparams, status, sqlcode, ...).
25
+ */
26
+ export declare function toError(err: any): Error;
27
+ /**
28
+ * Run a callback-style operation and return a Promise for its result.
29
+ * Usage: fromCallback<Database>(cb => attach(options, cb))
30
+ */
31
+ export declare function fromCallback<T = any>(executor: (cb: Callback<T>) => void): Promise<T>;
19
32
  export declare function doError(obj: any, callback?: (...args: any[]) => void): void;
20
33
  export declare function doCallback<T>(obj: T, callback?: Callback<T>): void;
package/lib/callback.js CHANGED
@@ -1,7 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toError = toError;
4
+ exports.fromCallback = fromCallback;
3
5
  exports.doError = doError;
4
6
  exports.doCallback = doCallback;
7
+ /**
8
+ * Normalize the values the driver passes as the callback error argument.
9
+ * Most code paths already deliver Error instances, but a few older ones
10
+ * pass plain objects (status vectors, `{error, message}` wrappers). A
11
+ * Promise must reject with an Error, so wrap those while preserving all
12
+ * their properties (gdscode, gdsparams, status, sqlcode, ...).
13
+ */
14
+ function toError(err) {
15
+ if (err instanceof Error)
16
+ return err;
17
+ var error = new Error(err != null && typeof err === 'object' && err.message ? err.message : String(err));
18
+ if (err != null && typeof err === 'object')
19
+ Object.assign(error, err);
20
+ return error;
21
+ }
22
+ /**
23
+ * Run a callback-style operation and return a Promise for its result.
24
+ * Usage: fromCallback<Database>(cb => attach(options, cb))
25
+ */
26
+ function fromCallback(executor) {
27
+ return new Promise(function (resolve, reject) {
28
+ executor(function (err, result) {
29
+ if (err)
30
+ reject(toError(err));
31
+ else
32
+ resolve(result);
33
+ });
34
+ });
35
+ }
5
36
  function doError(obj, callback) {
6
37
  if (callback)
7
38
  callback(obj);
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import Connection from './wire/connection';
2
2
  import { escape as escapeValue } from './utils';
3
- import type { Options, SvcMgrOptions, DatabaseCallback, ServiceManagerCallback, SimpleCallback, ConnectionPool } from './types';
3
+ import type { Options, SvcMgrOptions, DatabaseCallback, ServiceManagerCallback, SimpleCallback, ConnectionPool, Database, ServiceManager } from './types';
4
4
  export * from './types';
5
5
  export { GDSCode } from './gdscodes';
6
6
  export declare const AUTH_PLUGIN_LEGACY: string;
@@ -35,3 +35,8 @@ export declare function drop(options: Options, callback: SimpleCallback): void;
35
35
  export declare function create(options: Options, callback: DatabaseCallback): void;
36
36
  export declare function attachOrCreate(options: Options, callback: DatabaseCallback): void;
37
37
  export declare function pool(max: number, options: Options): ConnectionPool;
38
+ export declare function attachAsync(options: SvcMgrOptions): Promise<ServiceManager>;
39
+ export declare function attachAsync(options: Options): Promise<Database>;
40
+ export declare function createAsync(options: Options): Promise<Database>;
41
+ export declare function attachOrCreateAsync(options: Options): Promise<Database>;
42
+ export declare function dropAsync(options: Options): Promise<void>;
package/lib/index.js CHANGED
@@ -23,6 +23,10 @@ exports.drop = drop;
23
23
  exports.create = create;
24
24
  exports.attachOrCreate = attachOrCreate;
25
25
  exports.pool = pool;
26
+ exports.attachAsync = attachAsync;
27
+ exports.createAsync = createAsync;
28
+ exports.attachOrCreateAsync = attachOrCreateAsync;
29
+ exports.dropAsync = dropAsync;
26
30
  const const_1 = __importDefault(require("./wire/const"));
27
31
  const callback_1 = require("./callback");
28
32
  const connection_1 = __importDefault(require("./wire/connection"));
@@ -137,3 +141,15 @@ function attachOrCreate(options, callback) {
137
141
  function pool(max, options) {
138
142
  return new pool_1.default(attach, max, Object.assign({}, options, { isPool: true }));
139
143
  }
144
+ function attachAsync(options) {
145
+ return (0, callback_1.fromCallback)(function (cb) { attach(options, cb); });
146
+ }
147
+ function createAsync(options) {
148
+ return (0, callback_1.fromCallback)(function (cb) { create(options, cb); });
149
+ }
150
+ function attachOrCreateAsync(options) {
151
+ return (0, callback_1.fromCallback)(function (cb) { attachOrCreate(options, cb); });
152
+ }
153
+ function dropAsync(options) {
154
+ return (0, callback_1.fromCallback)(function (cb) { drop(options, cb); });
155
+ }
package/lib/pool.d.ts CHANGED
@@ -19,5 +19,12 @@ declare class Pool {
19
19
  get(callback: Callback): this;
20
20
  check(): this;
21
21
  destroy(callback?: (err?: any) => void): void;
22
+ getAsync(): Promise<any>;
23
+ destroyAsync(): Promise<void>;
24
+ /**
25
+ * Run `work` with a connection from the pool, returning it to the pool
26
+ * (detach) when the returned promise settles — success or failure.
27
+ */
28
+ withConnection<T>(work: (db: any) => Promise<T> | T): Promise<T>;
22
29
  }
23
30
  export = Pool;
package/lib/pool.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * Simple Pooling
5
5
  *
6
6
  ***************************************/
7
+ const callback_1 = require("./callback");
7
8
  class Pool {
8
9
  constructor(attach, max, options) {
9
10
  this.attach = attach;
@@ -178,5 +179,29 @@ class Pool {
178
179
  }
179
180
  });
180
181
  }
182
+ /* Promise / async-await API — wrappers over the callback methods above. */
183
+ getAsync() {
184
+ var self = this;
185
+ return (0, callback_1.fromCallback)(function (cb) { self.get(cb); });
186
+ }
187
+ destroyAsync() {
188
+ var self = this;
189
+ return (0, callback_1.fromCallback)(function (cb) { self.destroy(cb); });
190
+ }
191
+ /**
192
+ * Run `work` with a connection from the pool, returning it to the pool
193
+ * (detach) when the returned promise settles — success or failure.
194
+ */
195
+ async withConnection(work) {
196
+ const db = await this.getAsync();
197
+ try {
198
+ return await work(db);
199
+ }
200
+ finally {
201
+ // A pooled detach only returns the connection to the pool; do not
202
+ // let a detach hiccup mask the outcome of `work`.
203
+ await new Promise(function (resolve) { db.detach(function () { resolve(); }); });
204
+ }
205
+ }
181
206
  }
182
207
  module.exports = Pool;
package/lib/types.d.ts CHANGED
@@ -82,6 +82,18 @@ export interface Database {
82
82
  alterTablespace(name: string, filePath: string, callback?: QueryCallback): Database;
83
83
  dropTablespace(name: string, callback?: QueryCallback): Database;
84
84
  createSchema(schemaName: string, tablespaceName?: string | QueryCallback, callback?: QueryCallback): Database;
85
+ queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
86
+ executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
87
+ sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
88
+ sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
89
+ transactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
90
+ startTransactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
91
+ newStatementAsync(query: string): Promise<Statement>;
92
+ detachAsync(force?: boolean): Promise<void>;
93
+ dropAsync(): Promise<void>;
94
+ attachEventAsync(): Promise<any>;
95
+ /** Starts a transaction, commits when `work` resolves, rolls back when it rejects. */
96
+ withTransaction<T>(work: (transaction: Transaction) => Promise<T> | T, options?: TransactionOptions | Isolation): Promise<T>;
85
97
  }
86
98
  export interface Transaction {
87
99
  newStatement(query: string, callback: (err: Error | null, statement: Statement) => void): void;
@@ -92,6 +104,15 @@ export interface Transaction {
92
104
  commitRetaining(callback?: SimpleCallback): void;
93
105
  rollback(callback?: SimpleCallback): void;
94
106
  rollbackRetaining(callback?: SimpleCallback): void;
107
+ queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
108
+ executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
109
+ sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
110
+ sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
111
+ newStatementAsync(query: string): Promise<Statement>;
112
+ commitAsync(): Promise<void>;
113
+ commitRetainingAsync(): Promise<void>;
114
+ rollbackAsync(): Promise<void>;
115
+ rollbackRetainingAsync(): Promise<void>;
95
116
  }
96
117
  export interface Statement {
97
118
  close(callback?: SimpleCallback): void;
@@ -101,6 +122,13 @@ export interface Statement {
101
122
  fetch(transaction: Transaction, count: number, callback: QueryCallback): void;
102
123
  fetchScroll(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset: number, count: number, callback: QueryCallback): void;
103
124
  fetchAll(transaction: Transaction, callback: QueryCallback): void;
125
+ executeAsync(transaction: Transaction, params?: any[], options?: QueryOptions): Promise<any>;
126
+ fetchAsync(transaction: Transaction, count: number | 'all'): Promise<any>;
127
+ fetchScrollAsync(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset?: number, count?: number): Promise<any>;
128
+ fetchAllAsync(transaction: Transaction): Promise<any>;
129
+ closeAsync(): Promise<void>;
130
+ dropAsync(): Promise<void>;
131
+ releaseAsync(): Promise<void>;
104
132
  }
105
133
  export type SupportedCharacterSet = 'NONE' | 'CP943C' | 'DOS737' | 'DOS775' | 'DOS858' | 'DOS862' | 'DOS864' | 'DOS866' | 'DOS869' | 'GB18030' | 'GBK' | 'ISO8859_1' | 'ISO8859_2' | 'ISO8859_3' | 'ISO8859_4' | 'ISO8859_5' | 'ISO8859_6' | 'ISO8859_7' | 'ISO8859_8' | 'ISO8859_9' | 'ISO8859_13' | 'KOI8R' | 'KOI8U' | 'TIS620' | 'UTF8' | 'WIN1251' | 'WIN1252' | 'WIN1253' | 'WIN1254' | 'WIN1255' | 'WIN1256' | 'WIN1257' | 'WIN1258' | 'WIN_1258';
106
134
  export interface Options {
@@ -188,6 +216,10 @@ export interface SvcMgrOptions extends Options {
188
216
  export interface ConnectionPool {
189
217
  get(callback: DatabaseCallback): void;
190
218
  destroy(callback?: SimpleCallback): void;
219
+ getAsync(): Promise<Database>;
220
+ destroyAsync(): Promise<void>;
221
+ /** Acquire a connection, run `work`, always return the connection to the pool. */
222
+ withConnection<T>(work: (db: Database) => Promise<T> | T): Promise<T>;
191
223
  }
192
224
  export interface ReadableOptions {
193
225
  optread?: 'byline' | 'buffer';
@@ -1823,16 +1823,20 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
1823
1823
  public: BigInt('0x' + d.buffer.slice(keyStart, d.buffer.length).toString('utf8')),
1824
1824
  pluginName: accept.pluginName
1825
1825
  };
1826
- console.log('--- DEBUG SRP Handshake ---');
1827
- console.log('salt:', cnx.serverKeys.salt);
1828
- console.log('server public key:', cnx.serverKeys.public.toString(16));
1829
- console.log('client public key:', cnx.clientKeys.public.toString(16));
1830
- console.log('client private key:', cnx.clientKeys.private.toString(16));
1831
- console.log('hashAlgo:', accept.srpAlgo);
1826
+ if (process.env.FIREBIRD_DEBUG) {
1827
+ console.log('--- DEBUG SRP Handshake ---');
1828
+ console.log('salt:', cnx.serverKeys.salt);
1829
+ console.log('server public key:', cnx.serverKeys.public.toString(16));
1830
+ console.log('client public key:', cnx.clientKeys.public.toString(16));
1831
+ console.log('hashAlgo:', accept.srpAlgo);
1832
+ }
1832
1833
  const _t1 = Date.now();
1833
1834
  var proof = srp.clientProof(cnx.options.user.toUpperCase(), cnx.options.password, cnx.serverKeys.salt, cnx.clientKeys.public, cnx.serverKeys.public, cnx.clientKeys.private, accept.srpAlgo);
1834
- console.log('client proof M1:', proof.authData.toString(16));
1835
- console.log('client session key K:', proof.clientSessionKey.toString(16));
1835
+ if (process.env.FIREBIRD_DEBUG) {
1836
+ // Never log the private key or the session key: the
1837
+ // session key is the wire-encryption key material.
1838
+ console.log('client proof M1:', proof.authData.toString(16));
1839
+ }
1836
1840
  if (process.env.FIREBIRD_DEBUG) {
1837
1841
  console.log('[fb-debug] srp.clientProof(%s): %dms', accept.srpAlgo, Date.now() - _t1);
1838
1842
  }
@@ -1917,12 +1921,13 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
1917
1921
  Srp512: 'sha512'
1918
1922
  };
1919
1923
  var srpAlgo = crypto[pluginName];
1920
- console.log('--- DEBUG SRP Handshake ---');
1921
- console.log('salt:', cnx.serverKeys.salt);
1922
- console.log('server public key:', cnx.serverKeys.public.toString(16));
1923
- console.log('client public key:', cnx.clientKeys.public.toString(16));
1924
- console.log('client private key:', cnx.clientKeys.private.toString(16));
1925
- console.log('hashAlgo:', srpAlgo);
1924
+ if (process.env.FIREBIRD_DEBUG) {
1925
+ console.log('--- DEBUG SRP Handshake ---');
1926
+ console.log('salt:', cnx.serverKeys.salt);
1927
+ console.log('server public key:', cnx.serverKeys.public.toString(16));
1928
+ console.log('client public key:', cnx.clientKeys.public.toString(16));
1929
+ console.log('hashAlgo:', srpAlgo);
1930
+ }
1926
1931
  const _t1 = Date.now();
1927
1932
  var proof = srp.clientProof(cnx.options.user.toUpperCase(), cnx.options.password, cnx.serverKeys.salt, cnx.clientKeys.public, cnx.serverKeys.public, cnx.clientKeys.private, srpAlgo);
1928
1933
  if (process.env.FIREBIRD_DEBUG) {
@@ -53,5 +53,20 @@ declare class Database extends Events.EventEmitter {
53
53
  * @returns {Database}
54
54
  */
55
55
  createSchema(schemaName: string, tablespaceName?: string | ((err?: any) => void), callback?: any): this;
56
+ queryAsync(query: string, params?: any, options?: any): Promise<any[]>;
57
+ executeAsync(query: string, params?: any, options?: any): Promise<any[]>;
58
+ sequentiallyAsync(query: string, params?: any, on?: any, options?: any): Promise<void>;
59
+ transactionAsync(options?: any): Promise<any>;
60
+ startTransactionAsync(options?: any): Promise<any>;
61
+ newStatementAsync(query: string): Promise<any>;
62
+ detachAsync(force?: boolean): Promise<void>;
63
+ dropAsync(): Promise<void>;
64
+ attachEventAsync(): Promise<any>;
65
+ /**
66
+ * Run `work` inside a transaction: commits when the returned promise
67
+ * resolves, rolls back when it rejects (the original error is rethrown,
68
+ * even if the rollback itself fails).
69
+ */
70
+ withTransaction<T>(work: (transaction: any) => Promise<T> | T, options?: any): Promise<T>;
56
71
  }
57
72
  export = Database;
@@ -377,5 +377,71 @@ class Database extends events_1.default.EventEmitter {
377
377
  }
378
378
  return this.execute(sql, [], callback);
379
379
  }
380
+ /*
381
+ * Promise / async-await API.
382
+ * Each *Async method wraps its callback counterpart; the callback API
383
+ * stays untouched. Result metadata is only available through the
384
+ * callback API — the promises resolve with the rows alone.
385
+ */
386
+ queryAsync(query, params, options) {
387
+ var self = this;
388
+ return (0, callback_1.fromCallback)(function (cb) { self.query(query, params, cb, options); });
389
+ }
390
+ executeAsync(query, params, options) {
391
+ var self = this;
392
+ return (0, callback_1.fromCallback)(function (cb) { self.execute(query, params, cb, options); });
393
+ }
394
+ sequentiallyAsync(query, params, on, options) {
395
+ if (params instanceof Function) {
396
+ options = on;
397
+ on = params;
398
+ params = undefined;
399
+ }
400
+ var self = this;
401
+ return (0, callback_1.fromCallback)(function (cb) { self.sequentially(query, params, on, cb, options); });
402
+ }
403
+ transactionAsync(options) {
404
+ var self = this;
405
+ return (0, callback_1.fromCallback)(function (cb) { self.startTransaction(options, cb); });
406
+ }
407
+ startTransactionAsync(options) {
408
+ return this.transactionAsync(options);
409
+ }
410
+ newStatementAsync(query) {
411
+ var self = this;
412
+ return (0, callback_1.fromCallback)(function (cb) { self.newStatement(query, cb); });
413
+ }
414
+ detachAsync(force) {
415
+ var self = this;
416
+ return (0, callback_1.fromCallback)(function (cb) { self.detach(cb, force); });
417
+ }
418
+ dropAsync() {
419
+ var self = this;
420
+ return (0, callback_1.fromCallback)(function (cb) { self.drop(cb); });
421
+ }
422
+ attachEventAsync() {
423
+ var self = this;
424
+ return (0, callback_1.fromCallback)(function (cb) { self.attachEvent(cb); });
425
+ }
426
+ /**
427
+ * Run `work` inside a transaction: commits when the returned promise
428
+ * resolves, rolls back when it rejects (the original error is rethrown,
429
+ * even if the rollback itself fails).
430
+ */
431
+ async withTransaction(work, options) {
432
+ const transaction = await this.transactionAsync(options);
433
+ try {
434
+ const result = await work(transaction);
435
+ await (0, callback_1.fromCallback)(function (cb) { transaction.commit(cb); });
436
+ return result;
437
+ }
438
+ catch (err) {
439
+ try {
440
+ await (0, callback_1.fromCallback)(function (cb) { transaction.rollback(cb); });
441
+ }
442
+ catch { /* surface the original error, not the rollback failure */ }
443
+ throw err;
444
+ }
445
+ }
380
446
  }
381
447
  module.exports = Database;
@@ -21,5 +21,12 @@ declare class Statement {
21
21
  fetch(transaction: any, count: number | string, callback: (err: any, result?: any) => void): void;
22
22
  fetchScroll(transaction: any, direction: string | number, offset?: any, count?: any, callback?: any): void;
23
23
  fetchAll(transaction: any, callback: (err: any, result?: any) => void): void;
24
+ executeAsync(transaction: any, params?: any, options?: any): Promise<any>;
25
+ fetchAsync(transaction: any, count: number | string): Promise<any>;
26
+ fetchScrollAsync(transaction: any, direction: string | number, offset?: any, count?: any): Promise<any>;
27
+ fetchAllAsync(transaction: any): Promise<any>;
28
+ closeAsync(): Promise<void>;
29
+ dropAsync(): Promise<void>;
30
+ releaseAsync(): Promise<void>;
24
31
  }
25
32
  export = Statement;
@@ -4,6 +4,7 @@
4
4
  * Statement
5
5
  *
6
6
  ***************************************/
7
+ const callback_1 = require("../callback");
7
8
  class Statement {
8
9
  constructor(connection) {
9
10
  this.connection = connection;
@@ -48,5 +49,34 @@ class Statement {
48
49
  fetchAll(transaction, callback) {
49
50
  this.connection.fetchAll(this, transaction, callback);
50
51
  }
52
+ /* Promise / async-await API — wrappers over the callback methods above. */
53
+ executeAsync(transaction, params, options) {
54
+ var self = this;
55
+ return (0, callback_1.fromCallback)(function (cb) { self.execute(transaction, params, cb, options); });
56
+ }
57
+ fetchAsync(transaction, count) {
58
+ var self = this;
59
+ return (0, callback_1.fromCallback)(function (cb) { self.fetch(transaction, count, cb); });
60
+ }
61
+ fetchScrollAsync(transaction, direction, offset, count) {
62
+ var self = this;
63
+ return (0, callback_1.fromCallback)(function (cb) { self.fetchScroll(transaction, direction, offset, count, cb); });
64
+ }
65
+ fetchAllAsync(transaction) {
66
+ var self = this;
67
+ return (0, callback_1.fromCallback)(function (cb) { self.fetchAll(transaction, cb); });
68
+ }
69
+ closeAsync() {
70
+ var self = this;
71
+ return (0, callback_1.fromCallback)(function (cb) { self.close(cb); });
72
+ }
73
+ dropAsync() {
74
+ var self = this;
75
+ return (0, callback_1.fromCallback)(function (cb) { self.drop(cb); });
76
+ }
77
+ releaseAsync() {
78
+ var self = this;
79
+ return (0, callback_1.fromCallback)(function (cb) { self.release(cb); });
80
+ }
51
81
  }
52
82
  module.exports = Statement;
@@ -17,5 +17,13 @@ declare class Transaction {
17
17
  rollback(callback?: (err?: any) => void): void;
18
18
  commitRetaining(callback?: (err?: any) => void): void;
19
19
  rollbackRetaining(callback?: (err?: any) => void): void;
20
+ queryAsync(query: string, params?: any, options?: any): Promise<any[]>;
21
+ executeAsync(query: string, params?: any, options?: any): Promise<any[]>;
22
+ sequentiallyAsync(query: string, params?: any, on?: any, options?: any): Promise<void>;
23
+ newStatementAsync(query: string): Promise<any>;
24
+ commitAsync(): Promise<void>;
25
+ rollbackAsync(): Promise<void>;
26
+ commitRetainingAsync(): Promise<void>;
27
+ rollbackRetainingAsync(): Promise<void>;
20
28
  }
21
29
  export = Transaction;
@@ -174,5 +174,43 @@ class Transaction {
174
174
  rollbackRetaining(callback) {
175
175
  this.connection.rollbackRetaining(this, callback);
176
176
  }
177
+ /* Promise / async-await API — wrappers over the callback methods above. */
178
+ queryAsync(query, params, options) {
179
+ var self = this;
180
+ return (0, callback_1.fromCallback)(function (cb) { self.query(query, params, cb, options); });
181
+ }
182
+ executeAsync(query, params, options) {
183
+ var self = this;
184
+ return (0, callback_1.fromCallback)(function (cb) { self.execute(query, params, cb, options); });
185
+ }
186
+ sequentiallyAsync(query, params, on, options) {
187
+ if (params instanceof Function) {
188
+ options = on;
189
+ on = params;
190
+ params = undefined;
191
+ }
192
+ var self = this;
193
+ return (0, callback_1.fromCallback)(function (cb) { self.sequentially(query, params, on, cb, options); });
194
+ }
195
+ newStatementAsync(query) {
196
+ var self = this;
197
+ return (0, callback_1.fromCallback)(function (cb) { self.newStatement(query, cb); });
198
+ }
199
+ commitAsync() {
200
+ var self = this;
201
+ return (0, callback_1.fromCallback)(function (cb) { self.commit(cb); });
202
+ }
203
+ rollbackAsync() {
204
+ var self = this;
205
+ return (0, callback_1.fromCallback)(function (cb) { self.rollback(cb); });
206
+ }
207
+ commitRetainingAsync() {
208
+ var self = this;
209
+ return (0, callback_1.fromCallback)(function (cb) { self.commitRetaining(cb); });
210
+ }
211
+ rollbackRetainingAsync() {
212
+ var self = this;
213
+ return (0, callback_1.fromCallback)(function (cb) { self.rollbackRetaining(cb); });
214
+ }
177
215
  }
178
216
  module.exports = Transaction;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-firebird",
3
- "version": "2.4.1",
3
+ "version": "2.5.0",
4
4
  "description": "Pure JavaScript and Asynchronous Firebird client for Node.js.",
5
5
  "keywords": [
6
6
  "firebird",
package/src/callback.ts CHANGED
@@ -20,6 +20,42 @@ export interface FbError extends Error {
20
20
  export type SimpleCallback = (err?: any) => void;
21
21
  export type Callback<T = any> = (err?: any, result?: T) => void;
22
22
 
23
+ /**
24
+ * Normalize the values the driver passes as the callback error argument.
25
+ * Most code paths already deliver Error instances, but a few older ones
26
+ * pass plain objects (status vectors, `{error, message}` wrappers). A
27
+ * Promise must reject with an Error, so wrap those while preserving all
28
+ * their properties (gdscode, gdsparams, status, sqlcode, ...).
29
+ */
30
+ export function toError(err: any): Error {
31
+ if (err instanceof Error)
32
+ return err;
33
+
34
+ var error: FbError = new Error(
35
+ err != null && typeof err === 'object' && err.message ? err.message : String(err)
36
+ );
37
+
38
+ if (err != null && typeof err === 'object')
39
+ Object.assign(error, err);
40
+
41
+ return error;
42
+ }
43
+
44
+ /**
45
+ * Run a callback-style operation and return a Promise for its result.
46
+ * Usage: fromCallback<Database>(cb => attach(options, cb))
47
+ */
48
+ export function fromCallback<T = any>(executor: (cb: Callback<T>) => void): Promise<T> {
49
+ return new Promise<T>(function(resolve, reject) {
50
+ executor(function(err?: any, result?: T) {
51
+ if (err)
52
+ reject(toError(err));
53
+ else
54
+ resolve(result as T);
55
+ });
56
+ });
57
+ }
58
+
23
59
  export function doError(obj: any, callback?: (...args: any[]) => void): void {
24
60
  if (callback)
25
61
  callback(obj)
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import Const from './wire/const';
2
- import { doError, doCallback } from './callback';
2
+ import { doError, doCallback, fromCallback } from './callback';
3
3
  import Connection from './wire/connection';
4
4
  import Pool from './pool';
5
5
  import { escape as escapeValue } from './utils';
@@ -10,6 +10,8 @@ import type {
10
10
  ServiceManagerCallback,
11
11
  SimpleCallback,
12
12
  ConnectionPool,
13
+ Database,
14
+ ServiceManager,
13
15
  } from './types';
14
16
 
15
17
  export * from './types';
@@ -155,3 +157,28 @@ export function attachOrCreate(options: Options, callback: DatabaseCallback): vo
155
157
  export function pool(max: number, options: Options): ConnectionPool {
156
158
  return new Pool(attach, max, Object.assign({}, options, { isPool: true }));
157
159
  }
160
+
161
+ /*
162
+ * Promise / async-await API.
163
+ * Wrappers over the callback functions above; the callback API stays
164
+ * untouched. Rejections are always Error instances carrying the usual
165
+ * Firebird properties (gdscode, gdsparams, ...).
166
+ */
167
+
168
+ export function attachAsync(options: SvcMgrOptions): Promise<ServiceManager>;
169
+ export function attachAsync(options: Options): Promise<Database>;
170
+ export function attachAsync(options: any): Promise<any> {
171
+ return fromCallback(function(cb) { attach(options, cb); });
172
+ }
173
+
174
+ export function createAsync(options: Options): Promise<Database> {
175
+ return fromCallback(function(cb) { create(options, cb); });
176
+ }
177
+
178
+ export function attachOrCreateAsync(options: Options): Promise<Database> {
179
+ return fromCallback(function(cb) { attachOrCreate(options, cb); });
180
+ }
181
+
182
+ export function dropAsync(options: Options): Promise<void> {
183
+ return fromCallback(function(cb) { drop(options, cb); });
184
+ }
package/src/pool.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  *
5
5
  ***************************************/
6
6
 
7
+ import { fromCallback } from './callback';
7
8
  import type { Callback } from './callback';
8
9
 
9
10
  type AttachFn = (options: any, callback: Callback) => void;
@@ -204,6 +205,33 @@ class Pool {
204
205
  }
205
206
  });
206
207
  }
208
+
209
+ /* Promise / async-await API — wrappers over the callback methods above. */
210
+
211
+ getAsync(): Promise<any> {
212
+ var self = this;
213
+ return fromCallback(function(cb) { self.get(cb); });
214
+ }
215
+
216
+ destroyAsync(): Promise<void> {
217
+ var self = this;
218
+ return fromCallback(function(cb) { self.destroy(cb); });
219
+ }
220
+
221
+ /**
222
+ * Run `work` with a connection from the pool, returning it to the pool
223
+ * (detach) when the returned promise settles — success or failure.
224
+ */
225
+ async withConnection<T>(work: (db: any) => Promise<T> | T): Promise<T> {
226
+ const db = await this.getAsync();
227
+ try {
228
+ return await work(db);
229
+ } finally {
230
+ // A pooled detach only returns the connection to the pool; do not
231
+ // let a detach hiccup mask the outcome of `work`.
232
+ await new Promise<void>(function(resolve) { db.detach(function() { resolve(); }); });
233
+ }
234
+ }
207
235
  }
208
236
 
209
237
  export = Pool;
package/src/types.ts CHANGED
@@ -94,6 +94,21 @@ export interface Database {
94
94
  alterTablespace(name: string, filePath: string, callback?: QueryCallback): Database;
95
95
  dropTablespace(name: string, callback?: QueryCallback): Database;
96
96
  createSchema(schemaName: string, tablespaceName?: string | QueryCallback, callback?: QueryCallback): Database;
97
+
98
+ // Promise / async-await API (see README § Promises / async–await).
99
+ // Result metadata is only available through the callback API.
100
+ queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
101
+ executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
102
+ sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
103
+ sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
104
+ transactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
105
+ startTransactionAsync(options?: TransactionOptions | Isolation): Promise<Transaction>;
106
+ newStatementAsync(query: string): Promise<Statement>;
107
+ detachAsync(force?: boolean): Promise<void>;
108
+ dropAsync(): Promise<void>;
109
+ attachEventAsync(): Promise<any>;
110
+ /** Starts a transaction, commits when `work` resolves, rolls back when it rejects. */
111
+ withTransaction<T>(work: (transaction: Transaction) => Promise<T> | T, options?: TransactionOptions | Isolation): Promise<T>;
97
112
  }
98
113
 
99
114
  export interface Transaction {
@@ -105,6 +120,17 @@ export interface Transaction {
105
120
  commitRetaining(callback?: SimpleCallback): void;
106
121
  rollback(callback?: SimpleCallback): void;
107
122
  rollbackRetaining(callback?: SimpleCallback): void;
123
+
124
+ // Promise / async-await API
125
+ queryAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
126
+ executeAsync<T = any>(query: string, params?: any[], options?: QueryOptions): Promise<T[]>;
127
+ sequentiallyAsync(query: string, params: any[] | undefined, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
128
+ sequentiallyAsync(query: string, rowCallback: SequentialCallback, options?: QueryOptions | boolean): Promise<void>;
129
+ newStatementAsync(query: string): Promise<Statement>;
130
+ commitAsync(): Promise<void>;
131
+ commitRetainingAsync(): Promise<void>;
132
+ rollbackAsync(): Promise<void>;
133
+ rollbackRetainingAsync(): Promise<void>;
108
134
  }
109
135
 
110
136
  export interface Statement {
@@ -115,6 +141,15 @@ export interface Statement {
115
141
  fetch(transaction: Transaction, count: number, callback: QueryCallback): void;
116
142
  fetchScroll(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset: number, count: number, callback: QueryCallback): void;
117
143
  fetchAll(transaction: Transaction, callback: QueryCallback): void;
144
+
145
+ // Promise / async-await API
146
+ executeAsync(transaction: Transaction, params?: any[], options?: QueryOptions): Promise<any>;
147
+ fetchAsync(transaction: Transaction, count: number | 'all'): Promise<any>;
148
+ fetchScrollAsync(transaction: Transaction, direction: 'NEXT' | 'PRIOR' | 'FIRST' | 'LAST' | 'ABSOLUTE' | 'RELATIVE' | number, offset?: number, count?: number): Promise<any>;
149
+ fetchAllAsync(transaction: Transaction): Promise<any>;
150
+ closeAsync(): Promise<void>;
151
+ dropAsync(): Promise<void>;
152
+ releaseAsync(): Promise<void>;
118
153
  }
119
154
 
120
155
  export type SupportedCharacterSet = |
@@ -240,6 +275,12 @@ export interface SvcMgrOptions extends Options {
240
275
  export interface ConnectionPool {
241
276
  get(callback: DatabaseCallback): void;
242
277
  destroy(callback?: SimpleCallback): void;
278
+
279
+ // Promise / async-await API
280
+ getAsync(): Promise<Database>;
281
+ destroyAsync(): Promise<void>;
282
+ /** Acquire a connection, run `work`, always return the connection to the pool. */
283
+ withConnection<T>(work: (db: Database) => Promise<T> | T): Promise<T>;
243
284
  }
244
285
 
245
286
  export interface ReadableOptions {
@@ -2185,12 +2185,13 @@ function decodeResponse(data: any, callback: any, cnx: any, lowercase_keys: any,
2185
2185
  pluginName: accept.pluginName
2186
2186
  };
2187
2187
 
2188
- console.log('--- DEBUG SRP Handshake ---');
2189
- console.log('salt:', cnx.serverKeys.salt);
2190
- console.log('server public key:', cnx.serverKeys.public.toString(16));
2191
- console.log('client public key:', cnx.clientKeys.public.toString(16));
2192
- console.log('client private key:', cnx.clientKeys.private.toString(16));
2193
- console.log('hashAlgo:', accept.srpAlgo);
2188
+ if (process.env.FIREBIRD_DEBUG) {
2189
+ console.log('--- DEBUG SRP Handshake ---');
2190
+ console.log('salt:', cnx.serverKeys.salt);
2191
+ console.log('server public key:', cnx.serverKeys.public.toString(16));
2192
+ console.log('client public key:', cnx.clientKeys.public.toString(16));
2193
+ console.log('hashAlgo:', accept.srpAlgo);
2194
+ }
2194
2195
 
2195
2196
  const _t1 = Date.now();
2196
2197
  var proof = srp.clientProof(
@@ -2203,8 +2204,11 @@ function decodeResponse(data: any, callback: any, cnx: any, lowercase_keys: any,
2203
2204
  accept.srpAlgo
2204
2205
  );
2205
2206
 
2206
- console.log('client proof M1:', proof.authData.toString(16));
2207
- console.log('client session key K:', proof.clientSessionKey.toString(16));
2207
+ if (process.env.FIREBIRD_DEBUG) {
2208
+ // Never log the private key or the session key: the
2209
+ // session key is the wire-encryption key material.
2210
+ console.log('client proof M1:', proof.authData.toString(16));
2211
+ }
2208
2212
 
2209
2213
  if (process.env.FIREBIRD_DEBUG) {
2210
2214
  console.log('[fb-debug] srp.clientProof(%s): %dms', accept.srpAlgo, Date.now() - _t1);
@@ -2309,12 +2313,13 @@ function decodeResponse(data: any, callback: any, cnx: any, lowercase_keys: any,
2309
2313
  };
2310
2314
  var srpAlgo = crypto[pluginName];
2311
2315
 
2312
- console.log('--- DEBUG SRP Handshake ---');
2313
- console.log('salt:', cnx.serverKeys.salt);
2314
- console.log('server public key:', cnx.serverKeys.public.toString(16));
2315
- console.log('client public key:', cnx.clientKeys.public.toString(16));
2316
- console.log('client private key:', cnx.clientKeys.private.toString(16));
2317
- console.log('hashAlgo:', srpAlgo);
2316
+ if (process.env.FIREBIRD_DEBUG) {
2317
+ console.log('--- DEBUG SRP Handshake ---');
2318
+ console.log('salt:', cnx.serverKeys.salt);
2319
+ console.log('server public key:', cnx.serverKeys.public.toString(16));
2320
+ console.log('client public key:', cnx.clientKeys.public.toString(16));
2321
+ console.log('hashAlgo:', srpAlgo);
2322
+ }
2318
2323
 
2319
2324
  const _t1 = Date.now();
2320
2325
  var proof = srp.clientProof(
@@ -1,5 +1,5 @@
1
1
  import Events from 'events';
2
- import { doError } from '../callback';
2
+ import { doError, fromCallback } from '../callback';
3
3
  import { escape } from '../utils';
4
4
  import Const from './const';
5
5
  import EventConnection from './eventConnection';
@@ -439,6 +439,81 @@ class Database extends Events.EventEmitter {
439
439
  }
440
440
  return this.execute(sql, [], callback);
441
441
  }
442
+
443
+ /*
444
+ * Promise / async-await API.
445
+ * Each *Async method wraps its callback counterpart; the callback API
446
+ * stays untouched. Result metadata is only available through the
447
+ * callback API — the promises resolve with the rows alone.
448
+ */
449
+
450
+ queryAsync(query: string, params?: any, options?: any): Promise<any[]> {
451
+ var self = this;
452
+ return fromCallback(function(cb) { self.query(query, params, cb, options); });
453
+ }
454
+
455
+ executeAsync(query: string, params?: any, options?: any): Promise<any[]> {
456
+ var self = this;
457
+ return fromCallback(function(cb) { self.execute(query, params, cb, options); });
458
+ }
459
+
460
+ sequentiallyAsync(query: string, params?: any, on?: any, options?: any): Promise<void> {
461
+ if (params instanceof Function) {
462
+ options = on;
463
+ on = params;
464
+ params = undefined;
465
+ }
466
+ var self = this;
467
+ return fromCallback(function(cb) { self.sequentially(query, params, on, cb, options); });
468
+ }
469
+
470
+ transactionAsync(options?: any): Promise<any> {
471
+ var self = this;
472
+ return fromCallback(function(cb) { self.startTransaction(options, cb); });
473
+ }
474
+
475
+ startTransactionAsync(options?: any): Promise<any> {
476
+ return this.transactionAsync(options);
477
+ }
478
+
479
+ newStatementAsync(query: string): Promise<any> {
480
+ var self = this;
481
+ return fromCallback(function(cb) { self.newStatement(query, cb); });
482
+ }
483
+
484
+ detachAsync(force?: boolean): Promise<void> {
485
+ var self = this;
486
+ return fromCallback(function(cb) { self.detach(cb, force); });
487
+ }
488
+
489
+ dropAsync(): Promise<void> {
490
+ var self = this;
491
+ return fromCallback(function(cb) { self.drop(cb); });
492
+ }
493
+
494
+ attachEventAsync(): Promise<any> {
495
+ var self = this;
496
+ return fromCallback(function(cb) { self.attachEvent(cb); });
497
+ }
498
+
499
+ /**
500
+ * Run `work` inside a transaction: commits when the returned promise
501
+ * resolves, rolls back when it rejects (the original error is rethrown,
502
+ * even if the rollback itself fails).
503
+ */
504
+ async withTransaction<T>(work: (transaction: any) => Promise<T> | T, options?: any): Promise<T> {
505
+ const transaction = await this.transactionAsync(options);
506
+ try {
507
+ const result = await work(transaction);
508
+ await fromCallback(function(cb) { transaction.commit(cb); });
509
+ return result;
510
+ } catch (err) {
511
+ try {
512
+ await fromCallback(function(cb) { transaction.rollback(cb); });
513
+ } catch { /* surface the original error, not the rollback failure */ }
514
+ throw err;
515
+ }
516
+ }
442
517
  }
443
518
 
444
519
  export = Database;
@@ -4,6 +4,8 @@
4
4
  *
5
5
  ***************************************/
6
6
 
7
+ import { fromCallback } from '../callback';
8
+
7
9
  class Statement {
8
10
  connection: any;
9
11
  query: string;
@@ -66,6 +68,43 @@ class Statement {
66
68
  fetchAll(transaction: any, callback: (err: any, result?: any) => void): void {
67
69
  this.connection.fetchAll(this, transaction, callback);
68
70
  }
71
+
72
+ /* Promise / async-await API — wrappers over the callback methods above. */
73
+
74
+ executeAsync(transaction: any, params?: any, options?: any): Promise<any> {
75
+ var self = this;
76
+ return fromCallback(function(cb) { self.execute(transaction, params, cb, options); });
77
+ }
78
+
79
+ fetchAsync(transaction: any, count: number | string): Promise<any> {
80
+ var self = this;
81
+ return fromCallback(function(cb) { self.fetch(transaction, count, cb); });
82
+ }
83
+
84
+ fetchScrollAsync(transaction: any, direction: string | number, offset?: any, count?: any): Promise<any> {
85
+ var self = this;
86
+ return fromCallback(function(cb) { self.fetchScroll(transaction, direction, offset, count, cb); });
87
+ }
88
+
89
+ fetchAllAsync(transaction: any): Promise<any> {
90
+ var self = this;
91
+ return fromCallback(function(cb) { self.fetchAll(transaction, cb); });
92
+ }
93
+
94
+ closeAsync(): Promise<void> {
95
+ var self = this;
96
+ return fromCallback(function(cb) { self.close(cb); });
97
+ }
98
+
99
+ dropAsync(): Promise<void> {
100
+ var self = this;
101
+ return fromCallback(function(cb) { self.drop(cb); });
102
+ }
103
+
104
+ releaseAsync(): Promise<void> {
105
+ var self = this;
106
+ return fromCallback(function(cb) { self.release(cb); });
107
+ }
69
108
  }
70
109
 
71
110
  export = Statement;
@@ -1,4 +1,4 @@
1
- import { doCallback, doError } from '../callback';
1
+ import { doCallback, doError, fromCallback } from '../callback';
2
2
  import { noop } from '../utils';
3
3
  import Const from './const';
4
4
 
@@ -207,6 +207,53 @@ class Transaction {
207
207
  rollbackRetaining(callback?: (err?: any) => void): void {
208
208
  this.connection.rollbackRetaining(this, callback);
209
209
  }
210
+
211
+ /* Promise / async-await API — wrappers over the callback methods above. */
212
+
213
+ queryAsync(query: string, params?: any, options?: any): Promise<any[]> {
214
+ var self = this;
215
+ return fromCallback(function(cb) { self.query(query, params, cb, options); });
216
+ }
217
+
218
+ executeAsync(query: string, params?: any, options?: any): Promise<any[]> {
219
+ var self = this;
220
+ return fromCallback(function(cb) { self.execute(query, params, cb, options); });
221
+ }
222
+
223
+ sequentiallyAsync(query: string, params?: any, on?: any, options?: any): Promise<void> {
224
+ if (params instanceof Function) {
225
+ options = on;
226
+ on = params;
227
+ params = undefined;
228
+ }
229
+ var self = this;
230
+ return fromCallback(function(cb) { self.sequentially(query, params, on, cb, options); });
231
+ }
232
+
233
+ newStatementAsync(query: string): Promise<any> {
234
+ var self = this;
235
+ return fromCallback(function(cb) { self.newStatement(query, cb); });
236
+ }
237
+
238
+ commitAsync(): Promise<void> {
239
+ var self = this;
240
+ return fromCallback(function(cb) { self.commit(cb); });
241
+ }
242
+
243
+ rollbackAsync(): Promise<void> {
244
+ var self = this;
245
+ return fromCallback(function(cb) { self.rollback(cb); });
246
+ }
247
+
248
+ commitRetainingAsync(): Promise<void> {
249
+ var self = this;
250
+ return fromCallback(function(cb) { self.commitRetaining(cb); });
251
+ }
252
+
253
+ rollbackRetainingAsync(): Promise<void> {
254
+ var self = this;
255
+ return fromCallback(function(cb) { self.rollbackRetaining(cb); });
256
+ }
210
257
  }
211
258
 
212
259
  export = Transaction;