velocious 1.0.613 → 1.0.614
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 +1 -1
- package/build/database/drivers/base.js +19 -0
- package/build/database/drivers/mysql/index.js +12 -0
- package/build/database/drivers/mysql/sql/alter-table.js +14 -2
- package/build/database/drivers/pgsql/index.js +13 -0
- package/build/database/migration/change-table.js +324 -0
- package/build/database/migration/index.js +197 -13
- package/build/src/database/drivers/base.d.ts +13 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +18 -1
- package/build/src/database/drivers/mysql/index.d.ts +10 -0
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +11 -1
- package/build/src/database/drivers/mysql/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/alter-table.js +14 -3
- package/build/src/database/drivers/pgsql/index.d.ts +11 -0
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +12 -1
- package/build/src/database/migration/change-table.d.ts +403 -0
- package/build/src/database/migration/change-table.d.ts.map +1 -0
- package/build/src/database/migration/change-table.js +286 -0
- package/build/src/database/migration/index.d.ts +46 -38
- package/build/src/database/migration/index.d.ts.map +1 -1
- package/build/src/database/migration/index.js +179 -14
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/database/drivers/base.js +19 -0
- package/src/database/drivers/mysql/index.js +12 -0
- package/src/database/drivers/mysql/sql/alter-table.js +14 -2
- package/src/database/drivers/pgsql/index.js +13 -0
- package/src/database/migration/change-table.js +324 -0
- package/src/database/migration/index.js +197 -13
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Connection-scoped advisory locks with automatic cleanup before pooled connections are reused or closed (see [docs/advisory-locks.md](docs/advisory-locks.md))
|
|
8
8
|
* Built-in record auditing for model lifecycle changes (see [docs/auditing.md](docs/auditing.md))
|
|
9
9
|
* Declarative state machines for models, with typed event methods generated into the base model (see [docs/state-machine.md](docs/state-machine.md))
|
|
10
|
-
* Migrations for schema changes and UTC datetime storage (see [docs/database-migrations.md](docs/database-migrations.md))
|
|
10
|
+
* Migrations for schema changes and UTC datetime storage, including recorded `changeTable` batches that combine operations into one `ALTER` on bulk-capable drivers (see [docs/database-migrations.md](docs/database-migrations.md) and [docs/change-table.md](docs/change-table.md))
|
|
11
11
|
* Tenant-selected base-model and structure generation with one immutable, fail-closed physical database context; tenant-only model metadata initializes only after that context is active (see [docs/tenant-selected-database-generation.md](docs/tenant-selected-database-generation.md))
|
|
12
12
|
* Read-only tenant migration deploy preflight with stable JSON output and fail-closed ledger reads (see [docs/tenant-migration-deploy-preflight.md](docs/tenant-migration-deploy-preflight.md))
|
|
13
13
|
* External packages (engines) that contribute data models, frontend-model resources and migrations to a consuming app (see [docs/packages.md](docs/packages.md))
|
|
@@ -908,6 +908,25 @@ export default class VelociousDatabaseDriversBase {
|
|
|
908
908
|
throw new Error("'type' not implemented")
|
|
909
909
|
}
|
|
910
910
|
|
|
911
|
+
/**
|
|
912
|
+
* Whether this driver can combine unrelated alter-table operations into a
|
|
913
|
+
* single `ALTER TABLE` statement (Rails' `supports_bulk_alter`).
|
|
914
|
+
* @returns {boolean} - Whether bulk alter is supported.
|
|
915
|
+
*/
|
|
916
|
+
supportsBulkAlter() {
|
|
917
|
+
return false
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Whether a bulk `ALTER TABLE` statement can also carry `ADD INDEX` clauses.
|
|
922
|
+
* Only drivers that support this keep index adds inside the combined batch;
|
|
923
|
+
* the rest execute each index as its own statement.
|
|
924
|
+
* @returns {boolean} - Whether indexes can be added inside a bulk alter.
|
|
925
|
+
*/
|
|
926
|
+
supportsBulkAlterIndexes() {
|
|
927
|
+
return false
|
|
928
|
+
}
|
|
929
|
+
|
|
911
930
|
/**
|
|
912
931
|
* Runs insert.
|
|
913
932
|
* @param {InsertSqlArgsType} args - Options object.
|
|
@@ -345,6 +345,18 @@ export default class VelociousDatabaseDriversMysql extends Base{
|
|
|
345
345
|
*/
|
|
346
346
|
getType() { return "mysql" }
|
|
347
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Whether this driver supports combining operations into one bulk `ALTER`.
|
|
350
|
+
* @returns {boolean} - Whether bulk alter is supported.
|
|
351
|
+
*/
|
|
352
|
+
supportsBulkAlter() { return true }
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Whether the bulk `ALTER` can also carry `ADD INDEX` clauses.
|
|
356
|
+
* @returns {boolean} - Whether indexes can be added inside a bulk alter.
|
|
357
|
+
*/
|
|
358
|
+
supportsBulkAlterIndexes() { return true }
|
|
359
|
+
|
|
348
360
|
/**
|
|
349
361
|
* Runs retryable database error.
|
|
350
362
|
* @param {Error} error - Error instance.
|
|
@@ -26,13 +26,25 @@ export default class VelociousDatabaseConnectionDriversMysqlSqlAlterTable extend
|
|
|
26
26
|
const indexes = this.tableData.getIndexes()
|
|
27
27
|
|
|
28
28
|
if (indexes.length === 0) return sqls
|
|
29
|
-
if (sqls.length
|
|
29
|
+
if (sqls.length > 1) throw new Error("Expected one MySQL ALTER TABLE statement when adding indexes")
|
|
30
30
|
|
|
31
31
|
const options = this.getOptions()
|
|
32
32
|
let sql = sqls[0]
|
|
33
|
+
let needsIndexSeparator = true
|
|
34
|
+
|
|
35
|
+
if (sql === undefined) {
|
|
36
|
+
sql = `ALTER TABLE ${options.quoteTableName(this.tableData.getName())} `
|
|
37
|
+
needsIndexSeparator = false
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
for (const index of indexes) {
|
|
35
|
-
|
|
41
|
+
if (needsIndexSeparator) {
|
|
42
|
+
sql += ", "
|
|
43
|
+
} else {
|
|
44
|
+
needsIndexSeparator = true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
sql += "ADD"
|
|
36
48
|
|
|
37
49
|
if (index.getUnique()) sql += " UNIQUE"
|
|
38
50
|
|
|
@@ -203,6 +203,19 @@ export default class VelociousDatabaseDriversPgsql extends Base{
|
|
|
203
203
|
|
|
204
204
|
getType() { return "pgsql" }
|
|
205
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Whether this driver supports combining operations into one bulk `ALTER`.
|
|
208
|
+
* @returns {boolean} - Whether bulk alter is supported.
|
|
209
|
+
*/
|
|
210
|
+
supportsBulkAlter() { return true }
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Whether the bulk `ALTER` can also carry `ADD INDEX` clauses. PostgreSQL's
|
|
214
|
+
* `ALTER TABLE` cannot express index creation, so indexes stay standalone.
|
|
215
|
+
* @returns {boolean} - Whether indexes can be added inside a bulk alter.
|
|
216
|
+
*/
|
|
217
|
+
supportsBulkAlterIndexes() { return false }
|
|
218
|
+
|
|
206
219
|
/**
|
|
207
220
|
* Runs query actual.
|
|
208
221
|
* @param {string} sql - SQL string.
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ChangeTableAddIndexArgsType type.
|
|
5
|
+
* @typedef {object} ChangeTableAddIndexArgsType
|
|
6
|
+
* @property {boolean} [ifNotExists] - Skip creation if the index already exists.
|
|
7
|
+
* @property {string} [name] - Explicit index name to use.
|
|
8
|
+
* @property {boolean} [unique] - Whether the index should be unique.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ChangeTableRemoveIndexArgsType type.
|
|
13
|
+
* @typedef {object} ChangeTableRemoveIndexArgsType
|
|
14
|
+
* @property {string} [name] - Explicit index name to remove.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* ChangeTableRemoveReferenceArgsType type.
|
|
19
|
+
* @typedef {object} ChangeTableRemoveReferenceArgsType
|
|
20
|
+
* @property {string} [columnName] - Override the derived reference column name.
|
|
21
|
+
* @property {string} [indexName] - Explicit generated index name to remove.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* ChangeTableAddColumnOperationType type.
|
|
26
|
+
* @typedef {object} ChangeTableAddColumnOperationType
|
|
27
|
+
* @property {"addColumn"} type - Operation type.
|
|
28
|
+
* @property {string} columnName - Column name.
|
|
29
|
+
* @property {string} columnType - Column type.
|
|
30
|
+
* @property {import("../table-data/table-column.js").TableColumnArgsType | undefined} args - Column args.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* ChangeTableRemoveColumnOperationType type.
|
|
35
|
+
* @typedef {object} ChangeTableRemoveColumnOperationType
|
|
36
|
+
* @property {"removeColumn"} type - Operation type.
|
|
37
|
+
* @property {string} columnName - Column name.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* ChangeTableAddIndexOperationType type.
|
|
42
|
+
* @typedef {object} ChangeTableAddIndexOperationType
|
|
43
|
+
* @property {"addIndex"} type - Operation type.
|
|
44
|
+
* @property {Array<string | import("../table-data/table-column.js").default>} columns - Columns to index.
|
|
45
|
+
* @property {ChangeTableAddIndexArgsType | undefined} args - Index args.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* ChangeTableRemoveIndexOperationType type.
|
|
50
|
+
* @typedef {object} ChangeTableRemoveIndexOperationType
|
|
51
|
+
* @property {"removeIndex"} type - Operation type.
|
|
52
|
+
* @property {string | Array<string | import("../table-data/table-column.js").default>} nameOrColumns - Index name or columns.
|
|
53
|
+
* @property {ChangeTableRemoveIndexArgsType | undefined} args - Index args.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* ChangeTableAddReferenceOperationType type.
|
|
58
|
+
* @typedef {object} ChangeTableAddReferenceOperationType
|
|
59
|
+
* @property {"addReference"} type - Operation type.
|
|
60
|
+
* @property {string} referenceName - Reference name.
|
|
61
|
+
* @property {object | undefined} args - Reference args.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* ChangeTableRemoveReferenceOperationType type.
|
|
66
|
+
* @typedef {object} ChangeTableRemoveReferenceOperationType
|
|
67
|
+
* @property {"removeReference"} type - Operation type.
|
|
68
|
+
* @property {string} referenceName - Reference name.
|
|
69
|
+
* @property {ChangeTableRemoveReferenceArgsType | undefined} args - Reference args.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* ChangeTableRenameColumnOperationType type.
|
|
74
|
+
* @typedef {object} ChangeTableRenameColumnOperationType
|
|
75
|
+
* @property {"renameColumn"} type - Operation type.
|
|
76
|
+
* @property {string} oldColumnName - Previous column name.
|
|
77
|
+
* @property {string} newColumnName - New column name.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* ChangeTableChangeColumnNullOperationType type.
|
|
82
|
+
* @typedef {object} ChangeTableChangeColumnNullOperationType
|
|
83
|
+
* @property {"changeColumnNull"} type - Operation type.
|
|
84
|
+
* @property {string} columnName - Column name.
|
|
85
|
+
* @property {boolean} nullable - Whether the column becomes nullable.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* ChangeTableOperationType type.
|
|
90
|
+
* @typedef {ChangeTableAddColumnOperationType | ChangeTableRemoveColumnOperationType | ChangeTableAddIndexOperationType | ChangeTableRemoveIndexOperationType | ChangeTableAddReferenceOperationType | ChangeTableRemoveReferenceOperationType | ChangeTableRenameColumnOperationType | ChangeTableChangeColumnNullOperationType} ChangeTableOperationType
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Table-scoped recorder used by `migration.changeTable`. Each call records a
|
|
95
|
+
* single DDL operation synchronously; `changeTable` replays them after the
|
|
96
|
+
* callback completes so a failed callback executes zero recorded DDL.
|
|
97
|
+
*/
|
|
98
|
+
export default class VelociousDatabaseMigrationChangeTable {
|
|
99
|
+
/**
|
|
100
|
+
* Operations.
|
|
101
|
+
* @type {ChangeTableOperationType[]} */
|
|
102
|
+
_operations = []
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Runs constructor.
|
|
106
|
+
* @param {object} args - Options object.
|
|
107
|
+
* @param {string} args.tableName - Table name.
|
|
108
|
+
*/
|
|
109
|
+
constructor({tableName}) {
|
|
110
|
+
if (!tableName) throw new Error(`Invalid table name: ${tableName}`)
|
|
111
|
+
|
|
112
|
+
this._tableName = tableName
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Runs get table name.
|
|
117
|
+
* @returns {string} - The table name.
|
|
118
|
+
*/
|
|
119
|
+
getTableName() { return this._tableName }
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Runs get operations.
|
|
123
|
+
* @returns {ChangeTableOperationType[]} - The recorded operations.
|
|
124
|
+
*/
|
|
125
|
+
getOperations() { return this._operations }
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Records a new column.
|
|
129
|
+
* @param {string} name - Column name.
|
|
130
|
+
* @param {string} type - Column type.
|
|
131
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
132
|
+
* @returns {void} - No return value.
|
|
133
|
+
*/
|
|
134
|
+
column(name, type, args) {
|
|
135
|
+
this._operations.push({type: "addColumn", columnName: name, columnType: type, args})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Records a bigint column.
|
|
140
|
+
* @param {string} name - Column name.
|
|
141
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
142
|
+
* @returns {void} - No return value.
|
|
143
|
+
*/
|
|
144
|
+
bigint(name, args) { this.column(name, "bigint", args) }
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Records a blob column.
|
|
148
|
+
* @param {string} name - Column name.
|
|
149
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
150
|
+
* @returns {void} - No return value.
|
|
151
|
+
*/
|
|
152
|
+
blob(name, args) { this.column(name, "blob", args) }
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Records a boolean column.
|
|
156
|
+
* @param {string} name - Column name.
|
|
157
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
158
|
+
* @returns {void} - No return value.
|
|
159
|
+
*/
|
|
160
|
+
boolean(name, args) { this.column(name, "boolean", args) }
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Records a datetime column.
|
|
164
|
+
* @param {string} name - Column name.
|
|
165
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
166
|
+
* @returns {void} - No return value.
|
|
167
|
+
*/
|
|
168
|
+
datetime(name, args) { this.column(name, "datetime", args) }
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Records a decimal column.
|
|
172
|
+
* @param {string} name - Column name.
|
|
173
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
174
|
+
* @returns {void} - No return value.
|
|
175
|
+
*/
|
|
176
|
+
decimal(name, args) { this.column(name, "decimal", args) }
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Records an integer column.
|
|
180
|
+
* @param {string} name - Column name.
|
|
181
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
182
|
+
* @returns {void} - No return value.
|
|
183
|
+
*/
|
|
184
|
+
integer(name, args) { this.column(name, "integer", args) }
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Records a json column.
|
|
188
|
+
* @param {string} name - Column name.
|
|
189
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
190
|
+
* @returns {void} - No return value.
|
|
191
|
+
*/
|
|
192
|
+
json(name, args) { this.column(name, "json", args) }
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Records a string column.
|
|
196
|
+
* @param {string} name - Column name.
|
|
197
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
198
|
+
* @returns {void} - No return value.
|
|
199
|
+
*/
|
|
200
|
+
string(name, args) { this.column(name, "string", args) }
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Records a text column.
|
|
204
|
+
* @param {string} name - Column name.
|
|
205
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
206
|
+
* @returns {void} - No return value.
|
|
207
|
+
*/
|
|
208
|
+
text(name, args) { this.column(name, "text", args) }
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Records a tinyint column.
|
|
212
|
+
* @param {string} name - Column name.
|
|
213
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
214
|
+
* @returns {void} - No return value.
|
|
215
|
+
*/
|
|
216
|
+
tinyint(name, args) { this.column(name, "tinyint", args) }
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Records a uuid column.
|
|
220
|
+
* @param {string} name - Column name.
|
|
221
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
222
|
+
* @returns {void} - No return value.
|
|
223
|
+
*/
|
|
224
|
+
uuid(name, args) { this.column(name, "uuid", args) }
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Records created_at and updated_at datetime columns.
|
|
228
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
229
|
+
* @returns {void} - No return value.
|
|
230
|
+
*/
|
|
231
|
+
timestamps(args) {
|
|
232
|
+
this.datetime("created_at", args)
|
|
233
|
+
this.datetime("updated_at", args)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Records a new index.
|
|
238
|
+
* @param {string | Array<string | import("../table-data/table-column.js").default>} columns - Column name or array of column names.
|
|
239
|
+
* @param {ChangeTableAddIndexArgsType} [args] - Options object.
|
|
240
|
+
* @returns {void} - No return value.
|
|
241
|
+
*/
|
|
242
|
+
index(columns, args) {
|
|
243
|
+
const normalizedColumns = typeof columns == "string" ? [columns] : columns
|
|
244
|
+
|
|
245
|
+
this._operations.push({type: "addIndex", columns: normalizedColumns, args})
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Records a reference column, index, and optional foreign key.
|
|
250
|
+
* @param {string} name - Reference name.
|
|
251
|
+
* @param {object} [args] - Options object.
|
|
252
|
+
* @returns {void} - No return value.
|
|
253
|
+
*/
|
|
254
|
+
references(name, args) {
|
|
255
|
+
this._operations.push({type: "addReference", referenceName: name, args})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Alias for {@link references}.
|
|
260
|
+
* @param {string} name - Reference name.
|
|
261
|
+
* @param {object} [args] - Options object.
|
|
262
|
+
* @returns {void} - No return value.
|
|
263
|
+
*/
|
|
264
|
+
belongsTo(name, args) { this.references(name, args) }
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Records removal of one or more columns.
|
|
268
|
+
* @param {string[]} columnNames - Column names to remove.
|
|
269
|
+
* @returns {void} - No return value.
|
|
270
|
+
*/
|
|
271
|
+
remove(...columnNames) {
|
|
272
|
+
for (const columnName of columnNames) {
|
|
273
|
+
this._operations.push({type: "removeColumn", columnName})
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Records removal of an index.
|
|
279
|
+
* @param {string | Array<string | import("../table-data/table-column.js").default>} nameOrColumns - Index name or columns.
|
|
280
|
+
* @param {ChangeTableRemoveIndexArgsType} [args] - Options object.
|
|
281
|
+
* @returns {void} - No return value.
|
|
282
|
+
*/
|
|
283
|
+
removeIndex(nameOrColumns, args) {
|
|
284
|
+
this._operations.push({type: "removeIndex", nameOrColumns, args})
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Records removal of a reference column and its generated index and foreign keys.
|
|
289
|
+
* @param {string} name - Reference name.
|
|
290
|
+
* @param {ChangeTableRemoveReferenceArgsType} [args] - Options object.
|
|
291
|
+
* @returns {void} - No return value.
|
|
292
|
+
*/
|
|
293
|
+
removeReferences(name, args) {
|
|
294
|
+
this._operations.push({type: "removeReference", referenceName: name, args})
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Records removal of the created_at and updated_at columns.
|
|
299
|
+
* @returns {void} - No return value.
|
|
300
|
+
*/
|
|
301
|
+
removeTimestamps() {
|
|
302
|
+
this.remove("created_at", "updated_at")
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Records a column rename.
|
|
307
|
+
* @param {string} oldColumnName - Previous column name.
|
|
308
|
+
* @param {string} newColumnName - New column name.
|
|
309
|
+
* @returns {void} - No return value.
|
|
310
|
+
*/
|
|
311
|
+
rename(oldColumnName, newColumnName) {
|
|
312
|
+
this._operations.push({type: "renameColumn", oldColumnName, newColumnName})
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Records a change to a column's nullability.
|
|
317
|
+
* @param {string} columnName - Column name.
|
|
318
|
+
* @param {boolean} nullable - Whether the column becomes nullable.
|
|
319
|
+
* @returns {void} - No return value.
|
|
320
|
+
*/
|
|
321
|
+
changeNull(columnName, nullable) {
|
|
322
|
+
this._operations.push({type: "changeColumnNull", columnName, nullable})
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* AddColumnArgsType type.
|
|
5
|
-
* @typedef {object} AddColumnArgsType
|
|
6
|
-
* @property {ReturnType<typeof JSON.parse>} [default] - Default value for the column.
|
|
7
|
-
* @property {object} [foreignKey] - Foreign key definition for the column.
|
|
8
|
-
* @property {boolean | {unique: boolean}} [index] - Whether to add an index (optionally unique).
|
|
9
|
-
* @property {number} [limit] - Alias for maxLength (varchar length limit) on string-like columns.
|
|
10
|
-
* @property {number} [maxLength] - Maximum length for string-like columns (e.g. varchar length).
|
|
11
|
-
* @property {boolean} [null] - Whether the column allows null values.
|
|
12
|
-
* @property {boolean} [primaryKey] - Whether the column is a primary key.
|
|
13
|
-
* @property {boolean} [unique] - Whether the column enforces uniqueness.
|
|
14
|
-
*/
|
|
15
3
|
/**
|
|
16
4
|
* CreateTableIdArgsType type.
|
|
17
5
|
* @typedef {object} CreateTableIdArgsType
|
|
@@ -39,8 +27,11 @@
|
|
|
39
27
|
import { convertLegacyDateValueToUtcStorage } from "../datetime-storage.js"
|
|
40
28
|
import * as inflection from "inflection"
|
|
41
29
|
import restArgsError from "../../utils/rest-args-error.js"
|
|
30
|
+
import ChangeTable from "./change-table.js"
|
|
42
31
|
import CreateIndexBase from "../query/create-index-base.js"
|
|
32
|
+
import TableColumn from "../table-data/table-column.js"
|
|
43
33
|
import TableData from "../table-data/index.js"
|
|
34
|
+
import TableIndex from "../table-data/table-index.js"
|
|
44
35
|
class NotImplementedError extends Error {}
|
|
45
36
|
|
|
46
37
|
export {NotImplementedError}
|
|
@@ -118,7 +109,7 @@ export default class VelociousDatabaseMigration {
|
|
|
118
109
|
* @param {string} tableName - Table name.
|
|
119
110
|
* @param {string} columnName - Column name.
|
|
120
111
|
* @param {string} columnType - Column type.
|
|
121
|
-
* @param {
|
|
112
|
+
* @param {import("../table-data/table-column.js").TableColumnArgsType} [args] - Options object.
|
|
122
113
|
* @returns {Promise<void>} - Resolves when complete.
|
|
123
114
|
*/
|
|
124
115
|
async addColumn(tableName, columnName, columnType, args) {
|
|
@@ -687,6 +678,199 @@ export default class VelociousDatabaseMigration {
|
|
|
687
678
|
}
|
|
688
679
|
}
|
|
689
680
|
|
|
681
|
+
/**
|
|
682
|
+
* ChangeTableArgsType type.
|
|
683
|
+
* @typedef {object} ChangeTableArgsType
|
|
684
|
+
* @property {boolean} [bulk] - Combine compatible contiguous DDL into single
|
|
685
|
+
* ALTER TABLE statements on drivers that support bulk alters (MySQL/MariaDB
|
|
686
|
+
* and PostgreSQL). `bulk` controls DDL grouping only, not transactional
|
|
687
|
+
* atomicity; unchanged drivers execute the recorded commands sequentially.
|
|
688
|
+
*/
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* ChangeTableCallbackType type.
|
|
692
|
+
* @typedef {(table: import("./change-table.js").default) => void | Promise<void>} ChangeTableCallbackType
|
|
693
|
+
*/
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Changes a table using a Rails-style table-scoped recorder.
|
|
697
|
+
* @overload
|
|
698
|
+
* @param {string} tableName - Table name.
|
|
699
|
+
* @param {ChangeTableCallbackType} callback - Callback function.
|
|
700
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
701
|
+
*/
|
|
702
|
+
/**
|
|
703
|
+
* Changes a table with explicit options.
|
|
704
|
+
* @overload
|
|
705
|
+
* @param {string} tableName - Table name.
|
|
706
|
+
* @param {ChangeTableArgsType} args - Options object.
|
|
707
|
+
* @param {ChangeTableCallbackType} callback - Callback function.
|
|
708
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
709
|
+
*/
|
|
710
|
+
/**
|
|
711
|
+
* Runs change table.
|
|
712
|
+
* @param {string} tableName - Table name.
|
|
713
|
+
* @param {ChangeTableArgsType | ChangeTableCallbackType} arg1 - Arg1.
|
|
714
|
+
* @param {ChangeTableCallbackType | undefined} [arg2] - Arg2.
|
|
715
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
716
|
+
*/
|
|
717
|
+
async changeTable(tableName, arg1, arg2) {
|
|
718
|
+
let args = /** @type {ChangeTableArgsType} */ ({})
|
|
719
|
+
let callback
|
|
720
|
+
|
|
721
|
+
if (typeof arg1 == "function") {
|
|
722
|
+
callback = arg1
|
|
723
|
+
} else {
|
|
724
|
+
args = arg1 || {}
|
|
725
|
+
callback = arg2
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (typeof callback != "function") throw new Error("No callback given")
|
|
729
|
+
|
|
730
|
+
const {bulk = false, ...restArgs} = args
|
|
731
|
+
|
|
732
|
+
restArgsError(restArgs)
|
|
733
|
+
|
|
734
|
+
const table = new ChangeTable({tableName})
|
|
735
|
+
|
|
736
|
+
await callback(table)
|
|
737
|
+
|
|
738
|
+
await this._executeChangeTableOperations(tableName, table.getOperations(), {bulk})
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Executes recorded changeTable operations. With `bulk` enabled on a
|
|
743
|
+
* supporting driver, compatible contiguous column operations accumulate into
|
|
744
|
+
* a single TableData flushed through `alterTableSQLs`; incompatible commands
|
|
745
|
+
* flush the batch first and run through the existing migration helpers.
|
|
746
|
+
* @param {string} tableName - Table name.
|
|
747
|
+
* @param {import("./change-table.js").ChangeTableOperationType[]} operations - Recorded operations.
|
|
748
|
+
* @param {object} args - Options object.
|
|
749
|
+
* @param {boolean} args.bulk - Whether to enable bulk command grouping.
|
|
750
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
751
|
+
*/
|
|
752
|
+
async _executeChangeTableOperations(tableName, operations, {bulk}) {
|
|
753
|
+
const driver = this.getDriver()
|
|
754
|
+
const bulkSupported = bulk && driver.supportsBulkAlter()
|
|
755
|
+
|
|
756
|
+
if (!bulkSupported) {
|
|
757
|
+
for (const operation of operations) {
|
|
758
|
+
await this._executeChangeTableOperation(tableName, operation)
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
let batch = new TableData(tableName)
|
|
765
|
+
|
|
766
|
+
const flushBatch = async () => {
|
|
767
|
+
if (batch.getColumns().length == 0 && batch.getIndexes().length == 0) return
|
|
768
|
+
|
|
769
|
+
const sqls = await driver.alterTableSQLs(batch)
|
|
770
|
+
|
|
771
|
+
for (const sql of sqls) {
|
|
772
|
+
await driver.query(sql)
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
batch = new TableData(tableName)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
for (const operation of operations) {
|
|
779
|
+
switch (operation.type) {
|
|
780
|
+
case "addColumn": {
|
|
781
|
+
if (!operation.columnType) throw new Error("No column type given")
|
|
782
|
+
|
|
783
|
+
// Flush an already-recorded index batch first so the emitted SQL keeps
|
|
784
|
+
// the recorded declaration order (index before column).
|
|
785
|
+
if (batch.getIndexes().length > 0) await flushBatch()
|
|
786
|
+
|
|
787
|
+
batch.addColumn(new TableColumn(operation.columnName, Object.assign({isNewColumn: true, type: operation.columnType}, operation.args)))
|
|
788
|
+
break
|
|
789
|
+
}
|
|
790
|
+
case "removeColumn":
|
|
791
|
+
// Flush an already-recorded index batch first so the emitted SQL keeps
|
|
792
|
+
// the recorded declaration order (index before column).
|
|
793
|
+
if (batch.getIndexes().length > 0) await flushBatch()
|
|
794
|
+
|
|
795
|
+
batch.addColumn(new TableColumn(operation.columnName, {dropColumn: true}))
|
|
796
|
+
break
|
|
797
|
+
case "addIndex":
|
|
798
|
+
// Drivers without `supportsBulkAlterIndexes` (PostgreSQL) keep indexes
|
|
799
|
+
// standalone because their ALTER TABLE does not carry CREATE INDEX
|
|
800
|
+
// clauses. An ifNotExists index is never combined because the combined
|
|
801
|
+
// bulk form cannot express that guard.
|
|
802
|
+
if (!driver.supportsBulkAlterIndexes() || operation.args?.ifNotExists) {
|
|
803
|
+
await flushBatch()
|
|
804
|
+
await this._executeChangeTableOperation(tableName, operation)
|
|
805
|
+
} else {
|
|
806
|
+
batch.addIndex(this._changeTableTableIndex(tableName, operation))
|
|
807
|
+
}
|
|
808
|
+
break
|
|
809
|
+
default:
|
|
810
|
+
await flushBatch()
|
|
811
|
+
await this._executeChangeTableOperation(tableName, operation)
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
await flushBatch()
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Builds a TableIndex for a batch from a recorded addIndex operation,
|
|
820
|
+
* resolving the default addIndex name eagerly so a combined MySQL ALTER
|
|
821
|
+
* never silently names the index differently.
|
|
822
|
+
* @param {string} tableName - Table name.
|
|
823
|
+
* @param {import("./change-table.js").ChangeTableAddIndexOperationType} operation - Recorded operation.
|
|
824
|
+
* @returns {TableIndex} - The table index.
|
|
825
|
+
*/
|
|
826
|
+
_changeTableTableIndex(tableName, operation) {
|
|
827
|
+
const {args, columns} = operation
|
|
828
|
+
// An ifNotExists index never reaches batching (it is flushed standalone),
|
|
829
|
+
// so the combined ALTER cannot carry that guard.
|
|
830
|
+
const {name, ...restIndexArgs} = args || {}
|
|
831
|
+
const indexName = name || new CreateIndexBase({columns, driver: this.getDriver(), tableName}).generateIndexName()
|
|
832
|
+
|
|
833
|
+
return new TableIndex(columns, Object.assign({}, restIndexArgs, {name: indexName}))
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Executes a single recorded changeTable operation through the existing
|
|
838
|
+
* migration helper with the same semantics as a direct helper call.
|
|
839
|
+
* @param {string} tableName - Table name.
|
|
840
|
+
* @param {import("./change-table.js").ChangeTableOperationType} operation - Recorded operation.
|
|
841
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
842
|
+
*/
|
|
843
|
+
async _executeChangeTableOperation(tableName, operation) {
|
|
844
|
+
switch (operation.type) {
|
|
845
|
+
case "addColumn":
|
|
846
|
+
await this.addColumn(tableName, operation.columnName, operation.columnType, operation.args)
|
|
847
|
+
break
|
|
848
|
+
case "removeColumn":
|
|
849
|
+
await this.removeColumn(tableName, operation.columnName)
|
|
850
|
+
break
|
|
851
|
+
case "addIndex":
|
|
852
|
+
await this.addIndex(tableName, operation.columns, operation.args)
|
|
853
|
+
break
|
|
854
|
+
case "removeIndex":
|
|
855
|
+
await this.removeIndex(tableName, operation.nameOrColumns, operation.args)
|
|
856
|
+
break
|
|
857
|
+
case "addReference":
|
|
858
|
+
await this.addReference(tableName, operation.referenceName, operation.args || {})
|
|
859
|
+
break
|
|
860
|
+
case "removeReference":
|
|
861
|
+
await this.removeReference(tableName, operation.referenceName, operation.args)
|
|
862
|
+
break
|
|
863
|
+
case "renameColumn":
|
|
864
|
+
await this.renameColumn(tableName, operation.oldColumnName, operation.newColumnName)
|
|
865
|
+
break
|
|
866
|
+
case "changeColumnNull":
|
|
867
|
+
await this.changeColumnNull(tableName, operation.columnName, operation.nullable)
|
|
868
|
+
break
|
|
869
|
+
default:
|
|
870
|
+
throw new Error("Unknown change table operation")
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
690
874
|
/**
|
|
691
875
|
* Runs drop table.
|
|
692
876
|
* @param {string} tableName - Table name.
|
|
@@ -717,6 +717,19 @@ export default class VelociousDatabaseDriversBase {
|
|
|
717
717
|
* @returns {string} - The type.
|
|
718
718
|
*/
|
|
719
719
|
getType(): string;
|
|
720
|
+
/**
|
|
721
|
+
* Whether this driver can combine unrelated alter-table operations into a
|
|
722
|
+
* single `ALTER TABLE` statement (Rails' `supports_bulk_alter`).
|
|
723
|
+
* @returns {boolean} - Whether bulk alter is supported.
|
|
724
|
+
*/
|
|
725
|
+
supportsBulkAlter(): boolean;
|
|
726
|
+
/**
|
|
727
|
+
* Whether a bulk `ALTER TABLE` statement can also carry `ADD INDEX` clauses.
|
|
728
|
+
* Only drivers that support this keep index adds inside the combined batch;
|
|
729
|
+
* the rest execute each index as its own statement.
|
|
730
|
+
* @returns {boolean} - Whether indexes can be added inside a bulk alter.
|
|
731
|
+
*/
|
|
732
|
+
supportsBulkAlterIndexes(): boolean;
|
|
720
733
|
/**
|
|
721
734
|
* Runs insert.
|
|
722
735
|
* @param {InsertSqlArgsType} args - Options object.
|