mssql 7.3.0 → 8.0.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/CHANGELOG.txt +13 -11
- package/README.md +124 -205
- package/lib/base/connection-pool.js +5 -5
- package/lib/base/index.js +7 -0
- package/lib/base/prepared-statement.js +0 -4
- package/lib/base/request.js +11 -13
- package/lib/base/transaction.js +0 -4
- package/lib/msnodesqlv8/connection-pool.js +4 -2
- package/lib/msnodesqlv8/index.js +7 -0
- package/lib/msnodesqlv8/request.js +7 -1
- package/lib/shared.js +7 -0
- package/lib/table.js +7 -0
- package/lib/tedious/connection-pool.js +10 -10
- package/lib/tedious/index.js +7 -0
- package/lib/tedious/request.js +9 -13
- package/package.json +5 -5
- package/lib/connectionstring.js +0 -289
package/CHANGELOG.txt
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
-------------------
|
|
3
|
-
[new]
|
|
4
|
-
[
|
|
1
|
+
v8.0.0 (2022-01-30)
|
|
2
|
+
-------------------
|
|
3
|
+
[new] Add table.rows.clear() method to allow for chunking updates ([#1094](https://github.com/tediousjs/node-mssql/pull/1094))
|
|
4
|
+
[new] Add valueHandler map to store callbacks that are used to process row values ([#1356](https://github.com/tediousjs/node-mssql/pull/1356))
|
|
5
|
+
[change] msnodesqlv8 driver detects os platform and attempts to pick correct connections string for it ((#1318)[https://github.com/tediousjs/node-mssql/pull/1318])
|
|
6
|
+
[change] Updated to latest Tedious 14 ((#1312)[https://github.com/tediousjs/node-mssql/pull/1312])
|
|
7
|
+
[change] Errors for bad bulk load parameters have slightly different error messages ((#1318)[https://github.com/tediousjs/node-mssql/pull/1318])
|
|
8
|
+
[change] Options provided to the driver via the config.options object will not be overridden with other values if set explicitly ((#1340)[https://github.com/tediousjs/node-mssql/pull/1340])
|
|
9
|
+
[change] Duplicate column names will now be presented as an array even if the values are empty ((#1240)[https://github.com/tediousjs/node-mssql/pull/1240])
|
|
10
|
+
[change] Update tarn pool dependency ((#1344)[https://github.com/tediousjs/node-mssql/pull/1344])
|
|
11
|
+
[removed] Remove connection string parser helpers ((#1342)[https://github.com/tediousjs/node-mssql/pull/1342])
|
|
5
12
|
|
|
6
13
|
v7.2.1 (2021-08-19)
|
|
7
14
|
-------------------
|
|
@@ -49,11 +56,6 @@ v7.0.0 (2021-05-06)
|
|
|
49
56
|
[fix] Connection config objects are now deep cloned when stored against a connection pool ([#1217](https://github.com/tediousjs/node-mssql/pull/1217))
|
|
50
57
|
[removed] Support for connection uri format has been removed (eg: `mssql://user:password@host/?params=values`)
|
|
51
58
|
|
|
52
|
-
v6.4.0 (2021-11-18)
|
|
53
|
-
-------------------
|
|
54
|
-
[new] Transaction/PreparedStatements expose the config from their parent connection
|
|
55
|
-
[fix] Fix inherited request configs from the pool. Specifically stream and arrayRowMode now inherit accurately from the connection config ([#1338](https://github.com/tediousjs/node-mssql/pull/1338))
|
|
56
|
-
|
|
57
59
|
v6.3.2 (2021-05-13)
|
|
58
60
|
-------------------
|
|
59
61
|
[fix] Bump various dependencies for security fixes
|
|
@@ -257,7 +259,7 @@ v4.0.4 (2017-04-25)
|
|
|
257
259
|
|
|
258
260
|
v4.0.3 (2017-04-25)
|
|
259
261
|
-------------------
|
|
260
|
-
[fix] Fixed broken CLI &
|
|
262
|
+
[fix] Fixed broken CLI & debugging
|
|
261
263
|
|
|
262
264
|
v4.0.2 (2017-04-19)
|
|
263
265
|
-------------------
|
|
@@ -278,7 +280,7 @@ v4.0.0 (2017-04-01)
|
|
|
278
280
|
[change] Removed verbose and debug mode
|
|
279
281
|
[change] Removed 'driver' from options
|
|
280
282
|
[change] Removed Transaction and Prepared Statement queues
|
|
281
|
-
[change] Removed 'multiple'
|
|
283
|
+
[change] Removed 'multiple' directive
|
|
282
284
|
[change] Connection renamed to ConnectionPool
|
|
283
285
|
[change] Updated to latest Tedious 2.0.0
|
|
284
286
|
|
package/README.md
CHANGED
|
@@ -370,264 +370,169 @@ function processRows() {
|
|
|
370
370
|
}
|
|
371
371
|
```
|
|
372
372
|
|
|
373
|
-
##
|
|
374
|
-
|
|
375
|
-
An important concept to understand when using this library is [Connection Pooling](https://en.wikipedia.org/wiki/Connection_pool)
|
|
376
|
-
as this library uses connection pooling extensively.
|
|
373
|
+
## Connection Pools
|
|
377
374
|
|
|
378
|
-
As one Node JS process is able to handle multiple requests at once, we can take advantage of this long running process
|
|
379
|
-
to create a pool of database connections for reuse; this saves overhead of connecting to the database for each request
|
|
375
|
+
An important concept to understand when using this library is [Connection Pooling](https://en.wikipedia.org/wiki/Connection_pool) as this library uses connection pooling extensively. As one Node JS process is able to handle multiple requests at once, we can take advantage of this long running process to create a pool of database connections for reuse; this saves overhead of connecting to the database for each request
|
|
380
376
|
(as would be the case in something like PHP, where one process handles one request).
|
|
381
377
|
|
|
382
|
-
With the advantages of pooling comes some added complexities, but these are mostly just conceptual and once you understand
|
|
383
|
-
how the pooling is working, it is simple to make use of it efficiently and effectively.
|
|
378
|
+
With the advantages of pooling comes some added complexities, but these are mostly just conceptual and once you understand how the pooling is working, it is simple to make use of it efficiently and effectively.
|
|
384
379
|
|
|
385
|
-
### The Global Connection
|
|
380
|
+
### The Global Connection Pool
|
|
386
381
|
|
|
387
|
-
To assist with pool management in your application there is the
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
it will resolve to the connected pool. For example:
|
|
382
|
+
To assist with pool management in your application there is the `sql.connect()` function that is used to connect to the global connection pool. You can make repeated calls to this function, and if the global pool is already connected, it will resolve to the connected pool. The following example obtains the global connection pool by running `sql.connect()`, and then runs the query against the pool.
|
|
383
|
+
|
|
384
|
+
NB: It's important to note that there can only be one global connection pool connected at a time. Providing a different connection config to the `connect()` function will not create a new connection if it is already connected.
|
|
391
385
|
|
|
392
386
|
```js
|
|
393
387
|
const sql = require('mssql')
|
|
388
|
+
const config = { ... }
|
|
394
389
|
|
|
395
390
|
// run a query against the global connection pool
|
|
396
391
|
function runQuery(query) {
|
|
397
392
|
// sql.connect() will return the existing global pool if it exists or create a new one if it doesn't
|
|
398
|
-
return sql.connect().then((pool) => {
|
|
393
|
+
return sql.connect(config).then((pool) => {
|
|
399
394
|
return pool.query(query)
|
|
400
395
|
})
|
|
401
396
|
}
|
|
402
|
-
|
|
403
397
|
```
|
|
404
398
|
|
|
405
|
-
|
|
406
|
-
We also do *not* close the pool after the query is executed and that is because other queries may need to be run against
|
|
407
|
-
this pool and closing it will add an overhead to running the query. We should only ever close the pool when our application
|
|
408
|
-
is finished. For example, if we are running some kind of CLI tool or a CRON job:
|
|
399
|
+
Awaiting or `.then`-ing the pool creation is a safe way to ensure that the pool is always ready, without knowing where it is needed first. In practice, once the pool is created then there will be no delay for the next `connect()` call.
|
|
409
400
|
|
|
410
|
-
|
|
411
|
-
const sql = require('mssql')
|
|
401
|
+
Also notice that we do *not* close the global pool by calling `sql.close()` after the query is executed, because other queries may need to be run against this pool and closing it will add additional overhead to running subsequent queries. You should only ever close the global pool if you're certain the application is finished. Or for example, if you are running some kind of CLI tool or a CRON job you can close the pool at the end of the script.
|
|
412
402
|
|
|
413
|
-
|
|
414
|
-
sql.connect().then(pool => {
|
|
415
|
-
return pool.query('SELECT 1')
|
|
416
|
-
}).then(result => {
|
|
417
|
-
// do something with result
|
|
418
|
-
}).then(() => {
|
|
419
|
-
return sql.close()
|
|
420
|
-
})
|
|
421
|
-
})()
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
Here the connection will be closed and the node process will exit once the queries and other application logic has completed.
|
|
425
|
-
You should aim to only close the pool once in your application, when it is exiting or you know your application will never make
|
|
426
|
-
another SQL query.
|
|
403
|
+
### Global Pool Single Instance
|
|
427
404
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
In some instances you will not want to use the connection pool, you may have multiple databases to connect to or you may have
|
|
431
|
-
one pool for read-only operations and another pool for read-write. In this instance you will need to implement your own pool
|
|
432
|
-
management.
|
|
405
|
+
The ability to call `connect()` and `close()` repeatedly on the global pool is intended to make pool management easier, however it is better to maintain your own reference to the pool, where `connect()` is called **once**, and the resulting global pool's connection promise is re-used throughout the entire application.
|
|
433
406
|
|
|
434
|
-
|
|
407
|
+
For example, in Express applications, the following approach uses a single global pool instance added to the `app.locals` so the application has access to it when needed. The server start is then chained inside the `connect()` promise.
|
|
435
408
|
|
|
436
409
|
```js
|
|
437
|
-
const
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
410
|
+
const express = require('express')
|
|
411
|
+
const sql = require('mssql')
|
|
412
|
+
const config = {/*...*/}
|
|
413
|
+
//instantiate a connection pool
|
|
414
|
+
const appPool = new sql.ConnectionPool(config)
|
|
415
|
+
//require route handlers and use the same connection pool everywhere
|
|
416
|
+
const route1 = require('./routes/route1')
|
|
417
|
+
const app = express()
|
|
418
|
+
app.get('/path', route1)
|
|
419
|
+
|
|
420
|
+
//connect the pool and start the web server when done
|
|
421
|
+
appPool.connect().then(function(pool) {
|
|
422
|
+
app.locals.db = pool;
|
|
423
|
+
const server = app.listen(3000, function () {
|
|
424
|
+
const host = server.address().address
|
|
425
|
+
const port = server.address().port
|
|
426
|
+
console.log('Example app listening at http://%s:%s', host, port)
|
|
446
427
|
})
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const pool = getPool(name)
|
|
451
|
-
if (pool) {
|
|
452
|
-
delete POOLS[name]
|
|
453
|
-
return pool.close()
|
|
454
|
-
}
|
|
455
|
-
return Promise.resolve()
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function getPool(name) {
|
|
459
|
-
if (Object.prototype.hasOwnProperty.apply(POOLS, name)) {
|
|
460
|
-
return POOLS[name]
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
module.exports = {
|
|
465
|
-
closePool,
|
|
466
|
-
createPool,
|
|
467
|
-
getPool
|
|
468
|
-
}
|
|
428
|
+
}).catch(function(err) {
|
|
429
|
+
console.error('Error creating connection pool', err)
|
|
430
|
+
});
|
|
469
431
|
```
|
|
470
432
|
|
|
471
|
-
|
|
472
|
-
should aim to only close a pool when you know it will never be needed by the application again; typically this will be when
|
|
473
|
-
your application is shutting down.
|
|
474
|
-
|
|
475
|
-
## Connection Pools
|
|
476
|
-
|
|
477
|
-
Using a single connection pool for your application/service is recommended.
|
|
478
|
-
Instantiating a pool with a callback, or immediately calling `.connect`, is asynchronous to ensure a connection can be
|
|
479
|
-
established before returning. From that point, you're able to acquire connections as normal:
|
|
480
|
-
|
|
481
|
-
```javascript
|
|
482
|
-
const sql = require('mssql')
|
|
483
|
-
|
|
484
|
-
// async/await style:
|
|
485
|
-
const pool1 = new sql.ConnectionPool(config);
|
|
486
|
-
const pool1Connect = pool1.connect();
|
|
487
|
-
|
|
488
|
-
pool1.on('error', err => {
|
|
489
|
-
// ... error handler
|
|
490
|
-
})
|
|
433
|
+
Then the route uses the connection pool in the `app.locals` object:
|
|
491
434
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
435
|
+
```js
|
|
436
|
+
// ./routes/route1.js
|
|
437
|
+
const sql = require('mssql');
|
|
438
|
+
|
|
439
|
+
module.exports = function(req, res) {
|
|
440
|
+
req.locals.db.query('SELECT TOP 10 * FROM table_name', function(err, recordset) {
|
|
441
|
+
if (err) {
|
|
442
|
+
console.error(err)
|
|
443
|
+
res.status(500).send('SERVER ERROR')
|
|
444
|
+
return
|
|
501
445
|
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// promise style:
|
|
505
|
-
const pool2 = new sql.ConnectionPool(config)
|
|
506
|
-
const pool2Connect = pool2.connect()
|
|
507
|
-
|
|
508
|
-
pool2.on('error', err => {
|
|
509
|
-
// ... error handler
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
function runStoredProcedure() {
|
|
513
|
-
return pool2Connect.then((pool) => {
|
|
514
|
-
pool.request() // or: new sql.Request(pool2)
|
|
515
|
-
.input('input_parameter', sql.Int, 10)
|
|
516
|
-
.output('output_parameter', sql.VarChar(50))
|
|
517
|
-
.execute('procedure_name', (err, result) => {
|
|
518
|
-
// ... error checks
|
|
519
|
-
console.dir(result)
|
|
520
|
-
})
|
|
521
|
-
}).catch(err => {
|
|
522
|
-
// ... error handler
|
|
523
|
-
})
|
|
446
|
+
res.status(200).json({ message: 'success' })
|
|
447
|
+
})
|
|
524
448
|
}
|
|
525
449
|
```
|
|
526
450
|
|
|
527
|
-
|
|
528
|
-
is needed first. In practice, once the pool is created then there will be no delay for the next operation.
|
|
451
|
+
### Advanced Pool Management
|
|
529
452
|
|
|
530
|
-
|
|
531
|
-
thrown, allowing for the safe use of `mssql.connect().then(...)` throughout your code as well as making multiple calls to
|
|
532
|
-
close when your application is shutting down.
|
|
453
|
+
For some use-cases you may want to implement your own connection pool management, rather than using the global connection pool. Reasons for doing this include:
|
|
533
454
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
Repeatedly calling `connect()` when running queries risks running into problems when `close()` is called on the pool.
|
|
455
|
+
* Supporting connections to multiple databases
|
|
456
|
+
* Creation of separate pools for read vs read/write operations
|
|
537
457
|
|
|
538
|
-
|
|
458
|
+
The following code is an example of a custom connection pool implementation.
|
|
539
459
|
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
console.dir(result)
|
|
545
|
-
}).catch(err => {
|
|
546
|
-
// ... error checks
|
|
547
|
-
})
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
All values are automatically sanitized against sql injection.
|
|
460
|
+
```js
|
|
461
|
+
// pool-manager.js
|
|
462
|
+
const mssql = require('mssql')
|
|
463
|
+
const pools = new Map();
|
|
551
464
|
|
|
552
|
-
|
|
465
|
+
module.exports = {
|
|
466
|
+
/**
|
|
467
|
+
* Get or create a pool. If a pool doesn't exist the config must be provided.
|
|
468
|
+
* If the pool does exist the config is ignored (even if it was different to the one provided
|
|
469
|
+
* when creating the pool)
|
|
470
|
+
*
|
|
471
|
+
* @param {string} name
|
|
472
|
+
* @param {{}} [config]
|
|
473
|
+
* @return {Promise.<mssql.ConnectionPool>}
|
|
474
|
+
*/
|
|
475
|
+
get: (name, config) => {
|
|
476
|
+
if (!pools.has(name)) {
|
|
477
|
+
if (!config) {
|
|
478
|
+
throw new Error('Pool does not exist');
|
|
479
|
+
}
|
|
480
|
+
const pool = new mssql.ConnectionPool(config);
|
|
481
|
+
// automatically remove the pool from the cache if `pool.close()` is called
|
|
482
|
+
const close = pool.close.bind(pool);
|
|
483
|
+
pool.close = (...args) => {
|
|
484
|
+
pools.delete(name);
|
|
485
|
+
return close(...args);
|
|
486
|
+
}
|
|
487
|
+
pools.set(name, pool.connect());
|
|
488
|
+
}
|
|
489
|
+
return pools.get(name);
|
|
490
|
+
},
|
|
491
|
+
/**
|
|
492
|
+
* Closes all the pools and removes them from the store
|
|
493
|
+
*
|
|
494
|
+
* @return {Promise<mssql.ConnectionPool[]>}
|
|
495
|
+
*/
|
|
496
|
+
closeAll: () => Promise.all(Array.from(pools.values()).map((connect) => {
|
|
497
|
+
return connect.then((pool) => pool.close());
|
|
498
|
+
})),
|
|
499
|
+
};
|
|
500
|
+
```
|
|
553
501
|
|
|
554
|
-
|
|
555
|
-
of a single pool this library exposes a set of functions to access a single global connection. eg:
|
|
502
|
+
This file can then be used in your application to create, fetch, and close pools.
|
|
556
503
|
|
|
557
504
|
```js
|
|
558
|
-
|
|
505
|
+
const { get } = require('./pool-manager')
|
|
559
506
|
|
|
560
|
-
|
|
561
|
-
const
|
|
507
|
+
async function example() {
|
|
508
|
+
const pool = await get('default')
|
|
509
|
+
return pool.request().query('SELECT 1')
|
|
510
|
+
}
|
|
511
|
+
```
|
|
562
512
|
|
|
563
|
-
|
|
513
|
+
Similar to the global connection pool, you should aim to only close a pool when you know it will never be needed by the application again. Typically this will only be when your application is shutting down.
|
|
564
514
|
|
|
565
|
-
|
|
566
|
-
return sql.query('SELECT 1')
|
|
567
|
-
}).then(result => {
|
|
568
|
-
console.dir(result)
|
|
569
|
-
})
|
|
515
|
+
### Result value manipulation
|
|
570
516
|
|
|
571
|
-
|
|
572
|
-
poolPromise.then(() => {
|
|
573
|
-
return sql.close()
|
|
574
|
-
})
|
|
575
|
-
```
|
|
517
|
+
In some instances it is desirable to manipulate the record data as it is returned from the database, this may be to cast it as a particular object (eg: `moment` object instead of `Date`) or similar.
|
|
576
518
|
|
|
577
|
-
|
|
578
|
-
pool), then you will need to manage your pools yourself. The best way to do this is to create a shared library file that
|
|
579
|
-
can hold references to the pools for you. For example:
|
|
519
|
+
In v8.0.0+ it is possible to register per-datatype handlers:
|
|
580
520
|
|
|
581
521
|
```js
|
|
582
522
|
const sql = require('mssql')
|
|
583
523
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// manage a set of pools by name (config will be required to create the pool)
|
|
587
|
-
// a pool will be removed when it is closed
|
|
588
|
-
async function getPool(name, config) {
|
|
589
|
-
if (!Object.prototype.hasOwnProperty.call(pools, name)) {
|
|
590
|
-
const pool = new sql.ConnectionPool(config)
|
|
591
|
-
const close = pool.close.bind(pool)
|
|
592
|
-
pool.close = (...args) => {
|
|
593
|
-
delete pools[name]
|
|
594
|
-
return close(...args)
|
|
595
|
-
}
|
|
596
|
-
await pool.connect()
|
|
597
|
-
pools[name] = pool
|
|
598
|
-
}
|
|
599
|
-
return pools[name]
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// close all pools
|
|
603
|
-
function closeAll() {
|
|
604
|
-
return Promise.all(Object.values(pools).map((pool) => {
|
|
605
|
-
return pool.close()
|
|
606
|
-
}))
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
module.exports = {
|
|
610
|
-
closeAll,
|
|
611
|
-
getPool
|
|
612
|
-
}
|
|
613
|
-
```
|
|
614
|
-
|
|
615
|
-
You can then use this library file in your code to get a connected pool when you need it:
|
|
524
|
+
// in this example all integer values will return 1 more than their actual value in the database
|
|
525
|
+
sql.valueHandler.set(sql.TYPES.Int, (value) => value + 1)
|
|
616
526
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
// run a query
|
|
621
|
-
async function runQuery(query, config) {
|
|
622
|
-
// pool will always be connected when the promise has resolved - may reject if the connection config is invalid
|
|
623
|
-
const pool = await getPool('default', config)
|
|
624
|
-
const result = await pool.request().query(query)
|
|
625
|
-
return result
|
|
626
|
-
}
|
|
527
|
+
sql.query('SELECT * FROM [example]').then((result) => {
|
|
528
|
+
// all `int` columns will return a manipulated value as per the callback above
|
|
529
|
+
})
|
|
627
530
|
```
|
|
628
531
|
|
|
629
532
|
## Configuration
|
|
630
533
|
|
|
534
|
+
The following is an example configuration object:
|
|
535
|
+
|
|
631
536
|
```javascript
|
|
632
537
|
const config = {
|
|
633
538
|
user: '...',
|
|
@@ -1616,6 +1521,20 @@ request.execute('MyCustomStoredProcedure', (err, result) => {
|
|
|
1616
1521
|
|
|
1617
1522
|
**TIP**: You can also create Table variable from any recordset with `recordset.toTable()`. You can optionally specify table type name in the first argument.
|
|
1618
1523
|
|
|
1524
|
+
You can clear the table rows for easier batching by using `table.rows.clear()`
|
|
1525
|
+
|
|
1526
|
+
```js
|
|
1527
|
+
const tvp = new sql.Table() // You can optionally specify table type name in the first argument.
|
|
1528
|
+
|
|
1529
|
+
// Columns must correspond with type we have created in database.
|
|
1530
|
+
tvp.columns.add('a', sql.VarChar(50))
|
|
1531
|
+
tvp.columns.add('b', sql.Int)
|
|
1532
|
+
|
|
1533
|
+
// Add rows
|
|
1534
|
+
tvp.rows.add('hello tvp', 777) // Values are in same order as columns.
|
|
1535
|
+
tvp.rows.clear()
|
|
1536
|
+
```
|
|
1537
|
+
|
|
1619
1538
|
## Response Schema
|
|
1620
1539
|
|
|
1621
1540
|
An object returned from a `sucessful` basic query would look like the following.
|
|
@@ -7,7 +7,7 @@ const tarn = require('tarn')
|
|
|
7
7
|
const { IDS } = require('../utils')
|
|
8
8
|
const ConnectionError = require('../error/connection-error')
|
|
9
9
|
const shared = require('../shared')
|
|
10
|
-
const
|
|
10
|
+
const clone = require('rfdc/default')
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Class ConnectionPool.
|
|
@@ -44,7 +44,7 @@ class ConnectionPool extends EventEmitter {
|
|
|
44
44
|
|
|
45
45
|
if (typeof config === 'string') {
|
|
46
46
|
try {
|
|
47
|
-
this.config = this.parseConnectionString(config)
|
|
47
|
+
this.config = this.constructor.parseConnectionString(config)
|
|
48
48
|
} catch (ex) {
|
|
49
49
|
if (typeof callback === 'function') {
|
|
50
50
|
return setImmediate(callback, ex)
|
|
@@ -52,7 +52,7 @@ class ConnectionPool extends EventEmitter {
|
|
|
52
52
|
throw ex
|
|
53
53
|
}
|
|
54
54
|
} else {
|
|
55
|
-
this.config =
|
|
55
|
+
this.config = clone(config)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// set defaults
|
|
@@ -85,11 +85,11 @@ class ConnectionPool extends EventEmitter {
|
|
|
85
85
|
return this._healthy
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
parseConnectionString (connectionString) {
|
|
88
|
+
static parseConnectionString (connectionString) {
|
|
89
89
|
return this._parseConnectionString(connectionString)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
_parseConnectionString (connectionString) {
|
|
92
|
+
static _parseConnectionString (connectionString) {
|
|
93
93
|
const parsed = parseSqlConnectionString(connectionString, true, true)
|
|
94
94
|
return Object.entries(parsed).reduce((config, [key, value]) => {
|
|
95
95
|
switch (key) {
|
package/lib/base/index.js
CHANGED
|
@@ -54,6 +54,13 @@ Object.defineProperty(module.exports, 'Promise', {
|
|
|
54
54
|
}
|
|
55
55
|
})
|
|
56
56
|
|
|
57
|
+
Object.defineProperty(module.exports, 'valueHandler', {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
value: shared.valueHandler,
|
|
60
|
+
writable: false,
|
|
61
|
+
configurable: false
|
|
62
|
+
})
|
|
63
|
+
|
|
57
64
|
for (const key in TYPES) {
|
|
58
65
|
const value = TYPES[key]
|
|
59
66
|
module.exports.exports[key] = value
|
package/lib/base/request.js
CHANGED
|
@@ -26,7 +26,7 @@ class Request extends EventEmitter {
|
|
|
26
26
|
/**
|
|
27
27
|
* Create new Request.
|
|
28
28
|
*
|
|
29
|
-
* @param {Connection|ConnectionPool|Transaction|PreparedStatement} parent If
|
|
29
|
+
* @param {Connection|ConnectionPool|Transaction|PreparedStatement} parent If ommited, global connection is used instead.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
constructor (parent) {
|
|
@@ -39,8 +39,6 @@ class Request extends EventEmitter {
|
|
|
39
39
|
this._paused = false
|
|
40
40
|
this.parent = parent || globalConnection.pool
|
|
41
41
|
this.parameters = {}
|
|
42
|
-
this.stream = null
|
|
43
|
-
this.arrayRowMode = null
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
get paused () {
|
|
@@ -226,8 +224,8 @@ class Request extends EventEmitter {
|
|
|
226
224
|
*/
|
|
227
225
|
|
|
228
226
|
batch (batch, callback) {
|
|
229
|
-
if (this.stream
|
|
230
|
-
if (this.arrayRowMode
|
|
227
|
+
if (this.stream == null && this.connection) this.stream = this.connection.config.stream
|
|
228
|
+
if (this.arrayRowMode == null && this.connection) this.arrayRowMode = this.connection.config.arrayRowMode
|
|
231
229
|
this.rowsAffected = 0
|
|
232
230
|
|
|
233
231
|
if (typeof callback === 'function') {
|
|
@@ -290,11 +288,11 @@ class Request extends EventEmitter {
|
|
|
290
288
|
*/
|
|
291
289
|
|
|
292
290
|
_batch (batch, callback) {
|
|
293
|
-
if (!this.
|
|
291
|
+
if (!this.connection) {
|
|
294
292
|
return setImmediate(callback, new RequestError('No connection is specified for that request.', 'ENOCONN'))
|
|
295
293
|
}
|
|
296
294
|
|
|
297
|
-
if (!this.
|
|
295
|
+
if (!this.connection.connected) {
|
|
298
296
|
return setImmediate(callback, new ConnectionError('Connection is closed.', 'ECONNCLOSED'))
|
|
299
297
|
}
|
|
300
298
|
|
|
@@ -319,8 +317,8 @@ class Request extends EventEmitter {
|
|
|
319
317
|
options = {}
|
|
320
318
|
}
|
|
321
319
|
|
|
322
|
-
if (this.stream
|
|
323
|
-
if (this.arrayRowMode
|
|
320
|
+
if (this.stream == null && this.connection) this.stream = this.connection.config.stream
|
|
321
|
+
if (this.arrayRowMode == null && this.connection) this.arrayRowMode = this.connection.config.arrayRowMode
|
|
324
322
|
|
|
325
323
|
if (this.stream || typeof callback === 'function') {
|
|
326
324
|
this._bulk(table, options, (err, rowsAffected) => {
|
|
@@ -421,8 +419,8 @@ class Request extends EventEmitter {
|
|
|
421
419
|
*/
|
|
422
420
|
|
|
423
421
|
query (command, callback) {
|
|
424
|
-
if (this.stream
|
|
425
|
-
if (this.arrayRowMode
|
|
422
|
+
if (this.stream == null && this.connection) this.stream = this.connection.config.stream
|
|
423
|
+
if (this.arrayRowMode == null && this.connection) this.arrayRowMode = this.connection.config.arrayRowMode
|
|
426
424
|
this.rowsAffected = 0
|
|
427
425
|
|
|
428
426
|
if (typeof callback === 'function') {
|
|
@@ -510,8 +508,8 @@ class Request extends EventEmitter {
|
|
|
510
508
|
*/
|
|
511
509
|
|
|
512
510
|
execute (command, callback) {
|
|
513
|
-
if (this.stream
|
|
514
|
-
if (this.arrayRowMode
|
|
511
|
+
if (this.stream == null && this.connection) this.stream = this.connection.config.stream
|
|
512
|
+
if (this.arrayRowMode == null && this.connection) this.arrayRowMode = this.connection.config.arrayRowMode
|
|
515
513
|
this.rowsAffected = 0
|
|
516
514
|
|
|
517
515
|
if (typeof callback === 'function') {
|
package/lib/base/transaction.js
CHANGED
|
@@ -6,9 +6,11 @@ const BaseConnectionPool = require('../base/connection-pool')
|
|
|
6
6
|
const { IDS, INCREMENT } = require('../utils')
|
|
7
7
|
const shared = require('../shared')
|
|
8
8
|
const ConnectionError = require('../error/connection-error')
|
|
9
|
+
const { platform } = require('os')
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
11
|
+
const CONNECTION_DRIVER = ['darwin', 'linux'].includes(platform()) ? 'ODBC Driver 17 for SQL Server' : 'SQL Server Native Client 11.0'
|
|
12
|
+
const CONNECTION_STRING_PORT = `Driver=${CONNECTION_DRIVER};Server=#{server},#{port};Database=#{database};Uid=#{user};Pwd=#{password};Trusted_Connection=#{trusted};Encrypt=#{encrypt};`
|
|
13
|
+
const CONNECTION_STRING_NAMED_INSTANCE = `Driver=${CONNECTION_DRIVER};Server=#{server}\\#{instance};Database=#{database};Uid=#{user};Pwd=#{password};Trusted_Connection=#{trusted};Encrypt=#{encrypt};`
|
|
12
14
|
|
|
13
15
|
class ConnectionPool extends BaseConnectionPool {
|
|
14
16
|
_poolCreate () {
|
package/lib/msnodesqlv8/index.js
CHANGED
|
@@ -22,6 +22,13 @@ Object.defineProperty(module.exports, 'Promise', {
|
|
|
22
22
|
}
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
+
Object.defineProperty(module.exports, 'valueHandler', {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
value: base.valueHandler,
|
|
28
|
+
writable: false,
|
|
29
|
+
configurable: false
|
|
30
|
+
})
|
|
31
|
+
|
|
25
32
|
base.driver.name = 'msnodesqlv8'
|
|
26
33
|
base.driver.ConnectionPool = ConnectionPool
|
|
27
34
|
base.driver.Transaction = Transaction
|
|
@@ -8,6 +8,7 @@ const { IDS, objectHasProperty } = require('../utils')
|
|
|
8
8
|
const { TYPES, DECLARATIONS, declare } = require('../datatypes')
|
|
9
9
|
const { PARSERS: UDT } = require('../udt')
|
|
10
10
|
const Table = require('../table')
|
|
11
|
+
const { valueHandler } = require('../shared')
|
|
11
12
|
|
|
12
13
|
const JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B'
|
|
13
14
|
const XML_COLUMN_ID = 'XML_F52E2B61-18A1-11d1-B105-00805F49916B'
|
|
@@ -122,7 +123,12 @@ const createColumns = function (metadata, arrayRowMode) {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
const valueCorrection = function (value, metadata) {
|
|
125
|
-
|
|
126
|
+
const type = metadata && objectHasProperty(metadata, 'sqlType') && objectHasProperty(DECLARATIONS, metadata.sqlType)
|
|
127
|
+
? DECLARATIONS[metadata.sqlType]
|
|
128
|
+
: null
|
|
129
|
+
if (type && valueHandler.has(type)) {
|
|
130
|
+
return valueHandler.get(type)(value)
|
|
131
|
+
} else if ((metadata.sqlType === 'time') && (value != null)) {
|
|
126
132
|
value.setFullYear(1970)
|
|
127
133
|
return value
|
|
128
134
|
} else if ((metadata.sqlType === 'udt') && (value != null)) {
|
package/lib/shared.js
CHANGED
package/lib/table.js
CHANGED
|
@@ -34,24 +34,23 @@ class ConnectionPool extends BaseConnectionPool {
|
|
|
34
34
|
}, this.config.authentication)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
cfg.options.database = this.config.database
|
|
38
|
-
cfg.options.port = this.config.port
|
|
39
|
-
cfg.options.connectTimeout = this.config.connectionTimeout || this.config.timeout || 15000
|
|
40
|
-
cfg.options.requestTimeout = this.config.requestTimeout != null ? this.config.requestTimeout : 15000
|
|
37
|
+
cfg.options.database = cfg.options.database || this.config.database
|
|
38
|
+
cfg.options.port = cfg.options.port || this.config.port
|
|
39
|
+
cfg.options.connectTimeout = cfg.options.connectTimeout || this.config.connectionTimeout || this.config.timeout || 15000
|
|
40
|
+
cfg.options.requestTimeout = cfg.options.requestTimeout || this.config.requestTimeout != null ? this.config.requestTimeout : 15000
|
|
41
41
|
cfg.options.tdsVersion = cfg.options.tdsVersion || '7_4'
|
|
42
|
-
cfg.options.rowCollectionOnDone = false
|
|
43
|
-
cfg.options.rowCollectionOnRequestCompletion = false
|
|
44
|
-
cfg.options.useColumnNames = false
|
|
42
|
+
cfg.options.rowCollectionOnDone = cfg.options.rowCollectionOnDone || false
|
|
43
|
+
cfg.options.rowCollectionOnRequestCompletion = cfg.options.rowCollectionOnRequestCompletion || false
|
|
44
|
+
cfg.options.useColumnNames = cfg.options.useColumnNames || false
|
|
45
45
|
cfg.options.appName = cfg.options.appName || 'node-mssql'
|
|
46
46
|
|
|
47
47
|
// tedious always connect via tcp when port is specified
|
|
48
48
|
if (cfg.options.instanceName) delete cfg.options.port
|
|
49
49
|
|
|
50
50
|
if (isNaN(cfg.options.requestTimeout)) cfg.options.requestTimeout = 15000
|
|
51
|
-
if (cfg.options.requestTimeout === Infinity) cfg.options.requestTimeout = 0
|
|
52
|
-
if (cfg.options.requestTimeout < 0) cfg.options.requestTimeout = 0
|
|
51
|
+
if (cfg.options.requestTimeout === Infinity || cfg.options.requestTimeout < 0) cfg.options.requestTimeout = 0
|
|
53
52
|
|
|
54
|
-
if (this.config.debug) {
|
|
53
|
+
if (!cfg.options.debug && this.config.debug) {
|
|
55
54
|
cfg.options.debug = {
|
|
56
55
|
packet: true,
|
|
57
56
|
token: true,
|
|
@@ -73,6 +72,7 @@ class ConnectionPool extends BaseConnectionPool {
|
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
debug('connection(%d): established', IDS.get(tedious))
|
|
75
|
+
this.collation = tedious.databaseCollation
|
|
76
76
|
resolveOnce(tedious)
|
|
77
77
|
})
|
|
78
78
|
IDS.add(tedious, 'Connection')
|
package/lib/tedious/index.js
CHANGED
|
@@ -22,6 +22,13 @@ Object.defineProperty(module.exports, 'Promise', {
|
|
|
22
22
|
}
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
+
Object.defineProperty(module.exports, 'valueHandler', {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
value: base.valueHandler,
|
|
28
|
+
writable: false,
|
|
29
|
+
configurable: false
|
|
30
|
+
})
|
|
31
|
+
|
|
25
32
|
base.driver.name = 'tedious'
|
|
26
33
|
base.driver.ConnectionPool = ConnectionPool
|
|
27
34
|
base.driver.Transaction = Transaction
|
package/lib/tedious/request.js
CHANGED
|
@@ -8,6 +8,7 @@ const { IDS, objectHasProperty } = require('../utils')
|
|
|
8
8
|
const { TYPES, DECLARATIONS, declare, cast } = require('../datatypes')
|
|
9
9
|
const Table = require('../table')
|
|
10
10
|
const { PARSERS: UDT } = require('../udt')
|
|
11
|
+
const { valueHandler } = require('../shared')
|
|
11
12
|
|
|
12
13
|
const JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B'
|
|
13
14
|
const XML_COLUMN_ID = 'XML_F52E2B61-18A1-11d1-B105-00805F49916B'
|
|
@@ -159,7 +160,10 @@ const createColumns = function (metadata, arrayRowMode) {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
const valueCorrection = function (value, metadata) {
|
|
162
|
-
|
|
163
|
+
const type = getMssqlType(metadata.type)
|
|
164
|
+
if (valueHandler.has(type)) {
|
|
165
|
+
return valueHandler.get(type)(value)
|
|
166
|
+
} else if ((metadata.type === tds.TYPES.UDT) && (value != null)) {
|
|
163
167
|
if (UDT[metadata.udtInfo.typeName]) {
|
|
164
168
|
return UDT[metadata.udtInfo.typeName](value)
|
|
165
169
|
} else {
|
|
@@ -334,26 +338,18 @@ class Request extends BaseRequest {
|
|
|
334
338
|
bulk.addColumn(col.name, getTediousType(col.type), { nullable: col.nullable, length: col.length, scale: col.scale, precision: col.precision })
|
|
335
339
|
}
|
|
336
340
|
|
|
337
|
-
for (const row of table.rows) {
|
|
338
|
-
try {
|
|
339
|
-
bulk.addRow(row)
|
|
340
|
-
} catch (e) {
|
|
341
|
-
return handleError(true, connection, e)
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
341
|
if (table.create) {
|
|
346
342
|
const objectid = table.temporary ? `tempdb..[${table.name}]` : table.path
|
|
347
343
|
const req = new tds.Request(`if object_id('${objectid.replace(/'/g, '\'\'')}') is null ${table.declare()}`, err => {
|
|
348
344
|
if (err) return done(err)
|
|
349
345
|
|
|
350
|
-
connection.execBulkLoad(bulk)
|
|
346
|
+
connection.execBulkLoad(bulk, table.rows)
|
|
351
347
|
})
|
|
352
348
|
this._setCurrentRequest(req)
|
|
353
349
|
|
|
354
350
|
connection.execSqlBatch(req)
|
|
355
351
|
} else {
|
|
356
|
-
connection.execBulkLoad(bulk)
|
|
352
|
+
connection.execBulkLoad(bulk, table.rows)
|
|
357
353
|
}
|
|
358
354
|
})
|
|
359
355
|
})
|
|
@@ -608,7 +604,7 @@ class Request extends BaseRequest {
|
|
|
608
604
|
row.push(col.value)
|
|
609
605
|
} else {
|
|
610
606
|
const exi = row[col.metadata.colName]
|
|
611
|
-
if (exi
|
|
607
|
+
if (exi !== undefined) {
|
|
612
608
|
if (exi instanceof Array) {
|
|
613
609
|
exi.push(col.value)
|
|
614
610
|
} else {
|
|
@@ -645,7 +641,7 @@ class Request extends BaseRequest {
|
|
|
645
641
|
const param = this.parameters[name]
|
|
646
642
|
|
|
647
643
|
try {
|
|
648
|
-
param.value = getTediousType(param.type).validate(param.value)
|
|
644
|
+
param.value = getTediousType(param.type).validate(param.value, this.parent.collation)
|
|
649
645
|
} catch (e) {
|
|
650
646
|
e.message = `Validation failed for parameter '${name}'. ${e.message}`
|
|
651
647
|
const err = new RequestError(e, 'EPARAM')
|
package/package.json
CHANGED
|
@@ -21,19 +21,19 @@
|
|
|
21
21
|
"azure",
|
|
22
22
|
"node-mssql"
|
|
23
23
|
],
|
|
24
|
-
"version": "
|
|
24
|
+
"version": "8.0.0",
|
|
25
25
|
"main": "index.js",
|
|
26
26
|
"repository": "github:tediousjs/node-mssql",
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@tediousjs/connection-string": "^0.3.0",
|
|
30
|
-
"debug": "^4.3.
|
|
30
|
+
"debug": "^4.3.3",
|
|
31
31
|
"rfdc": "^1.3.0",
|
|
32
|
-
"tarn": "^3.0.
|
|
33
|
-
"tedious": "^
|
|
32
|
+
"tarn": "^3.0.2",
|
|
33
|
+
"tedious": "^14.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"mocha": "^
|
|
36
|
+
"mocha": "^9.1.3",
|
|
37
37
|
"standard": "^16.0.4"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
package/lib/connectionstring.js
DELETED
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const URL = require('url').URL
|
|
4
|
-
|
|
5
|
-
const IGNORE_KEYS = ['stream']
|
|
6
|
-
const oror = function () {
|
|
7
|
-
for (let i = 0, l = arguments.length; i < l; i++) {
|
|
8
|
-
if (arguments[i] !== null && arguments[i] !== undefined) {
|
|
9
|
-
return arguments[i]
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const parseConnectionURI = function (uri) {
|
|
15
|
-
const parsed = new URL(uri)
|
|
16
|
-
let instance
|
|
17
|
-
let user
|
|
18
|
-
let password
|
|
19
|
-
|
|
20
|
-
const path = parsed.pathname.substr(1).split('/')
|
|
21
|
-
if (path.length > 1) {
|
|
22
|
-
instance = path.shift()
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (parsed.username) {
|
|
26
|
-
const auth = [parsed.username, parsed.password]
|
|
27
|
-
user = decodeURIComponent(auth.shift())
|
|
28
|
-
password = decodeURIComponent(auth.join(':'))
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const port = parsed.port ? `,${parsed.port}` : (instance ? `\\${instance}` : '')
|
|
32
|
-
const object = {
|
|
33
|
-
server: `${parsed.hostname}${port}`,
|
|
34
|
-
uid: user || '',
|
|
35
|
-
pwd: password || '',
|
|
36
|
-
database: path[0]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (parsed.searchParams) {
|
|
40
|
-
parsed.searchParams.forEach((value, key) => {
|
|
41
|
-
if (key === 'domain') {
|
|
42
|
-
object.uid = `${value}\\${object.uid}`
|
|
43
|
-
} else {
|
|
44
|
-
object[key] = value
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
Object.defineProperty(object, 'toString', {
|
|
50
|
-
value () {
|
|
51
|
-
const out = []
|
|
52
|
-
for (const key in this) {
|
|
53
|
-
if (IGNORE_KEYS.indexOf(key) === -1) {
|
|
54
|
-
out.push(`${key}={${this[key]}}`)
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return out.join(';')
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
return object
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const parseConnectionString = function (string) {
|
|
65
|
-
let cursor = 0
|
|
66
|
-
let parsing = 'name'
|
|
67
|
-
let param = null
|
|
68
|
-
let buffer = ''
|
|
69
|
-
let quotes = null
|
|
70
|
-
const parsed = {}
|
|
71
|
-
const original = {}
|
|
72
|
-
|
|
73
|
-
Object.defineProperty(parsed, '__original__', { value: original })
|
|
74
|
-
Object.defineProperty(parsed, 'toString', {
|
|
75
|
-
value () {
|
|
76
|
-
const out = []
|
|
77
|
-
for (const key in this) {
|
|
78
|
-
if (IGNORE_KEYS.indexOf(key) === -1) {
|
|
79
|
-
const esc = original[key].escape || ['', '']
|
|
80
|
-
out.push(`${original[key].name}=${esc[0] || ''}${this[key]}${esc[1] || ''}`)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return out.join(';')
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
while (cursor < string.length) {
|
|
88
|
-
const char = string.charAt(cursor)
|
|
89
|
-
switch (char) {
|
|
90
|
-
case '=':
|
|
91
|
-
if (parsing === 'name') {
|
|
92
|
-
buffer = buffer.trim()
|
|
93
|
-
param = buffer.toLowerCase()
|
|
94
|
-
original[param] = { name: buffer }
|
|
95
|
-
parsing = 'value'
|
|
96
|
-
buffer = ''
|
|
97
|
-
} else {
|
|
98
|
-
buffer += char
|
|
99
|
-
}
|
|
100
|
-
break
|
|
101
|
-
|
|
102
|
-
case '\'': case '"':
|
|
103
|
-
if (parsing === 'value') {
|
|
104
|
-
if (!buffer.trim().length) {
|
|
105
|
-
// value is wrapped in qotes
|
|
106
|
-
original[param].escape = [char, char]
|
|
107
|
-
quotes = char
|
|
108
|
-
buffer = ''
|
|
109
|
-
} else if (quotes) {
|
|
110
|
-
if (char === quotes) {
|
|
111
|
-
// found same char as used for wrapping quotes
|
|
112
|
-
if (char === string.charAt(cursor + 1)) {
|
|
113
|
-
// escaped quote
|
|
114
|
-
buffer += char
|
|
115
|
-
cursor++
|
|
116
|
-
} else {
|
|
117
|
-
// end of value
|
|
118
|
-
parsed[param] = buffer
|
|
119
|
-
param = null
|
|
120
|
-
parsing = null
|
|
121
|
-
buffer = ''
|
|
122
|
-
quotes = null
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
buffer += char
|
|
126
|
-
}
|
|
127
|
-
} else {
|
|
128
|
-
buffer += char
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
throw new Error('Invalid connection string.')
|
|
132
|
-
}
|
|
133
|
-
break
|
|
134
|
-
|
|
135
|
-
case '{':
|
|
136
|
-
if (parsing === 'value') {
|
|
137
|
-
if (buffer.trim().length === 0) {
|
|
138
|
-
// value is wrapped in qotes
|
|
139
|
-
original[param].escape = ['{', '}']
|
|
140
|
-
quotes = '{}'
|
|
141
|
-
buffer = ''
|
|
142
|
-
} else {
|
|
143
|
-
buffer += char
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
throw new Error('Invalid connection string.')
|
|
147
|
-
}
|
|
148
|
-
break
|
|
149
|
-
|
|
150
|
-
case '}':
|
|
151
|
-
if (parsing === 'value') {
|
|
152
|
-
if (quotes === '{}') {
|
|
153
|
-
// end of value
|
|
154
|
-
parsed[param] = buffer
|
|
155
|
-
param = null
|
|
156
|
-
parsing = null
|
|
157
|
-
buffer = ''
|
|
158
|
-
quotes = null
|
|
159
|
-
} else {
|
|
160
|
-
buffer += char
|
|
161
|
-
}
|
|
162
|
-
} else {
|
|
163
|
-
throw new Error('Invalid connection string.')
|
|
164
|
-
}
|
|
165
|
-
break
|
|
166
|
-
|
|
167
|
-
case ';':
|
|
168
|
-
if (parsing === 'value') {
|
|
169
|
-
if (quotes) {
|
|
170
|
-
buffer += char
|
|
171
|
-
} else {
|
|
172
|
-
// end of value
|
|
173
|
-
parsed[param] = buffer
|
|
174
|
-
param = null
|
|
175
|
-
parsing = 'name'
|
|
176
|
-
buffer = ''
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
buffer = ''
|
|
180
|
-
parsing = 'name'
|
|
181
|
-
}
|
|
182
|
-
break
|
|
183
|
-
|
|
184
|
-
default:
|
|
185
|
-
buffer += char
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
cursor++
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (parsing === 'value') {
|
|
192
|
-
// end of value
|
|
193
|
-
parsed[param] = buffer
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return parsed
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const resolveConnectionString = function (string, driver) {
|
|
200
|
-
const parsed = /^(mssql|tedious|msnodesql|tds):\/\//i.test(string) ? parseConnectionURI(string) : parseConnectionString(string)
|
|
201
|
-
const stream = (parsed.stream || '').toLowerCase()
|
|
202
|
-
const encrypt = (parsed.encrypt || '').toLowerCase()
|
|
203
|
-
|
|
204
|
-
if (driver === 'msnodesqlv8') {
|
|
205
|
-
parsed.driver = 'SQL Server Native Client 11.0'
|
|
206
|
-
|
|
207
|
-
if (parsed.__original__) {
|
|
208
|
-
parsed.__original__.driver = { name: 'Driver', escape: ['{', '}'] }
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return { connectionString: parsed.toString() }
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
let user = parsed.uid || parsed.uid || parsed['user id']
|
|
215
|
-
let server = parsed.server || parsed.address || parsed.addr || parsed['data source'] || parsed['network address']
|
|
216
|
-
|
|
217
|
-
const config = {
|
|
218
|
-
password: oror(parsed.pwd, parsed.password),
|
|
219
|
-
database: oror(parsed.database, parsed['initial catalog']),
|
|
220
|
-
connectionTimeout: oror(parsed.connectionTimeout, parsed.timeout, parsed['connect timeout'], parsed['connection timeout']),
|
|
221
|
-
requestTimeout: oror(parsed.requestTimeout, parsed['request timeout']),
|
|
222
|
-
stream: stream === 'true' || stream === 'yes' || stream === '1',
|
|
223
|
-
options: {
|
|
224
|
-
readOnlyIntent: parsed.applicationintent && parsed.applicationintent.toLowerCase() === 'readonly',
|
|
225
|
-
encrypt: encrypt === 'true' || encrypt === 'yes' || encrypt === '1'
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (parsed.trustservercertificate && ['true', '1', 'yes'].includes(parsed.trustservercertificate.toLowerCase())) {
|
|
230
|
-
Object.assign(config.options, {
|
|
231
|
-
trustServerCertificate: true
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (parsed.useUTC != null) {
|
|
236
|
-
const utc = parsed.useUTC.toLowerCase()
|
|
237
|
-
config.options.useUTC = utc === 'true' || utc === 'yes' || utc === '1'
|
|
238
|
-
}
|
|
239
|
-
if (config.connectionTimeout != null) {
|
|
240
|
-
config.connectionTimeout = parseInt(config.connectionTimeout, 10) * 1000
|
|
241
|
-
}
|
|
242
|
-
if (config.requestTimeout != null) {
|
|
243
|
-
config.requestTimeout = parseInt(config.requestTimeout, 10)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (parsed.multisubnetfailover != null) {
|
|
247
|
-
config.options.multiSubnetFailover = parsed.multisubnetfailover.toLowerCase() === 'true'
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (/^(.*)\\(.*)$/.exec(user)) {
|
|
251
|
-
config.domain = RegExp.$1
|
|
252
|
-
user = RegExp.$2
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (server) {
|
|
256
|
-
server = server.trim()
|
|
257
|
-
|
|
258
|
-
if (/^np:/i.test(server)) {
|
|
259
|
-
throw new Error('Connection via Named Pipes is not supported.')
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (/^tcp:/i.test(server)) {
|
|
263
|
-
server = server.substr(4)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (/^(.*)\\(.*)$/.exec(server)) {
|
|
267
|
-
server = RegExp.$1
|
|
268
|
-
config.options.instanceName = RegExp.$2
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (/^(.*),(.*)$/.exec(server)) {
|
|
272
|
-
server = RegExp.$1.trim()
|
|
273
|
-
config.port = parseInt(RegExp.$2.trim(), 10)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (server === '.' || server === '(.)' || server.toLowerCase() === '(localdb)' || server.toLowerCase() === '(local)') {
|
|
277
|
-
server = 'localhost'
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
config.user = user
|
|
282
|
-
config.server = server
|
|
283
|
-
return config
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
module.exports = {
|
|
287
|
-
parse: parseConnectionString,
|
|
288
|
-
resolve: resolveConnectionString
|
|
289
|
-
}
|