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 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
+ };