dbtasker 2.5.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/addcolumn.js +264 -0
- package/altercolumn.js +595 -0
- package/dbop.js +41 -51
- package/dropcolumn.js +174 -0
- package/function.js +567 -104
- package/index.js +98 -32
- package/package.json +1 -1
- package/tableop.js +273 -0
- package/validation.js +224 -159
- package/columnop.js +0 -748
- package/tables.js +0 -0
package/README.md
CHANGED
|
@@ -191,6 +191,19 @@ reserveddb, reserved_db
|
|
|
191
191
|
donttouch: ["production_db", "legacy_db"]
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
+
|
|
195
|
+
**Force Update Column**
|
|
196
|
+
If you want to update column if it is referanced by another column as foreign key you can update it by adding another key to the config which is force update column which is turned on by default. You can turn it of if you think necessary.
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
'forceupdatecol', 'forcemodifycol', 'forceupdatecolumn', 'forcemodifycolumn', 'force_update_col', 'force_modify_col', 'force_update_column', 'force_modify_column', 'forcealtercol', 'forcealtercolumn', 'force_alter_col', 'force_alter_column'
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Example:**
|
|
203
|
+
```js
|
|
204
|
+
forceupdatecol: true/1/\"1\"
|
|
205
|
+
```
|
|
206
|
+
|
|
194
207
|
### Separator Option
|
|
195
208
|
|
|
196
209
|
You can define a custom separator for internal operations:
|
|
@@ -456,7 +469,7 @@ foreign_key: {
|
|
|
456
469
|
## Foreign Key Aliases (Case-Insensitive)
|
|
457
470
|
DBTASKER accepts:
|
|
458
471
|
```js
|
|
459
|
-
"fk", "foreign_key", "foreignkey"
|
|
472
|
+
"fk", "fuck", "foreign_key", "foreignkey"
|
|
460
473
|
```
|
|
461
474
|
|
|
462
475
|
Foreign Key Properties & Aliases
|
package/addcolumn.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
const fncs = require("./function");
|
|
2
|
+
const cstyler = require("cstyler");
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async function addForeignKeyWithIndexQuery(config, databaseName, tableName, columnName, refTable, refColumn, options = {}) {
|
|
7
|
+
const ACTIONS = new Set(["RESTRICT", "CASCADE", "SET NULL", "NO ACTION"]);
|
|
8
|
+
|
|
9
|
+
const onDelete = ACTIONS.has(options.onDelete) ? options.onDelete : "RESTRICT";
|
|
10
|
+
const onUpdate = ACTIONS.has(options.onUpdate) ? options.onUpdate : "RESTRICT";
|
|
11
|
+
|
|
12
|
+
const indexName = `idx_${tableName}_${columnName}`;
|
|
13
|
+
const constraintName = `fk_${tableName}_${columnName}`;
|
|
14
|
+
|
|
15
|
+
// Combine both ADD INDEX and ADD CONSTRAINT into a single statement
|
|
16
|
+
// Using a comma to separate the actions
|
|
17
|
+
const combinedQuery = `
|
|
18
|
+
ALTER TABLE \`${tableName}\`
|
|
19
|
+
ADD INDEX \`${indexName}\` (\`${columnName}\`),
|
|
20
|
+
ADD CONSTRAINT \`${constraintName}\`
|
|
21
|
+
FOREIGN KEY (\`${columnName}\`)
|
|
22
|
+
REFERENCES \`${refTable}\` (\`${refColumn}\`)
|
|
23
|
+
ON DELETE ${onDelete}
|
|
24
|
+
ON UPDATE ${onUpdate}
|
|
25
|
+
`.trim();
|
|
26
|
+
|
|
27
|
+
const runquery = await fncs.runQuery(config, databaseName, combinedQuery);
|
|
28
|
+
return runquery;
|
|
29
|
+
}
|
|
30
|
+
async function addColumnQuery(columndata, columnName, tableName, databaseName, config) {
|
|
31
|
+
try {
|
|
32
|
+
if (!columndata || !columndata.columntype) {
|
|
33
|
+
throw new Error("columntype is required to add a column. Table: " + tableName + " Column name: " + columnName);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Simple identifier escaping for MySQL identifiers
|
|
37
|
+
const escId = (s) => `\`${String(s).replace(/`/g, '``')}\``;
|
|
38
|
+
|
|
39
|
+
let queryText = `ALTER TABLE ${escId(tableName)} ADD COLUMN ${escId(columnName)}`;
|
|
40
|
+
|
|
41
|
+
// column type
|
|
42
|
+
queryText += ` ${columndata.columntype}`;
|
|
43
|
+
|
|
44
|
+
// length / enum / set
|
|
45
|
+
if (columndata.hasOwnProperty("length_value")) {
|
|
46
|
+
const lengthval = columndata.length_value;
|
|
47
|
+
|
|
48
|
+
if (typeof lengthval === "number") {
|
|
49
|
+
queryText += `(${lengthval})`;
|
|
50
|
+
} else if (
|
|
51
|
+
Array.isArray(lengthval) &&
|
|
52
|
+
lengthval.length === 2 &&
|
|
53
|
+
lengthval.every(v => typeof v === "number")
|
|
54
|
+
) {
|
|
55
|
+
queryText += `(${lengthval[0]},${lengthval[1]})`;
|
|
56
|
+
} else if (
|
|
57
|
+
Array.isArray(lengthval) &&
|
|
58
|
+
lengthval.every(v => typeof v === "string")
|
|
59
|
+
) {
|
|
60
|
+
const escaped = lengthval.map(v => `'${v.replace(/'/g, "''")}'`);
|
|
61
|
+
queryText += `(${escaped.join(",")})`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
queryText += " ";
|
|
65
|
+
|
|
66
|
+
if (columndata.unsigned === true) queryText += "UNSIGNED ";
|
|
67
|
+
if (columndata.zerofill === true) queryText += "ZEROFILL ";
|
|
68
|
+
|
|
69
|
+
if (columndata.hasOwnProperty("defaults")) {
|
|
70
|
+
const d = columndata.defaults;
|
|
71
|
+
if (d === null) queryText += "DEFAULT NULL ";
|
|
72
|
+
else if (typeof d === "number") queryText += `DEFAULT ${d} `;
|
|
73
|
+
else if (/^CURRENT_TIMESTAMP$/i.test(String(d))) queryText += `DEFAULT ${d} `;
|
|
74
|
+
else queryText += `DEFAULT '${String(d).replace(/'/g, "''")}' `;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (columndata.autoincrement === true) {
|
|
78
|
+
queryText += "AUTO_INCREMENT ";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (columndata._charset_) queryText += `CHARACTER SET ${columndata._charset_} `;
|
|
82
|
+
if (columndata._collate_) queryText += `COLLATE ${columndata._collate_} `;
|
|
83
|
+
|
|
84
|
+
if (columndata.hasOwnProperty("nulls")) {
|
|
85
|
+
queryText += columndata.nulls ? "NULL " : "NOT NULL ";
|
|
86
|
+
}
|
|
87
|
+
if (columndata.comment) {
|
|
88
|
+
queryText += `COMMENT '${String(columndata.comment).replace(/'/g, "''")}' `;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// If no index specified, return the column add SQL
|
|
92
|
+
if (!columndata.hasOwnProperty("index") || columndata.index === undefined || columndata.index === null || columndata.index === '') {
|
|
93
|
+
return queryText.trim();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// index is provided — interpret it as a string describing index type
|
|
97
|
+
let rawIndex = columndata.index;
|
|
98
|
+
if (typeof rawIndex !== 'string') {
|
|
99
|
+
// defensively convert to string if possible
|
|
100
|
+
rawIndex = String(rawIndex);
|
|
101
|
+
}
|
|
102
|
+
const idxToken = rawIndex.trim().toUpperCase();
|
|
103
|
+
|
|
104
|
+
// Optional explicit index name
|
|
105
|
+
const idxNameRaw = columndata.index_name || columndata.indexName || null;
|
|
106
|
+
const idxName = idxNameRaw ? escId(idxNameRaw) : null;
|
|
107
|
+
|
|
108
|
+
// Optional prefix length for index (integer)
|
|
109
|
+
let idxLength = null;
|
|
110
|
+
if (Number.isInteger(columndata.index_length) && columndata.index_length > 0) {
|
|
111
|
+
idxLength = columndata.index_length;
|
|
112
|
+
} else if (Number.isInteger(columndata.indexLength) && columndata.indexLength > 0) {
|
|
113
|
+
idxLength = columndata.indexLength;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const colRef = idxLength ? `${escId(columnName)}(${idxLength})` : escId(columnName);
|
|
117
|
+
|
|
118
|
+
// normalize common variants to canonical type
|
|
119
|
+
let idxType = 'INDEX';
|
|
120
|
+
if (idxToken === 'PRIMARY' || idxToken === 'PRIMARY KEY') idxType = 'PRIMARY';
|
|
121
|
+
else if (idxToken === 'UNIQUE' || idxToken === 'UNIQUE KEY' || idxToken === 'UNIQUE INDEX') idxType = 'UNIQUE';
|
|
122
|
+
else if (idxToken === 'FULLTEXT' || idxToken === 'FULLTEXT KEY' || idxToken === 'FULLTEXT INDEX') idxType = 'FULLTEXT';
|
|
123
|
+
else if (idxToken === 'SPATIAL' || idxToken === 'SPATIAL KEY' || idxToken === 'SPATIAL INDEX') idxType = 'SPATIAL';
|
|
124
|
+
else idxType = 'INDEX'; // covers INDEX, KEY and others
|
|
125
|
+
|
|
126
|
+
// Build index clause. Prepend with comma because we already used ALTER TABLE ... ADD COLUMN ...
|
|
127
|
+
let indexClause = '';
|
|
128
|
+
switch (idxType) {
|
|
129
|
+
case 'PRIMARY':
|
|
130
|
+
// primary key doesn't accept a name
|
|
131
|
+
indexClause = `, ADD PRIMARY KEY (${colRef})`;
|
|
132
|
+
break;
|
|
133
|
+
case 'UNIQUE':
|
|
134
|
+
{
|
|
135
|
+
const name = idxName || escId(`uniq_${String(tableName).replace(/\W+/g, '_')}_${String(columnName).replace(/\W+/g, '_')}`);
|
|
136
|
+
indexClause = `, ADD UNIQUE KEY ${name} (${colRef})`;
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
case 'FULLTEXT':
|
|
140
|
+
{
|
|
141
|
+
const name = idxName || escId(`ft_${String(tableName).replace(/\W+/g, '_')}_${String(columnName).replace(/\W+/g, '_')}`);
|
|
142
|
+
indexClause = `, ADD FULLTEXT KEY ${name} (${colRef})`;
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
case 'SPATIAL':
|
|
146
|
+
{
|
|
147
|
+
const name = idxName || escId(`sp_${String(tableName).replace(/\W+/g, '_')}_${String(columnName).replace(/\W+/g, '_')}`);
|
|
148
|
+
indexClause = `, ADD SPATIAL KEY ${name} (${colRef})`;
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
case 'INDEX':
|
|
152
|
+
default:
|
|
153
|
+
{
|
|
154
|
+
const name = idxName || escId(`idx_${String(tableName).replace(/\W+/g, '_')}_${String(columnName).replace(/\W+/g, '_')}`);
|
|
155
|
+
indexClause = `, ADD INDEX ${name} (${colRef})`;
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
queryText += indexClause + ' ';
|
|
161
|
+
|
|
162
|
+
queryText = queryText.trim();
|
|
163
|
+
const runquery = await fncs.runQuery(config, databaseName, queryText);
|
|
164
|
+
return runquery;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.error(err && err.message ? err.message : String(err));
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function addColumnIfNeeded(config, jsondata, separator) {
|
|
171
|
+
try {
|
|
172
|
+
console.log(cstyler.bold.yellow("Let's initiate addColumn to table if needed..."));
|
|
173
|
+
for (const jsondb of Object.keys(jsondata)) {
|
|
174
|
+
let remainfk = {};
|
|
175
|
+
const loopdb = fncs.perseDatabaseNameWithLoop(jsondb, separator);
|
|
176
|
+
if (loopdb === false) {
|
|
177
|
+
console.error("There must be some mistake. Please re install the module.")
|
|
178
|
+
}
|
|
179
|
+
const databaseName = loopdb.loopname;
|
|
180
|
+
const getalltable = await fncs.getTableNames(config, databaseName);
|
|
181
|
+
if (getalltable === null) {
|
|
182
|
+
console.error(cstyler.red(`Failed to get table names for database ${databaseName}. Skipping...`));
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
for (const jsontable of Object.keys(jsondata[jsondb])) {
|
|
186
|
+
if (fncs.isJsonObject(jsondata[jsondb][jsontable]) === false) { continue }
|
|
187
|
+
let queryText = [];
|
|
188
|
+
let foreignKeyQueries = [];
|
|
189
|
+
const looptable = fncs.perseTableNameWithLoop(jsontable, separator);
|
|
190
|
+
if (looptable === false) {
|
|
191
|
+
console.error("There must be some mistake. Please re install the module.")
|
|
192
|
+
}
|
|
193
|
+
const tableName = looptable.loopname;
|
|
194
|
+
if (!getalltable.includes(tableName)) {
|
|
195
|
+
console.error(cstyler.red(`Table ${tableName} does not exist in database ${databaseName}. Skipping...`));
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const allcols = await fncs.getColumnNames(config, databaseName, tableName);
|
|
199
|
+
if (allcols === null) {
|
|
200
|
+
console.error(cstyler.red(`Failed to get column names for table ${tableName} in database ${databaseName}. Skipping...`));
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
for (const jsoncolumn of Object.keys(jsondata[jsondb][jsontable])) {
|
|
204
|
+
if (allcols.includes(jsoncolumn) || fncs.isJsonObject(jsondata[jsondb][jsontable][jsoncolumn]) === false) {
|
|
205
|
+
// column exists, skip
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
// lets add the column
|
|
209
|
+
const columndata = jsondata[jsondb][jsontable][jsoncolumn];
|
|
210
|
+
const addcolquery = await addColumnQuery(columndata, jsoncolumn, tableName, databaseName, config);
|
|
211
|
+
if (addcolquery === null) {
|
|
212
|
+
console.error(cstyler.red(`Failed to add column ${jsoncolumn} to table ${tableName} in database ${databaseName}. Skipping...`));
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
// check for foreign key
|
|
216
|
+
if (columndata.hasOwnProperty("foreign_key")) {
|
|
217
|
+
const fk = columndata.foreign_key;
|
|
218
|
+
const columnExists = await fncs.columnExists(config, databaseName, tableName, jsoncolumn);
|
|
219
|
+
if (columnExists === null) {
|
|
220
|
+
console.error(cstyler.red(`Failed to verify existence of column ${jsoncolumn} in table ${tableName}. Skipping foreign key addition.`));
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
if (columnExists === false) {
|
|
224
|
+
if (!remainfk.hasOwnProperty(tableName)) remainfk[tableName] = {};
|
|
225
|
+
remainfk[tableName][jsoncolumn] = fk;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const fkquery = await addForeignKeyWithIndexQuery(config, databaseName, tableName, jsoncolumn, fk.table, fk.column, { onDelete: fk.deleteOption, onUpdate: fk.updateOption });
|
|
229
|
+
if (fkquery === null) {
|
|
230
|
+
console.error(cstyler.red(`Failed to prepare foreign key query for column ${jsoncolumn} on table ${tableName} in database ${databaseName}. Skipping...`));
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// lets add remaining foreign keys
|
|
237
|
+
let foreignKeyQueries = [];
|
|
238
|
+
for (const tbl of Object.keys(remainfk)) {
|
|
239
|
+
for (const col of Object.keys(remainfk[tbl])) {
|
|
240
|
+
const fk = remainfk[tbl][col];
|
|
241
|
+
const fkquery = await addForeignKeyWithIndexQuery(config, databaseName, tbl, col, fk.table, fk.column, { onDelete: fk.deleteOption, onUpdate: fk.updateOption });
|
|
242
|
+
if (fkquery === null) {
|
|
243
|
+
console.error(cstyler.red(`Failed to execute query on column ${col} on table ${tbl} in database ${databaseName}. Query: ${fkquery}`));
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log(cstyler.green(`Successfully added foreignkey on column ${col} on table ${tbl} in database ${databaseName}. Query: ${fkquery}`));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
console.log(cstyler.cyan("Adding Column to tables are completed successfully."));
|
|
253
|
+
return true;
|
|
254
|
+
} catch (err) {
|
|
255
|
+
console.error(err.message);
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = {
|
|
261
|
+
addColumnQuery,
|
|
262
|
+
addForeignKeyWithIndexQuery,
|
|
263
|
+
addColumnIfNeeded
|
|
264
|
+
};
|