dbtasker 3.1.1 → 3.1.3
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 +30 -7
- package/addcolumn.js +54 -92
- package/altercolumn.js +13 -6
- package/dropcolumn.js +12 -56
- package/function.js +28 -25
- package/index.js +1 -2
- package/package.json +3 -2
- package/pgfunctions.js +54 -0
- package/pgvalidation.js +0 -0
- /package/{validation.js → mysqlvalidation.js} +0 -0
package/README.md
CHANGED
|
@@ -26,16 +26,16 @@ npm install dbtasker
|
|
|
26
26
|
btasker requires a configuration object to manage connection credentials and safety behaviors.
|
|
27
27
|
Property | Description
|
|
28
28
|
| :--- | :--- |
|
|
29
|
-
| **host** | MySQL Host (e.g., `localhost`)
|
|
30
|
-
| **user** | Database Username |
|
|
31
|
-
| **password** | Database Password |
|
|
32
|
-
| **port** | Connection Port (
|
|
29
|
+
| **host** | MySQL Host (e.g., `localhost`) `(required)`|
|
|
30
|
+
| **user** | Database Username `(required)` |
|
|
31
|
+
| **password** | Database Password `(required)` |
|
|
32
|
+
| **port** | Connection Port (e.g., `3306`) `(required)` |
|
|
33
33
|
| **drop database** | `Boolean`: If true, allows dropping databases. |
|
|
34
34
|
| **drop table** | `Boolean`: If true, allows dropping tables. |
|
|
35
35
|
| **drop column** | `Boolean`: If true, allows dropping columns. |
|
|
36
36
|
| **do not touch** | `Array`: List of database names protected from deletion. |
|
|
37
37
|
| **force delete column** | `Boolean`: If true, allow dropping column even if referanced by any other column. |
|
|
38
|
-
| **force update column** | `Boolean`: It is set `default: false` If true, allow updating column even if referanced by any other column. This one is very powerful. If you turn it on it will remove any foreign key that is referanced to the column is being updated then update the column then add the foreign key to those column again and if the column is set to `UNIQUE` it will remove all the column value that are
|
|
38
|
+
| **force update column** | `Boolean`: It is set `default: false` If true, allow updating column even if referanced by any other column. This one is very powerful. If you turn it on it will remove any foreign key that is referanced to the column is being updated then update the column then add the foreign key to those column again and if the column is set to `UNIQUE` it will remove all the column value that are same. |
|
|
39
39
|
|
|
40
40
|
### Configuration Example
|
|
41
41
|
`JavaScript`
|
|
@@ -49,6 +49,8 @@ const config = {
|
|
|
49
49
|
droptable: true,
|
|
50
50
|
dropcol: false,
|
|
51
51
|
donttouch: ["production_db", "analytics_db"],
|
|
52
|
+
forceupdatecolumn: true,
|
|
53
|
+
forcedeletecolumn: true,
|
|
52
54
|
sep: "_"
|
|
53
55
|
};
|
|
54
56
|
```
|
|
@@ -82,10 +84,30 @@ const schema = {
|
|
|
82
84
|
defaults: "CURRENT_TIMESTAMP"
|
|
83
85
|
}
|
|
84
86
|
}
|
|
87
|
+
},
|
|
88
|
+
"(year)web_db": {
|
|
89
|
+
"tableName(month)": {
|
|
90
|
+
id: {
|
|
91
|
+
type: "int",
|
|
92
|
+
primarykey: true,
|
|
93
|
+
autoincrement: true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
85
96
|
}
|
|
86
97
|
};
|
|
87
98
|
```
|
|
88
99
|
|
|
100
|
+
### The Amazing and powerful one
|
|
101
|
+
When you use years, months or days as parameter when declering database name or in table name the system will generate date as extension of the name with a seperator. Such as:
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
const sep = "_"
|
|
105
|
+
if(name == "(year)name" || "name(year)") generated == "name_2026_";
|
|
106
|
+
if(name == "(month)name" || "name(month)") generated == "name_2026_12_";
|
|
107
|
+
if(name == "(day)name" || "name(day)") generated == "name_2026_12_26_";
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Now if the schema have any of this kind of name then you must run the function after that particular periode. So that funciton can create that database or table each time for your particular need.
|
|
89
111
|
|
|
90
112
|
|
|
91
113
|
## 🔗 Foreign Keys
|
|
@@ -154,9 +176,10 @@ ColumnTwo: {
|
|
|
154
176
|
const dbtasker = require("dbtasker");
|
|
155
177
|
|
|
156
178
|
const config = {
|
|
157
|
-
host: "localhost",
|
|
179
|
+
host: "localhost",
|
|
180
|
+
port: 3306,
|
|
158
181
|
user: "root",
|
|
159
|
-
password: "password"
|
|
182
|
+
password: "password"
|
|
160
183
|
};
|
|
161
184
|
|
|
162
185
|
const schema = {
|
package/addcolumn.js
CHANGED
|
@@ -30,39 +30,31 @@ async function addForeignKeyWithIndexQuery(config, databaseName, tableName, colu
|
|
|
30
30
|
async function addColumnQuery(columndata, columnName, tableName, databaseName, config) {
|
|
31
31
|
try {
|
|
32
32
|
if (!columndata || !columndata.columntype) {
|
|
33
|
-
throw new Error(
|
|
33
|
+
throw new Error(`columntype is required to add a column. Table: ${tableName} Column name: ${columnName}`);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// Helper to escape backticks in names
|
|
37
37
|
const escId = (s) => `\`${String(s).replace(/`/g, '``')}\``;
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
// 1. Base query to add the column
|
|
40
|
+
let queryText = `ALTER TABLE ${escId(tableName)} ADD COLUMN ${escId(columnName)} ${columndata.columntype}`;
|
|
40
41
|
|
|
41
|
-
//
|
|
42
|
-
queryText += ` ${columndata.columntype}`;
|
|
43
|
-
|
|
44
|
-
// length / enum / set
|
|
42
|
+
// 2. Handle length / enum / set
|
|
45
43
|
if (columndata.hasOwnProperty("length_value")) {
|
|
46
44
|
const lengthval = columndata.length_value;
|
|
47
|
-
|
|
48
45
|
if (typeof lengthval === "number") {
|
|
49
46
|
queryText += `(${lengthval})`;
|
|
50
|
-
} else if (
|
|
51
|
-
Array.isArray(lengthval) &&
|
|
52
|
-
lengthval.length === 2 &&
|
|
53
|
-
lengthval.every(v => typeof v === "number")
|
|
54
|
-
) {
|
|
47
|
+
} else if (Array.isArray(lengthval) && lengthval.length === 2 && lengthval.every(v => typeof v === "number")) {
|
|
55
48
|
queryText += `(${lengthval[0]},${lengthval[1]})`;
|
|
56
|
-
} else if (
|
|
57
|
-
Array.isArray(lengthval) &&
|
|
58
|
-
lengthval.every(v => typeof v === "string")
|
|
59
|
-
) {
|
|
49
|
+
} else if (Array.isArray(lengthval) && lengthval.every(v => typeof v === "string")) {
|
|
60
50
|
const escaped = lengthval.map(v => `'${v.replace(/'/g, "''")}'`);
|
|
61
51
|
queryText += `(${escaped.join(",")})`;
|
|
62
52
|
}
|
|
63
53
|
}
|
|
54
|
+
|
|
64
55
|
queryText += " ";
|
|
65
56
|
|
|
57
|
+
// 3. Handle attributes
|
|
66
58
|
if (columndata.unsigned === true) queryText += "UNSIGNED ";
|
|
67
59
|
if (columndata.zerofill === true) queryText += "ZEROFILL ";
|
|
68
60
|
|
|
@@ -74,96 +66,66 @@ async function addColumnQuery(columndata, columnName, tableName, databaseName, c
|
|
|
74
66
|
else queryText += `DEFAULT '${String(d).replace(/'/g, "''")}' `;
|
|
75
67
|
}
|
|
76
68
|
|
|
77
|
-
if (columndata.autoincrement === true)
|
|
78
|
-
queryText += "AUTO_INCREMENT ";
|
|
79
|
-
}
|
|
80
|
-
|
|
69
|
+
if (columndata.autoincrement === true) queryText += "AUTO_INCREMENT ";
|
|
81
70
|
if (columndata._charset_) queryText += `CHARACTER SET ${columndata._charset_} `;
|
|
82
71
|
if (columndata._collate_) queryText += `COLLATE ${columndata._collate_} `;
|
|
83
72
|
|
|
84
73
|
if (columndata.hasOwnProperty("nulls")) {
|
|
85
74
|
queryText += columndata.nulls ? "NULL " : "NOT NULL ";
|
|
86
75
|
}
|
|
76
|
+
|
|
87
77
|
if (columndata.comment) {
|
|
88
78
|
queryText += `COMMENT '${String(columndata.comment).replace(/'/g, "''")}' `;
|
|
89
79
|
}
|
|
90
80
|
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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;
|
|
81
|
+
// 4. Handle Index (Optional)
|
|
82
|
+
const hasIndex = columndata.hasOwnProperty("index") && columndata.index !== undefined && columndata.index !== null && columndata.index !== '';
|
|
83
|
+
|
|
84
|
+
if (hasIndex) {
|
|
85
|
+
let rawIndex = String(columndata.index).trim().toUpperCase();
|
|
86
|
+
const idxNameRaw = columndata.index_name || columndata.indexName || null;
|
|
87
|
+
const idxName = idxNameRaw ? escId(idxNameRaw) : null;
|
|
88
|
+
|
|
89
|
+
let idxLength = null;
|
|
90
|
+
if (Number.isInteger(columndata.index_length) && columndata.index_length > 0) idxLength = columndata.index_length;
|
|
91
|
+
else if (Number.isInteger(columndata.indexLength) && columndata.indexLength > 0) idxLength = columndata.indexLength;
|
|
92
|
+
|
|
93
|
+
const colRef = idxLength ? `${escId(columnName)}(${idxLength})` : escId(columnName);
|
|
94
|
+
|
|
95
|
+
let idxType = 'INDEX';
|
|
96
|
+
if (rawIndex === 'PRIMARY' || rawIndex === 'PRIMARY KEY') idxType = 'PRIMARY';
|
|
97
|
+
else if (rawIndex.includes('UNIQUE')) idxType = 'UNIQUE';
|
|
98
|
+
else if (rawIndex.includes('FULLTEXT')) idxType = 'FULLTEXT';
|
|
99
|
+
else if (rawIndex.includes('SPATIAL')) idxType = 'SPATIAL';
|
|
100
|
+
|
|
101
|
+
switch (idxType) {
|
|
102
|
+
case 'PRIMARY':
|
|
103
|
+
queryText += `, ADD PRIMARY KEY (${colRef})`;
|
|
104
|
+
break;
|
|
105
|
+
case 'UNIQUE':
|
|
106
|
+
queryText += `, ADD UNIQUE KEY ${idxName || escId(`uniq_${tableName}_${columnName}`)} (${colRef})`;
|
|
107
|
+
break;
|
|
108
|
+
case 'FULLTEXT':
|
|
109
|
+
queryText += `, ADD FULLTEXT KEY ${idxName || escId(`ft_${tableName}_${columnName}`)} (${colRef})`;
|
|
110
|
+
break;
|
|
111
|
+
case 'SPATIAL':
|
|
112
|
+
queryText += `, ADD SPATIAL KEY ${idxName || escId(`sp_${tableName}_${columnName}`)} (${colRef})`;
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
queryText += `, ADD INDEX ${idxName || escId(`idx_${tableName}_${columnName}`)} (${colRef})`;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
158
118
|
}
|
|
159
119
|
|
|
160
|
-
|
|
161
|
-
|
|
120
|
+
// 5. Final Execution (Happens every time)
|
|
162
121
|
queryText = queryText.trim();
|
|
163
122
|
const runquery = await fncs.runQuery(config, databaseName, queryText);
|
|
123
|
+
|
|
124
|
+
console.log(`Success: Added column ${columnName} to ${tableName}`);
|
|
164
125
|
return runquery;
|
|
126
|
+
|
|
165
127
|
} catch (err) {
|
|
166
|
-
console.error(err && err.message ? err.message : String(err));
|
|
128
|
+
console.error("Error in addColumnQuery:", err && err.message ? err.message : String(err));
|
|
167
129
|
return null;
|
|
168
130
|
}
|
|
169
131
|
}
|
|
@@ -252,7 +214,7 @@ async function addColumnIfNeeded(config, jsondata, separator) {
|
|
|
252
214
|
}
|
|
253
215
|
}
|
|
254
216
|
}
|
|
255
|
-
if(count > 0){
|
|
217
|
+
if (count > 0) {
|
|
256
218
|
console.log(cstyler.green.bold("Successfully added " + count + " columns."));
|
|
257
219
|
} else {
|
|
258
220
|
console.log("No column found to be added. All the column are added already.");
|
package/altercolumn.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fncs = require("./function");
|
|
2
2
|
const cstyler = require("cstyler");
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
+
const addcolumn = require("./addcolumn");
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
|
|
@@ -8,13 +9,13 @@ const crypto = require('crypto');
|
|
|
8
9
|
|
|
9
10
|
function generateSafeIndexName(prefix, table, column) {
|
|
10
11
|
const fullName = `${prefix}_${table}_${column}`;
|
|
11
|
-
|
|
12
|
+
|
|
12
13
|
// If it fits, just return it
|
|
13
14
|
if (fullName.length <= 64) return fullName;
|
|
14
15
|
|
|
15
16
|
// If too long, hash the full string and append it to a slice
|
|
16
17
|
const hash = crypto.createHash('sha256').update(fullName).digest('hex').slice(0, 8);
|
|
17
|
-
|
|
18
|
+
|
|
18
19
|
// 55 chars + 1 underscore + 8 chars hash = 64 chars total
|
|
19
20
|
return `${fullName.slice(0, 55)}_${hash}`;
|
|
20
21
|
}
|
|
@@ -310,7 +311,9 @@ async function alterColumnQuery(dbConfig, columndata, columnName, tableName, dat
|
|
|
310
311
|
}
|
|
311
312
|
if (hasdupes === true) {
|
|
312
313
|
if (forceupdatecolumn) {
|
|
314
|
+
console.log(cstyler.bold.underline.yellow("********************************************************"));
|
|
313
315
|
console.log(cstyler.bold.underline.yellow("***Cleaning duplicate rows before adding UNIQUE index***"));
|
|
316
|
+
console.log(cstyler.bold.underline.yellow("********************************************************"));
|
|
314
317
|
const cleancol = await fncs.cleanDuplicateRows(dbConfig, database, tableName, columnName);
|
|
315
318
|
if (cleancol === null) {
|
|
316
319
|
console.error("Having server connection problem cleaning duplicate rows from database");
|
|
@@ -404,6 +407,7 @@ async function addForeignKeyWithIndexQuery(config, databaseName, tableName, colu
|
|
|
404
407
|
async function alterColumnIfNeeded(config, jsondata, forceupdatecolumn, separator) {
|
|
405
408
|
try {
|
|
406
409
|
console.log(cstyler.bold.yellow("Let's initiate Alter Column to table if needed..."));
|
|
410
|
+
console.log(cstyler.bold.blue("Please wait..."));
|
|
407
411
|
let count = 0;
|
|
408
412
|
for (const jsondb of Object.keys(jsondata)) {
|
|
409
413
|
const loopdb = fncs.perseTableNameWithLoop(jsondb, separator);
|
|
@@ -439,7 +443,13 @@ async function alterColumnIfNeeded(config, jsondata, forceupdatecolumn, separato
|
|
|
439
443
|
if (!fncs.isJsonObject(jsondata[jsondb][jsontable][jsoncolumn])) { continue; }
|
|
440
444
|
if (!allcols.includes(jsoncolumn)) {
|
|
441
445
|
// column does not exist, skip it
|
|
442
|
-
console.error(cstyler.red(`Column ${jsoncolumn} does not exist in table ${tableName} of database ${databaseName}.
|
|
446
|
+
console.error(cstyler.red(`Column ${jsoncolumn} does not exist in table ${tableName} of database ${databaseName}. Adding the column...`));
|
|
447
|
+
const addcol = await addcolumn.addColumnQuery(jsondata[jsondb][jsontable][jsoncolumn], jsoncolumn, tableName, databaseName, config);
|
|
448
|
+
if (addcol === null) {
|
|
449
|
+
console.error(cstyler.red(`Failed to add column ${jsoncolumn} to table ${tableName} in database ${databaseName}. Skipping...`));
|
|
450
|
+
} else if (addcol === true) {
|
|
451
|
+
console.log(cstyler.green(`Successfully added column ${jsoncolumn} to table ${tableName} in database ${databaseName}.`));
|
|
452
|
+
}
|
|
443
453
|
continue;
|
|
444
454
|
}
|
|
445
455
|
// lets add the column
|
|
@@ -550,10 +560,8 @@ async function alterColumnIfNeeded(config, jsondata, forceupdatecolumn, separato
|
|
|
550
560
|
}
|
|
551
561
|
}
|
|
552
562
|
} else {
|
|
553
|
-
console.log(cstyler.blue("Database:"), cstyler.hex("#00d9ffff")(databaseName), cstyler.blue("Table:"), cstyler.hex("#00d9ffff")(tableName), cstyler.blue("Column Name:"), cstyler.hex("#00d9ffff")(jsoncolumn), cstyler.yellow("- Given column data and server data are same."));
|
|
554
563
|
// lets check if foreign key need update
|
|
555
564
|
if (fkdetails === false && !columndata.hasOwnProperty("foreign_key")) {
|
|
556
|
-
console.log(cstyler.underline("Column do not have foreign key."));
|
|
557
565
|
continue;
|
|
558
566
|
} else if (fkdetails === false && columndata.hasOwnProperty("foreign_key")) {
|
|
559
567
|
console.log(cstyler.yellow("Foreign key need to be added for "), cstyler.blue("Database: "), cstyler.hex("#00d9ffff")(databaseName), cstyler.blue(" Table: "), cstyler.hex("#00d9ffff")(tableName), cstyler.blue(" Column Name: "), cstyler.hex("#00d9ffff")(jsoncolumn));
|
|
@@ -575,7 +583,6 @@ async function alterColumnIfNeeded(config, jsondata, forceupdatecolumn, separato
|
|
|
575
583
|
fkdetails.column === fkdt.column &&
|
|
576
584
|
fkdetails.deleteOption === fkdt.deleteOption &&
|
|
577
585
|
(fkdt.updateOption === undefined || fkdetails.updateOption === fkdt.updateOption)) {
|
|
578
|
-
console.log(cstyler.underline("Foreign key details are matched. No changes needed."))
|
|
579
586
|
continue;
|
|
580
587
|
}
|
|
581
588
|
console.log(cstyler.blue("Database: "), cstyler.hex("#00d9ffff")(databaseName), cstyler.blue(" Table: "), cstyler.hex("#00d9ffff")(tableName), cstyler.blue(" Column Name: "), cstyler.hex("#00d9ffff")(jsoncolumn), cstyler.yellow("- Foreign key details are different, updating foreign key."));
|
package/dropcolumn.js
CHANGED
|
@@ -17,11 +17,7 @@ async function dropcolumn(config, tableJson, forceDropColumn, separator = "_") {
|
|
|
17
17
|
for (const jsDb of Object.keys(tableJson)) {
|
|
18
18
|
const parsedDb = fncs.perseDatabaseNameWithLoop(jsDb, separator);
|
|
19
19
|
if (!parsedDb) {
|
|
20
|
-
console.error(
|
|
21
|
-
cstyler.bold.red("Cannot parse database name."),
|
|
22
|
-
jsDb,
|
|
23
|
-
parsedDb
|
|
24
|
-
);
|
|
20
|
+
console.error(cstyler.bold.red("Cannot parse database name."), jsDb, parsedDb);
|
|
25
21
|
return null;
|
|
26
22
|
}
|
|
27
23
|
|
|
@@ -34,20 +30,13 @@ async function dropcolumn(config, tableJson, forceDropColumn, separator = "_") {
|
|
|
34
30
|
|
|
35
31
|
const parsedTable = fncs.perseTableNameWithLoop(jsTable, separator);
|
|
36
32
|
if (!parsedTable) {
|
|
37
|
-
console.error(
|
|
38
|
-
cstyler.bold.red("Cannot parse table name."),
|
|
39
|
-
jsTable
|
|
40
|
-
);
|
|
33
|
+
console.error(cstyler.bold.red("Cannot parse table name."), jsTable);
|
|
41
34
|
return null;
|
|
42
35
|
}
|
|
43
36
|
|
|
44
37
|
const tableName = parsedTable.loopname;
|
|
45
38
|
|
|
46
|
-
const existingColumns = await fncs.getColumnNames(
|
|
47
|
-
config,
|
|
48
|
-
databaseName,
|
|
49
|
-
tableName
|
|
50
|
-
);
|
|
39
|
+
const existingColumns = await fncs.getColumnNames(config, databaseName, tableName);
|
|
51
40
|
|
|
52
41
|
if (!existingColumns) {
|
|
53
42
|
console.error(
|
|
@@ -60,14 +49,10 @@ async function dropcolumn(config, tableJson, forceDropColumn, separator = "_") {
|
|
|
60
49
|
}
|
|
61
50
|
|
|
62
51
|
for (const column of existingColumns) {
|
|
63
|
-
const definedInJson = Object.prototype.hasOwnProperty.call(
|
|
64
|
-
tableDef,
|
|
65
|
-
column
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const isValidDefinition =
|
|
69
|
-
definedInJson && fncs.isJsonObject(tableDef[column]);
|
|
52
|
+
const definedInJson = Object.prototype.hasOwnProperty.call(tableDef, column);
|
|
70
53
|
|
|
54
|
+
const isValidDefinition = definedInJson && fncs.isJsonObject(tableDef[column]);
|
|
55
|
+
|
|
71
56
|
if (isValidDefinition) continue;
|
|
72
57
|
|
|
73
58
|
console.log(
|
|
@@ -79,19 +64,10 @@ async function dropcolumn(config, tableJson, forceDropColumn, separator = "_") {
|
|
|
79
64
|
cstyler.yellow(column)
|
|
80
65
|
);
|
|
81
66
|
|
|
82
|
-
const referencingColumns =
|
|
83
|
-
await fncs.findReferencingFromColumns(
|
|
84
|
-
config,
|
|
85
|
-
databaseName,
|
|
86
|
-
tableName,
|
|
87
|
-
column
|
|
88
|
-
);
|
|
67
|
+
const referencingColumns = await fncs.findReferencingFromColumns(config, databaseName, tableName, column);
|
|
89
68
|
|
|
90
69
|
if (referencingColumns === null) {
|
|
91
|
-
console.error(
|
|
92
|
-
cstyler.bold.red("Failed to resolve FK references for column:"),
|
|
93
|
-
cstyler.hex("#00d9ffff")(column)
|
|
94
|
-
);
|
|
70
|
+
console.error(cstyler.bold.red("Failed to resolve FK references for column:"), cstyler.hex("#00d9ffff")(column));
|
|
95
71
|
return null;
|
|
96
72
|
}
|
|
97
73
|
|
|
@@ -108,12 +84,7 @@ async function dropcolumn(config, tableJson, forceDropColumn, separator = "_") {
|
|
|
108
84
|
let allFkRemoved = true;
|
|
109
85
|
|
|
110
86
|
for (const ref of referencingColumns) {
|
|
111
|
-
const removed = await fncs.removeForeignKeyFromColumn(
|
|
112
|
-
config,
|
|
113
|
-
ref.child_schema,
|
|
114
|
-
ref.child_table,
|
|
115
|
-
ref.child_columns[0]
|
|
116
|
-
);
|
|
87
|
+
const removed = await fncs.removeForeignKeyFromColumn(config, ref.child_schema, ref.child_table, ref.child_columns[0]);
|
|
117
88
|
|
|
118
89
|
if (removed === null) {
|
|
119
90
|
console.error(
|
|
@@ -127,21 +98,12 @@ async function dropcolumn(config, tableJson, forceDropColumn, separator = "_") {
|
|
|
127
98
|
}
|
|
128
99
|
|
|
129
100
|
if (!allFkRemoved) {
|
|
130
|
-
console.error(
|
|
131
|
-
cstyler.bold.red("Aborting column drop due to FK failures:"),
|
|
132
|
-
cstyler.hex("#00d9ffff")(column)
|
|
133
|
-
);
|
|
101
|
+
console.error(cstyler.bold.red("Aborting column drop due to FK failures:"), cstyler.hex("#00d9ffff")(column));
|
|
134
102
|
continue;
|
|
135
103
|
}
|
|
136
104
|
}
|
|
137
105
|
|
|
138
|
-
const dropped = await fncs.dropColumn(
|
|
139
|
-
config,
|
|
140
|
-
databaseName,
|
|
141
|
-
tableName,
|
|
142
|
-
column
|
|
143
|
-
);
|
|
144
|
-
|
|
106
|
+
const dropped = await fncs.dropColumn(config, databaseName, tableName, column);
|
|
145
107
|
if (dropped === null) {
|
|
146
108
|
console.error(
|
|
147
109
|
cstyler.bold.red("Failed to drop column:"),
|
|
@@ -151,17 +113,11 @@ async function dropcolumn(config, tableJson, forceDropColumn, separator = "_") {
|
|
|
151
113
|
}
|
|
152
114
|
console.log(cstyler.blue("Database: "), cstyler.hex("#00d9ffff")(databaseName), cstyler.blue(" Table: "), cstyler.hex("#00d9ffff")(tableName), cstyler.blue("Column: "), cstyler.hex("#00d9ffff")(column), cstyler.green("- dropped successfully."))
|
|
153
115
|
count += 1;
|
|
154
|
-
console.log(
|
|
155
|
-
cstyler.bold.green("Successfully dropped column:"),
|
|
156
|
-
cstyler.hex("#00d9ffff")(column),
|
|
157
|
-
cstyler.bold.green("from table:"),
|
|
158
|
-
cstyler.hex("#00d9ffff")(tableName)
|
|
159
|
-
);
|
|
160
116
|
}
|
|
161
117
|
}
|
|
162
118
|
}
|
|
163
119
|
if (count > 0) {
|
|
164
|
-
console.log(cstyler.green("Successfully dropped "
|
|
120
|
+
console.log(cstyler.bold.green("Successfully dropped " + count + " columns."));
|
|
165
121
|
} else {
|
|
166
122
|
console.log("There is not column found to be dropped.");
|
|
167
123
|
}
|
package/function.js
CHANGED
|
@@ -289,35 +289,33 @@ async function dropTable(config, databaseName, tableName) {
|
|
|
289
289
|
async function dropColumn(config, databaseName, tableName, columnName) {
|
|
290
290
|
let connection;
|
|
291
291
|
try {
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
// Ensure the database is selected in the config
|
|
293
|
+
const dbConfig = { ...config, database: databaseName };
|
|
294
|
+
connection = await mysql.createConnection(dbConfig);
|
|
294
295
|
|
|
295
|
-
// 1️⃣ Check if column exists
|
|
296
|
+
// 1️⃣ Check if column exists and get its metadata
|
|
296
297
|
const [columns] = await connection.query(
|
|
297
|
-
`SELECT COLUMN_NAME, COLUMN_KEY
|
|
298
|
-
FROM INFORMATION_SCHEMA.COLUMNS
|
|
298
|
+
`SELECT COLUMN_NAME, COLUMN_KEY, COLUMN_TYPE, EXTRA
|
|
299
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
299
300
|
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND COLUMN_NAME = ?`,
|
|
300
301
|
[databaseName, tableName, columnName]
|
|
301
302
|
);
|
|
302
303
|
|
|
303
|
-
// ❌ Column does not exist → return false
|
|
304
304
|
if (columns.length === 0) {
|
|
305
|
-
console.log(
|
|
306
|
-
`Column '${columnName}' does not exist in ${databaseName}.${tableName}`
|
|
307
|
-
);
|
|
305
|
+
console.log(`Column '${columnName}' does not exist in ${tableName}.`);
|
|
308
306
|
return false;
|
|
309
307
|
}
|
|
310
308
|
|
|
311
|
-
const columnKey = columns[0].COLUMN_KEY;
|
|
309
|
+
const columnKey = columns[0].COLUMN_KEY; // "PRI", "UNI", or ""
|
|
310
|
+
const columnType = columns[0].COLUMN_TYPE; // e.g., "int(11)"
|
|
311
|
+
const isAutoIncrement = columns[0].EXTRA.toLowerCase().includes('auto_increment');
|
|
312
312
|
|
|
313
|
-
// 2️⃣ Drop
|
|
313
|
+
// 2️⃣ Drop Foreign Key constraints first
|
|
314
314
|
const [fkConstraints] = await connection.query(
|
|
315
315
|
`SELECT CONSTRAINT_NAME
|
|
316
316
|
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
317
|
-
WHERE TABLE_SCHEMA = ?
|
|
318
|
-
|
|
319
|
-
AND COLUMN_NAME = ?
|
|
320
|
-
AND REFERENCED_TABLE_NAME IS NOT NULL`,
|
|
317
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND COLUMN_NAME = ?
|
|
318
|
+
AND REFERENCED_TABLE_NAME IS NOT NULL`,
|
|
321
319
|
[databaseName, tableName, columnName]
|
|
322
320
|
);
|
|
323
321
|
|
|
@@ -327,23 +325,28 @@ async function dropColumn(config, databaseName, tableName, columnName) {
|
|
|
327
325
|
);
|
|
328
326
|
}
|
|
329
327
|
|
|
330
|
-
// 3️⃣
|
|
331
|
-
|
|
328
|
+
// 3️⃣ Handle Primary Key and Auto-Increment Conflict
|
|
329
|
+
// If it's Auto-Increment, we MUST remove that attribute before dropping the key
|
|
330
|
+
if (isAutoIncrement) {
|
|
331
|
+
// Modifying without the 'AUTO_INCREMENT' keyword
|
|
332
332
|
await connection.query(
|
|
333
|
-
`ALTER TABLE \`${tableName}\`
|
|
333
|
+
`ALTER TABLE \`${tableName}\` MODIFY COLUMN \`${columnName}\` ${columnType}`
|
|
334
334
|
);
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
-
//
|
|
337
|
+
// Now it is safe to drop the Primary Key constraint if it was one
|
|
338
|
+
if (columnKey === "PRI") {
|
|
339
|
+
await connection.query(`ALTER TABLE \`${tableName}\` DROP PRIMARY KEY`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 4️⃣ Final Step: Drop the column
|
|
338
343
|
await connection.query(
|
|
339
344
|
`ALTER TABLE \`${tableName}\` DROP COLUMN \`${columnName}\``
|
|
340
345
|
);
|
|
341
346
|
|
|
342
|
-
console.log(
|
|
343
|
-
`Column '${columnName}' dropped successfully from ${databaseName}.${tableName}`
|
|
344
|
-
);
|
|
345
|
-
|
|
347
|
+
console.log(`Column '${columnName}' dropped successfully from ${tableName}`);
|
|
346
348
|
return true;
|
|
349
|
+
|
|
347
350
|
} catch (err) {
|
|
348
351
|
console.error("Error dropping column:", err.message);
|
|
349
352
|
return null;
|
|
@@ -1873,7 +1876,7 @@ async function checkDuplicates(config, databaseName, tableName, columnName) {
|
|
|
1873
1876
|
) AS dup_query`;
|
|
1874
1877
|
|
|
1875
1878
|
const [rows] = await connection.execute(sql);
|
|
1876
|
-
|
|
1879
|
+
|
|
1877
1880
|
// Returns true if duplicates exist, false if not
|
|
1878
1881
|
return rows[0].duplicateCount > 0;
|
|
1879
1882
|
|
|
@@ -1901,7 +1904,7 @@ async function cleanDuplicateRows(config, databaseName, tableName, columnName) {
|
|
|
1901
1904
|
AND t1.id > t2.id`;
|
|
1902
1905
|
|
|
1903
1906
|
const [result] = await connection.execute(sql);
|
|
1904
|
-
|
|
1907
|
+
|
|
1905
1908
|
// Returns the number of deleted rows
|
|
1906
1909
|
return result.affectedRows;
|
|
1907
1910
|
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dbtasker",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"funding": {
|
|
5
5
|
"type": "patreon",
|
|
6
6
|
"url": "https://www.patreon.com/kormoi/gift"
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
"cstyler": "^4.0.0",
|
|
46
46
|
"fs": "^0.0.1-security",
|
|
47
47
|
"mysql2": "^3.14.3",
|
|
48
|
-
"path": "^0.12.7"
|
|
48
|
+
"path": "^0.12.7",
|
|
49
|
+
"pg": "^8.16.3"
|
|
49
50
|
},
|
|
50
51
|
"repository": {
|
|
51
52
|
"type": "git",
|
package/pgfunctions.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { Client } = require('pg');
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const client = new Client({
|
|
7
|
+
user: 'postgres',
|
|
8
|
+
host: 'localhost',
|
|
9
|
+
database: 'postgres', // Connect to the default db first
|
|
10
|
+
password: 'your_password',
|
|
11
|
+
port: 5432,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
async function test() {
|
|
15
|
+
await client.connect();
|
|
16
|
+
const res = await client.query('SELECT NOW()');
|
|
17
|
+
console.log("Postgres Time:", res.rows[0].now);
|
|
18
|
+
await client.end();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Connects to Postgres and returns the Locale and Encoding settings.
|
|
22
|
+
*/
|
|
23
|
+
async function getPostgresLocale(config) {
|
|
24
|
+
const client = new Client(config);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await client.connect();
|
|
28
|
+
|
|
29
|
+
// We query three specific system settings:
|
|
30
|
+
// 1. server_encoding: The character set (e.g., UTF8)
|
|
31
|
+
// 2. lc_collate: The alphabetical sorting rules
|
|
32
|
+
// 3. lc_ctype: The character classification (language)
|
|
33
|
+
const query = `
|
|
34
|
+
SELECT
|
|
35
|
+
current_setting('server_encoding') as encoding,
|
|
36
|
+
current_setting('lc_collate') as collate,
|
|
37
|
+
current_setting('lc_ctype') as ctype
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const res = await client.query(query);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
success: true,
|
|
44
|
+
data: res.rows[0]
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("Error fetching Postgres settings:", error.message);
|
|
49
|
+
return { success: false, error: error.message };
|
|
50
|
+
} finally {
|
|
51
|
+
// Always close the client!
|
|
52
|
+
await client.end();
|
|
53
|
+
}
|
|
54
|
+
}
|
package/pgvalidation.js
ADDED
|
File without changes
|
|
File without changes
|