mssql 6.2.3 → 6.4.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 +27 -0
- package/LICENSE.md +22 -22
- package/README.md +187 -2
- package/bin/mssql +0 -0
- package/lib/base/connection-pool.js +1 -0
- package/lib/base/prepared-statement.js +5 -0
- package/lib/base/request.js +33 -19
- package/lib/base/transaction.js +4 -0
- package/lib/cli.js +113 -113
- package/lib/isolationlevel.js +9 -9
- package/lib/msnodesqlv8/request.js +100 -29
- package/lib/tedious/request.js +54 -27
- package/lib/udt.js +1 -1
- package/package.json +5 -5
- package/test.js +40 -40
package/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
v6.4.0 (2021-11-18)
|
|
2
|
+
-------------------
|
|
3
|
+
[new] Transaction/PreparedStatements expose the config from their parent connection
|
|
4
|
+
[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))
|
|
5
|
+
|
|
6
|
+
v6.3.2 (2021-05-13)
|
|
7
|
+
-------------------
|
|
8
|
+
[fix] Bump various dependencies for security fixes
|
|
9
|
+
|
|
10
|
+
v6.3.1 (2021-01-01)
|
|
11
|
+
-------------------
|
|
12
|
+
[fix] Ensure line endings are unix style (LF)
|
|
13
|
+
|
|
14
|
+
v6.3.0 (2020-12-14)
|
|
15
|
+
-------------------
|
|
16
|
+
[new] Result sets with duplicate keys can now be handled using `request.arrayRowMode` ([#1130](https://github.com/tediousjs/node-mssql/pull/1130))
|
|
17
|
+
[fix] Issue with geography v2 parsing resolve ([#1138](https://github.com/tediousjs/node-mssql/pull/1138))
|
|
18
|
+
[fix] Fixed issue where msnodesqlv8 could sometimes terminate queries before all errors has been emitted causing queries to hang ([#1145](https://github.com/tediousjs/node-mssql/pull/1145))
|
|
19
|
+
|
|
1
20
|
v6.2.3 (2020-09-25)
|
|
2
21
|
-------------------
|
|
3
22
|
[fix] Remove reference to global pool on connection error ([#1107](https://github.com/tediousjs/node-mssql/pull/1107))
|
|
@@ -53,6 +72,14 @@ v6.0.0 (2019-11-05)
|
|
|
53
72
|
[change] Replaced pool library (`generic-pool`) with `tarn.js` ([#808](https://github.com/tediousjs/node-mssql/pull/808))
|
|
54
73
|
[removed] Backoff try strategy for creating connections removed, `tarn.js` built-in retry strategy used instead
|
|
55
74
|
|
|
75
|
+
v5.1.5 (2021-05-13)
|
|
76
|
+
-------------------
|
|
77
|
+
[fix] Bump various dependencies for security fixes
|
|
78
|
+
|
|
79
|
+
v5.1.4 (2021-01-01)
|
|
80
|
+
-------------------
|
|
81
|
+
[fix] Ensure line endings are unix style (LF)
|
|
82
|
+
|
|
56
83
|
v5.1.3 (2020-09-18)
|
|
57
84
|
-------------------
|
|
58
85
|
[fix] Avoid using deprecated `.inspect` on Objects ([#1071](https://github.com/tediousjs/node-mssql/pull/1071))
|
package/LICENSE.md
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2013-2018 Patrik Simek and contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013-2018 Patrik Simek and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Microsoft SQL Server client for Node.js
|
|
|
6
6
|
|
|
7
7
|
Supported TDS drivers:
|
|
8
8
|
- [Tedious][tedious-url] (pure JavaScript - Windows/macOS/Linux, default)
|
|
9
|
-
- [Microsoft / Contributors Node V8 Driver for Node.js for SQL Server][msnodesqlv8-url] (native - Windows
|
|
9
|
+
- [Microsoft / Contributors Node V8 Driver for Node.js for SQL Server][msnodesqlv8-url] (v1 native - Windows only)
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -96,6 +96,7 @@ Parts of the connection URI should be correctly URL encoded so that the URI can
|
|
|
96
96
|
* [Table-Valued Parameter](#table-valued-parameter-tvp)
|
|
97
97
|
* [Affected Rows](#affected-rows)
|
|
98
98
|
* [JSON support](#json-support)
|
|
99
|
+
* [Handling Duplicate Column Names](#handling-duplicate-column-names)
|
|
99
100
|
* [Errors](#errors)
|
|
100
101
|
* [Informational messages](#informational-messages)
|
|
101
102
|
* [Metadata](#metadata)
|
|
@@ -611,6 +612,7 @@ const config = {
|
|
|
611
612
|
- **pool.max** - The maximum number of connections there can be in the pool (default: `10`).
|
|
612
613
|
- **pool.min** - The minimum of connections there can be in the pool (default: `0`).
|
|
613
614
|
- **pool.idleTimeoutMillis** - The Number of milliseconds before closing an unused connection (default: `30000`).
|
|
615
|
+
- **arrayRowMode** - Return row results as a an array instead of a keyed object. Also adds `columns` array. See [Handling Duplicate Column Names](#handling-duplicate-column-names)
|
|
614
616
|
|
|
615
617
|
Complete list of pool options can be found [here](https://github.com/vincit/tarn.js/#usage).
|
|
616
618
|
|
|
@@ -658,7 +660,7 @@ More information about Tedious specific options: http://tediousjs.github.io/tedi
|
|
|
658
660
|
|
|
659
661
|
### Microsoft / Contributors Node V8 Driver for Node.js for SQL Server
|
|
660
662
|
|
|
661
|
-
**Requires Node.js v10+ or newer. Windows
|
|
663
|
+
**Requires Node.js v10+ or newer. Windows only.** This driver is not part of the default package and must be installed separately by `npm install msnodesqlv8@^1`. To use this driver, use this require syntax: `const sql = require('mssql/msnodesqlv8')`.
|
|
662
664
|
|
|
663
665
|
**Extra options:**
|
|
664
666
|
|
|
@@ -1566,6 +1568,189 @@ recordset = [ { a: { b: { c: 1, d: 2 }, x: 3, y: 4 } } ]
|
|
|
1566
1568
|
|
|
1567
1569
|
More information about JSON support can be found in [official documentation](https://msdn.microsoft.com/en-us/library/dn921882.aspx).
|
|
1568
1570
|
|
|
1571
|
+
## Handling Duplicate Column Names
|
|
1572
|
+
|
|
1573
|
+
If your queries contain output columns with identical names, the default behaviour of `mssql` will only return column metadata for the last column with that name. You will also not always be able to re-assemble the order of output columns requested.
|
|
1574
|
+
|
|
1575
|
+
Default behaviour:
|
|
1576
|
+
```javascript
|
|
1577
|
+
const request = new sql.Request()
|
|
1578
|
+
request
|
|
1579
|
+
.query("select 'asdf' as name, 'qwerty' as other_name, 'jkl' as name")
|
|
1580
|
+
.then(result => {
|
|
1581
|
+
console.log(result)
|
|
1582
|
+
});
|
|
1583
|
+
```
|
|
1584
|
+
Results in:
|
|
1585
|
+
```javascript
|
|
1586
|
+
{
|
|
1587
|
+
recordsets: [
|
|
1588
|
+
[ { name: [ 'asdf', 'jkl' ], other_name: 'qwerty' } ]
|
|
1589
|
+
],
|
|
1590
|
+
recordset: [ { name: [ 'asdf', 'jkl' ], other_name: 'qwerty' } ],
|
|
1591
|
+
output: {},
|
|
1592
|
+
rowsAffected: [ 1 ]
|
|
1593
|
+
}
|
|
1594
|
+
```
|
|
1595
|
+
|
|
1596
|
+
You can use the `arrayRowMode` configuration parameter to return the row values as arrays and add a separate array of column values. `arrayRowMode` can be set globally during the initial connection, or per-request.
|
|
1597
|
+
|
|
1598
|
+
```javascript
|
|
1599
|
+
const request = new sql.Request()
|
|
1600
|
+
request.arrayRowMode = true
|
|
1601
|
+
request
|
|
1602
|
+
.query("select 'asdf' as name, 'qwerty' as other_name, 'jkl' as name")
|
|
1603
|
+
.then(result => {
|
|
1604
|
+
console.log(result)
|
|
1605
|
+
});
|
|
1606
|
+
```
|
|
1607
|
+
|
|
1608
|
+
Results in:
|
|
1609
|
+
```javascript
|
|
1610
|
+
{
|
|
1611
|
+
recordsets: [ [ [ 'asdf', 'qwerty', 'jkl' ] ] ],
|
|
1612
|
+
recordset: [ [ 'asdf', 'qwerty', 'jkl' ] ],
|
|
1613
|
+
output: {},
|
|
1614
|
+
rowsAffected: [ 1 ],
|
|
1615
|
+
columns: [
|
|
1616
|
+
[
|
|
1617
|
+
{
|
|
1618
|
+
index: 0,
|
|
1619
|
+
name: 'name',
|
|
1620
|
+
length: 4,
|
|
1621
|
+
type: [sql.VarChar],
|
|
1622
|
+
scale: undefined,
|
|
1623
|
+
precision: undefined,
|
|
1624
|
+
nullable: false,
|
|
1625
|
+
caseSensitive: false,
|
|
1626
|
+
identity: false,
|
|
1627
|
+
readOnly: true
|
|
1628
|
+
},
|
|
1629
|
+
{
|
|
1630
|
+
index: 1,
|
|
1631
|
+
name: 'other_name',
|
|
1632
|
+
length: 6,
|
|
1633
|
+
type: [sql.VarChar],
|
|
1634
|
+
scale: undefined,
|
|
1635
|
+
precision: undefined,
|
|
1636
|
+
nullable: false,
|
|
1637
|
+
caseSensitive: false,
|
|
1638
|
+
identity: false,
|
|
1639
|
+
readOnly: true
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
index: 2,
|
|
1643
|
+
name: 'name',
|
|
1644
|
+
length: 3,
|
|
1645
|
+
type: [sql.VarChar],
|
|
1646
|
+
scale: undefined,
|
|
1647
|
+
precision: undefined,
|
|
1648
|
+
nullable: false,
|
|
1649
|
+
caseSensitive: false,
|
|
1650
|
+
identity: false,
|
|
1651
|
+
readOnly: true
|
|
1652
|
+
}
|
|
1653
|
+
]
|
|
1654
|
+
]
|
|
1655
|
+
}
|
|
1656
|
+
```
|
|
1657
|
+
|
|
1658
|
+
__Streaming Duplicate Column Names__
|
|
1659
|
+
|
|
1660
|
+
When using `arrayRowMode` with `stream` enabled, the output from the `recordset` event (as described in [Streaming](#streaming)) is returned as an array of column metadata, instead of as a keyed object. The order of the column metadata provided by the `recordset` event will match the order of row values when `arrayRowMode` is enabled.
|
|
1661
|
+
|
|
1662
|
+
Default behaviour (without `arrayRowMode`):
|
|
1663
|
+
```javascript
|
|
1664
|
+
const request = new sql.Request()
|
|
1665
|
+
request.stream = true
|
|
1666
|
+
request.query("select 'asdf' as name, 'qwerty' as other_name, 'jkl' as name")
|
|
1667
|
+
request.on('recordset', recordset => console.log(recordset))
|
|
1668
|
+
```
|
|
1669
|
+
|
|
1670
|
+
|
|
1671
|
+
Results in:
|
|
1672
|
+
|
|
1673
|
+
```javascript
|
|
1674
|
+
{
|
|
1675
|
+
name: {
|
|
1676
|
+
index: 2,
|
|
1677
|
+
name: 'name',
|
|
1678
|
+
length: 3,
|
|
1679
|
+
type: [sql.VarChar],
|
|
1680
|
+
scale: undefined,
|
|
1681
|
+
precision: undefined,
|
|
1682
|
+
nullable: false,
|
|
1683
|
+
caseSensitive: false,
|
|
1684
|
+
identity: false,
|
|
1685
|
+
readOnly: true
|
|
1686
|
+
},
|
|
1687
|
+
other_name: {
|
|
1688
|
+
index: 1,
|
|
1689
|
+
name: 'other_name',
|
|
1690
|
+
length: 6,
|
|
1691
|
+
type: [sql.VarChar],
|
|
1692
|
+
scale: undefined,
|
|
1693
|
+
precision: undefined,
|
|
1694
|
+
nullable: false,
|
|
1695
|
+
caseSensitive: false,
|
|
1696
|
+
identity: false,
|
|
1697
|
+
readOnly: true
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
```
|
|
1701
|
+
|
|
1702
|
+
With `arrayRowMode`:
|
|
1703
|
+
```javascript
|
|
1704
|
+
const request = new sql.Request()
|
|
1705
|
+
request.stream = true
|
|
1706
|
+
request.arrayRowMode = true
|
|
1707
|
+
request.query("select 'asdf' as name, 'qwerty' as other_name, 'jkl' as name")
|
|
1708
|
+
|
|
1709
|
+
request.on('recordset', recordset => console.log(recordset))
|
|
1710
|
+
```
|
|
1711
|
+
|
|
1712
|
+
Results in:
|
|
1713
|
+
```javascript
|
|
1714
|
+
[
|
|
1715
|
+
{
|
|
1716
|
+
index: 0,
|
|
1717
|
+
name: 'name',
|
|
1718
|
+
length: 4,
|
|
1719
|
+
type: [sql.VarChar],
|
|
1720
|
+
scale: undefined,
|
|
1721
|
+
precision: undefined,
|
|
1722
|
+
nullable: false,
|
|
1723
|
+
caseSensitive: false,
|
|
1724
|
+
identity: false,
|
|
1725
|
+
readOnly: true
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
index: 1,
|
|
1729
|
+
name: 'other_name',
|
|
1730
|
+
length: 6,
|
|
1731
|
+
type: [sql.VarChar],
|
|
1732
|
+
scale: undefined,
|
|
1733
|
+
precision: undefined,
|
|
1734
|
+
nullable: false,
|
|
1735
|
+
caseSensitive: false,
|
|
1736
|
+
identity: false,
|
|
1737
|
+
readOnly: true
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
index: 2,
|
|
1741
|
+
name: 'name',
|
|
1742
|
+
length: 3,
|
|
1743
|
+
type: [sql.VarChar],
|
|
1744
|
+
scale: undefined,
|
|
1745
|
+
precision: undefined,
|
|
1746
|
+
nullable: false,
|
|
1747
|
+
caseSensitive: false,
|
|
1748
|
+
identity: false,
|
|
1749
|
+
readOnly: true
|
|
1750
|
+
}
|
|
1751
|
+
]
|
|
1752
|
+
```
|
|
1753
|
+
|
|
1569
1754
|
## Errors
|
|
1570
1755
|
|
|
1571
1756
|
There are 4 types of errors you can handle:
|
package/bin/mssql
CHANGED
|
File without changes
|
|
@@ -59,6 +59,7 @@ class ConnectionPool extends EventEmitter {
|
|
|
59
59
|
this.config.options = this.config.options || {}
|
|
60
60
|
this.config.stream = this.config.stream || false
|
|
61
61
|
this.config.parseJSON = this.config.parseJSON || false
|
|
62
|
+
this.config.arrayRowMode = this.config.arrayRowMode || false
|
|
62
63
|
|
|
63
64
|
if (/^(.*)\\(.*)$/.exec(this.config.server)) {
|
|
64
65
|
this.config.server = RegExp.$1
|
|
@@ -35,6 +35,10 @@ class PreparedStatement extends EventEmitter {
|
|
|
35
35
|
this.parameters = {}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
get config () {
|
|
39
|
+
return this.parent.config
|
|
40
|
+
}
|
|
41
|
+
|
|
38
42
|
get connected () {
|
|
39
43
|
return this.parent.connected
|
|
40
44
|
}
|
|
@@ -292,6 +296,7 @@ class PreparedStatement extends EventEmitter {
|
|
|
292
296
|
_execute (values, callback) {
|
|
293
297
|
const req = new shared.driver.Request(this)
|
|
294
298
|
req.stream = this.stream
|
|
299
|
+
req.arrayRowMode = this.arrayRowMode
|
|
295
300
|
req.input('handle', TYPES.Int, this._handle)
|
|
296
301
|
|
|
297
302
|
// copy parameters with new values
|
package/lib/base/request.js
CHANGED
|
@@ -25,7 +25,7 @@ class Request extends EventEmitter {
|
|
|
25
25
|
/**
|
|
26
26
|
* Create new Request.
|
|
27
27
|
*
|
|
28
|
-
* @param {Connection|ConnectionPool|Transaction|PreparedStatement} parent If
|
|
28
|
+
* @param {Connection|ConnectionPool|Transaction|PreparedStatement} parent If omitted, global connection is used instead.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
constructor (parent) {
|
|
@@ -38,6 +38,8 @@ class Request extends EventEmitter {
|
|
|
38
38
|
this._paused = false
|
|
39
39
|
this.parent = parent || globalConnection.pool
|
|
40
40
|
this.parameters = {}
|
|
41
|
+
this.stream = null
|
|
42
|
+
this.arrayRowMode = null
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
get paused () {
|
|
@@ -223,7 +225,8 @@ class Request extends EventEmitter {
|
|
|
223
225
|
*/
|
|
224
226
|
|
|
225
227
|
batch (batch, callback) {
|
|
226
|
-
if (this.stream
|
|
228
|
+
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
|
229
|
+
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
|
227
230
|
this.rowsAffected = 0
|
|
228
231
|
|
|
229
232
|
if (typeof callback === 'function') {
|
|
@@ -286,11 +289,11 @@ class Request extends EventEmitter {
|
|
|
286
289
|
*/
|
|
287
290
|
|
|
288
291
|
_batch (batch, callback) {
|
|
289
|
-
if (!this.
|
|
292
|
+
if (!this.parent) {
|
|
290
293
|
return setImmediate(callback, new RequestError('No connection is specified for that request.', 'ENOCONN'))
|
|
291
294
|
}
|
|
292
295
|
|
|
293
|
-
if (!this.
|
|
296
|
+
if (!this.parent.connected) {
|
|
294
297
|
return setImmediate(callback, new ConnectionError('Connection is closed.', 'ECONNCLOSED'))
|
|
295
298
|
}
|
|
296
299
|
|
|
@@ -315,7 +318,8 @@ class Request extends EventEmitter {
|
|
|
315
318
|
options = {}
|
|
316
319
|
}
|
|
317
320
|
|
|
318
|
-
if (this.stream
|
|
321
|
+
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
|
322
|
+
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
|
319
323
|
|
|
320
324
|
if (this.stream || typeof callback === 'function') {
|
|
321
325
|
this._bulk(table, options, (err, rowsAffected) => {
|
|
@@ -391,11 +395,12 @@ class Request extends EventEmitter {
|
|
|
391
395
|
*/
|
|
392
396
|
|
|
393
397
|
query (command, callback) {
|
|
394
|
-
if (this.stream
|
|
398
|
+
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
|
399
|
+
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
|
395
400
|
this.rowsAffected = 0
|
|
396
401
|
|
|
397
402
|
if (typeof callback === 'function') {
|
|
398
|
-
this._query(command, (err, recordsets, output, rowsAffected) => {
|
|
403
|
+
this._query(command, (err, recordsets, output, rowsAffected, columns) => {
|
|
399
404
|
if (this.stream) {
|
|
400
405
|
if (err) this.emit('error', err)
|
|
401
406
|
err = null
|
|
@@ -407,12 +412,14 @@ class Request extends EventEmitter {
|
|
|
407
412
|
}
|
|
408
413
|
|
|
409
414
|
if (err) return callback(err)
|
|
410
|
-
|
|
415
|
+
const result = {
|
|
411
416
|
recordsets,
|
|
412
417
|
recordset: recordsets && recordsets[0],
|
|
413
418
|
output,
|
|
414
419
|
rowsAffected
|
|
415
|
-
}
|
|
420
|
+
}
|
|
421
|
+
if (this.arrayRowMode) result.columns = columns
|
|
422
|
+
callback(null, result)
|
|
416
423
|
})
|
|
417
424
|
return this
|
|
418
425
|
}
|
|
@@ -425,7 +432,7 @@ class Request extends EventEmitter {
|
|
|
425
432
|
}
|
|
426
433
|
|
|
427
434
|
return new shared.Promise((resolve, reject) => {
|
|
428
|
-
this._query(command, (err, recordsets, output, rowsAffected) => {
|
|
435
|
+
this._query(command, (err, recordsets, output, rowsAffected, columns) => {
|
|
429
436
|
if (this.stream) {
|
|
430
437
|
if (err) this.emit('error', err)
|
|
431
438
|
err = null
|
|
@@ -437,12 +444,14 @@ class Request extends EventEmitter {
|
|
|
437
444
|
}
|
|
438
445
|
|
|
439
446
|
if (err) return reject(err)
|
|
440
|
-
|
|
447
|
+
const result = {
|
|
441
448
|
recordsets,
|
|
442
449
|
recordset: recordsets && recordsets[0],
|
|
443
450
|
output,
|
|
444
451
|
rowsAffected
|
|
445
|
-
}
|
|
452
|
+
}
|
|
453
|
+
if (this.arrayRowMode) result.columns = columns
|
|
454
|
+
resolve(result)
|
|
446
455
|
})
|
|
447
456
|
})
|
|
448
457
|
}
|
|
@@ -475,11 +484,12 @@ class Request extends EventEmitter {
|
|
|
475
484
|
*/
|
|
476
485
|
|
|
477
486
|
execute (command, callback) {
|
|
478
|
-
if (this.stream
|
|
487
|
+
if (this.stream === null && this.parent) this.stream = this.parent.config.stream
|
|
488
|
+
if (this.arrayRowMode === null && this.parent) this.arrayRowMode = this.parent.config.arrayRowMode
|
|
479
489
|
this.rowsAffected = 0
|
|
480
490
|
|
|
481
491
|
if (typeof callback === 'function') {
|
|
482
|
-
this._execute(command, (err, recordsets, output, returnValue, rowsAffected) => {
|
|
492
|
+
this._execute(command, (err, recordsets, output, returnValue, rowsAffected, columns) => {
|
|
483
493
|
if (this.stream) {
|
|
484
494
|
if (err) this.emit('error', err)
|
|
485
495
|
err = null
|
|
@@ -492,19 +502,21 @@ class Request extends EventEmitter {
|
|
|
492
502
|
}
|
|
493
503
|
|
|
494
504
|
if (err) return callback(err)
|
|
495
|
-
|
|
505
|
+
const result = {
|
|
496
506
|
recordsets,
|
|
497
507
|
recordset: recordsets && recordsets[0],
|
|
498
508
|
output,
|
|
499
509
|
rowsAffected,
|
|
500
510
|
returnValue
|
|
501
|
-
}
|
|
511
|
+
}
|
|
512
|
+
if (this.arrayRowMode) result.columns = columns
|
|
513
|
+
callback(null, result)
|
|
502
514
|
})
|
|
503
515
|
return this
|
|
504
516
|
}
|
|
505
517
|
|
|
506
518
|
return new shared.Promise((resolve, reject) => {
|
|
507
|
-
this._execute(command, (err, recordsets, output, returnValue, rowsAffected) => {
|
|
519
|
+
this._execute(command, (err, recordsets, output, returnValue, rowsAffected, columns) => {
|
|
508
520
|
if (this.stream) {
|
|
509
521
|
if (err) this.emit('error', err)
|
|
510
522
|
err = null
|
|
@@ -517,13 +529,15 @@ class Request extends EventEmitter {
|
|
|
517
529
|
}
|
|
518
530
|
|
|
519
531
|
if (err) return reject(err)
|
|
520
|
-
|
|
532
|
+
const result = {
|
|
521
533
|
recordsets,
|
|
522
534
|
recordset: recordsets && recordsets[0],
|
|
523
535
|
output,
|
|
524
536
|
rowsAffected,
|
|
525
537
|
returnValue
|
|
526
|
-
}
|
|
538
|
+
}
|
|
539
|
+
if (this.arrayRowMode) result.columns = columns
|
|
540
|
+
resolve(result)
|
|
527
541
|
})
|
|
528
542
|
})
|
|
529
543
|
}
|
package/lib/base/transaction.js
CHANGED
package/lib/cli.js
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
const path = require('path')
|
|
5
|
-
const sql = require('./tedious')
|
|
6
|
-
|
|
7
|
-
const exit = process.exit
|
|
8
|
-
const write = text => process.stdout.write(text)
|
|
9
|
-
Buffer.prototype.toJSON = () => {
|
|
10
|
-
return `0x${this.toString('hex')}`
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Resolve config path
|
|
14
|
-
|
|
15
|
-
let cfgPath = path.resolve(process.argv[2] || process.cwd())
|
|
16
|
-
if (fs.lstatSync(cfgPath).isDirectory()) {
|
|
17
|
-
cfgPath = path.resolve(cfgPath, './.mssql.json')
|
|
18
|
-
}
|
|
19
|
-
if (!fs.existsSync(cfgPath)) {
|
|
20
|
-
console.error('Config file not found.')
|
|
21
|
-
exit(1)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Config checks & parse
|
|
25
|
-
|
|
26
|
-
let config
|
|
27
|
-
try {
|
|
28
|
-
config = fs.readFileSync(cfgPath)
|
|
29
|
-
} catch (err) {
|
|
30
|
-
console.error(`Failed to load config file. ${err.message}`)
|
|
31
|
-
exit(1)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
config = JSON.parse(config)
|
|
36
|
-
} catch (err) {
|
|
37
|
-
console.error(`Failed to parse config file. ${err.message}`)
|
|
38
|
-
exit(1)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Read stdin
|
|
42
|
-
|
|
43
|
-
const buffer = []
|
|
44
|
-
|
|
45
|
-
process.stdin.setEncoding('utf8')
|
|
46
|
-
process.stdin.on('readable', () => buffer.push(process.stdin.read()))
|
|
47
|
-
|
|
48
|
-
process.stdin.on('end', () => {
|
|
49
|
-
const statement = buffer.join('')
|
|
50
|
-
let rst = 0
|
|
51
|
-
let index = 0
|
|
52
|
-
|
|
53
|
-
if (!statement.length) {
|
|
54
|
-
console.error('Statement is empty.')
|
|
55
|
-
exit(1)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
sql.connect(config, err => {
|
|
59
|
-
if (err) {
|
|
60
|
-
console.error(err.message)
|
|
61
|
-
exit(1)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
write('[')
|
|
65
|
-
|
|
66
|
-
const request = new sql.Request()
|
|
67
|
-
request.stream = true
|
|
68
|
-
request.on('recordset', () => {
|
|
69
|
-
index = 0
|
|
70
|
-
if (rst++ > 0) {
|
|
71
|
-
write('],')
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return write('[')
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
request.on('error', err => {
|
|
78
|
-
console.error(err.message)
|
|
79
|
-
sql.close()
|
|
80
|
-
return exit(1)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
request.on('row', row => {
|
|
84
|
-
if (index++ > 0) {
|
|
85
|
-
write(',')
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return write(JSON.stringify(row))
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
request.on('done', () => {
|
|
92
|
-
if (rst > 0) {
|
|
93
|
-
write(']')
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
write(']\n')
|
|
97
|
-
sql.close()
|
|
98
|
-
return exit(0)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
request.query(statement)
|
|
102
|
-
})
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
process.on('uncaughtException', err => {
|
|
106
|
-
if (err.code === 'EPIPE') {
|
|
107
|
-
console.error('Failed to pipe output stream.')
|
|
108
|
-
} else {
|
|
109
|
-
console.error(err.message)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
exit(1)
|
|
113
|
-
})
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const sql = require('./tedious')
|
|
6
|
+
|
|
7
|
+
const exit = process.exit
|
|
8
|
+
const write = text => process.stdout.write(text)
|
|
9
|
+
Buffer.prototype.toJSON = () => {
|
|
10
|
+
return `0x${this.toString('hex')}`
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Resolve config path
|
|
14
|
+
|
|
15
|
+
let cfgPath = path.resolve(process.argv[2] || process.cwd())
|
|
16
|
+
if (fs.lstatSync(cfgPath).isDirectory()) {
|
|
17
|
+
cfgPath = path.resolve(cfgPath, './.mssql.json')
|
|
18
|
+
}
|
|
19
|
+
if (!fs.existsSync(cfgPath)) {
|
|
20
|
+
console.error('Config file not found.')
|
|
21
|
+
exit(1)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Config checks & parse
|
|
25
|
+
|
|
26
|
+
let config
|
|
27
|
+
try {
|
|
28
|
+
config = fs.readFileSync(cfgPath)
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.error(`Failed to load config file. ${err.message}`)
|
|
31
|
+
exit(1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
config = JSON.parse(config)
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error(`Failed to parse config file. ${err.message}`)
|
|
38
|
+
exit(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Read stdin
|
|
42
|
+
|
|
43
|
+
const buffer = []
|
|
44
|
+
|
|
45
|
+
process.stdin.setEncoding('utf8')
|
|
46
|
+
process.stdin.on('readable', () => buffer.push(process.stdin.read()))
|
|
47
|
+
|
|
48
|
+
process.stdin.on('end', () => {
|
|
49
|
+
const statement = buffer.join('')
|
|
50
|
+
let rst = 0
|
|
51
|
+
let index = 0
|
|
52
|
+
|
|
53
|
+
if (!statement.length) {
|
|
54
|
+
console.error('Statement is empty.')
|
|
55
|
+
exit(1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
sql.connect(config, err => {
|
|
59
|
+
if (err) {
|
|
60
|
+
console.error(err.message)
|
|
61
|
+
exit(1)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
write('[')
|
|
65
|
+
|
|
66
|
+
const request = new sql.Request()
|
|
67
|
+
request.stream = true
|
|
68
|
+
request.on('recordset', () => {
|
|
69
|
+
index = 0
|
|
70
|
+
if (rst++ > 0) {
|
|
71
|
+
write('],')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return write('[')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
request.on('error', err => {
|
|
78
|
+
console.error(err.message)
|
|
79
|
+
sql.close()
|
|
80
|
+
return exit(1)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
request.on('row', row => {
|
|
84
|
+
if (index++ > 0) {
|
|
85
|
+
write(',')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return write(JSON.stringify(row))
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
request.on('done', () => {
|
|
92
|
+
if (rst > 0) {
|
|
93
|
+
write(']')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
write(']\n')
|
|
97
|
+
sql.close()
|
|
98
|
+
return exit(0)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
request.query(statement)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
process.on('uncaughtException', err => {
|
|
106
|
+
if (err.code === 'EPIPE') {
|
|
107
|
+
console.error('Failed to pipe output stream.')
|
|
108
|
+
} else {
|
|
109
|
+
console.error(err.message)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
exit(1)
|
|
113
|
+
})
|
package/lib/isolationlevel.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
READ_UNCOMMITTED: 0x01,
|
|
5
|
-
READ_COMMITTED: 0x02,
|
|
6
|
-
REPEATABLE_READ: 0x03,
|
|
7
|
-
SERIALIZABLE: 0x04,
|
|
8
|
-
SNAPSHOT: 0x05
|
|
9
|
-
}
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
READ_UNCOMMITTED: 0x01,
|
|
5
|
+
READ_COMMITTED: 0x02,
|
|
6
|
+
REPEATABLE_READ: 0x03,
|
|
7
|
+
SERIALIZABLE: 0x04,
|
|
8
|
+
SNAPSHOT: 0x05
|
|
9
|
+
}
|
|
@@ -88,11 +88,13 @@ const castParameter = function (value, type) {
|
|
|
88
88
|
return value
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
const createColumns = function (metadata) {
|
|
92
|
-
|
|
91
|
+
const createColumns = function (metadata, arrayRowMode) {
|
|
92
|
+
let out = {}
|
|
93
|
+
if (arrayRowMode) out = []
|
|
93
94
|
for (let index = 0, length = metadata.length; index < length; index++) {
|
|
94
95
|
const column = metadata[index]
|
|
95
|
-
|
|
96
|
+
const colName = column.name
|
|
97
|
+
const outColumn = {
|
|
96
98
|
index,
|
|
97
99
|
name: column.name,
|
|
98
100
|
length: column.size,
|
|
@@ -101,14 +103,19 @@ const createColumns = function (metadata) {
|
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
if (column.udtType != null) {
|
|
104
|
-
|
|
106
|
+
outColumn.udt = {
|
|
105
107
|
name: column.udtType
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
if (DECLARATIONS[column.udtType]) {
|
|
109
|
-
|
|
111
|
+
outColumn.type = DECLARATIONS[column.udtType]
|
|
110
112
|
}
|
|
111
113
|
}
|
|
114
|
+
if (arrayRowMode) {
|
|
115
|
+
out.push(outColumn)
|
|
116
|
+
} else {
|
|
117
|
+
out[colName] = outColumn
|
|
118
|
+
}
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
return out
|
|
@@ -236,6 +243,7 @@ class Request extends BaseRequest {
|
|
|
236
243
|
}
|
|
237
244
|
|
|
238
245
|
const recordsets = []
|
|
246
|
+
const recordsetcolumns = []
|
|
239
247
|
const errors = []
|
|
240
248
|
const errorHandlers = {}
|
|
241
249
|
const output = {}
|
|
@@ -410,7 +418,7 @@ class Request extends BaseRequest {
|
|
|
410
418
|
Object.defineProperty(recordset, 'columns', {
|
|
411
419
|
enumerable: false,
|
|
412
420
|
configurable: true,
|
|
413
|
-
value: createColumns(metadata)
|
|
421
|
+
value: createColumns(metadata, this.arrayRowMode)
|
|
414
422
|
})
|
|
415
423
|
|
|
416
424
|
Object.defineProperty(recordset, 'toTable', {
|
|
@@ -425,19 +433,35 @@ class Request extends BaseRequest {
|
|
|
425
433
|
chunksBuffer = []
|
|
426
434
|
}
|
|
427
435
|
|
|
436
|
+
let hasReturnColumn = false
|
|
437
|
+
if (recordset.columns.___return___ != null) {
|
|
438
|
+
hasReturnColumn = true
|
|
439
|
+
} else if (this.arrayRowMode) {
|
|
440
|
+
for (let i = 0; i < columns.length; i++) {
|
|
441
|
+
if (columns[i].name === '___return___') {
|
|
442
|
+
hasReturnColumn = true
|
|
443
|
+
break
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
428
447
|
if (this.stream) {
|
|
429
|
-
if (
|
|
448
|
+
if (!hasReturnColumn) {
|
|
430
449
|
this.emit('recordset', recordset.columns)
|
|
431
450
|
}
|
|
432
451
|
} else {
|
|
433
452
|
recordsets.push(recordset)
|
|
434
453
|
}
|
|
454
|
+
if (this.arrayRowMode) recordsetcolumns.push(recordset.columns)
|
|
435
455
|
})
|
|
436
456
|
|
|
437
457
|
req.on('row', rownumber => {
|
|
438
458
|
if (row && isChunkedRecordset) return
|
|
439
459
|
|
|
440
|
-
|
|
460
|
+
if (this.arrayRowMode) {
|
|
461
|
+
row = []
|
|
462
|
+
} else {
|
|
463
|
+
row = {}
|
|
464
|
+
}
|
|
441
465
|
|
|
442
466
|
if (!this.stream) recordset.push(row)
|
|
443
467
|
})
|
|
@@ -448,17 +472,32 @@ class Request extends BaseRequest {
|
|
|
448
472
|
} else {
|
|
449
473
|
data = valueCorrection(data, columns[idx])
|
|
450
474
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
475
|
+
if (this.arrayRowMode) {
|
|
476
|
+
row.push(data)
|
|
477
|
+
} else {
|
|
478
|
+
const exi = row[columns[idx].name]
|
|
479
|
+
if (exi != null) {
|
|
480
|
+
if (exi instanceof Array) {
|
|
481
|
+
exi.push(data)
|
|
482
|
+
} else {
|
|
483
|
+
row[columns[idx].name] = [exi, data]
|
|
484
|
+
}
|
|
455
485
|
} else {
|
|
456
|
-
row[columns[idx].name] =
|
|
486
|
+
row[columns[idx].name] = data
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
let hasReturnColumn = false
|
|
490
|
+
if (row && row.___return___ != null) {
|
|
491
|
+
hasReturnColumn = true
|
|
492
|
+
} else if (this.arrayRowMode) {
|
|
493
|
+
for (let i = 0; i < columns.length; i++) {
|
|
494
|
+
if (columns[i].name === '___return___') {
|
|
495
|
+
hasReturnColumn = true
|
|
496
|
+
break
|
|
497
|
+
}
|
|
457
498
|
}
|
|
458
|
-
} else {
|
|
459
|
-
row[columns[idx].name] = data
|
|
460
499
|
}
|
|
461
|
-
if (
|
|
500
|
+
if (!hasReturnColumn) {
|
|
462
501
|
if (this.stream && !this.paused && idx === columns.length - 1) {
|
|
463
502
|
this.emit('row', row)
|
|
464
503
|
}
|
|
@@ -484,6 +523,14 @@ class Request extends BaseRequest {
|
|
|
484
523
|
serverName: msg.serverName,
|
|
485
524
|
procName: msg.procName
|
|
486
525
|
})
|
|
526
|
+
|
|
527
|
+
// query terminated
|
|
528
|
+
if (msg.code === 3621 && !hasReturned) {
|
|
529
|
+
// if the query has been terminated it's probably best to throw the last meaningful error if there was one
|
|
530
|
+
// pop it off the errors array so it doesn't get put in twice
|
|
531
|
+
const error = errors.length > 0 ? errors.pop() : msg
|
|
532
|
+
handleError(req, connection, error.originalError || error, false)
|
|
533
|
+
}
|
|
487
534
|
})
|
|
488
535
|
|
|
489
536
|
req.on('error', errorHandlers.error = handleError.bind(null, req, connection))
|
|
@@ -551,9 +598,9 @@ class Request extends BaseRequest {
|
|
|
551
598
|
debug('request(%d): completed', IDS.get(this))
|
|
552
599
|
|
|
553
600
|
if (this.stream) {
|
|
554
|
-
callback(null, this._nested ? row : null, output, rowsAffected)
|
|
601
|
+
callback(null, this._nested ? row : null, output, rowsAffected, recordsetcolumns)
|
|
555
602
|
} else {
|
|
556
|
-
callback(null, recordsets, output, rowsAffected)
|
|
603
|
+
callback(null, recordsets, output, rowsAffected, recordsetcolumns)
|
|
557
604
|
}
|
|
558
605
|
})
|
|
559
606
|
})
|
|
@@ -610,7 +657,7 @@ class Request extends BaseRequest {
|
|
|
610
657
|
|
|
611
658
|
this._nested = true
|
|
612
659
|
|
|
613
|
-
this._query(cmd, (err, recordsets, output, rowsAffected) => {
|
|
660
|
+
this._query(cmd, (err, recordsets, output, rowsAffected, recordsetcolumns) => {
|
|
614
661
|
this._nested = false
|
|
615
662
|
|
|
616
663
|
if (err) return callback(err)
|
|
@@ -622,25 +669,49 @@ class Request extends BaseRequest {
|
|
|
622
669
|
last = recordsets.pop()
|
|
623
670
|
if (last) last = last[0]
|
|
624
671
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
672
|
+
const lastColumns = recordsetcolumns.pop()
|
|
673
|
+
|
|
674
|
+
if (last && this.arrayRowMode && lastColumns) {
|
|
675
|
+
let returnColumnIdx = null
|
|
676
|
+
const parametersNameToLastIdxDict = {}
|
|
677
|
+
for (let i = 0; i < lastColumns.length; i++) {
|
|
678
|
+
if (lastColumns[i].name === '___return___') {
|
|
679
|
+
returnColumnIdx = i
|
|
680
|
+
} else if (objectHasProperty(this.parameters, lastColumns[i].name)) {
|
|
681
|
+
parametersNameToLastIdxDict[lastColumns[i].name] = i
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
if (returnColumnIdx != null) {
|
|
685
|
+
returnValue = last[returnColumnIdx]
|
|
686
|
+
}
|
|
687
|
+
for (const name in parametersNameToLastIdxDict) {
|
|
688
|
+
if (!objectHasProperty(parametersNameToLastIdxDict, name)) {
|
|
631
689
|
continue
|
|
632
690
|
}
|
|
633
691
|
const param = this.parameters[name]
|
|
634
692
|
if (param.io === 2) {
|
|
635
|
-
output[param.name] = last[
|
|
693
|
+
output[param.name] = last[parametersNameToLastIdxDict[name]]
|
|
636
694
|
}
|
|
637
695
|
}
|
|
638
|
-
}
|
|
696
|
+
} else {
|
|
697
|
+
if (last && (last.___return___ != null)) {
|
|
698
|
+
returnValue = last.___return___
|
|
639
699
|
|
|
700
|
+
for (const name in this.parameters) {
|
|
701
|
+
if (!objectHasProperty(this.parameters, name)) {
|
|
702
|
+
continue
|
|
703
|
+
}
|
|
704
|
+
const param = this.parameters[name]
|
|
705
|
+
if (param.io === 2) {
|
|
706
|
+
output[param.name] = last[param.name]
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
640
711
|
if (this.stream) {
|
|
641
|
-
callback(null, null, output, returnValue, rowsAffected)
|
|
712
|
+
callback(null, null, output, returnValue, rowsAffected, recordsetcolumns)
|
|
642
713
|
} else {
|
|
643
|
-
callback(null, recordsets, output, returnValue, rowsAffected)
|
|
714
|
+
callback(null, recordsets, output, returnValue, rowsAffected, recordsetcolumns)
|
|
644
715
|
}
|
|
645
716
|
})
|
|
646
717
|
})
|
package/lib/tedious/request.js
CHANGED
|
@@ -117,11 +117,12 @@ const getMssqlType = function (type, length) {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
const createColumns = function (metadata) {
|
|
121
|
-
|
|
120
|
+
const createColumns = function (metadata, arrayRowMode) {
|
|
121
|
+
let out = {}
|
|
122
|
+
if (arrayRowMode) out = []
|
|
122
123
|
for (let index = 0, length = metadata.length; index < length; index++) {
|
|
123
124
|
const column = metadata[index]
|
|
124
|
-
|
|
125
|
+
const outColumn = {
|
|
125
126
|
index,
|
|
126
127
|
name: column.colName,
|
|
127
128
|
length: column.dataLength,
|
|
@@ -135,7 +136,7 @@ const createColumns = function (metadata) {
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
if (column.udtInfo) {
|
|
138
|
-
|
|
139
|
+
outColumn.udt = {
|
|
139
140
|
name: column.udtInfo.typeName,
|
|
140
141
|
database: column.udtInfo.dbname,
|
|
141
142
|
schema: column.udtInfo.owningSchema,
|
|
@@ -143,9 +144,15 @@ const createColumns = function (metadata) {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
if (DECLARATIONS[column.udtInfo.typeName]) {
|
|
146
|
-
|
|
147
|
+
outColumn.type = DECLARATIONS[column.udtInfo.typeName]
|
|
147
148
|
}
|
|
148
149
|
}
|
|
150
|
+
|
|
151
|
+
if (arrayRowMode) {
|
|
152
|
+
out.push(outColumn)
|
|
153
|
+
} else {
|
|
154
|
+
out[column.colName] = outColumn
|
|
155
|
+
}
|
|
149
156
|
}
|
|
150
157
|
|
|
151
158
|
return out
|
|
@@ -347,6 +354,7 @@ class Request extends BaseRequest {
|
|
|
347
354
|
if (err) return callback(err)
|
|
348
355
|
|
|
349
356
|
const recordsets = []
|
|
357
|
+
const recordsetcolumns = []
|
|
350
358
|
const errors = []
|
|
351
359
|
const errorHandlers = {}
|
|
352
360
|
const output = {}
|
|
@@ -466,9 +474,9 @@ class Request extends BaseRequest {
|
|
|
466
474
|
}
|
|
467
475
|
|
|
468
476
|
if (this.stream) {
|
|
469
|
-
callback(null, null, output, rowsAffected)
|
|
477
|
+
callback(null, null, output, rowsAffected, recordsetcolumns)
|
|
470
478
|
} else {
|
|
471
|
-
callback(error, recordsets, output, rowsAffected)
|
|
479
|
+
callback(error, recordsets, output, rowsAffected, recordsetcolumns)
|
|
472
480
|
}
|
|
473
481
|
}
|
|
474
482
|
})
|
|
@@ -476,7 +484,7 @@ class Request extends BaseRequest {
|
|
|
476
484
|
this._setCurrentRequest(req)
|
|
477
485
|
|
|
478
486
|
req.on('columnMetadata', metadata => {
|
|
479
|
-
columns = createColumns(metadata)
|
|
487
|
+
columns = createColumns(metadata, this.arrayRowMode)
|
|
480
488
|
|
|
481
489
|
isChunkedRecordset = false
|
|
482
490
|
if (metadata.length === 1 && (metadata[0].colName === JSON_COLUMN_ID || metadata[0].colName === XML_COLUMN_ID)) {
|
|
@@ -494,6 +502,7 @@ class Request extends BaseRequest {
|
|
|
494
502
|
this.emit('recordset', columns)
|
|
495
503
|
}
|
|
496
504
|
}
|
|
505
|
+
if (this.arrayRowMode) recordsetcolumns.push(columns)
|
|
497
506
|
})
|
|
498
507
|
|
|
499
508
|
const doneHandler = (rowCount, more) => {
|
|
@@ -568,19 +577,27 @@ class Request extends BaseRequest {
|
|
|
568
577
|
return chunksBuffer.push(columns[0].value)
|
|
569
578
|
}
|
|
570
579
|
|
|
571
|
-
|
|
580
|
+
if (this.arrayRowMode) {
|
|
581
|
+
row = []
|
|
582
|
+
} else {
|
|
583
|
+
row = {}
|
|
584
|
+
}
|
|
572
585
|
for (const col of columns) {
|
|
573
586
|
col.value = valueCorrection(col.value, col.metadata)
|
|
574
587
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
588
|
+
if (this.arrayRowMode) {
|
|
589
|
+
row.push(col.value)
|
|
590
|
+
} else {
|
|
591
|
+
const exi = row[col.metadata.colName]
|
|
592
|
+
if (exi != null) {
|
|
593
|
+
if (exi instanceof Array) {
|
|
594
|
+
exi.push(col.value)
|
|
595
|
+
} else {
|
|
596
|
+
row[col.metadata.colName] = [exi, col.value]
|
|
597
|
+
}
|
|
579
598
|
} else {
|
|
580
|
-
row[col.metadata.colName] =
|
|
599
|
+
row[col.metadata.colName] = col.value
|
|
581
600
|
}
|
|
582
|
-
} else {
|
|
583
|
-
row[col.metadata.colName] = col.value
|
|
584
601
|
}
|
|
585
602
|
}
|
|
586
603
|
|
|
@@ -684,6 +701,7 @@ class Request extends BaseRequest {
|
|
|
684
701
|
if (err) return callback(err)
|
|
685
702
|
|
|
686
703
|
const recordsets = []
|
|
704
|
+
const recordsetcolumns = []
|
|
687
705
|
const errors = []
|
|
688
706
|
const errorHandlers = {}
|
|
689
707
|
const output = {}
|
|
@@ -800,9 +818,9 @@ class Request extends BaseRequest {
|
|
|
800
818
|
}
|
|
801
819
|
|
|
802
820
|
if (this.stream) {
|
|
803
|
-
callback(null, null, output, returnValue, rowsAffected)
|
|
821
|
+
callback(null, null, output, returnValue, rowsAffected, recordsetcolumns)
|
|
804
822
|
} else {
|
|
805
|
-
callback(error, recordsets, output, returnValue, rowsAffected)
|
|
823
|
+
callback(error, recordsets, output, returnValue, rowsAffected, recordsetcolumns)
|
|
806
824
|
}
|
|
807
825
|
}
|
|
808
826
|
})
|
|
@@ -810,7 +828,7 @@ class Request extends BaseRequest {
|
|
|
810
828
|
this._setCurrentRequest(req)
|
|
811
829
|
|
|
812
830
|
req.on('columnMetadata', metadata => {
|
|
813
|
-
columns = createColumns(metadata)
|
|
831
|
+
columns = createColumns(metadata, this.arrayRowMode)
|
|
814
832
|
|
|
815
833
|
isChunkedRecordset = false
|
|
816
834
|
if ((metadata.length === 1) && (metadata[0].colName === JSON_COLUMN_ID || metadata[0].colName === XML_COLUMN_ID)) {
|
|
@@ -819,6 +837,7 @@ class Request extends BaseRequest {
|
|
|
819
837
|
}
|
|
820
838
|
|
|
821
839
|
if (this.stream) this.emit('recordset', columns)
|
|
840
|
+
if (this.arrayRowMode) recordsetcolumns.push(columns)
|
|
822
841
|
})
|
|
823
842
|
|
|
824
843
|
req.on('row', columns => {
|
|
@@ -828,19 +847,27 @@ class Request extends BaseRequest {
|
|
|
828
847
|
return chunksBuffer.push(columns[0].value)
|
|
829
848
|
}
|
|
830
849
|
|
|
831
|
-
|
|
850
|
+
if (this.arrayRowMode) {
|
|
851
|
+
row = []
|
|
852
|
+
} else {
|
|
853
|
+
row = {}
|
|
854
|
+
}
|
|
832
855
|
for (const col of columns) {
|
|
833
856
|
col.value = valueCorrection(col.value, col.metadata)
|
|
834
857
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
858
|
+
if (this.arrayRowMode) {
|
|
859
|
+
row.push(col.value)
|
|
860
|
+
} else {
|
|
861
|
+
const exi = row[col.metadata.colName]
|
|
862
|
+
if (exi != null) {
|
|
863
|
+
if (exi instanceof Array) {
|
|
864
|
+
exi.push(col.value)
|
|
865
|
+
} else {
|
|
866
|
+
row[col.metadata.colName] = [exi, col.value]
|
|
867
|
+
}
|
|
839
868
|
} else {
|
|
840
|
-
row[col.metadata.colName] =
|
|
869
|
+
row[col.metadata.colName] = col.value
|
|
841
870
|
}
|
|
842
|
-
} else {
|
|
843
|
-
row[col.metadata.colName] = col.value
|
|
844
871
|
}
|
|
845
872
|
}
|
|
846
873
|
|
package/lib/udt.js
CHANGED
|
@@ -271,7 +271,7 @@ const parseGeography = buffer => {
|
|
|
271
271
|
|
|
272
272
|
// console.log( "shapes", shapes)
|
|
273
273
|
|
|
274
|
-
if (value.version === 2) {
|
|
274
|
+
if (value.version === 2 && buffer.position < buffer.length) {
|
|
275
275
|
const numberOfSegments = buffer.readUInt32LE(buffer.position)
|
|
276
276
|
buffer.position += 4
|
|
277
277
|
|
package/package.json
CHANGED
|
@@ -21,18 +21,18 @@
|
|
|
21
21
|
"azure",
|
|
22
22
|
"node-mssql"
|
|
23
23
|
],
|
|
24
|
-
"version": "6.
|
|
24
|
+
"version": "6.4.0",
|
|
25
25
|
"main": "index.js",
|
|
26
26
|
"repository": "github:tediousjs/node-mssql",
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"debug": "^4",
|
|
29
|
+
"debug": "^4.3.2",
|
|
30
30
|
"tarn": "^1.1.5",
|
|
31
|
-
"tedious": "^6.
|
|
31
|
+
"tedious": "^6.7.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"mocha": "^6.2.
|
|
35
|
-
"standard": "^14"
|
|
34
|
+
"mocha": "^6.2.3",
|
|
35
|
+
"standard": "^14.3.4"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=6"
|
package/test.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const sqlConfig = {
|
|
7
|
-
password: '
|
|
8
|
-
database: '
|
|
9
|
-
stream: false,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
console.log('
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
})
|
|
1
|
+
'use strict'
|
|
2
|
+
const mssql = require('./')
|
|
3
|
+
|
|
4
|
+
const [, , max] = process.argv
|
|
5
|
+
|
|
6
|
+
const sqlConfig = {
|
|
7
|
+
password: 'P@ssw0rd',
|
|
8
|
+
database: 'test',
|
|
9
|
+
stream: false,
|
|
10
|
+
// driver: 'msnodesqlv8',
|
|
11
|
+
options: {
|
|
12
|
+
trustServerCertificate: true,
|
|
13
|
+
enableArithAbort: true,
|
|
14
|
+
encrypt: true,
|
|
15
|
+
abortTransactionOnError: false
|
|
16
|
+
},
|
|
17
|
+
pool: {
|
|
18
|
+
max: max ? parseInt(max) : 10,
|
|
19
|
+
min: 0
|
|
20
|
+
},
|
|
21
|
+
port: 1433,
|
|
22
|
+
user: 'sa',
|
|
23
|
+
server: 'localhost',
|
|
24
|
+
arrayRowMode: true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
mssql.connect(sqlConfig).then((pool) => {
|
|
28
|
+
console.log('Connected to SQL server')
|
|
29
|
+
const request = pool.request()
|
|
30
|
+
request.stream = true
|
|
31
|
+
request.on('error', (err) => {
|
|
32
|
+
console.error('good', err)
|
|
33
|
+
})
|
|
34
|
+
return request.query('SELECT * FROM nontable')
|
|
35
|
+
}).catch((err) => {
|
|
36
|
+
console.error('bad', err)
|
|
37
|
+
}).then(() => {
|
|
38
|
+
console.log('Disconnecting from SQL server')
|
|
39
|
+
return mssql.close()
|
|
40
|
+
}).then(() => console.log('Disconnected'))
|