dbtasker 3.1.2 → 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 +29 -6
- package/addcolumn.js +54 -92
- package/altercolumn.js +10 -3
- 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,10 +26,10 @@ 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 (e.g., `3306`) |
|
|
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. |
|
|
@@ -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
|
}
|
|
@@ -442,7 +443,13 @@ async function alterColumnIfNeeded(config, jsondata, forceupdatecolumn, separato
|
|
|
442
443
|
if (!fncs.isJsonObject(jsondata[jsondb][jsontable][jsoncolumn])) { continue; }
|
|
443
444
|
if (!allcols.includes(jsoncolumn)) {
|
|
444
445
|
// column does not exist, skip it
|
|
445
|
-
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
|
+
}
|
|
446
453
|
continue;
|
|
447
454
|
}
|
|
448
455
|
// lets add the column
|
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
|