knex 2.4.1 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/CONTRIBUTING.md +194 -194
- package/LICENSE +22 -22
- package/README.md +148 -148
- package/UPGRADING.md +233 -233
- package/bin/cli.js +473 -472
- package/bin/utils/cli-config-utils.js +210 -210
- package/bin/utils/constants.js +7 -7
- package/bin/utils/migrationsLister.js +37 -37
- package/knex.js +23 -23
- package/lib/builder-interface-augmenter.js +120 -120
- package/lib/client.js +475 -475
- package/lib/constants.js +61 -61
- package/lib/dialects/better-sqlite3/index.js +72 -72
- package/lib/dialects/cockroachdb/crdb-columncompiler.js +14 -14
- package/lib/dialects/cockroachdb/crdb-querybuilder.js +11 -11
- package/lib/dialects/cockroachdb/crdb-querycompiler.js +122 -122
- package/lib/dialects/cockroachdb/crdb-tablecompiler.js +37 -37
- package/lib/dialects/cockroachdb/crdb-viewcompiler.js +15 -15
- package/lib/dialects/cockroachdb/index.js +86 -86
- package/lib/dialects/mssql/index.js +495 -495
- package/lib/dialects/mssql/mssql-formatter.js +34 -34
- package/lib/dialects/mssql/query/mssql-querycompiler.js +600 -600
- package/lib/dialects/mssql/schema/mssql-columncompiler.js +185 -185
- package/lib/dialects/mssql/schema/mssql-compiler.js +91 -91
- package/lib/dialects/mssql/schema/mssql-tablecompiler.js +378 -378
- package/lib/dialects/mssql/schema/mssql-viewcompiler.js +55 -55
- package/lib/dialects/mssql/transaction.js +176 -176
- package/lib/dialects/mysql/index.js +201 -201
- package/lib/dialects/mysql/query/mysql-querycompiler.js +274 -274
- package/lib/dialects/mysql/schema/mysql-columncompiler.js +193 -193
- package/lib/dialects/mysql/schema/mysql-compiler.js +60 -60
- package/lib/dialects/mysql/schema/mysql-tablecompiler.js +381 -381
- package/lib/dialects/mysql/schema/mysql-viewbuilder.js +21 -21
- package/lib/dialects/mysql/schema/mysql-viewcompiler.js +15 -15
- package/lib/dialects/mysql/transaction.js +46 -46
- package/lib/dialects/mysql2/index.js +33 -33
- package/lib/dialects/mysql2/transaction.js +44 -44
- package/lib/dialects/oracle/DEAD_CODE.md +5 -5
- package/lib/dialects/oracle/index.js +92 -92
- package/lib/dialects/oracle/query/oracle-querycompiler.js +342 -342
- package/lib/dialects/oracle/schema/internal/incrementUtils.js +20 -20
- package/lib/dialects/oracle/schema/internal/trigger.js +135 -135
- package/lib/dialects/oracle/schema/oracle-columnbuilder.js +17 -17
- package/lib/dialects/oracle/schema/oracle-columncompiler.js +126 -126
- package/lib/dialects/oracle/schema/oracle-compiler.js +122 -122
- package/lib/dialects/oracle/schema/oracle-tablecompiler.js +190 -190
- package/lib/dialects/oracle/utils.js +87 -87
- package/lib/dialects/oracledb/index.js +327 -327
- package/lib/dialects/oracledb/query/oracledb-querycompiler.js +481 -481
- package/lib/dialects/oracledb/schema/oracledb-columncompiler.js +55 -55
- package/lib/dialects/oracledb/schema/oracledb-tablecompiler.js +19 -19
- package/lib/dialects/oracledb/schema/oracledb-viewbuilder.js +13 -13
- package/lib/dialects/oracledb/schema/oracledb-viewcompiler.js +19 -19
- package/lib/dialects/oracledb/transaction.js +98 -98
- package/lib/dialects/oracledb/utils.js +208 -208
- package/lib/dialects/pgnative/index.js +60 -60
- package/lib/dialects/postgres/execution/pg-transaction.js +12 -12
- package/lib/dialects/postgres/index.js +358 -358
- package/lib/dialects/postgres/query/pg-querybuilder.js +38 -38
- package/lib/dialects/postgres/query/pg-querycompiler.js +395 -395
- package/lib/dialects/postgres/schema/pg-columncompiler.js +156 -156
- package/lib/dialects/postgres/schema/pg-compiler.js +138 -136
- package/lib/dialects/postgres/schema/pg-tablecompiler.js +299 -299
- package/lib/dialects/postgres/schema/pg-viewbuilder.js +21 -21
- package/lib/dialects/postgres/schema/pg-viewcompiler.js +35 -35
- package/lib/dialects/redshift/index.js +86 -86
- package/lib/dialects/redshift/query/redshift-querycompiler.js +163 -163
- package/lib/dialects/redshift/schema/redshift-columnbuilder.js +22 -22
- package/lib/dialects/redshift/schema/redshift-columncompiler.js +67 -67
- package/lib/dialects/redshift/schema/redshift-compiler.js +14 -14
- package/lib/dialects/redshift/schema/redshift-tablecompiler.js +122 -122
- package/lib/dialects/redshift/schema/redshift-viewcompiler.js +11 -11
- package/lib/dialects/redshift/transaction.js +25 -25
- package/lib/dialects/sqlite3/execution/sqlite-transaction.js +18 -18
- package/lib/dialects/sqlite3/index.js +250 -250
- package/lib/dialects/sqlite3/query/sqlite-querybuilder.js +33 -33
- package/lib/dialects/sqlite3/query/sqlite-querycompiler.js +334 -334
- package/lib/dialects/sqlite3/schema/ddl.js +400 -400
- package/lib/dialects/sqlite3/schema/internal/compiler.js +327 -327
- package/lib/dialects/sqlite3/schema/internal/parser-combinator.js +161 -161
- package/lib/dialects/sqlite3/schema/internal/parser.js +638 -638
- package/lib/dialects/sqlite3/schema/internal/sqlite-ddl-operations.js +41 -41
- package/lib/dialects/sqlite3/schema/internal/tokenizer.js +38 -38
- package/lib/dialects/sqlite3/schema/internal/utils.js +12 -12
- package/lib/dialects/sqlite3/schema/sqlite-columncompiler.js +50 -50
- package/lib/dialects/sqlite3/schema/sqlite-compiler.js +80 -80
- package/lib/dialects/sqlite3/schema/sqlite-tablecompiler.js +347 -347
- package/lib/dialects/sqlite3/schema/sqlite-viewcompiler.js +40 -40
- package/lib/execution/batch-insert.js +51 -51
- package/lib/execution/internal/delay.js +6 -6
- package/lib/execution/internal/ensure-connection-callback.js +41 -41
- package/lib/execution/internal/query-executioner.js +62 -62
- package/lib/execution/runner.js +307 -307
- package/lib/execution/transaction.js +401 -401
- package/lib/formatter/formatterUtils.js +42 -42
- package/lib/formatter/rawFormatter.js +84 -84
- package/lib/formatter/wrappingFormatter.js +250 -250
- package/lib/formatter.js +25 -25
- package/lib/index.js +3 -3
- package/lib/knex-builder/FunctionHelper.js +54 -54
- package/lib/knex-builder/Knex.js +59 -59
- package/lib/knex-builder/internal/config-resolver.js +57 -57
- package/lib/knex-builder/internal/parse-connection.js +87 -87
- package/lib/knex-builder/make-knex.js +340 -340
- package/lib/logger.js +76 -76
- package/lib/migrations/common/MigrationsLoader.js +36 -36
- package/lib/migrations/migrate/MigrationGenerator.js +84 -82
- package/lib/migrations/migrate/Migrator.js +598 -598
- package/lib/migrations/migrate/migrate-stub.js +17 -17
- package/lib/migrations/migrate/migration-list-resolver.js +33 -33
- package/lib/migrations/migrate/migrator-configuration-merger.js +58 -58
- package/lib/migrations/migrate/sources/fs-migrations.js +74 -74
- package/lib/migrations/migrate/stub/cjs.stub +15 -15
- package/lib/migrations/migrate/stub/coffee.stub +13 -13
- package/lib/migrations/migrate/stub/eg.stub +14 -14
- package/lib/migrations/migrate/stub/js-schema.stub +22 -22
- package/lib/migrations/migrate/stub/js.stub +22 -22
- package/lib/migrations/migrate/stub/knexfile-coffee.stub +34 -34
- package/lib/migrations/migrate/stub/knexfile-eg.stub +43 -43
- package/lib/migrations/migrate/stub/knexfile-js.stub +47 -47
- package/lib/migrations/migrate/stub/knexfile-ls.stub +35 -35
- package/lib/migrations/migrate/stub/knexfile-ts.stub +47 -47
- package/lib/migrations/migrate/stub/ls.stub +14 -14
- package/lib/migrations/migrate/stub/mjs.stub +23 -23
- package/lib/migrations/migrate/stub/ts-schema.stub +21 -21
- package/lib/migrations/migrate/stub/ts.stub +21 -21
- package/lib/migrations/migrate/table-creator.js +77 -77
- package/lib/migrations/migrate/table-resolver.js +27 -27
- package/lib/migrations/seed/Seeder.js +137 -137
- package/lib/migrations/seed/seed-stub.js +13 -13
- package/lib/migrations/seed/seeder-configuration-merger.js +60 -60
- package/lib/migrations/seed/sources/fs-seeds.js +65 -65
- package/lib/migrations/seed/stub/coffee.stub +9 -9
- package/lib/migrations/seed/stub/eg.stub +11 -11
- package/lib/migrations/seed/stub/js.stub +13 -13
- package/lib/migrations/seed/stub/ls.stub +11 -11
- package/lib/migrations/seed/stub/mjs.stub +12 -12
- package/lib/migrations/seed/stub/ts.stub +13 -13
- package/lib/migrations/util/fs.js +86 -86
- package/lib/migrations/util/import-file.js +12 -12
- package/lib/migrations/util/is-module-type.js +9 -9
- package/lib/migrations/util/template.js +52 -52
- package/lib/migrations/util/timestamp.js +14 -14
- package/lib/query/analytic.js +52 -52
- package/lib/query/constants.js +15 -15
- package/lib/query/joinclause.js +270 -270
- package/lib/query/method-constants.js +135 -135
- package/lib/query/querybuilder.js +1794 -1794
- package/lib/query/querycompiler.js +1580 -1580
- package/lib/raw.js +139 -139
- package/lib/ref.js +39 -39
- package/lib/schema/builder.js +115 -114
- package/lib/schema/columnbuilder.js +146 -145
- package/lib/schema/columncompiler.js +307 -307
- package/lib/schema/compiler.js +187 -187
- package/lib/schema/internal/helpers.js +55 -55
- package/lib/schema/tablebuilder.js +376 -375
- package/lib/schema/tablecompiler.js +433 -433
- package/lib/schema/viewbuilder.js +92 -93
- package/lib/schema/viewcompiler.js +138 -138
- package/lib/util/finally-mixin.js +13 -13
- package/lib/util/helpers.js +95 -95
- package/lib/util/is.js +32 -32
- package/lib/util/nanoid.js +40 -40
- package/lib/util/noop.js +1 -1
- package/lib/util/save-async-stack.js +14 -14
- package/lib/util/string.js +190 -190
- package/lib/util/timeout.js +29 -29
- package/package.json +8 -6
- package/scripts/build.js +125 -125
- package/scripts/clean.js +31 -29
- package/scripts/docker-compose.yml +152 -152
- package/scripts/next-release-howto.md +24 -24
- package/scripts/oracledb-install-driver-libs.sh +82 -82
- package/scripts/release.sh +34 -34
- package/scripts/runkit-example.js +34 -34
- package/scripts/stress-test/README.txt +18 -18
- package/scripts/stress-test/docker-compose.yml +57 -57
- package/scripts/stress-test/knex-stress-test.js +208 -208
- package/scripts/stress-test/mysql2-random-hanging-every-now-and-then.js +145 -145
- package/scripts/stress-test/mysql2-sudden-exit-without-error.js +100 -100
- package/scripts/stress-test/reconnect-test-mysql-based-drivers.js +184 -184
- package/scripts/update_gitignore_for_tsc_output.js +90 -86
- package/types/index.d.ts +3233 -3233
- package/types/result.d.ts +27 -27
- package/types/tables.d.ts +4 -4
|
@@ -1,600 +1,600 @@
|
|
|
1
|
-
// MSSQL Query Compiler
|
|
2
|
-
// ------
|
|
3
|
-
const QueryCompiler = require('../../../query/querycompiler');
|
|
4
|
-
|
|
5
|
-
const compact = require('lodash/compact');
|
|
6
|
-
const identity = require('lodash/identity');
|
|
7
|
-
const isEmpty = require('lodash/isEmpty');
|
|
8
|
-
const Raw = require('../../../raw.js');
|
|
9
|
-
const {
|
|
10
|
-
columnize: columnize_,
|
|
11
|
-
} = require('../../../formatter/wrappingFormatter');
|
|
12
|
-
|
|
13
|
-
const components = [
|
|
14
|
-
'columns',
|
|
15
|
-
'join',
|
|
16
|
-
'lock',
|
|
17
|
-
'where',
|
|
18
|
-
'union',
|
|
19
|
-
'group',
|
|
20
|
-
'having',
|
|
21
|
-
'order',
|
|
22
|
-
'limit',
|
|
23
|
-
'offset',
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
class QueryCompiler_MSSQL extends QueryCompiler {
|
|
27
|
-
constructor(client, builder, formatter) {
|
|
28
|
-
super(client, builder, formatter);
|
|
29
|
-
|
|
30
|
-
const { onConflict } = this.single;
|
|
31
|
-
if (onConflict) {
|
|
32
|
-
throw new Error('.onConflict() is not supported for mssql.');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
this._emptyInsertValue = 'default values';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
with() {
|
|
39
|
-
// WITH RECURSIVE is a syntax error:
|
|
40
|
-
// SQL Server does not syntactically distinguish recursive and non-recursive CTEs.
|
|
41
|
-
// So mark all statements as non-recursive, generate the SQL, then restore.
|
|
42
|
-
// This approach ensures any changes in base class with() get propagated here.
|
|
43
|
-
const undoList = [];
|
|
44
|
-
if (this.grouped.with) {
|
|
45
|
-
for (const stmt of this.grouped.with) {
|
|
46
|
-
if (stmt.recursive) {
|
|
47
|
-
undoList.push(stmt);
|
|
48
|
-
stmt.recursive = false;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const result = super.with();
|
|
54
|
-
|
|
55
|
-
// Restore the recursive markings, in case this same query gets cloned and passed to other drivers.
|
|
56
|
-
for (const stmt of undoList) {
|
|
57
|
-
stmt.recursive = true;
|
|
58
|
-
}
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
select() {
|
|
63
|
-
const sql = this.with();
|
|
64
|
-
const statements = components.map((component) => this[component](this));
|
|
65
|
-
return sql + compact(statements).join(' ');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
//#region Insert
|
|
69
|
-
// Compiles an "insert" query, allowing for multiple
|
|
70
|
-
// inserts using a single query statement.
|
|
71
|
-
insert() {
|
|
72
|
-
if (
|
|
73
|
-
this.single.options &&
|
|
74
|
-
this.single.options.includeTriggerModifications
|
|
75
|
-
) {
|
|
76
|
-
return this.insertWithTriggers();
|
|
77
|
-
} else {
|
|
78
|
-
return this.standardInsert();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
insertWithTriggers() {
|
|
83
|
-
const insertValues = this.single.insert || [];
|
|
84
|
-
const { returning } = this.single;
|
|
85
|
-
let sql =
|
|
86
|
-
this.with() +
|
|
87
|
-
`${this._buildTempTable(returning)}insert into ${this.tableName} `;
|
|
88
|
-
const returningSql = returning
|
|
89
|
-
? this._returning('insert', returning, true) + ' '
|
|
90
|
-
: '';
|
|
91
|
-
|
|
92
|
-
if (Array.isArray(insertValues)) {
|
|
93
|
-
if (insertValues.length === 0) {
|
|
94
|
-
return '';
|
|
95
|
-
}
|
|
96
|
-
} else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
|
|
97
|
-
return {
|
|
98
|
-
sql:
|
|
99
|
-
sql +
|
|
100
|
-
returningSql +
|
|
101
|
-
this._emptyInsertValue +
|
|
102
|
-
this._buildReturningSelect(returning),
|
|
103
|
-
returning,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
sql += this._buildInsertData(insertValues, returningSql);
|
|
107
|
-
|
|
108
|
-
if (returning) {
|
|
109
|
-
sql += this._buildReturningSelect(returning);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
sql,
|
|
114
|
-
returning,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
_buildInsertData(insertValues, returningSql) {
|
|
119
|
-
let sql = '';
|
|
120
|
-
const insertData = this._prepInsert(insertValues);
|
|
121
|
-
if (typeof insertData === 'string') {
|
|
122
|
-
sql += insertData;
|
|
123
|
-
} else {
|
|
124
|
-
if (insertData.columns.length) {
|
|
125
|
-
sql += `(${this.formatter.columnize(insertData.columns)}`;
|
|
126
|
-
sql +=
|
|
127
|
-
`) ${returningSql}values (` +
|
|
128
|
-
this._buildInsertValues(insertData) +
|
|
129
|
-
')';
|
|
130
|
-
} else if (insertValues.length === 1 && insertValues[0]) {
|
|
131
|
-
sql += returningSql + this._emptyInsertValue;
|
|
132
|
-
} else {
|
|
133
|
-
return '';
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return sql;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
standardInsert() {
|
|
140
|
-
const insertValues = this.single.insert || [];
|
|
141
|
-
let sql = this.with() + `insert into ${this.tableName} `;
|
|
142
|
-
const { returning } = this.single;
|
|
143
|
-
const returningSql = returning
|
|
144
|
-
? this._returning('insert', returning) + ' '
|
|
145
|
-
: '';
|
|
146
|
-
|
|
147
|
-
if (Array.isArray(insertValues)) {
|
|
148
|
-
if (insertValues.length === 0) {
|
|
149
|
-
return '';
|
|
150
|
-
}
|
|
151
|
-
} else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
|
|
152
|
-
return {
|
|
153
|
-
sql: sql + returningSql + this._emptyInsertValue,
|
|
154
|
-
returning,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
sql += this._buildInsertData(insertValues, returningSql);
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
sql,
|
|
162
|
-
returning,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
//#endregion
|
|
166
|
-
|
|
167
|
-
//#region Update
|
|
168
|
-
// Compiles an `update` query, allowing for a return value.
|
|
169
|
-
update() {
|
|
170
|
-
if (
|
|
171
|
-
this.single.options &&
|
|
172
|
-
this.single.options.includeTriggerModifications
|
|
173
|
-
) {
|
|
174
|
-
return this.updateWithTriggers();
|
|
175
|
-
} else {
|
|
176
|
-
return this.standardUpdate();
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
updateWithTriggers() {
|
|
181
|
-
const top = this.top();
|
|
182
|
-
const withSQL = this.with();
|
|
183
|
-
const updates = this._prepUpdate(this.single.update);
|
|
184
|
-
const join = this.join();
|
|
185
|
-
const where = this.where();
|
|
186
|
-
const order = this.order();
|
|
187
|
-
const { returning } = this.single;
|
|
188
|
-
const declaredTemp = this._buildTempTable(returning);
|
|
189
|
-
return {
|
|
190
|
-
sql:
|
|
191
|
-
withSQL +
|
|
192
|
-
declaredTemp +
|
|
193
|
-
`update ${top ? top + ' ' : ''}${this.tableName}` +
|
|
194
|
-
' set ' +
|
|
195
|
-
updates.join(', ') +
|
|
196
|
-
(returning ? ` ${this._returning('update', returning, true)}` : '') +
|
|
197
|
-
(join ? ` from ${this.tableName} ${join}` : '') +
|
|
198
|
-
(where ? ` ${where}` : '') +
|
|
199
|
-
(order ? ` ${order}` : '') +
|
|
200
|
-
(!returning
|
|
201
|
-
? this._returning('rowcount', '@@rowcount')
|
|
202
|
-
: this._buildReturningSelect(returning)),
|
|
203
|
-
returning: returning || '@@rowcount',
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
_formatGroupsItemValue(value, nulls) {
|
|
208
|
-
const column = super._formatGroupsItemValue(value);
|
|
209
|
-
// MSSQL dont support 'is null' syntax in order by,
|
|
210
|
-
// so we override this function and add MSSQL specific syntax.
|
|
211
|
-
if (nulls && !(value instanceof Raw)) {
|
|
212
|
-
const collNulls = `IIF(${column} is null,`;
|
|
213
|
-
if (nulls === 'first') {
|
|
214
|
-
return `${collNulls}0,1)`;
|
|
215
|
-
} else if (nulls === 'last') {
|
|
216
|
-
return `${collNulls}1,0)`;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return column;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
standardUpdate() {
|
|
223
|
-
const top = this.top();
|
|
224
|
-
const withSQL = this.with();
|
|
225
|
-
const updates = this._prepUpdate(this.single.update);
|
|
226
|
-
const join = this.join();
|
|
227
|
-
const where = this.where();
|
|
228
|
-
const order = this.order();
|
|
229
|
-
const { returning } = this.single;
|
|
230
|
-
return {
|
|
231
|
-
sql:
|
|
232
|
-
withSQL +
|
|
233
|
-
`update ${top ? top + ' ' : ''}${this.tableName}` +
|
|
234
|
-
' set ' +
|
|
235
|
-
updates.join(', ') +
|
|
236
|
-
(returning ? ` ${this._returning('update', returning)}` : '') +
|
|
237
|
-
(join ? ` from ${this.tableName} ${join}` : '') +
|
|
238
|
-
(where ? ` ${where}` : '') +
|
|
239
|
-
(order ? ` ${order}` : '') +
|
|
240
|
-
(!returning ? this._returning('rowcount', '@@rowcount') : ''),
|
|
241
|
-
returning: returning || '@@rowcount',
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
//#endregion
|
|
245
|
-
|
|
246
|
-
//#region Delete
|
|
247
|
-
// Compiles a `delete` query.
|
|
248
|
-
del() {
|
|
249
|
-
if (
|
|
250
|
-
this.single.options &&
|
|
251
|
-
this.single.options.includeTriggerModifications
|
|
252
|
-
) {
|
|
253
|
-
return this.deleteWithTriggers();
|
|
254
|
-
} else {
|
|
255
|
-
return this.standardDelete();
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
deleteWithTriggers() {
|
|
260
|
-
// Make sure tableName is processed by the formatter first.
|
|
261
|
-
const withSQL = this.with();
|
|
262
|
-
const { tableName } = this;
|
|
263
|
-
const wheres = this.where();
|
|
264
|
-
const joins = this.join();
|
|
265
|
-
const { returning } = this.single;
|
|
266
|
-
const returningStr = returning
|
|
267
|
-
? ` ${this._returning('del', returning, true)}`
|
|
268
|
-
: '';
|
|
269
|
-
const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
|
|
270
|
-
return {
|
|
271
|
-
sql:
|
|
272
|
-
withSQL +
|
|
273
|
-
`${this._buildTempTable(
|
|
274
|
-
returning
|
|
275
|
-
)}delete ${deleteSelector}from ${tableName}` +
|
|
276
|
-
(!joins ? returningStr : '') +
|
|
277
|
-
(joins ? ` ${joins}` : '') +
|
|
278
|
-
(wheres ? ` ${wheres}` : '') +
|
|
279
|
-
(!returning
|
|
280
|
-
? this._returning('rowcount', '@@rowcount')
|
|
281
|
-
: this._buildReturningSelect(returning)),
|
|
282
|
-
returning: returning || '@@rowcount',
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
standardDelete() {
|
|
287
|
-
// Make sure tableName is processed by the formatter first.
|
|
288
|
-
const withSQL = this.with();
|
|
289
|
-
const { tableName } = this;
|
|
290
|
-
const wheres = this.where();
|
|
291
|
-
const joins = this.join();
|
|
292
|
-
const { returning } = this.single;
|
|
293
|
-
const returningStr = returning
|
|
294
|
-
? ` ${this._returning('del', returning)}`
|
|
295
|
-
: '';
|
|
296
|
-
// returning needs to be before "from" when using join
|
|
297
|
-
const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
|
|
298
|
-
return {
|
|
299
|
-
sql:
|
|
300
|
-
withSQL +
|
|
301
|
-
`delete ${deleteSelector}from ${tableName}` +
|
|
302
|
-
(!joins ? returningStr : '') +
|
|
303
|
-
(joins ? ` ${joins}` : '') +
|
|
304
|
-
(wheres ? ` ${wheres}` : '') +
|
|
305
|
-
(!returning ? this._returning('rowcount', '@@rowcount') : ''),
|
|
306
|
-
returning: returning || '@@rowcount',
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
//#endregion
|
|
310
|
-
|
|
311
|
-
// Compiles the columns in the query, specifying if an item was distinct.
|
|
312
|
-
columns() {
|
|
313
|
-
let distinctClause = '';
|
|
314
|
-
if (this.onlyUnions()) return '';
|
|
315
|
-
const top = this.top();
|
|
316
|
-
const hints = this._hintComments();
|
|
317
|
-
const columns = this.grouped.columns || [];
|
|
318
|
-
let i = -1,
|
|
319
|
-
sql = [];
|
|
320
|
-
if (columns) {
|
|
321
|
-
while (++i < columns.length) {
|
|
322
|
-
const stmt = columns[i];
|
|
323
|
-
if (stmt.distinct) distinctClause = 'distinct ';
|
|
324
|
-
if (stmt.distinctOn) {
|
|
325
|
-
distinctClause = this.distinctOn(stmt.value);
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
if (stmt.type === 'aggregate') {
|
|
329
|
-
sql.push(...this.aggregate(stmt));
|
|
330
|
-
} else if (stmt.type === 'aggregateRaw') {
|
|
331
|
-
sql.push(this.aggregateRaw(stmt));
|
|
332
|
-
} else if (stmt.type === 'analytic') {
|
|
333
|
-
sql.push(this.analytic(stmt));
|
|
334
|
-
} else if (stmt.type === 'json') {
|
|
335
|
-
sql.push(this.json(stmt));
|
|
336
|
-
} else if (stmt.value && stmt.value.length > 0) {
|
|
337
|
-
sql.push(this.formatter.columnize(stmt.value));
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
if (sql.length === 0) sql = ['*'];
|
|
342
|
-
const select = this.onlyJson() ? '' : 'select ';
|
|
343
|
-
return (
|
|
344
|
-
`${select}${hints}${distinctClause}` +
|
|
345
|
-
(top ? top + ' ' : '') +
|
|
346
|
-
sql.join(', ') +
|
|
347
|
-
(this.tableName ? ` from ${this.tableName}` : '')
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
_returning(method, value, withTrigger) {
|
|
352
|
-
switch (method) {
|
|
353
|
-
case 'update':
|
|
354
|
-
case 'insert':
|
|
355
|
-
return value
|
|
356
|
-
? `output ${this.formatter.columnizeWithPrefix('inserted.', value)}${
|
|
357
|
-
withTrigger ? ' into #out' : ''
|
|
358
|
-
}`
|
|
359
|
-
: '';
|
|
360
|
-
case 'del':
|
|
361
|
-
return value
|
|
362
|
-
? `output ${this.formatter.columnizeWithPrefix('deleted.', value)}${
|
|
363
|
-
withTrigger ? ' into #out' : ''
|
|
364
|
-
}`
|
|
365
|
-
: '';
|
|
366
|
-
case 'rowcount':
|
|
367
|
-
return value ? ';select @@rowcount' : '';
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
_buildTempTable(values) {
|
|
372
|
-
// If value is nothing then return an empty string
|
|
373
|
-
if (values && values.length > 0) {
|
|
374
|
-
let selections = '';
|
|
375
|
-
|
|
376
|
-
// Build values that will be returned from this procedure
|
|
377
|
-
if (Array.isArray(values)) {
|
|
378
|
-
selections = values
|
|
379
|
-
.map((value) => `[t].${this.formatter.columnize(value)}`)
|
|
380
|
-
.join(',');
|
|
381
|
-
} else {
|
|
382
|
-
selections = `[t].${this.formatter.columnize(values)}`;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Force #out to be correctly populated with the correct column structure.
|
|
386
|
-
let sql = `select top(0) ${selections} into #out `;
|
|
387
|
-
sql += `from ${this.tableName} as t `;
|
|
388
|
-
sql += `left join ${this.tableName} on 0=1;`;
|
|
389
|
-
|
|
390
|
-
return sql;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return '';
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
_buildReturningSelect(values) {
|
|
397
|
-
// If value is nothing then return an empty string
|
|
398
|
-
if (values && values.length > 0) {
|
|
399
|
-
let selections = '';
|
|
400
|
-
|
|
401
|
-
// Build columns to return
|
|
402
|
-
if (Array.isArray(values)) {
|
|
403
|
-
selections = values
|
|
404
|
-
.map((value) => `${this.formatter.columnize(value)}`)
|
|
405
|
-
.join(',');
|
|
406
|
-
} else {
|
|
407
|
-
selections = this.formatter.columnize(values);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Get the returned values
|
|
411
|
-
let sql = `; select ${selections} from #out; `;
|
|
412
|
-
// Drop the temp table to prevent memory leaks
|
|
413
|
-
sql += `drop table #out;`;
|
|
414
|
-
|
|
415
|
-
return sql;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
return '';
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Compiles a `truncate` query.
|
|
422
|
-
truncate() {
|
|
423
|
-
return `truncate table ${this.tableName}`;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
forUpdate() {
|
|
427
|
-
// this doesn't work exacltly as it should, one should also mention index while locking
|
|
428
|
-
// https://stackoverflow.com/a/9818448/360060
|
|
429
|
-
return 'with (UPDLOCK)';
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
forShare() {
|
|
433
|
-
// http://www.sqlteam.com/article/introduction-to-locking-in-sql-server
|
|
434
|
-
return 'with (HOLDLOCK)';
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Compiles a `columnInfo` query.
|
|
438
|
-
columnInfo() {
|
|
439
|
-
const column = this.single.columnInfo;
|
|
440
|
-
let schema = this.single.schema;
|
|
441
|
-
|
|
442
|
-
// The user may have specified a custom wrapIdentifier function in the config. We
|
|
443
|
-
// need to run the identifiers through that function, but not format them as
|
|
444
|
-
// identifiers otherwise.
|
|
445
|
-
const table = this.client.customWrapIdentifier(this.single.table, identity);
|
|
446
|
-
|
|
447
|
-
if (schema) {
|
|
448
|
-
schema = this.client.customWrapIdentifier(schema, identity);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// GOTCHA: INFORMATION_SCHEMA.COLUMNS must be capitalized to work when the database has a case-sensitive collation. [#4573]
|
|
452
|
-
let sql = `select [COLUMN_NAME], [COLUMN_DEFAULT], [DATA_TYPE], [CHARACTER_MAXIMUM_LENGTH], [IS_NULLABLE] from INFORMATION_SCHEMA.COLUMNS where table_name = ? and table_catalog = ?`;
|
|
453
|
-
const bindings = [table, this.client.database()];
|
|
454
|
-
|
|
455
|
-
if (schema) {
|
|
456
|
-
sql += ' and table_schema = ?';
|
|
457
|
-
bindings.push(schema);
|
|
458
|
-
} else {
|
|
459
|
-
sql += ` and table_schema = 'dbo'`;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
return {
|
|
463
|
-
sql,
|
|
464
|
-
bindings: bindings,
|
|
465
|
-
output(resp) {
|
|
466
|
-
const out = resp.reduce((columns, val) => {
|
|
467
|
-
columns[val[0].value] = {
|
|
468
|
-
defaultValue: val[1].value,
|
|
469
|
-
type: val[2].value,
|
|
470
|
-
maxLength: val[3].value,
|
|
471
|
-
nullable: val[4].value === 'YES',
|
|
472
|
-
};
|
|
473
|
-
return columns;
|
|
474
|
-
}, {});
|
|
475
|
-
return (column && out[column]) || out;
|
|
476
|
-
},
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
top() {
|
|
481
|
-
const noLimit = !this.single.limit && this.single.limit !== 0;
|
|
482
|
-
const noOffset = !this.single.offset;
|
|
483
|
-
if (noLimit || !noOffset) return '';
|
|
484
|
-
return `top (${this._getValueOrParameterFromAttribute('limit')})`;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
limit() {
|
|
488
|
-
return '';
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
offset() {
|
|
492
|
-
const noLimit = !this.single.limit && this.single.limit !== 0;
|
|
493
|
-
const noOffset = !this.single.offset;
|
|
494
|
-
if (noOffset) return '';
|
|
495
|
-
let offset = `offset ${
|
|
496
|
-
noOffset ? '0' : this._getValueOrParameterFromAttribute('offset')
|
|
497
|
-
} rows`;
|
|
498
|
-
if (!noLimit) {
|
|
499
|
-
offset += ` fetch next ${this._getValueOrParameterFromAttribute(
|
|
500
|
-
'limit'
|
|
501
|
-
)} rows only`;
|
|
502
|
-
}
|
|
503
|
-
return offset;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
whereLike(statement) {
|
|
507
|
-
return `${this._columnClause(
|
|
508
|
-
statement
|
|
509
|
-
)} collate SQL_Latin1_General_CP1_CS_AS ${this._not(
|
|
510
|
-
statement,
|
|
511
|
-
'like '
|
|
512
|
-
)}${this._valueClause(statement)}`;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
whereILike(statement) {
|
|
516
|
-
return `${this._columnClause(
|
|
517
|
-
statement
|
|
518
|
-
)} collate SQL_Latin1_General_CP1_CI_AS ${this._not(
|
|
519
|
-
statement,
|
|
520
|
-
'like '
|
|
521
|
-
)}${this._valueClause(statement)}`;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
jsonExtract(params) {
|
|
525
|
-
// JSON_VALUE return NULL if we query object or array
|
|
526
|
-
// JSON_QUERY return NULL if we query literal/single value
|
|
527
|
-
return this._jsonExtract(
|
|
528
|
-
params.singleValue ? 'JSON_VALUE' : 'JSON_QUERY',
|
|
529
|
-
params
|
|
530
|
-
);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
jsonSet(params) {
|
|
534
|
-
return this._jsonSet('JSON_MODIFY', params);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
jsonInsert(params) {
|
|
538
|
-
return this._jsonSet('JSON_MODIFY', params);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
jsonRemove(params) {
|
|
542
|
-
const jsonCol = `JSON_MODIFY(${columnize_(
|
|
543
|
-
params.column,
|
|
544
|
-
this.builder,
|
|
545
|
-
this.client,
|
|
546
|
-
this.bindingsHolder
|
|
547
|
-
)},${this.client.parameter(
|
|
548
|
-
params.path,
|
|
549
|
-
this.builder,
|
|
550
|
-
this.bindingsHolder
|
|
551
|
-
)}, NULL)`;
|
|
552
|
-
return params.alias
|
|
553
|
-
? this.client.alias(jsonCol, this.formatter.wrap(params.alias))
|
|
554
|
-
: jsonCol;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
whereJsonPath(statement) {
|
|
558
|
-
return this._whereJsonPath('JSON_VALUE', statement);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
whereJsonSupersetOf(statement) {
|
|
562
|
-
throw new Error(
|
|
563
|
-
'Json superset where clause not actually supported by MSSQL'
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
whereJsonSubsetOf(statement) {
|
|
568
|
-
throw new Error('Json subset where clause not actually supported by MSSQL');
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
_getExtracts(statement, operator) {
|
|
572
|
-
const column = columnize_(
|
|
573
|
-
statement.column,
|
|
574
|
-
this.builder,
|
|
575
|
-
this.client,
|
|
576
|
-
this.bindingsHolder
|
|
577
|
-
);
|
|
578
|
-
return (
|
|
579
|
-
Array.isArray(statement.values) ? statement.values : [statement.values]
|
|
580
|
-
)
|
|
581
|
-
.map(function (value) {
|
|
582
|
-
return (
|
|
583
|
-
'JSON_VALUE(' +
|
|
584
|
-
column +
|
|
585
|
-
',' +
|
|
586
|
-
this.client.parameter(value, this.builder, this.bindingsHolder) +
|
|
587
|
-
')'
|
|
588
|
-
);
|
|
589
|
-
}, this)
|
|
590
|
-
.join(operator);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
onJsonPathEquals(clause) {
|
|
594
|
-
return this._onJsonPathEquals('JSON_VALUE', clause);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// Set the QueryBuilder & QueryCompiler on the client object,
|
|
599
|
-
// in case anyone wants to modify things to suit their own purposes.
|
|
600
|
-
module.exports = QueryCompiler_MSSQL;
|
|
1
|
+
// MSSQL Query Compiler
|
|
2
|
+
// ------
|
|
3
|
+
const QueryCompiler = require('../../../query/querycompiler');
|
|
4
|
+
|
|
5
|
+
const compact = require('lodash/compact');
|
|
6
|
+
const identity = require('lodash/identity');
|
|
7
|
+
const isEmpty = require('lodash/isEmpty');
|
|
8
|
+
const Raw = require('../../../raw.js');
|
|
9
|
+
const {
|
|
10
|
+
columnize: columnize_,
|
|
11
|
+
} = require('../../../formatter/wrappingFormatter');
|
|
12
|
+
|
|
13
|
+
const components = [
|
|
14
|
+
'columns',
|
|
15
|
+
'join',
|
|
16
|
+
'lock',
|
|
17
|
+
'where',
|
|
18
|
+
'union',
|
|
19
|
+
'group',
|
|
20
|
+
'having',
|
|
21
|
+
'order',
|
|
22
|
+
'limit',
|
|
23
|
+
'offset',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
class QueryCompiler_MSSQL extends QueryCompiler {
|
|
27
|
+
constructor(client, builder, formatter) {
|
|
28
|
+
super(client, builder, formatter);
|
|
29
|
+
|
|
30
|
+
const { onConflict } = this.single;
|
|
31
|
+
if (onConflict) {
|
|
32
|
+
throw new Error('.onConflict() is not supported for mssql.');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this._emptyInsertValue = 'default values';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
with() {
|
|
39
|
+
// WITH RECURSIVE is a syntax error:
|
|
40
|
+
// SQL Server does not syntactically distinguish recursive and non-recursive CTEs.
|
|
41
|
+
// So mark all statements as non-recursive, generate the SQL, then restore.
|
|
42
|
+
// This approach ensures any changes in base class with() get propagated here.
|
|
43
|
+
const undoList = [];
|
|
44
|
+
if (this.grouped.with) {
|
|
45
|
+
for (const stmt of this.grouped.with) {
|
|
46
|
+
if (stmt.recursive) {
|
|
47
|
+
undoList.push(stmt);
|
|
48
|
+
stmt.recursive = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const result = super.with();
|
|
54
|
+
|
|
55
|
+
// Restore the recursive markings, in case this same query gets cloned and passed to other drivers.
|
|
56
|
+
for (const stmt of undoList) {
|
|
57
|
+
stmt.recursive = true;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
select() {
|
|
63
|
+
const sql = this.with();
|
|
64
|
+
const statements = components.map((component) => this[component](this));
|
|
65
|
+
return sql + compact(statements).join(' ');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
//#region Insert
|
|
69
|
+
// Compiles an "insert" query, allowing for multiple
|
|
70
|
+
// inserts using a single query statement.
|
|
71
|
+
insert() {
|
|
72
|
+
if (
|
|
73
|
+
this.single.options &&
|
|
74
|
+
this.single.options.includeTriggerModifications
|
|
75
|
+
) {
|
|
76
|
+
return this.insertWithTriggers();
|
|
77
|
+
} else {
|
|
78
|
+
return this.standardInsert();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
insertWithTriggers() {
|
|
83
|
+
const insertValues = this.single.insert || [];
|
|
84
|
+
const { returning } = this.single;
|
|
85
|
+
let sql =
|
|
86
|
+
this.with() +
|
|
87
|
+
`${this._buildTempTable(returning)}insert into ${this.tableName} `;
|
|
88
|
+
const returningSql = returning
|
|
89
|
+
? this._returning('insert', returning, true) + ' '
|
|
90
|
+
: '';
|
|
91
|
+
|
|
92
|
+
if (Array.isArray(insertValues)) {
|
|
93
|
+
if (insertValues.length === 0) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
} else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
|
|
97
|
+
return {
|
|
98
|
+
sql:
|
|
99
|
+
sql +
|
|
100
|
+
returningSql +
|
|
101
|
+
this._emptyInsertValue +
|
|
102
|
+
this._buildReturningSelect(returning),
|
|
103
|
+
returning,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
sql += this._buildInsertData(insertValues, returningSql);
|
|
107
|
+
|
|
108
|
+
if (returning) {
|
|
109
|
+
sql += this._buildReturningSelect(returning);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
sql,
|
|
114
|
+
returning,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
_buildInsertData(insertValues, returningSql) {
|
|
119
|
+
let sql = '';
|
|
120
|
+
const insertData = this._prepInsert(insertValues);
|
|
121
|
+
if (typeof insertData === 'string') {
|
|
122
|
+
sql += insertData;
|
|
123
|
+
} else {
|
|
124
|
+
if (insertData.columns.length) {
|
|
125
|
+
sql += `(${this.formatter.columnize(insertData.columns)}`;
|
|
126
|
+
sql +=
|
|
127
|
+
`) ${returningSql}values (` +
|
|
128
|
+
this._buildInsertValues(insertData) +
|
|
129
|
+
')';
|
|
130
|
+
} else if (insertValues.length === 1 && insertValues[0]) {
|
|
131
|
+
sql += returningSql + this._emptyInsertValue;
|
|
132
|
+
} else {
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return sql;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
standardInsert() {
|
|
140
|
+
const insertValues = this.single.insert || [];
|
|
141
|
+
let sql = this.with() + `insert into ${this.tableName} `;
|
|
142
|
+
const { returning } = this.single;
|
|
143
|
+
const returningSql = returning
|
|
144
|
+
? this._returning('insert', returning) + ' '
|
|
145
|
+
: '';
|
|
146
|
+
|
|
147
|
+
if (Array.isArray(insertValues)) {
|
|
148
|
+
if (insertValues.length === 0) {
|
|
149
|
+
return '';
|
|
150
|
+
}
|
|
151
|
+
} else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
|
|
152
|
+
return {
|
|
153
|
+
sql: sql + returningSql + this._emptyInsertValue,
|
|
154
|
+
returning,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
sql += this._buildInsertData(insertValues, returningSql);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
sql,
|
|
162
|
+
returning,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
|
|
167
|
+
//#region Update
|
|
168
|
+
// Compiles an `update` query, allowing for a return value.
|
|
169
|
+
update() {
|
|
170
|
+
if (
|
|
171
|
+
this.single.options &&
|
|
172
|
+
this.single.options.includeTriggerModifications
|
|
173
|
+
) {
|
|
174
|
+
return this.updateWithTriggers();
|
|
175
|
+
} else {
|
|
176
|
+
return this.standardUpdate();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
updateWithTriggers() {
|
|
181
|
+
const top = this.top();
|
|
182
|
+
const withSQL = this.with();
|
|
183
|
+
const updates = this._prepUpdate(this.single.update);
|
|
184
|
+
const join = this.join();
|
|
185
|
+
const where = this.where();
|
|
186
|
+
const order = this.order();
|
|
187
|
+
const { returning } = this.single;
|
|
188
|
+
const declaredTemp = this._buildTempTable(returning);
|
|
189
|
+
return {
|
|
190
|
+
sql:
|
|
191
|
+
withSQL +
|
|
192
|
+
declaredTemp +
|
|
193
|
+
`update ${top ? top + ' ' : ''}${this.tableName}` +
|
|
194
|
+
' set ' +
|
|
195
|
+
updates.join(', ') +
|
|
196
|
+
(returning ? ` ${this._returning('update', returning, true)}` : '') +
|
|
197
|
+
(join ? ` from ${this.tableName} ${join}` : '') +
|
|
198
|
+
(where ? ` ${where}` : '') +
|
|
199
|
+
(order ? ` ${order}` : '') +
|
|
200
|
+
(!returning
|
|
201
|
+
? this._returning('rowcount', '@@rowcount')
|
|
202
|
+
: this._buildReturningSelect(returning)),
|
|
203
|
+
returning: returning || '@@rowcount',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
_formatGroupsItemValue(value, nulls) {
|
|
208
|
+
const column = super._formatGroupsItemValue(value);
|
|
209
|
+
// MSSQL dont support 'is null' syntax in order by,
|
|
210
|
+
// so we override this function and add MSSQL specific syntax.
|
|
211
|
+
if (nulls && !(value instanceof Raw)) {
|
|
212
|
+
const collNulls = `IIF(${column} is null,`;
|
|
213
|
+
if (nulls === 'first') {
|
|
214
|
+
return `${collNulls}0,1)`;
|
|
215
|
+
} else if (nulls === 'last') {
|
|
216
|
+
return `${collNulls}1,0)`;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return column;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
standardUpdate() {
|
|
223
|
+
const top = this.top();
|
|
224
|
+
const withSQL = this.with();
|
|
225
|
+
const updates = this._prepUpdate(this.single.update);
|
|
226
|
+
const join = this.join();
|
|
227
|
+
const where = this.where();
|
|
228
|
+
const order = this.order();
|
|
229
|
+
const { returning } = this.single;
|
|
230
|
+
return {
|
|
231
|
+
sql:
|
|
232
|
+
withSQL +
|
|
233
|
+
`update ${top ? top + ' ' : ''}${this.tableName}` +
|
|
234
|
+
' set ' +
|
|
235
|
+
updates.join(', ') +
|
|
236
|
+
(returning ? ` ${this._returning('update', returning)}` : '') +
|
|
237
|
+
(join ? ` from ${this.tableName} ${join}` : '') +
|
|
238
|
+
(where ? ` ${where}` : '') +
|
|
239
|
+
(order ? ` ${order}` : '') +
|
|
240
|
+
(!returning ? this._returning('rowcount', '@@rowcount') : ''),
|
|
241
|
+
returning: returning || '@@rowcount',
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
|
|
246
|
+
//#region Delete
|
|
247
|
+
// Compiles a `delete` query.
|
|
248
|
+
del() {
|
|
249
|
+
if (
|
|
250
|
+
this.single.options &&
|
|
251
|
+
this.single.options.includeTriggerModifications
|
|
252
|
+
) {
|
|
253
|
+
return this.deleteWithTriggers();
|
|
254
|
+
} else {
|
|
255
|
+
return this.standardDelete();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
deleteWithTriggers() {
|
|
260
|
+
// Make sure tableName is processed by the formatter first.
|
|
261
|
+
const withSQL = this.with();
|
|
262
|
+
const { tableName } = this;
|
|
263
|
+
const wheres = this.where();
|
|
264
|
+
const joins = this.join();
|
|
265
|
+
const { returning } = this.single;
|
|
266
|
+
const returningStr = returning
|
|
267
|
+
? ` ${this._returning('del', returning, true)}`
|
|
268
|
+
: '';
|
|
269
|
+
const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
|
|
270
|
+
return {
|
|
271
|
+
sql:
|
|
272
|
+
withSQL +
|
|
273
|
+
`${this._buildTempTable(
|
|
274
|
+
returning
|
|
275
|
+
)}delete ${deleteSelector}from ${tableName}` +
|
|
276
|
+
(!joins ? returningStr : '') +
|
|
277
|
+
(joins ? ` ${joins}` : '') +
|
|
278
|
+
(wheres ? ` ${wheres}` : '') +
|
|
279
|
+
(!returning
|
|
280
|
+
? this._returning('rowcount', '@@rowcount')
|
|
281
|
+
: this._buildReturningSelect(returning)),
|
|
282
|
+
returning: returning || '@@rowcount',
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
standardDelete() {
|
|
287
|
+
// Make sure tableName is processed by the formatter first.
|
|
288
|
+
const withSQL = this.with();
|
|
289
|
+
const { tableName } = this;
|
|
290
|
+
const wheres = this.where();
|
|
291
|
+
const joins = this.join();
|
|
292
|
+
const { returning } = this.single;
|
|
293
|
+
const returningStr = returning
|
|
294
|
+
? ` ${this._returning('del', returning)}`
|
|
295
|
+
: '';
|
|
296
|
+
// returning needs to be before "from" when using join
|
|
297
|
+
const deleteSelector = joins ? `${tableName}${returningStr} ` : '';
|
|
298
|
+
return {
|
|
299
|
+
sql:
|
|
300
|
+
withSQL +
|
|
301
|
+
`delete ${deleteSelector}from ${tableName}` +
|
|
302
|
+
(!joins ? returningStr : '') +
|
|
303
|
+
(joins ? ` ${joins}` : '') +
|
|
304
|
+
(wheres ? ` ${wheres}` : '') +
|
|
305
|
+
(!returning ? this._returning('rowcount', '@@rowcount') : ''),
|
|
306
|
+
returning: returning || '@@rowcount',
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
|
|
311
|
+
// Compiles the columns in the query, specifying if an item was distinct.
|
|
312
|
+
columns() {
|
|
313
|
+
let distinctClause = '';
|
|
314
|
+
if (this.onlyUnions()) return '';
|
|
315
|
+
const top = this.top();
|
|
316
|
+
const hints = this._hintComments();
|
|
317
|
+
const columns = this.grouped.columns || [];
|
|
318
|
+
let i = -1,
|
|
319
|
+
sql = [];
|
|
320
|
+
if (columns) {
|
|
321
|
+
while (++i < columns.length) {
|
|
322
|
+
const stmt = columns[i];
|
|
323
|
+
if (stmt.distinct) distinctClause = 'distinct ';
|
|
324
|
+
if (stmt.distinctOn) {
|
|
325
|
+
distinctClause = this.distinctOn(stmt.value);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (stmt.type === 'aggregate') {
|
|
329
|
+
sql.push(...this.aggregate(stmt));
|
|
330
|
+
} else if (stmt.type === 'aggregateRaw') {
|
|
331
|
+
sql.push(this.aggregateRaw(stmt));
|
|
332
|
+
} else if (stmt.type === 'analytic') {
|
|
333
|
+
sql.push(this.analytic(stmt));
|
|
334
|
+
} else if (stmt.type === 'json') {
|
|
335
|
+
sql.push(this.json(stmt));
|
|
336
|
+
} else if (stmt.value && stmt.value.length > 0) {
|
|
337
|
+
sql.push(this.formatter.columnize(stmt.value));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (sql.length === 0) sql = ['*'];
|
|
342
|
+
const select = this.onlyJson() ? '' : 'select ';
|
|
343
|
+
return (
|
|
344
|
+
`${select}${hints}${distinctClause}` +
|
|
345
|
+
(top ? top + ' ' : '') +
|
|
346
|
+
sql.join(', ') +
|
|
347
|
+
(this.tableName ? ` from ${this.tableName}` : '')
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
_returning(method, value, withTrigger) {
|
|
352
|
+
switch (method) {
|
|
353
|
+
case 'update':
|
|
354
|
+
case 'insert':
|
|
355
|
+
return value
|
|
356
|
+
? `output ${this.formatter.columnizeWithPrefix('inserted.', value)}${
|
|
357
|
+
withTrigger ? ' into #out' : ''
|
|
358
|
+
}`
|
|
359
|
+
: '';
|
|
360
|
+
case 'del':
|
|
361
|
+
return value
|
|
362
|
+
? `output ${this.formatter.columnizeWithPrefix('deleted.', value)}${
|
|
363
|
+
withTrigger ? ' into #out' : ''
|
|
364
|
+
}`
|
|
365
|
+
: '';
|
|
366
|
+
case 'rowcount':
|
|
367
|
+
return value ? ';select @@rowcount' : '';
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
_buildTempTable(values) {
|
|
372
|
+
// If value is nothing then return an empty string
|
|
373
|
+
if (values && values.length > 0) {
|
|
374
|
+
let selections = '';
|
|
375
|
+
|
|
376
|
+
// Build values that will be returned from this procedure
|
|
377
|
+
if (Array.isArray(values)) {
|
|
378
|
+
selections = values
|
|
379
|
+
.map((value) => `[t].${this.formatter.columnize(value)}`)
|
|
380
|
+
.join(',');
|
|
381
|
+
} else {
|
|
382
|
+
selections = `[t].${this.formatter.columnize(values)}`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Force #out to be correctly populated with the correct column structure.
|
|
386
|
+
let sql = `select top(0) ${selections} into #out `;
|
|
387
|
+
sql += `from ${this.tableName} as t `;
|
|
388
|
+
sql += `left join ${this.tableName} on 0=1;`;
|
|
389
|
+
|
|
390
|
+
return sql;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return '';
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
_buildReturningSelect(values) {
|
|
397
|
+
// If value is nothing then return an empty string
|
|
398
|
+
if (values && values.length > 0) {
|
|
399
|
+
let selections = '';
|
|
400
|
+
|
|
401
|
+
// Build columns to return
|
|
402
|
+
if (Array.isArray(values)) {
|
|
403
|
+
selections = values
|
|
404
|
+
.map((value) => `${this.formatter.columnize(value)}`)
|
|
405
|
+
.join(',');
|
|
406
|
+
} else {
|
|
407
|
+
selections = this.formatter.columnize(values);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Get the returned values
|
|
411
|
+
let sql = `; select ${selections} from #out; `;
|
|
412
|
+
// Drop the temp table to prevent memory leaks
|
|
413
|
+
sql += `drop table #out;`;
|
|
414
|
+
|
|
415
|
+
return sql;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return '';
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Compiles a `truncate` query.
|
|
422
|
+
truncate() {
|
|
423
|
+
return `truncate table ${this.tableName}`;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
forUpdate() {
|
|
427
|
+
// this doesn't work exacltly as it should, one should also mention index while locking
|
|
428
|
+
// https://stackoverflow.com/a/9818448/360060
|
|
429
|
+
return 'with (UPDLOCK)';
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
forShare() {
|
|
433
|
+
// http://www.sqlteam.com/article/introduction-to-locking-in-sql-server
|
|
434
|
+
return 'with (HOLDLOCK)';
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Compiles a `columnInfo` query.
|
|
438
|
+
columnInfo() {
|
|
439
|
+
const column = this.single.columnInfo;
|
|
440
|
+
let schema = this.single.schema;
|
|
441
|
+
|
|
442
|
+
// The user may have specified a custom wrapIdentifier function in the config. We
|
|
443
|
+
// need to run the identifiers through that function, but not format them as
|
|
444
|
+
// identifiers otherwise.
|
|
445
|
+
const table = this.client.customWrapIdentifier(this.single.table, identity);
|
|
446
|
+
|
|
447
|
+
if (schema) {
|
|
448
|
+
schema = this.client.customWrapIdentifier(schema, identity);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// GOTCHA: INFORMATION_SCHEMA.COLUMNS must be capitalized to work when the database has a case-sensitive collation. [#4573]
|
|
452
|
+
let sql = `select [COLUMN_NAME], [COLUMN_DEFAULT], [DATA_TYPE], [CHARACTER_MAXIMUM_LENGTH], [IS_NULLABLE] from INFORMATION_SCHEMA.COLUMNS where table_name = ? and table_catalog = ?`;
|
|
453
|
+
const bindings = [table, this.client.database()];
|
|
454
|
+
|
|
455
|
+
if (schema) {
|
|
456
|
+
sql += ' and table_schema = ?';
|
|
457
|
+
bindings.push(schema);
|
|
458
|
+
} else {
|
|
459
|
+
sql += ` and table_schema = 'dbo'`;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
sql,
|
|
464
|
+
bindings: bindings,
|
|
465
|
+
output(resp) {
|
|
466
|
+
const out = resp.reduce((columns, val) => {
|
|
467
|
+
columns[val[0].value] = {
|
|
468
|
+
defaultValue: val[1].value,
|
|
469
|
+
type: val[2].value,
|
|
470
|
+
maxLength: val[3].value,
|
|
471
|
+
nullable: val[4].value === 'YES',
|
|
472
|
+
};
|
|
473
|
+
return columns;
|
|
474
|
+
}, {});
|
|
475
|
+
return (column && out[column]) || out;
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
top() {
|
|
481
|
+
const noLimit = !this.single.limit && this.single.limit !== 0;
|
|
482
|
+
const noOffset = !this.single.offset;
|
|
483
|
+
if (noLimit || !noOffset) return '';
|
|
484
|
+
return `top (${this._getValueOrParameterFromAttribute('limit')})`;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
limit() {
|
|
488
|
+
return '';
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
offset() {
|
|
492
|
+
const noLimit = !this.single.limit && this.single.limit !== 0;
|
|
493
|
+
const noOffset = !this.single.offset;
|
|
494
|
+
if (noOffset) return '';
|
|
495
|
+
let offset = `offset ${
|
|
496
|
+
noOffset ? '0' : this._getValueOrParameterFromAttribute('offset')
|
|
497
|
+
} rows`;
|
|
498
|
+
if (!noLimit) {
|
|
499
|
+
offset += ` fetch next ${this._getValueOrParameterFromAttribute(
|
|
500
|
+
'limit'
|
|
501
|
+
)} rows only`;
|
|
502
|
+
}
|
|
503
|
+
return offset;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
whereLike(statement) {
|
|
507
|
+
return `${this._columnClause(
|
|
508
|
+
statement
|
|
509
|
+
)} collate SQL_Latin1_General_CP1_CS_AS ${this._not(
|
|
510
|
+
statement,
|
|
511
|
+
'like '
|
|
512
|
+
)}${this._valueClause(statement)}`;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
whereILike(statement) {
|
|
516
|
+
return `${this._columnClause(
|
|
517
|
+
statement
|
|
518
|
+
)} collate SQL_Latin1_General_CP1_CI_AS ${this._not(
|
|
519
|
+
statement,
|
|
520
|
+
'like '
|
|
521
|
+
)}${this._valueClause(statement)}`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
jsonExtract(params) {
|
|
525
|
+
// JSON_VALUE return NULL if we query object or array
|
|
526
|
+
// JSON_QUERY return NULL if we query literal/single value
|
|
527
|
+
return this._jsonExtract(
|
|
528
|
+
params.singleValue ? 'JSON_VALUE' : 'JSON_QUERY',
|
|
529
|
+
params
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
jsonSet(params) {
|
|
534
|
+
return this._jsonSet('JSON_MODIFY', params);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
jsonInsert(params) {
|
|
538
|
+
return this._jsonSet('JSON_MODIFY', params);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
jsonRemove(params) {
|
|
542
|
+
const jsonCol = `JSON_MODIFY(${columnize_(
|
|
543
|
+
params.column,
|
|
544
|
+
this.builder,
|
|
545
|
+
this.client,
|
|
546
|
+
this.bindingsHolder
|
|
547
|
+
)},${this.client.parameter(
|
|
548
|
+
params.path,
|
|
549
|
+
this.builder,
|
|
550
|
+
this.bindingsHolder
|
|
551
|
+
)}, NULL)`;
|
|
552
|
+
return params.alias
|
|
553
|
+
? this.client.alias(jsonCol, this.formatter.wrap(params.alias))
|
|
554
|
+
: jsonCol;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
whereJsonPath(statement) {
|
|
558
|
+
return this._whereJsonPath('JSON_VALUE', statement);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
whereJsonSupersetOf(statement) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
'Json superset where clause not actually supported by MSSQL'
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
whereJsonSubsetOf(statement) {
|
|
568
|
+
throw new Error('Json subset where clause not actually supported by MSSQL');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
_getExtracts(statement, operator) {
|
|
572
|
+
const column = columnize_(
|
|
573
|
+
statement.column,
|
|
574
|
+
this.builder,
|
|
575
|
+
this.client,
|
|
576
|
+
this.bindingsHolder
|
|
577
|
+
);
|
|
578
|
+
return (
|
|
579
|
+
Array.isArray(statement.values) ? statement.values : [statement.values]
|
|
580
|
+
)
|
|
581
|
+
.map(function (value) {
|
|
582
|
+
return (
|
|
583
|
+
'JSON_VALUE(' +
|
|
584
|
+
column +
|
|
585
|
+
',' +
|
|
586
|
+
this.client.parameter(value, this.builder, this.bindingsHolder) +
|
|
587
|
+
')'
|
|
588
|
+
);
|
|
589
|
+
}, this)
|
|
590
|
+
.join(operator);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
onJsonPathEquals(clause) {
|
|
594
|
+
return this._onJsonPathEquals('JSON_VALUE', clause);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Set the QueryBuilder & QueryCompiler on the client object,
|
|
599
|
+
// in case anyone wants to modify things to suit their own purposes.
|
|
600
|
+
module.exports = QueryCompiler_MSSQL;
|