dbtasker 2.5.1 → 3.0.1

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:
@@ -233,12 +246,12 @@ DBTASKER is available via npm.
233
246
 
234
247
  Install it using either of the following commands:
235
248
  ```js
236
- npm install DBTASKER
249
+ npm install dbtasker
237
250
  ```
238
251
 
239
252
  or
240
253
  ```js
241
- npm i DBTASKER
254
+ npm i dbtasker
242
255
  ```
243
256
  Usage
244
257
 
@@ -255,10 +268,10 @@ DBTASKER handles the rest.
255
268
  Create a JavaScript file (for example: index.js) and import DBTASKER.
256
269
  ```js
257
270
  Using require (CommonJS)
258
- const DBTASKER = require("DBTASKER");
271
+ const dbtasker = require("dbtasker");
259
272
 
260
273
  Using import (ES Module)
261
- import DBTASKER from "DBTASKER";
274
+ import dbtasker from "dbtasker";
262
275
  ```
263
276
 
264
277
  #### Step 2: Create a Configuration Object
@@ -313,7 +326,7 @@ DBTASKER supports multiple aliases for column keys and is case-insensitive.
313
326
 
314
327
  Call DBTASKER by passing the config first, then the schema object.
315
328
  ```js
316
- DBTASKER(config, schema);
329
+ dbtasker(config, schema);
317
330
  ```
318
331
 
319
332
  That’s it.
@@ -326,7 +339,7 @@ That’s it.
326
339
 
327
340
  ### Full Minimal Example
328
341
  ```js
329
- const DBTASKER = require("DBTASKER");
342
+ const DBTASKER = require("dbtasker");
330
343
 
331
344
  const config = {
332
345
  host: "localhost",
@@ -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
+ };