@miatechnet/node-odbc 2.4.10-multiresult.1
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/CHANGELOG.md +179 -0
- package/LICENSE +23 -0
- package/README.md +1314 -0
- package/binding.gyp +100 -0
- package/lib/Connection.js +521 -0
- package/lib/Cursor.js +92 -0
- package/lib/Pool.js +470 -0
- package/lib/Statement.js +207 -0
- package/lib/bindings/napi-v8/odbc.node +0 -0
- package/lib/odbc.d.ts +210 -0
- package/lib/odbc.js +56 -0
- package/package.json +69 -0
- package/src/dynodbc.cpp +189 -0
- package/src/dynodbc.h +383 -0
- package/src/odbc.cpp +850 -0
- package/src/odbc.h +362 -0
- package/src/odbc_connection.cpp +4312 -0
- package/src/odbc_connection.h +114 -0
- package/src/odbc_cursor.cpp +265 -0
- package/src/odbc_cursor.h +52 -0
- package/src/odbc_statement.cpp +610 -0
- package/src/odbc_statement.h +43 -0
package/lib/Pool.js
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
const async = require('async');
|
|
3
|
+
const binary = require('@mapbox/node-pre-gyp');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const { Connection } = require('./Connection');
|
|
7
|
+
|
|
8
|
+
const bindingPath = binary.find(path.resolve(path.join(__dirname, '../package.json')));
|
|
9
|
+
|
|
10
|
+
const odbc = require(bindingPath);
|
|
11
|
+
|
|
12
|
+
const REUSE_CONNECTIONS_DEFAULT = true;
|
|
13
|
+
const INITIAL_SIZE_DEFAULT = 10;
|
|
14
|
+
const INCREMENT_SIZE_DEFAULT = 10;
|
|
15
|
+
const MAX_SIZE_DEFAULT = Number.MAX_SAFE_INTEGER;
|
|
16
|
+
const SHRINK_DEFAULT = true;
|
|
17
|
+
const CONNECTION_TIMEOUT_DEFAULT = 0;
|
|
18
|
+
const LOGIN_TIMEOUT_DEFAULT = 0;
|
|
19
|
+
const FETCH_ARRAY_DEFAULT = false;
|
|
20
|
+
const MAX_ACTIVELY_CONNECTING = 1;
|
|
21
|
+
|
|
22
|
+
// A queue for tracking the connections
|
|
23
|
+
class ConnectionQueue
|
|
24
|
+
{
|
|
25
|
+
static queuedConnections = [];
|
|
26
|
+
static activelyConnectingCount = 0;
|
|
27
|
+
static maxActivelyConnecting = MAX_ACTIVELY_CONNECTING;
|
|
28
|
+
|
|
29
|
+
static enqueue = async function(pool, promiseGenerator, configObject)
|
|
30
|
+
{
|
|
31
|
+
ConnectionQueue.queuedConnections.push({
|
|
32
|
+
pool,
|
|
33
|
+
promiseGenerator,
|
|
34
|
+
configObject
|
|
35
|
+
});
|
|
36
|
+
ConnectionQueue.dequeue();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static dequeue = async function()
|
|
41
|
+
{
|
|
42
|
+
if (this.activelyConnectingCount >= this.maxActivelyConnecting) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const item = this.queuedConnections.shift();
|
|
46
|
+
if (!item) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
ConnectionQueue.activelyConnectingCount++;
|
|
50
|
+
try {
|
|
51
|
+
await item.promiseGenerator(item.configObject);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// Errors are handled in the promiseGenerator through emitting a
|
|
54
|
+
// "connectionError" event. Error thrown and caught here simply to resolve
|
|
55
|
+
// the promise generated by the promiseGenerator.
|
|
56
|
+
}
|
|
57
|
+
ConnectionQueue.activelyConnectingCount--;
|
|
58
|
+
ConnectionQueue.dequeue();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class Pool {
|
|
64
|
+
|
|
65
|
+
constructor(connectionString) {
|
|
66
|
+
|
|
67
|
+
this.connectionConfig = {};
|
|
68
|
+
this.waitingConnectionWork = [];
|
|
69
|
+
this.isOpen = false;
|
|
70
|
+
this.freeConnections = [];
|
|
71
|
+
this.connectionsBeingCreatedCount = 0;
|
|
72
|
+
this.poolSize = 0;
|
|
73
|
+
|
|
74
|
+
// Keeps track of when connections have sucessfully connected
|
|
75
|
+
this.connectionEmitter = new EventEmitter();
|
|
76
|
+
|
|
77
|
+
// Fires when a connection has been made
|
|
78
|
+
this.connectionEmitter.on('connected', (connection) => {
|
|
79
|
+
|
|
80
|
+
// A connection has finished connecting, but there is some waiting call
|
|
81
|
+
// to connect() that is waiting for a connection. shift() that work from
|
|
82
|
+
// the front of the waitingConnectionWork queue, then either call the
|
|
83
|
+
// callback function provided by the user, or resolve the Promise that was
|
|
84
|
+
// returned to the user.
|
|
85
|
+
let connectionWork = this.waitingConnectionWork.shift();
|
|
86
|
+
|
|
87
|
+
// A connection finished connecting, and there was no work waiting for
|
|
88
|
+
// that connection to be made. Simply add it to the array of
|
|
89
|
+
// freeConnections, and the next time work comes in it can simply be
|
|
90
|
+
// retrieved from there.
|
|
91
|
+
if (typeof connectionWork == 'undefined')
|
|
92
|
+
{
|
|
93
|
+
this.freeConnections.push(connection);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// If the user passed a callback function, then call the function with
|
|
98
|
+
// no error in the first parameter and the connection in the second
|
|
99
|
+
// parameter
|
|
100
|
+
if (typeof connectionWork == 'function')
|
|
101
|
+
{
|
|
102
|
+
return connectionWork(null, connection);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// If the user didn't pass a callback function, we returned a promise to
|
|
106
|
+
// them. Resolve that promise with the connection that was just created.
|
|
107
|
+
// Promise (stored resolve function)
|
|
108
|
+
return connectionWork.resolveFunction(connection);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Fires when a connection has errored. If there is no work waiting, then
|
|
112
|
+
// the pool will simply be empty until the next time work is requested, at
|
|
113
|
+
// which point there WILL be waiting work and they will get an error.
|
|
114
|
+
this.connectionEmitter.on('connectionError', (error) => {
|
|
115
|
+
let connectionWork = this.waitingConnectionWork.shift();
|
|
116
|
+
if (typeof connectionWork == 'undefined')
|
|
117
|
+
{
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// If the user passed a callback function, then call the function with
|
|
121
|
+
// the error in the first parameter
|
|
122
|
+
if (typeof connectionWork == 'function')
|
|
123
|
+
{
|
|
124
|
+
return connectionWork(error, undefined);
|
|
125
|
+
// If the user didn't pass a callback function, we returned a promise to
|
|
126
|
+
// them. Reject that promise with the generated error
|
|
127
|
+
} else {
|
|
128
|
+
// Promise (stored resolve function)
|
|
129
|
+
return connectionWork.rejectFunction(error);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// connectionString is a string, so use defaults for all of the
|
|
134
|
+
// configuration options.
|
|
135
|
+
if (typeof connectionString === 'string') {
|
|
136
|
+
this.connectionConfig.connectionString = connectionString;
|
|
137
|
+
|
|
138
|
+
this.reuseConnections = REUSE_CONNECTIONS_DEFAULT;
|
|
139
|
+
this.initialSize = INITIAL_SIZE_DEFAULT;
|
|
140
|
+
this.incrementSize = INCREMENT_SIZE_DEFAULT;
|
|
141
|
+
this.maxSize = MAX_SIZE_DEFAULT;
|
|
142
|
+
this.shrink = SHRINK_DEFAULT;
|
|
143
|
+
this.connectionConfig.connectionTimeout = CONNECTION_TIMEOUT_DEFAULT;
|
|
144
|
+
this.connectionConfig.loginTimeout = LOGIN_TIMEOUT_DEFAULT;
|
|
145
|
+
this.connectionConfig.fetchArray = FETCH_ARRAY_DEFAULT;
|
|
146
|
+
}
|
|
147
|
+
// connectionString is an object, so ensure that connectionString is a
|
|
148
|
+
// property on that object and then copy over any configuration options.
|
|
149
|
+
else if (typeof connectionString === 'object')
|
|
150
|
+
{
|
|
151
|
+
const configObject = connectionString;
|
|
152
|
+
if (!Object.prototype.hasOwnProperty.call(configObject, 'connectionString')) {
|
|
153
|
+
throw new TypeError('Pool configuration object must contain "connectionString" key');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.connectionConfig.connectionString = configObject.connectionString;
|
|
157
|
+
|
|
158
|
+
// reuseConnections
|
|
159
|
+
this.reuseConnections = configObject.reuseConnections !== undefined ? configObject.reuseConnections : REUSE_CONNECTIONS_DEFAULT;
|
|
160
|
+
|
|
161
|
+
// initialSize
|
|
162
|
+
this.initialSize = configObject.initialSize !== undefined ? configObject.initialSize : INITIAL_SIZE_DEFAULT;
|
|
163
|
+
|
|
164
|
+
// incrementSize
|
|
165
|
+
this.incrementSize = configObject.incrementSize !== undefined ? configObject.incrementSize : INCREMENT_SIZE_DEFAULT;
|
|
166
|
+
|
|
167
|
+
// maxSize
|
|
168
|
+
this.maxSize = configObject.maxSize !== undefined ? configObject.maxSize : MAX_SIZE_DEFAULT;
|
|
169
|
+
|
|
170
|
+
// shrink
|
|
171
|
+
this.shrink = configObject.shrink !== undefined ? configObject.shrink : SHRINK_DEFAULT;
|
|
172
|
+
|
|
173
|
+
// connectionTimeout
|
|
174
|
+
this.connectionConfig.connectionTimeout = configObject.connectionTimeout !== undefined ? configObject.connectionTimeout : CONNECTION_TIMEOUT_DEFAULT;
|
|
175
|
+
|
|
176
|
+
// loginTimeout
|
|
177
|
+
this.connectionConfig.loginTimeout = configObject.loginTimeout !== undefined ? configObject.loginTimeout : LOGIN_TIMEOUT_DEFAULT;
|
|
178
|
+
|
|
179
|
+
// fetchArray
|
|
180
|
+
this.connectionConfig.fetchArray = configObject.fetchArray || FETCH_ARRAY_DEFAULT;
|
|
181
|
+
|
|
182
|
+
// connectingQueueMax
|
|
183
|
+
// unlike other configuration values, this one is set statically on the
|
|
184
|
+
// ConnectionQueue object and not on the Pool intance
|
|
185
|
+
if (configObject.maxActivelyConnecting !== undefined)
|
|
186
|
+
{
|
|
187
|
+
ConnectionQueue.maxActivelyConnecting = configObject.maxActivelyConnecting
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// connectionString was neither a string nor and object, so throw an error.
|
|
191
|
+
else
|
|
192
|
+
{
|
|
193
|
+
throw TypeError('Pool constructor must passed a connection string or a configuration object');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// returns a open connection, ready to use.
|
|
198
|
+
// should overwrite the 'close' function of the connection, and rename it is 'nativeClose', so
|
|
199
|
+
// that that close can still be called.
|
|
200
|
+
async connect(callback = undefined) {
|
|
201
|
+
|
|
202
|
+
let connection;
|
|
203
|
+
|
|
204
|
+
if (this.freeConnections.length == 0) {
|
|
205
|
+
|
|
206
|
+
// If the number of connections waiting is more (shouldn't happen) or
|
|
207
|
+
// equal to the number of connections connecting, and the number of
|
|
208
|
+
// connections in the pool, in the process of connecting, and that will be
|
|
209
|
+
// added is less than the maximum number of allowable connections, then
|
|
210
|
+
// we will need to create MORE connections.
|
|
211
|
+
if (this.connectionsBeingCreatedCount <= this.waitingConnectionWork.length &&
|
|
212
|
+
this.poolSize + this.connectionsBeingCreatedCount + this.incrementSize <= this.maxSize)
|
|
213
|
+
{
|
|
214
|
+
this.increasePoolSize(this.incrementSize);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// If no callback was provided when connect was called, we need to create
|
|
218
|
+
// a promise to return back. We also need to save off the resolve function
|
|
219
|
+
// of that promises callback, so that we can call it to resolve the
|
|
220
|
+
// function we returned
|
|
221
|
+
if (typeof callback == 'undefined') {
|
|
222
|
+
let resolveConnectionPromise;
|
|
223
|
+
let rejectConnectionPromise;
|
|
224
|
+
|
|
225
|
+
const promise = new Promise((resolve, reject) => {
|
|
226
|
+
resolveConnectionPromise = resolve;
|
|
227
|
+
rejectConnectionPromise = reject;
|
|
228
|
+
});
|
|
229
|
+
const promiseObj = {
|
|
230
|
+
promise: promise,
|
|
231
|
+
resolveFunction: resolveConnectionPromise,
|
|
232
|
+
rejectFunction: rejectConnectionPromise,
|
|
233
|
+
}
|
|
234
|
+
// push the promise onto the waitingConnectionWork queue, then return
|
|
235
|
+
// it to the user
|
|
236
|
+
this.waitingConnectionWork.push(promiseObj);
|
|
237
|
+
return promise;
|
|
238
|
+
}
|
|
239
|
+
// If a callback was provided, we can just add that to the
|
|
240
|
+
// waitingConnectionWork queue, then return undefined to the user. Their
|
|
241
|
+
// callback will execute when a connection is ready
|
|
242
|
+
else
|
|
243
|
+
{
|
|
244
|
+
this.waitingConnectionWork.push(callback)
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Else, there was a free connection available for the user, so either
|
|
249
|
+
// return an immediately resolved promise, or call their callback
|
|
250
|
+
// immediately.
|
|
251
|
+
else
|
|
252
|
+
{
|
|
253
|
+
connection = this.freeConnections.pop();
|
|
254
|
+
|
|
255
|
+
// promise...
|
|
256
|
+
if (typeof callback === 'undefined') {
|
|
257
|
+
return Promise.resolve(connection);
|
|
258
|
+
} else {
|
|
259
|
+
// ...or callback
|
|
260
|
+
return callback(null, connection);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
async query(sql, params, opts, cb) {
|
|
266
|
+
// determine the parameters passed
|
|
267
|
+
let callback = cb;
|
|
268
|
+
let parameters = params;
|
|
269
|
+
let options = opts;
|
|
270
|
+
|
|
271
|
+
if (typeof callback === 'undefined') {
|
|
272
|
+
if (typeof options === 'function')
|
|
273
|
+
{
|
|
274
|
+
callback = options;
|
|
275
|
+
if (typeof parameters === 'object' && !Array.isArray(parameters))
|
|
276
|
+
{
|
|
277
|
+
options = parameters;
|
|
278
|
+
parameters = null;
|
|
279
|
+
} else {
|
|
280
|
+
options = null;
|
|
281
|
+
}
|
|
282
|
+
} else if (
|
|
283
|
+
typeof options === 'undefined' &&
|
|
284
|
+
typeof parameters === 'function')
|
|
285
|
+
{
|
|
286
|
+
callback = parameters;
|
|
287
|
+
options = null;
|
|
288
|
+
parameters = null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (typeof callback !== 'function') {
|
|
293
|
+
return new Promise((resolve, reject) => {
|
|
294
|
+
this.connect((error, connection) => {
|
|
295
|
+
if (error) {
|
|
296
|
+
reject(error);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
connection.query(sql, parameters, options, (error, result) => {
|
|
300
|
+
if (error) {
|
|
301
|
+
reject(error);
|
|
302
|
+
} else {
|
|
303
|
+
resolve(result);
|
|
304
|
+
}
|
|
305
|
+
connection.close();
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ...or callback
|
|
312
|
+
this.connect((error, connection) => {
|
|
313
|
+
if (error) {
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return connection.query(sql, parameters, options, (error, result) => {
|
|
318
|
+
// after running, close the connection whether error or not
|
|
319
|
+
process.nextTick(() => {
|
|
320
|
+
callback(error, result);
|
|
321
|
+
});
|
|
322
|
+
connection.close();
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// close the pool and all of the connections
|
|
328
|
+
async close(callback = undefined) {
|
|
329
|
+
const connections = [...this.freeConnections];
|
|
330
|
+
this.freeConnections.length = 0;
|
|
331
|
+
this.isOpen = false;
|
|
332
|
+
|
|
333
|
+
if (typeof callback === 'undefined') {
|
|
334
|
+
return new Promise((resolve, reject) => {
|
|
335
|
+
async.each(connections, (connection, cb) => {
|
|
336
|
+
connection.nativeClose((error) => {
|
|
337
|
+
this.poolSize--;
|
|
338
|
+
cb(error);
|
|
339
|
+
});
|
|
340
|
+
}, (error) => {
|
|
341
|
+
if (error) {
|
|
342
|
+
reject(error);
|
|
343
|
+
} else {
|
|
344
|
+
resolve(error);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async.each(this.freeConnections, (connection, cb) => {
|
|
351
|
+
connection.nativeClose((error) => {
|
|
352
|
+
this.poolSize--;
|
|
353
|
+
cb(error);
|
|
354
|
+
});
|
|
355
|
+
}, error => callback(error));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async init(callback = undefined) {
|
|
359
|
+
if (!this.isOpen) {
|
|
360
|
+
this.isOpen = true;
|
|
361
|
+
// promise...
|
|
362
|
+
if (typeof callback === 'undefined') {
|
|
363
|
+
return new Promise(async (resolve, reject) => {
|
|
364
|
+
try {
|
|
365
|
+
await this.increasePoolSize(this.initialSize);
|
|
366
|
+
// Try to get one connection during init to make sure there were
|
|
367
|
+
// no errors with the connection string
|
|
368
|
+
const connection = await this.connect();
|
|
369
|
+
this.freeConnections.unshift(connection);
|
|
370
|
+
resolve();
|
|
371
|
+
} catch (error) {
|
|
372
|
+
reject(error);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ...or callback
|
|
378
|
+
try {
|
|
379
|
+
await this.increasePoolSize(this.initialSize);
|
|
380
|
+
let connection = await this.connect();
|
|
381
|
+
connection.close();
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return callback(error);
|
|
384
|
+
}
|
|
385
|
+
return callback(null);
|
|
386
|
+
}
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
generateConnectPromise = function(connectionConfig) {
|
|
391
|
+
return new Promise((resolve, reject) => {
|
|
392
|
+
odbc.connect(connectionConfig, (error, nativeConnection) => {
|
|
393
|
+
if (error) {
|
|
394
|
+
// When there is a connection error, emit a "connectionError" event
|
|
395
|
+
// with the error that is then either handled by any waiting work, or
|
|
396
|
+
// is silently swallowed if no work is waiting. This is so that a bad
|
|
397
|
+
// attempt at connecting doesn't throw an uncatchable error when there
|
|
398
|
+
// is no user work, crashing the entire application.
|
|
399
|
+
//
|
|
400
|
+
// Catchable errors occur:
|
|
401
|
+
// 1. When a pool is initialized and there was a problem with the
|
|
402
|
+
// first connection (no network, bad connection string, etc.)
|
|
403
|
+
// 2. When a user is attempting to do work and there are no active
|
|
404
|
+
// connections, the pool will attempt to connect, emit the error, and
|
|
405
|
+
// send it to the user work that was waiting (either as an error
|
|
406
|
+
// parameter in the user's callback, or as a thrown error that the
|
|
407
|
+
// user has to catch.)
|
|
408
|
+
this.pool.connectionEmitter.emit('connectionError', error);
|
|
409
|
+
|
|
410
|
+
// This reject is swallowed in the ConnectionQueue's dequeue function
|
|
411
|
+
// in all cases, and is simply to end the Promise that was generated
|
|
412
|
+
// by generateConnectPromise.
|
|
413
|
+
reject(error);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
this.pool.connectionsBeingCreatedCount--;
|
|
418
|
+
this.pool.poolSize++;
|
|
419
|
+
let connection = new Connection(nativeConnection);
|
|
420
|
+
connection.nativeClose = connection.close;
|
|
421
|
+
|
|
422
|
+
if (this.pool.reuseConnections) {
|
|
423
|
+
connection.close = async (closeCallback = undefined) => {
|
|
424
|
+
this.pool.connectionEmitter.emit('connected', connection);
|
|
425
|
+
|
|
426
|
+
if (typeof closeCallback === 'undefined') {
|
|
427
|
+
return new Promise((resolve, reject) => {
|
|
428
|
+
resolve();
|
|
429
|
+
})
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return closeCallback(null);
|
|
433
|
+
};
|
|
434
|
+
} else {
|
|
435
|
+
connection.close = async (closeCallback = undefined) => {
|
|
436
|
+
this.pool.increasePoolSize(1);
|
|
437
|
+
if (typeof closeCallback === 'undefined') {
|
|
438
|
+
return new Promise((resolve, reject) => {
|
|
439
|
+
connection.nativeClose((error, result) => {
|
|
440
|
+
this.pool.poolSize--;
|
|
441
|
+
if (error) {
|
|
442
|
+
reject(error);
|
|
443
|
+
} else {
|
|
444
|
+
resolve(result);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
connection.nativeClose(closeCallback);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
this.pool.connectionEmitter.emit('connected', connection);
|
|
455
|
+
resolve();
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// odbc.connect runs on an AsyncWorker, so this is truly non-blocking
|
|
461
|
+
async increasePoolSize(count) {
|
|
462
|
+
this.connectionsBeingCreatedCount += count;
|
|
463
|
+
for (let i = 0; i < count; i++)
|
|
464
|
+
{
|
|
465
|
+
ConnectionQueue.enqueue(this, this.generateConnectPromise, this.connectionConfig);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
module.exports.Pool = Pool;
|
package/lib/Statement.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
const { Cursor } = require('./Cursor');
|
|
2
|
+
|
|
3
|
+
class Statement {
|
|
4
|
+
|
|
5
|
+
STATEMENT_CLOSED_ERROR = 'Statement has already been closed!';
|
|
6
|
+
|
|
7
|
+
constructor(odbcStatement) {
|
|
8
|
+
this.odbcStatement = odbcStatement;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Prepare an SQL statement template to which parameters can be bound and the statement then executed.
|
|
13
|
+
* @param {string} sql - The SQL statement template to prepare, with or without unspecified parameters.
|
|
14
|
+
* @param {function} [callback] - The callback function that returns the result. If omitted, uses a Promise.
|
|
15
|
+
* @returns {undefined|Promise}
|
|
16
|
+
*/
|
|
17
|
+
prepare(sql, callback = undefined) {
|
|
18
|
+
if (typeof sql !== 'string'
|
|
19
|
+
|| (typeof callback !== 'function' && typeof callback !== 'undefined')) {
|
|
20
|
+
throw new TypeError('[node-odbc]: Incorrect function signature for call to statement.prepare({string}, {function}[optional]).');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// promise...
|
|
24
|
+
if (typeof callback === 'undefined') {
|
|
25
|
+
if (!this.odbcStatement)
|
|
26
|
+
{
|
|
27
|
+
throw new Error(Statement.STATEMENT_CLOSED_ERROR);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
this.odbcStatement.prepare(sql, (error, result) => {
|
|
32
|
+
if (error) {
|
|
33
|
+
reject(error);
|
|
34
|
+
} else {
|
|
35
|
+
resolve(result);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ...or callback
|
|
42
|
+
if (!this.odbcStatement)
|
|
43
|
+
{
|
|
44
|
+
callback(new Error(Statement.STATEMENT_CLOSED_ERROR));
|
|
45
|
+
} else {
|
|
46
|
+
this.odbcStatement.prepare(sql, callback);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Bind parameters on the previously prepared SQL statement template.
|
|
52
|
+
* @param {*[]} parameters - The parameters to bind to the previously prepared SQL statement.
|
|
53
|
+
* @param {function} [callback] - The callback function that returns the result. If omitted, uses a Promise.
|
|
54
|
+
* @return {undefined|Promise}
|
|
55
|
+
*/
|
|
56
|
+
bind(parameters, callback = undefined) {
|
|
57
|
+
if (!Array.isArray(parameters)
|
|
58
|
+
|| (typeof callback !== 'function' && typeof callback !== 'undefined')) {
|
|
59
|
+
throw new TypeError('[node-odbc]: Incorrect function signature for call to statement.bind({array}, {function}[optional]).');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// promise...
|
|
63
|
+
if (typeof callback === 'undefined') {
|
|
64
|
+
if (!this.odbcStatement)
|
|
65
|
+
{
|
|
66
|
+
throw new Error(Statement.STATEMENT_CLOSED_ERROR);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
this.odbcStatement.bind(parameters, (error, result) => {
|
|
71
|
+
if (error) {
|
|
72
|
+
reject(error);
|
|
73
|
+
} else {
|
|
74
|
+
resolve(result);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ...or callback
|
|
81
|
+
if (!this.odbcStatement)
|
|
82
|
+
{
|
|
83
|
+
callback(new Error(Statement.STATEMENT_CLOSED_ERROR));
|
|
84
|
+
} else {
|
|
85
|
+
this.odbcStatement.bind(parameters, callback);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Executes the prepared SQL statement template with the bound parameters, returning the result.
|
|
91
|
+
* @param {function} [callback] - The callback function that returns the result. If omitted, uses a Promise.
|
|
92
|
+
*/
|
|
93
|
+
execute(options, callback = undefined) {
|
|
94
|
+
|
|
95
|
+
if (options === undefined)
|
|
96
|
+
{
|
|
97
|
+
options = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (typeof options === 'function' && callback === undefined) {
|
|
101
|
+
callback = options;
|
|
102
|
+
options = null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if ((typeof callback !== 'function' && typeof callback !== 'undefined')
|
|
106
|
+
|| typeof options !== 'object' && typeof options !== 'undefined' ) {
|
|
107
|
+
throw new TypeError('[node-odbc]: Incorrect function signature for call to statement.execute({object}[optional], {function}[optional]).');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Promise...
|
|
111
|
+
if (typeof callback === 'undefined') {
|
|
112
|
+
if (!this.odbcStatement)
|
|
113
|
+
{
|
|
114
|
+
throw new Error(Statement.STATEMENT_CLOSED_ERROR);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
this.odbcStatement.execute(options, (error, result) => {
|
|
119
|
+
if (error) {
|
|
120
|
+
reject(error);
|
|
121
|
+
} else {
|
|
122
|
+
if (options &&
|
|
123
|
+
(
|
|
124
|
+
options.hasOwnProperty('fetchSize') ||
|
|
125
|
+
options.hasOwnProperty('cursor')
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
{
|
|
129
|
+
const cursor = new Cursor(result);
|
|
130
|
+
resolve(cursor);
|
|
131
|
+
}
|
|
132
|
+
else
|
|
133
|
+
{
|
|
134
|
+
resolve(result);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ...or callback
|
|
142
|
+
if (!this.odbcStatement)
|
|
143
|
+
{
|
|
144
|
+
callback(new Error(Statement.STATEMENT_CLOSED_ERROR));
|
|
145
|
+
} else {
|
|
146
|
+
process.nextTick(() => {
|
|
147
|
+
if (options &&
|
|
148
|
+
(
|
|
149
|
+
options.hasOwnProperty('fetchSize') ||
|
|
150
|
+
options.hasOwnProperty('cursor')
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
{
|
|
154
|
+
this.odbcStatement.execute(options, (error, result) => {
|
|
155
|
+
if (error) {
|
|
156
|
+
return callback(error);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const cursor = new Cursor(result);
|
|
160
|
+
return callback(error, cursor);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else
|
|
164
|
+
{
|
|
165
|
+
this.odbcStatement.execute(options, callback);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Closes the statement, deleting the prepared statement and freeing the handle, making further
|
|
173
|
+
* calls on the object invalid.
|
|
174
|
+
* @param {function} [callback] - The callback function that returns the result. If omitted, uses a Promise.
|
|
175
|
+
*/
|
|
176
|
+
close(callback = undefined) {
|
|
177
|
+
if (typeof callback !== 'function' && typeof callback !== 'undefined') {
|
|
178
|
+
throw new TypeError('[node-odbc]: Incorrect function signature for call to statement.close({function}[optional]).');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (typeof callback === 'undefined') {
|
|
182
|
+
if (!this.odbcStatement) {
|
|
183
|
+
throw new Error(Statement.STATEMENT_CLOSED_ERROR);
|
|
184
|
+
}
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
this.odbcStatement.close((error) => {
|
|
187
|
+
if (error) {
|
|
188
|
+
reject(error);
|
|
189
|
+
} else {
|
|
190
|
+
this.odbcStatement = null;
|
|
191
|
+
resolve();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ...or callback
|
|
198
|
+
return this.odbcStatement.close((error) => {
|
|
199
|
+
if (!error) {
|
|
200
|
+
this.odbcStatement = null;
|
|
201
|
+
}
|
|
202
|
+
callback(error);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports.Statement = Statement;
|
|
Binary file
|