knex 0.21.13 → 0.21.17
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 +48 -0
- package/README.md +2 -2
- package/bin/cli.js +0 -1
- package/lib/config-resolver.js +61 -0
- package/lib/constants.js +8 -0
- package/lib/dialects/mssql/index.js +0 -1
- package/lib/dialects/mssql/query/compiler.js +184 -9
- package/lib/dialects/mysql/schema/tablecompiler.js +1 -1
- package/lib/dialects/sqlite3/schema/ddl.js +153 -1
- package/lib/dialects/sqlite3/schema/tablecompiler.js +73 -4
- package/lib/helpers.js +0 -2
- package/lib/interface.js +12 -8
- package/lib/knex.js +5 -46
- package/lib/logger.js +0 -2
- package/lib/migrate/MigrationGenerator.js +0 -1
- package/lib/query/builder.js +32 -11
- package/lib/query/compiler.js +9 -1
- package/lib/raw.js +2 -2
- package/lib/runner.js +3 -3
- package/lib/seed/Seeder.js +0 -1
- package/lib/transaction.js +2 -3
- package/lib/util/fs.js +1 -1
- package/lib/util/import-file.js +4 -1
- package/lib/util/is.js +20 -11
- package/package.json +19 -11
- package/scripts/docker-compose.yml +1 -1
- package/types/index.d.ts +178 -144
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Master (Unreleased)
|
|
2
2
|
|
|
3
|
+
# 0.21.17 - 30 January, 2021
|
|
4
|
+
|
|
5
|
+
### Bug fixes:
|
|
6
|
+
|
|
7
|
+
- SQLite: Fix SQLite foreign on delete when altering a table #4261
|
|
8
|
+
|
|
9
|
+
### New features:
|
|
10
|
+
|
|
11
|
+
- Add support for optimizer hints (see https://github.com/knex/documentation/pull/306 for documentation) #4243
|
|
12
|
+
|
|
13
|
+
# 0.21.16 - 17 January, 2021
|
|
14
|
+
|
|
15
|
+
### Bug fixes:
|
|
16
|
+
|
|
17
|
+
- MSSQL: Avoid passing unsupported pool param. Fixes node-mssql 7+ support #4236
|
|
18
|
+
|
|
19
|
+
# 0.21.15 - 26 December, 2020
|
|
20
|
+
|
|
21
|
+
### New features:
|
|
22
|
+
|
|
23
|
+
- SQLite: Add primary/foreign support on alterTable #4162
|
|
24
|
+
- SQLite: Add dropPrimary/dropForeign support on alterTable #4162
|
|
25
|
+
|
|
26
|
+
### Typings:
|
|
27
|
+
|
|
28
|
+
- Add "after" and "first" to columnBuilder types #3549 #4169
|
|
29
|
+
|
|
30
|
+
### Test / internal changes:
|
|
31
|
+
|
|
32
|
+
- Extract knex config resolution logic #4166
|
|
33
|
+
- Run CI using GitHub Actions #4168
|
|
34
|
+
- Add Node.js 15 to CI matrix #4173
|
|
35
|
+
|
|
36
|
+
# 0.21.14 - 18 December, 2020
|
|
37
|
+
|
|
38
|
+
### New features:
|
|
39
|
+
|
|
40
|
+
- MSSQL: support "returning" on inserts, updates and deletes on tables with triggers #4152
|
|
41
|
+
- Use esm import if package.json type is "module" #4158
|
|
42
|
+
|
|
43
|
+
### Bug fixes:
|
|
44
|
+
|
|
45
|
+
- Make sure query-response and query-error events contain _knexTxId #4160
|
|
46
|
+
|
|
47
|
+
### Test / internal changes:
|
|
48
|
+
|
|
49
|
+
- Improved integration test framework #4161
|
|
50
|
+
|
|
3
51
|
# 0.21.13 - 12 December, 2020
|
|
4
52
|
|
|
5
53
|
### New features:
|
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://npmjs.org/package/knex)
|
|
4
4
|
[](https://npmjs.org/package/knex)
|
|
5
|
-
|
|
6
|
-
[
|
|
6
|
+
[](https://coveralls.io/r/knex/knex?branch=master)
|
|
7
7
|
[](https://david-dm.org/knex/knex)
|
|
8
8
|
[](https://gitter.im/tgriesser/knex)
|
|
9
9
|
[](https://lgtm.com/projects/g/knex/knex/context:javascript)
|
package/bin/cli.js
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const Client = require('./client');
|
|
2
|
+
const { SUPPORTED_CLIENTS } = require('./constants');
|
|
3
|
+
|
|
4
|
+
const parseConnection = require('./util/parse-connection');
|
|
5
|
+
const { resolveClientNameWithAliases } = require('./helpers');
|
|
6
|
+
|
|
7
|
+
function resolveConfig(config) {
|
|
8
|
+
let Dialect;
|
|
9
|
+
let resolvedConfig;
|
|
10
|
+
|
|
11
|
+
// If config is a string, try to parse it
|
|
12
|
+
const parsedConfig =
|
|
13
|
+
typeof config === 'string'
|
|
14
|
+
? Object.assign(parseConnection(config), arguments[2])
|
|
15
|
+
: config;
|
|
16
|
+
|
|
17
|
+
// If user provided no relevant parameters, use generic client
|
|
18
|
+
if (
|
|
19
|
+
arguments.length === 0 ||
|
|
20
|
+
(!parsedConfig.client && !parsedConfig.dialect)
|
|
21
|
+
) {
|
|
22
|
+
Dialect = Client;
|
|
23
|
+
}
|
|
24
|
+
// If user provided Client constructor as a parameter, use it
|
|
25
|
+
else if (
|
|
26
|
+
typeof parsedConfig.client === 'function' &&
|
|
27
|
+
parsedConfig.client.prototype instanceof Client
|
|
28
|
+
) {
|
|
29
|
+
Dialect = parsedConfig.client;
|
|
30
|
+
}
|
|
31
|
+
// If neither applies, let's assume user specified name of a client or dialect as a string
|
|
32
|
+
else {
|
|
33
|
+
const clientName = parsedConfig.client || parsedConfig.dialect;
|
|
34
|
+
if (!SUPPORTED_CLIENTS.includes(clientName)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`knex: Unknown configuration option 'client' value ${clientName}. Note that it is case-sensitive, check documentation for supported values.`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const resolvedClientName = resolveClientNameWithAliases(clientName);
|
|
41
|
+
Dialect = require(`./dialects/${resolvedClientName}/index.js`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// If config connection parameter is passed as string, try to parse it
|
|
45
|
+
if (typeof parsedConfig.connection === 'string') {
|
|
46
|
+
resolvedConfig = Object.assign({}, parsedConfig, {
|
|
47
|
+
connection: parseConnection(parsedConfig.connection).connection,
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
resolvedConfig = Object.assign({}, parsedConfig);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
resolvedConfig,
|
|
55
|
+
Dialect,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
resolveConfig,
|
|
61
|
+
};
|
package/lib/constants.js
CHANGED
|
@@ -29,8 +29,16 @@ const POOL_CONFIG_OPTIONS = Object.freeze([
|
|
|
29
29
|
'Promise',
|
|
30
30
|
]);
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Regex that only matches comma's in strings that aren't wrapped in parentheses. Can be used to
|
|
34
|
+
* safely split strings like `id int, name string, body text, primary key (id, name)` into definition
|
|
35
|
+
* rows
|
|
36
|
+
*/
|
|
37
|
+
const COMMA_NO_PAREN_REGEX = /,[\s](?![^(]*\))/g;
|
|
38
|
+
|
|
32
39
|
module.exports = {
|
|
33
40
|
CLIENT_ALIASES,
|
|
34
41
|
SUPPORTED_CLIENTS,
|
|
35
42
|
POOL_CONFIG_OPTIONS,
|
|
43
|
+
COMMA_NO_PAREN_REGEX,
|
|
36
44
|
};
|
|
@@ -37,9 +37,70 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
37
37
|
return sql + compact(statements).join(' ');
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
//#region Insert
|
|
40
41
|
// Compiles an "insert" query, allowing for multiple
|
|
41
42
|
// inserts using a single query statement.
|
|
42
43
|
insert() {
|
|
44
|
+
if (this.single.options && this.single.options.includeTriggerModifications) {
|
|
45
|
+
return this.insertWithTriggers();
|
|
46
|
+
} else {
|
|
47
|
+
return this.standardInsert();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
insertWithTriggers() {
|
|
52
|
+
const insertValues = this.single.insert || [];
|
|
53
|
+
const { returning } = this.single;
|
|
54
|
+
let sql = this.with() + `${this._buildTempTable(returning)}insert into ${this.tableName} `;
|
|
55
|
+
const returningSql = returning
|
|
56
|
+
? this._returning('insert', returning, true) + ' '
|
|
57
|
+
: '';
|
|
58
|
+
|
|
59
|
+
if (Array.isArray(insertValues)) {
|
|
60
|
+
if (insertValues.length === 0) {
|
|
61
|
+
return '';
|
|
62
|
+
}
|
|
63
|
+
} else if (typeof insertValues === 'object' && isEmpty(insertValues)) {
|
|
64
|
+
return {
|
|
65
|
+
sql: sql + returningSql + this._emptyInsertValue + this._buildReturningSelect(returning),
|
|
66
|
+
returning,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const insertData = this._prepInsert(insertValues);
|
|
71
|
+
if (typeof insertData === 'string') {
|
|
72
|
+
sql += insertData;
|
|
73
|
+
} else {
|
|
74
|
+
if (insertData.columns.length) {
|
|
75
|
+
sql += `(${this.formatter.columnize(insertData.columns)}`;
|
|
76
|
+
sql += `) ${returningSql}values (`;
|
|
77
|
+
let i = -1;
|
|
78
|
+
while (++i < insertData.values.length) {
|
|
79
|
+
if (i !== 0) sql += '), (';
|
|
80
|
+
sql += this.formatter.parameterize(
|
|
81
|
+
insertData.values[i],
|
|
82
|
+
this.client.valueForUndefined
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
sql += ')';
|
|
86
|
+
} else if (insertValues.length === 1 && insertValues[0]) {
|
|
87
|
+
sql += returningSql + this._emptyInsertValue;
|
|
88
|
+
} else {
|
|
89
|
+
sql = '';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (returning) {
|
|
94
|
+
sql += this._buildReturningSelect(returning);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
sql,
|
|
99
|
+
returning,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
standardInsert() {
|
|
43
104
|
const insertValues = this.single.insert || [];
|
|
44
105
|
let sql = this.with() + `insert into ${this.tableName} `;
|
|
45
106
|
const { returning } = this.single;
|
|
@@ -80,14 +141,52 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
80
141
|
sql = '';
|
|
81
142
|
}
|
|
82
143
|
}
|
|
144
|
+
|
|
83
145
|
return {
|
|
84
146
|
sql,
|
|
85
147
|
returning,
|
|
86
148
|
};
|
|
87
149
|
}
|
|
150
|
+
//#endregion
|
|
88
151
|
|
|
152
|
+
//#region Update
|
|
89
153
|
// Compiles an `update` query, allowing for a return value.
|
|
90
154
|
update() {
|
|
155
|
+
if (this.single.options && this.single.options.includeTriggerModifications) {
|
|
156
|
+
return this.updateWithTriggers();
|
|
157
|
+
} else {
|
|
158
|
+
return this.standardUpdate();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
updateWithTriggers() {
|
|
163
|
+
const top = this.top();
|
|
164
|
+
const withSQL = this.with();
|
|
165
|
+
const updates = this._prepUpdate(this.single.update);
|
|
166
|
+
const join = this.join();
|
|
167
|
+
const where = this.where();
|
|
168
|
+
const order = this.order();
|
|
169
|
+
const { returning } = this.single;
|
|
170
|
+
const declaredTemp = this._buildTempTable(returning);
|
|
171
|
+
return {
|
|
172
|
+
sql:
|
|
173
|
+
withSQL +
|
|
174
|
+
declaredTemp +
|
|
175
|
+
`update ${top ? top + ' ' : ''}${this.tableName}` +
|
|
176
|
+
' set ' +
|
|
177
|
+
updates.join(', ') +
|
|
178
|
+
(returning ? ` ${this._returning('update', returning, true)}` : '') +
|
|
179
|
+
(join ? ` from ${this.tableName} ${join}` : '') +
|
|
180
|
+
(where ? ` ${where}` : '') +
|
|
181
|
+
(order ? ` ${order}` : '') +
|
|
182
|
+
(!returning
|
|
183
|
+
? this._returning('rowcount', '@@rowcount')
|
|
184
|
+
: this._buildReturningSelect(returning)),
|
|
185
|
+
returning: returning || '@@rowcount',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
standardUpdate() {
|
|
91
190
|
const top = this.top();
|
|
92
191
|
const withSQL = this.with();
|
|
93
192
|
const updates = this._prepUpdate(this.single.update);
|
|
@@ -105,13 +204,42 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
105
204
|
(join ? ` from ${this.tableName} ${join}` : '') +
|
|
106
205
|
(where ? ` ${where}` : '') +
|
|
107
206
|
(order ? ` ${order}` : '') +
|
|
108
|
-
(!returning
|
|
207
|
+
(!returning
|
|
208
|
+
? this._returning('rowcount', '@@rowcount')
|
|
209
|
+
: ""),
|
|
109
210
|
returning: returning || '@@rowcount',
|
|
110
211
|
};
|
|
111
212
|
}
|
|
213
|
+
//#endregion
|
|
112
214
|
|
|
215
|
+
//#region Delete
|
|
113
216
|
// Compiles a `delete` query.
|
|
114
217
|
del() {
|
|
218
|
+
if (this.single.options && this.single.options.includeTriggerModifications) {
|
|
219
|
+
return this.deleteWithTriggers();
|
|
220
|
+
} else {
|
|
221
|
+
return this.standardDelete();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
deleteWithTriggers() {
|
|
226
|
+
// Make sure tableName is processed by the formatter first.
|
|
227
|
+
const withSQL = this.with();
|
|
228
|
+
const { tableName } = this;
|
|
229
|
+
const wheres = this.where();
|
|
230
|
+
const { returning } = this.single;
|
|
231
|
+
return {
|
|
232
|
+
sql:
|
|
233
|
+
withSQL +
|
|
234
|
+
`${this._buildTempTable(returning)}delete from ${tableName}` +
|
|
235
|
+
(returning ? ` ${this._returning('del', returning, true)}` : '') +
|
|
236
|
+
(wheres ? ` ${wheres}` : '') +
|
|
237
|
+
(!returning ? this._returning('rowcount', '@@rowcount') : this._buildReturningSelect(returning)),
|
|
238
|
+
returning: returning || '@@rowcount',
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
standardDelete() {
|
|
115
243
|
// Make sure tableName is processed by the formatter first.
|
|
116
244
|
const withSQL = this.with();
|
|
117
245
|
const { tableName } = this;
|
|
@@ -123,16 +251,18 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
123
251
|
`delete from ${tableName}` +
|
|
124
252
|
(returning ? ` ${this._returning('del', returning)}` : '') +
|
|
125
253
|
(wheres ? ` ${wheres}` : '') +
|
|
126
|
-
(!returning ? this._returning('rowcount', '@@rowcount') :
|
|
254
|
+
(!returning ? this._returning('rowcount', '@@rowcount') : ""),
|
|
127
255
|
returning: returning || '@@rowcount',
|
|
128
256
|
};
|
|
129
257
|
}
|
|
258
|
+
//#endregion
|
|
130
259
|
|
|
131
260
|
// Compiles the columns in the query, specifying if an item was distinct.
|
|
132
261
|
columns() {
|
|
133
262
|
let distinctClause = '';
|
|
134
263
|
if (this.onlyUnions()) return '';
|
|
135
264
|
const top = this.top();
|
|
265
|
+
const hints = this._hintComments()
|
|
136
266
|
const columns = this.grouped.columns || [];
|
|
137
267
|
let i = -1,
|
|
138
268
|
sql = [];
|
|
@@ -156,29 +286,75 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
156
286
|
if (sql.length === 0) sql = ['*'];
|
|
157
287
|
|
|
158
288
|
return (
|
|
159
|
-
`select ${distinctClause}` +
|
|
289
|
+
`select ${hints}${distinctClause}` +
|
|
160
290
|
(top ? top + ' ' : '') +
|
|
161
291
|
sql.join(', ') +
|
|
162
292
|
(this.tableName ? ` from ${this.tableName}` : '')
|
|
163
293
|
);
|
|
164
294
|
}
|
|
165
295
|
|
|
166
|
-
_returning(method, value) {
|
|
296
|
+
_returning(method, value, withTrigger) {
|
|
167
297
|
switch (method) {
|
|
168
298
|
case 'update':
|
|
169
299
|
case 'insert':
|
|
170
300
|
return value
|
|
171
|
-
? `output ${this.formatter.columnizeWithPrefix('inserted.', value)}`
|
|
301
|
+
? `output ${this.formatter.columnizeWithPrefix('inserted.', value)}${withTrigger ? " into #out" : ""}`
|
|
172
302
|
: '';
|
|
173
303
|
case 'del':
|
|
174
304
|
return value
|
|
175
|
-
? `output ${this.formatter.columnizeWithPrefix('deleted.', value)}`
|
|
305
|
+
? `output ${this.formatter.columnizeWithPrefix('deleted.', value)}${withTrigger ? " into #out" : ""}`
|
|
176
306
|
: '';
|
|
177
307
|
case 'rowcount':
|
|
178
308
|
return value ? ';select @@rowcount' : '';
|
|
179
309
|
}
|
|
180
310
|
}
|
|
181
311
|
|
|
312
|
+
_buildTempTable(values) {
|
|
313
|
+
// If value is nothing then return an empty string
|
|
314
|
+
if (values && values.length > 0) {
|
|
315
|
+
let selections = "";
|
|
316
|
+
|
|
317
|
+
// Build values that will be returned from this procedure
|
|
318
|
+
if (Array.isArray(values)) {
|
|
319
|
+
selections = values.map(value => `[t].${this.formatter.columnize(value)}`).join(",");
|
|
320
|
+
} else {
|
|
321
|
+
selections = `[t].${this.formatter.columnize(values)}`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Force #out to be correctly populated with the correct column structure.
|
|
325
|
+
let sql = `select top(0) ${selections} into #out `;
|
|
326
|
+
sql += `from ${this.tableName} as t `;
|
|
327
|
+
sql += `left join ${this.tableName} on 0=1;`;
|
|
328
|
+
|
|
329
|
+
return sql;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return '';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
_buildReturningSelect(values) {
|
|
336
|
+
// If value is nothing then return an empty string
|
|
337
|
+
if (values && values.length > 0) {
|
|
338
|
+
let selections = "";
|
|
339
|
+
|
|
340
|
+
// Build columns to return
|
|
341
|
+
if (Array.isArray(values)) {
|
|
342
|
+
selections = values.map(value => `${this.formatter.columnize(value)}`).join(",");
|
|
343
|
+
} else {
|
|
344
|
+
selections = this.formatter.columnize(values);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Get the returned values
|
|
348
|
+
let sql = `; select ${selections} from #out; `
|
|
349
|
+
// Drop the temp table to prevent memory leaks
|
|
350
|
+
sql += `drop table #out;`;
|
|
351
|
+
|
|
352
|
+
return sql;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return '';
|
|
356
|
+
}
|
|
357
|
+
|
|
182
358
|
// Compiles a `truncate` query.
|
|
183
359
|
truncate() {
|
|
184
360
|
return `truncate table ${this.tableName}`;
|
|
@@ -252,9 +428,8 @@ class QueryCompiler_MSSQL extends QueryCompiler {
|
|
|
252
428
|
const noLimit = !this.single.limit && this.single.limit !== 0;
|
|
253
429
|
const noOffset = !this.single.offset;
|
|
254
430
|
if (noOffset) return '';
|
|
255
|
-
let offset = `offset ${
|
|
256
|
-
|
|
257
|
-
} rows`;
|
|
431
|
+
let offset = `offset ${noOffset ? '0' : this.formatter.parameter(this.single.offset)
|
|
432
|
+
} rows`;
|
|
258
433
|
if (!noLimit) {
|
|
259
434
|
offset += ` fetch next ${this.formatter.parameter(
|
|
260
435
|
this.single.limit
|
|
@@ -14,6 +14,7 @@ const isEmpty = require('lodash/isEmpty');
|
|
|
14
14
|
const negate = require('lodash/negate');
|
|
15
15
|
const omit = require('lodash/omit');
|
|
16
16
|
const uniqueId = require('lodash/uniqueId');
|
|
17
|
+
const { COMMA_NO_PAREN_REGEX } = require('../../../constants');
|
|
17
18
|
|
|
18
19
|
// So altering the schema in SQLite3 is a major pain.
|
|
19
20
|
// We have our own object to deal with the renaming and altering the types
|
|
@@ -328,7 +329,7 @@ assign(SQLite3_DDL.prototype, {
|
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
const updatedDefs = defs
|
|
331
|
-
.split(
|
|
332
|
+
.split(COMMA_NO_PAREN_REGEX)
|
|
332
333
|
.map((line) => line.trim())
|
|
333
334
|
.filter((defLine) => {
|
|
334
335
|
if (
|
|
@@ -359,6 +360,157 @@ assign(SQLite3_DDL.prototype, {
|
|
|
359
360
|
);
|
|
360
361
|
},
|
|
361
362
|
|
|
363
|
+
dropPrimary: async function (constraintName) {
|
|
364
|
+
return this.client.transaction(
|
|
365
|
+
async (trx) => {
|
|
366
|
+
this.trx = trx;
|
|
367
|
+
|
|
368
|
+
const sql = await this.getTableSql();
|
|
369
|
+
|
|
370
|
+
const createTable = sql[0];
|
|
371
|
+
|
|
372
|
+
const oneLineSql = createTable.sql.replace(/\s+/g, ' ');
|
|
373
|
+
const matched = oneLineSql.match(/^CREATE TABLE\s+(\S+)\s*\((.*)\)/);
|
|
374
|
+
|
|
375
|
+
const defs = matched[2];
|
|
376
|
+
|
|
377
|
+
if (!defs) {
|
|
378
|
+
throw new Error('No column definitions in this statement!');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const updatedDefs = defs
|
|
382
|
+
.split(COMMA_NO_PAREN_REGEX)
|
|
383
|
+
.map((line) => line.trim())
|
|
384
|
+
.filter((defLine) => {
|
|
385
|
+
if (
|
|
386
|
+
defLine.startsWith('constraint') === false &&
|
|
387
|
+
defLine.includes('primary key') === false
|
|
388
|
+
)
|
|
389
|
+
return true;
|
|
390
|
+
|
|
391
|
+
if (constraintName) {
|
|
392
|
+
if (defLine.includes(constraintName)) return false;
|
|
393
|
+
return true;
|
|
394
|
+
} else {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
})
|
|
398
|
+
.join(', ');
|
|
399
|
+
|
|
400
|
+
const newSql = oneLineSql.replace(defs, updatedDefs);
|
|
401
|
+
|
|
402
|
+
return this.reinsertMapped(createTable, newSql, (row) => {
|
|
403
|
+
return row;
|
|
404
|
+
});
|
|
405
|
+
},
|
|
406
|
+
{ connection: this.connection }
|
|
407
|
+
);
|
|
408
|
+
},
|
|
409
|
+
|
|
410
|
+
primary: async function (columns, constraintName) {
|
|
411
|
+
return this.client.transaction(
|
|
412
|
+
async (trx) => {
|
|
413
|
+
this.trx = trx;
|
|
414
|
+
|
|
415
|
+
const tableInfo = (await this.getTableSql())[0];
|
|
416
|
+
const currentSQL = tableInfo.sql;
|
|
417
|
+
|
|
418
|
+
const oneLineSQL = currentSQL.replace(/\s+/g, ' ');
|
|
419
|
+
const matched = oneLineSQL.match(/^CREATE TABLE\s+(\S+)\s*\((.*)\)/);
|
|
420
|
+
|
|
421
|
+
const columnDefinitions = matched[2];
|
|
422
|
+
|
|
423
|
+
if (!columnDefinitions) {
|
|
424
|
+
throw new Error('No column definitions in this statement!');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const primaryKeyDef = `primary key(${columns.join(',')})`;
|
|
428
|
+
const constraintDef = constraintName
|
|
429
|
+
? `constraint ${constraintName} ${primaryKeyDef}`
|
|
430
|
+
: primaryKeyDef;
|
|
431
|
+
|
|
432
|
+
const newColumnDefinitions = [
|
|
433
|
+
...columnDefinitions
|
|
434
|
+
.split(COMMA_NO_PAREN_REGEX)
|
|
435
|
+
.map((line) => line.trim())
|
|
436
|
+
.filter((line) => line.startsWith('primary') === false)
|
|
437
|
+
.map((line) => line.replace(/primary key/i, '')),
|
|
438
|
+
constraintDef,
|
|
439
|
+
].join(', ');
|
|
440
|
+
|
|
441
|
+
const newSQL = oneLineSQL.replace(
|
|
442
|
+
columnDefinitions,
|
|
443
|
+
newColumnDefinitions
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
return this.reinsertMapped(tableInfo, newSQL, (row) => {
|
|
447
|
+
return row;
|
|
448
|
+
});
|
|
449
|
+
},
|
|
450
|
+
{ connection: this.connection }
|
|
451
|
+
);
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
foreign: async function (foreignInfo) {
|
|
455
|
+
return this.client.transaction(
|
|
456
|
+
async (trx) => {
|
|
457
|
+
this.trx = trx;
|
|
458
|
+
|
|
459
|
+
const tableInfo = (await this.getTableSql())[0];
|
|
460
|
+
const currentSQL = tableInfo.sql;
|
|
461
|
+
|
|
462
|
+
const oneLineSQL = currentSQL.replace(/\s+/g, ' ');
|
|
463
|
+
const matched = oneLineSQL.match(/^CREATE TABLE\s+(\S+)\s*\((.*)\)/);
|
|
464
|
+
|
|
465
|
+
const columnDefinitions = matched[2];
|
|
466
|
+
|
|
467
|
+
if (!columnDefinitions) {
|
|
468
|
+
throw new Error('No column definitions in this statement!');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const newColumnDefinitions = columnDefinitions
|
|
472
|
+
.split(COMMA_NO_PAREN_REGEX)
|
|
473
|
+
.map((line) => line.trim());
|
|
474
|
+
|
|
475
|
+
let newForeignSQL = '';
|
|
476
|
+
|
|
477
|
+
if (foreignInfo.keyName) {
|
|
478
|
+
newForeignSQL += `CONSTRAINT ${foreignInfo.keyName}`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
newForeignSQL += ` FOREIGN KEY (${foreignInfo.column.join(', ')}) `;
|
|
482
|
+
newForeignSQL += ` REFERENCES ${foreignInfo.inTable} (${foreignInfo.references})`;
|
|
483
|
+
|
|
484
|
+
if (foreignInfo.onUpdate) {
|
|
485
|
+
newForeignSQL += ` ON UPDATE ${foreignInfo.onUpdate}`;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (foreignInfo.onDelete) {
|
|
489
|
+
newForeignSQL += ` ON DELETE ${foreignInfo.onDelete}`;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
newColumnDefinitions.push(newForeignSQL);
|
|
493
|
+
|
|
494
|
+
const newSQL = oneLineSQL.replace(
|
|
495
|
+
columnDefinitions,
|
|
496
|
+
newColumnDefinitions.join(', ')
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
return this.reinsertMapped(tableInfo, newSQL, (row) => {
|
|
500
|
+
return row;
|
|
501
|
+
});
|
|
502
|
+
},
|
|
503
|
+
{ connection: this.connection }
|
|
504
|
+
);
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* @fixme
|
|
509
|
+
*
|
|
510
|
+
* There's a bunch of overlap between renameColumn/dropColumn/dropForeign/primary/foreign.
|
|
511
|
+
* It'll be helpful to refactor this file heavily to combine/optimize some of these calls
|
|
512
|
+
*/
|
|
513
|
+
|
|
362
514
|
reinsertMapped(createTable, newSql, mapRow) {
|
|
363
515
|
return Promise.resolve()
|
|
364
516
|
.then(() => this.createTempTable(createTable))
|