@sqb/mssql-dialect 5.0.6 → 6.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/README.md +16 -29
- package/ms-sql-serializer.d.ts +6 -1
- package/ms-sql-serializer.js +226 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,30 +6,25 @@
|
|
|
6
6
|
|
|
7
7
|
[![NPM Version][npm-image]][npm-url]
|
|
8
8
|
[![NPM Downloads][downloads-image]][downloads-url]
|
|
9
|
-
[![
|
|
9
|
+
[![CI Tests][ci-test-image]][ci-test-url]
|
|
10
10
|
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
11
|
-
[![Dependencies][dependencies-image]][dependencies-url]
|
|
12
|
-
[![DevDependencies][devdependencies-image]][devdependencies-url]
|
|
13
|
-
[![Package Quality][quality-image]][quality-url]
|
|
14
11
|
|
|
15
12
|
## About SQB
|
|
16
13
|
|
|
17
14
|
SQB is an extensible, multi-dialect SQL query builder and Database connection wrapper for NodeJS.
|
|
18
15
|
|
|
19
|
-
##
|
|
16
|
+
## About @sqb/mssql-dialect
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- Let applications work with large data tables efficiently
|
|
25
|
-
- Support latest JavaScript language standards
|
|
26
|
-
- Lightweight and extensible framework.
|
|
18
|
+
This package registers the `mssql` SQL serialization dialect (a `SerializerExtension` for
|
|
19
|
+
[`@sqb/builder`](../builder)) used by [`@sqb/mssql`](../mssql). It is loaded automatically when
|
|
20
|
+
`@sqb/mssql` is imported, so you normally don't need to depend on it directly.
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
It adapts query output to T-SQL: `@name` parameter placeholders instead of `:name`, `OFFSET ...
|
|
23
|
+
ROWS [FETCH NEXT ... ROWS ONLY]` instead of `LIMIT`/`OFFSET` (SQL Server requires an `ORDER BY`
|
|
24
|
+
before `OFFSET`, which the dialect adds automatically when a query needs pagination but has none),
|
|
25
|
+
and SQL Server's own reserved-word list. `RETURNING` is rewritten into SQL Server's native `OUTPUT
|
|
26
|
+
INSERTED.col` / `OUTPUT DELETED.col` clause rather than being stripped, since SQL Server has no
|
|
27
|
+
`RETURNING` keyword but its `OUTPUT` clause achieves the same thing without a follow-up `SELECT`.
|
|
33
28
|
|
|
34
29
|
## Installation
|
|
35
30
|
|
|
@@ -39,7 +34,7 @@ $ npm install @sqb/mssql-dialect --save
|
|
|
39
34
|
|
|
40
35
|
## Node Compatibility
|
|
41
36
|
|
|
42
|
-
- node >=
|
|
37
|
+
- node >= 20.x
|
|
43
38
|
|
|
44
39
|
### License
|
|
45
40
|
|
|
@@ -47,17 +42,9 @@ SQB is available under [MIT](LICENSE) license.
|
|
|
47
42
|
|
|
48
43
|
[npm-image]: https://img.shields.io/npm/v/@sqb/mssql-dialect.svg
|
|
49
44
|
[npm-url]: https://npmjs.org/package/@sqb/mssql-dialect
|
|
50
|
-
[travis-image]: https://img.shields.io/travis/sqbjs/@sqb/mssql-dialect/master.svg
|
|
51
|
-
[travis-url]: https://travis-ci.org/sqbjs/@sqb/mssql-dialect
|
|
52
|
-
[coveralls-image]: https://img.shields.io/coveralls/sqbjs/@sqb/mssql-dialect/master.svg
|
|
53
|
-
[coveralls-url]: https://coveralls.io/r/sqbjs/@sqb/mssql-dialect
|
|
54
45
|
[downloads-image]: https://img.shields.io/npm/dm/@sqb/mssql-dialect.svg
|
|
55
46
|
[downloads-url]: https://npmjs.org/package/@sqb/mssql-dialect
|
|
56
|
-
[
|
|
57
|
-
[
|
|
58
|
-
[
|
|
59
|
-
[
|
|
60
|
-
[devdependencies-image]: https://david-dm.org/sqbjs/@sqb/mssql-dialect/dev-status.svg
|
|
61
|
-
[devdependencies-url]: https://david-dm.org/sqbjs/@sqb/mssql-dialect?type=dev
|
|
62
|
-
[quality-image]: http://npm.packagequality.com/shield/@sqb/mssql-dialect.png
|
|
63
|
-
[quality-url]: http://packagequality.com/#?package=@sqb/mssql-dialect
|
|
47
|
+
[ci-test-image]: https://github.com/panates/sqb/actions/workflows/test.yml/badge.svg
|
|
48
|
+
[ci-test-url]: https://github.com/panates/sqb/actions/workflows/test.yml
|
|
49
|
+
[coveralls-image]: https://coveralls.io/repos/github/sqbjs/sqb/badge.svg?branch=master
|
|
50
|
+
[coveralls-url]: https://coveralls.io/github/sqbjs/sqb?branch=master
|
package/ms-sql-serializer.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { type DefaultSerializeFunction, SerializationType, SerializeContext, type SerializerExtension } from '@sqb/builder';
|
|
2
2
|
export declare class MSSqlSerializer implements SerializerExtension {
|
|
3
3
|
dialect: string;
|
|
4
|
-
|
|
4
|
+
reservedWords: Set<string>;
|
|
5
|
+
isReservedWord(_: any, s: any): any;
|
|
5
6
|
serialize(ctx: SerializeContext, type: SerializationType | string, o: any, defFn: DefaultSerializeFunction): string | undefined;
|
|
6
7
|
private _serializeSelect;
|
|
8
|
+
private _serializeComparison;
|
|
9
|
+
private _serializeBooleanValue;
|
|
10
|
+
private _serializeStringValue;
|
|
11
|
+
private _serializeReturning;
|
|
7
12
|
private _serializeParameter;
|
|
8
13
|
}
|
package/ms-sql-serializer.js
CHANGED
|
@@ -1,14 +1,151 @@
|
|
|
1
|
-
import { SerializationType, SerializeContext, } from '@sqb/builder';
|
|
2
|
-
|
|
1
|
+
import { isParam, OperatorType, SerializationType, SerializeContext, } from '@sqb/builder';
|
|
2
|
+
// T-SQL reserved keywords (Reserved Keywords (Transact-SQL), learn.microsoft.com)
|
|
3
|
+
// that are not already covered by SerializeContext's base reservedWords list.
|
|
4
|
+
const reservedWords = new Set([
|
|
5
|
+
'any',
|
|
6
|
+
'backup',
|
|
7
|
+
'begin',
|
|
8
|
+
'break',
|
|
9
|
+
'browse',
|
|
10
|
+
'bulk',
|
|
11
|
+
'checkpoint',
|
|
12
|
+
'close',
|
|
13
|
+
'clustered',
|
|
14
|
+
'coalesce',
|
|
15
|
+
'collate',
|
|
16
|
+
'comment',
|
|
17
|
+
'commit',
|
|
18
|
+
'compute',
|
|
19
|
+
'contains',
|
|
20
|
+
'containstable',
|
|
21
|
+
'continue',
|
|
22
|
+
'convert',
|
|
23
|
+
'cross',
|
|
24
|
+
'current',
|
|
25
|
+
'current_date',
|
|
26
|
+
'current_time',
|
|
27
|
+
'current_timestamp',
|
|
28
|
+
'current_user',
|
|
29
|
+
'cursor',
|
|
30
|
+
'database',
|
|
31
|
+
'dbcc',
|
|
32
|
+
'deallocate',
|
|
33
|
+
'declare',
|
|
34
|
+
'deny',
|
|
35
|
+
'disk',
|
|
36
|
+
'distributed',
|
|
37
|
+
'double',
|
|
38
|
+
'dump',
|
|
39
|
+
'errlvl',
|
|
40
|
+
'escape',
|
|
41
|
+
'except',
|
|
42
|
+
'exec',
|
|
43
|
+
'execute',
|
|
44
|
+
'exists',
|
|
45
|
+
'exit',
|
|
46
|
+
'external',
|
|
47
|
+
'fetch',
|
|
48
|
+
'file',
|
|
49
|
+
'fillfactor',
|
|
50
|
+
'freetext',
|
|
51
|
+
'freetexttable',
|
|
52
|
+
'function',
|
|
53
|
+
'goto',
|
|
54
|
+
'grant',
|
|
55
|
+
'holdlock',
|
|
56
|
+
'identity',
|
|
57
|
+
'identity_insert',
|
|
58
|
+
'identitycol',
|
|
59
|
+
'if',
|
|
60
|
+
'intersect',
|
|
61
|
+
'kill',
|
|
62
|
+
'lineno',
|
|
63
|
+
'load',
|
|
64
|
+
'national',
|
|
65
|
+
'nocheck',
|
|
66
|
+
'nonclustered',
|
|
67
|
+
'nullif',
|
|
68
|
+
'of',
|
|
69
|
+
'off',
|
|
70
|
+
'offsets',
|
|
71
|
+
'open',
|
|
72
|
+
'opendatasource',
|
|
73
|
+
'openquery',
|
|
74
|
+
'openrowset',
|
|
75
|
+
'openxml',
|
|
76
|
+
'option',
|
|
77
|
+
'over',
|
|
78
|
+
'percent',
|
|
79
|
+
'pivot',
|
|
80
|
+
'plan',
|
|
81
|
+
'precision',
|
|
82
|
+
'print',
|
|
83
|
+
'proc',
|
|
84
|
+
'procedure',
|
|
85
|
+
'public',
|
|
86
|
+
'raiserror',
|
|
87
|
+
'read',
|
|
88
|
+
'readtext',
|
|
89
|
+
'reconfigure',
|
|
90
|
+
'replication',
|
|
91
|
+
'restore',
|
|
92
|
+
'restrict',
|
|
93
|
+
'return',
|
|
94
|
+
'revert',
|
|
95
|
+
'revoke',
|
|
96
|
+
'rollback',
|
|
97
|
+
'rowcount',
|
|
98
|
+
'rowguidcol',
|
|
99
|
+
'rule',
|
|
100
|
+
'save',
|
|
101
|
+
'securityaudit',
|
|
102
|
+
'semantickeyphrasetable',
|
|
103
|
+
'semanticsimilaritydetailstable',
|
|
104
|
+
'semanticsimilaritytable',
|
|
105
|
+
'session_user',
|
|
106
|
+
'set',
|
|
107
|
+
'setuser',
|
|
108
|
+
'shutdown',
|
|
109
|
+
'some',
|
|
110
|
+
'statistics',
|
|
111
|
+
'system_user',
|
|
112
|
+
'tablesample',
|
|
113
|
+
'textsize',
|
|
114
|
+
'top',
|
|
115
|
+
'tran',
|
|
116
|
+
'transaction',
|
|
117
|
+
'trigger',
|
|
118
|
+
'truncate',
|
|
119
|
+
'try_convert',
|
|
120
|
+
'tsequal',
|
|
121
|
+
'unpivot',
|
|
122
|
+
'updatetext',
|
|
123
|
+
'use',
|
|
124
|
+
'values',
|
|
125
|
+
'varying',
|
|
126
|
+
'view',
|
|
127
|
+
'waitfor',
|
|
128
|
+
'while',
|
|
129
|
+
'writetext',
|
|
130
|
+
]);
|
|
3
131
|
export class MSSqlSerializer {
|
|
4
132
|
dialect = 'mssql';
|
|
5
|
-
|
|
6
|
-
|
|
133
|
+
reservedWords = reservedWords;
|
|
134
|
+
isReservedWord(_, s) {
|
|
135
|
+
return s && typeof s === 'string' && reservedWords.has(s.toLowerCase());
|
|
7
136
|
}
|
|
8
137
|
serialize(ctx, type, o, defFn) {
|
|
9
138
|
switch (type) {
|
|
10
139
|
case SerializationType.SELECT_QUERY:
|
|
11
140
|
return this._serializeSelect(ctx, o, defFn);
|
|
141
|
+
case SerializationType.COMPARISON_EXPRESSION:
|
|
142
|
+
return this._serializeComparison(ctx, o, defFn);
|
|
143
|
+
case SerializationType.BOOLEAN_VALUE:
|
|
144
|
+
return this._serializeBooleanValue(ctx, o);
|
|
145
|
+
case SerializationType.STRING_VALUE:
|
|
146
|
+
return this._serializeStringValue(ctx, o, defFn);
|
|
147
|
+
case SerializationType.RETURNING_BLOCK:
|
|
148
|
+
return this._serializeReturning();
|
|
12
149
|
case SerializationType.EXTERNAL_PARAMETER:
|
|
13
150
|
return this._serializeParameter(ctx, o, defFn);
|
|
14
151
|
default:
|
|
@@ -19,13 +156,95 @@ export class MSSqlSerializer {
|
|
|
19
156
|
let out = defFn(ctx, o);
|
|
20
157
|
const limit = o.limit || 0;
|
|
21
158
|
const offset = Math.max(o.offset || 0, 0);
|
|
22
|
-
if (offset)
|
|
159
|
+
if (limit || offset) {
|
|
160
|
+
// OFFSET/FETCH is only valid when preceded by an ORDER BY clause.
|
|
161
|
+
if (!o.orderBy)
|
|
162
|
+
out += '\nORDER BY (SELECT NULL)';
|
|
23
163
|
out += '\nOFFSET ' + offset + ' ROWS';
|
|
24
|
-
|
|
25
|
-
|
|
164
|
+
if (limit)
|
|
165
|
+
out += ' FETCH NEXT ' + limit + ' ROWS ONLY';
|
|
166
|
+
}
|
|
26
167
|
return out;
|
|
27
168
|
}
|
|
169
|
+
_serializeComparison(ctx, o, defFn) {
|
|
170
|
+
if (isParam(o.orgRight)) {
|
|
171
|
+
const n = o.orgRight._name;
|
|
172
|
+
const v = ctx.orgParams?.[n];
|
|
173
|
+
if (Array.isArray(v)) {
|
|
174
|
+
o.right.isParam = false;
|
|
175
|
+
if (o.operatorType === 'eq')
|
|
176
|
+
return defFn(ctx, {
|
|
177
|
+
...o,
|
|
178
|
+
operatorType: OperatorType.in,
|
|
179
|
+
symbol: 'in',
|
|
180
|
+
});
|
|
181
|
+
if (o.operatorType === 'ne')
|
|
182
|
+
return defFn(ctx, {
|
|
183
|
+
...o,
|
|
184
|
+
operatorType: OperatorType.notIn,
|
|
185
|
+
symbol: 'not in',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if ((o.right?.expression && o.right?.expression === 'null') ||
|
|
190
|
+
(o.right &&
|
|
191
|
+
o.right?.value == null &&
|
|
192
|
+
(!o.right.expression || o.right.expression.startsWith('@')))) {
|
|
193
|
+
if (o.right.expression?.startsWith('@')) {
|
|
194
|
+
const s = o.right.expression.substring(1);
|
|
195
|
+
if (ctx.params)
|
|
196
|
+
delete ctx.params[s];
|
|
197
|
+
if (ctx.preparedParams)
|
|
198
|
+
delete ctx.preparedParams[s];
|
|
199
|
+
if (ctx.paramOptions)
|
|
200
|
+
delete ctx.paramOptions[s];
|
|
201
|
+
o.right.expression = 'null';
|
|
202
|
+
o.right.isParam = false;
|
|
203
|
+
}
|
|
204
|
+
if (o.operatorType === 'eq')
|
|
205
|
+
return defFn(ctx, {
|
|
206
|
+
...o,
|
|
207
|
+
operatorType: OperatorType.is,
|
|
208
|
+
symbol: 'is',
|
|
209
|
+
});
|
|
210
|
+
if (o.operatorType === 'ne')
|
|
211
|
+
return defFn(ctx, {
|
|
212
|
+
...o,
|
|
213
|
+
operatorType: OperatorType.isNot,
|
|
214
|
+
symbol: 'is not',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return defFn(ctx, o);
|
|
218
|
+
}
|
|
219
|
+
_serializeBooleanValue(_ctx, o) {
|
|
220
|
+
return o == null ? 'null' : o ? '1' : '0';
|
|
221
|
+
}
|
|
222
|
+
_serializeStringValue(ctx, o, defFn) {
|
|
223
|
+
// T-SQL's implicit conversion of ISO 8601 strings carrying a timezone
|
|
224
|
+
// offset (e.g. from JSON test fixtures) into `datetime`/`date` is
|
|
225
|
+
// unreliable, so normalize to 'yyyy-mm-dd hh:mm:ss' ourselves.
|
|
226
|
+
if (typeof o === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(o)) {
|
|
227
|
+
const d = new Date(o);
|
|
228
|
+
if (!isNaN(d.getTime()))
|
|
229
|
+
return ctx.dateToSQL(d);
|
|
230
|
+
}
|
|
231
|
+
return defFn(ctx, o);
|
|
232
|
+
}
|
|
233
|
+
_serializeReturning() {
|
|
234
|
+
// T-SQL has no RETURNING clause; the OUTPUT clause it uses instead
|
|
235
|
+
// must be positioned before WHERE/VALUES rather than at the end of
|
|
236
|
+
// the statement, so it is injected at the connection layer instead.
|
|
237
|
+
return '';
|
|
238
|
+
}
|
|
28
239
|
_serializeParameter(ctx, o, defFn) {
|
|
240
|
+
if (ctx.rootQuery._type === SerializationType.SELECT_QUERY ||
|
|
241
|
+
ctx.rootQuery._type === SerializationType.DELETE_QUERY) {
|
|
242
|
+
const v = ctx.params?.[o.name];
|
|
243
|
+
if (Array.isArray(v)) {
|
|
244
|
+
delete ctx.params?.[o.name];
|
|
245
|
+
return ctx.anyToSQL(v);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
29
248
|
defFn(ctx, o);
|
|
30
249
|
return '@' + o.name;
|
|
31
250
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqb/mssql-dialect",
|
|
3
3
|
"description": "SQB serialization extension for MS-SQL database",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.0",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"tslib": "^2.8.1"
|
|
9
9
|
},
|
|
10
10
|
"peerDependencies": {
|
|
11
|
-
"@sqb/builder": "^
|
|
11
|
+
"@sqb/builder": "^6.0.0"
|
|
12
12
|
},
|
|
13
13
|
"type": "module",
|
|
14
14
|
"module": "./index.js",
|