mysql-db-mcp 0.1.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 +374 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.js +83 -0
- package/dist/config.js.map +1 -0
- package/dist/db/MySqlClient.d.ts +16 -0
- package/dist/db/MySqlClient.js +115 -0
- package/dist/db/MySqlClient.js.map +1 -0
- package/dist/db/MySqlPool.d.ts +3 -0
- package/dist/db/MySqlPool.js +36 -0
- package/dist/db/MySqlPool.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.js +53 -0
- package/dist/logger.js.map +1 -0
- package/dist/security.d.ts +17 -0
- package/dist/security.js +165 -0
- package/dist/security.js.map +1 -0
- package/dist/tools/alterTableAddColumn.d.ts +3 -0
- package/dist/tools/alterTableAddColumn.js +49 -0
- package/dist/tools/alterTableAddColumn.js.map +1 -0
- package/dist/tools/alterTableAddIndex.d.ts +3 -0
- package/dist/tools/alterTableAddIndex.js +46 -0
- package/dist/tools/alterTableAddIndex.js.map +1 -0
- package/dist/tools/alterTableDropColumn.d.ts +3 -0
- package/dist/tools/alterTableDropColumn.js +36 -0
- package/dist/tools/alterTableDropColumn.js.map +1 -0
- package/dist/tools/alterTableDropIndex.d.ts +3 -0
- package/dist/tools/alterTableDropIndex.js +33 -0
- package/dist/tools/alterTableDropIndex.js.map +1 -0
- package/dist/tools/alterTableModifyColumn.d.ts +3 -0
- package/dist/tools/alterTableModifyColumn.js +46 -0
- package/dist/tools/alterTableModifyColumn.js.map +1 -0
- package/dist/tools/alterTableRenameColumn.d.ts +3 -0
- package/dist/tools/alterTableRenameColumn.js +38 -0
- package/dist/tools/alterTableRenameColumn.js.map +1 -0
- package/dist/tools/describeTable.d.ts +3 -0
- package/dist/tools/describeTable.js +18 -0
- package/dist/tools/describeTable.js.map +1 -0
- package/dist/tools/explainSelect.d.ts +3 -0
- package/dist/tools/explainSelect.js +18 -0
- package/dist/tools/explainSelect.js.map +1 -0
- package/dist/tools/insertRow.d.ts +3 -0
- package/dist/tools/insertRow.js +30 -0
- package/dist/tools/insertRow.js.map +1 -0
- package/dist/tools/insertRows.d.ts +3 -0
- package/dist/tools/insertRows.js +40 -0
- package/dist/tools/insertRows.js.map +1 -0
- package/dist/tools/listSchemas.d.ts +3 -0
- package/dist/tools/listSchemas.js +7 -0
- package/dist/tools/listSchemas.js.map +1 -0
- package/dist/tools/listTables.d.ts +3 -0
- package/dist/tools/listTables.js +16 -0
- package/dist/tools/listTables.js.map +1 -0
- package/dist/tools/querySelect.d.ts +3 -0
- package/dist/tools/querySelect.js +20 -0
- package/dist/tools/querySelect.js.map +1 -0
- package/dist/tools/shared.d.ts +43 -0
- package/dist/tools/shared.js +113 -0
- package/dist/tools/shared.js.map +1 -0
- package/package.json +41 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { closePool } from "./db/MySqlPool.js";
|
|
5
|
+
import { MySqlClient } from "./db/MySqlClient.js";
|
|
6
|
+
import { logger } from "./logger.js";
|
|
7
|
+
import { registerAlterTableAddColumnTool } from "./tools/alterTableAddColumn.js";
|
|
8
|
+
import { registerAlterTableAddIndexTool } from "./tools/alterTableAddIndex.js";
|
|
9
|
+
import { registerAlterTableDropColumnTool } from "./tools/alterTableDropColumn.js";
|
|
10
|
+
import { registerAlterTableDropIndexTool } from "./tools/alterTableDropIndex.js";
|
|
11
|
+
import { registerAlterTableModifyColumnTool } from "./tools/alterTableModifyColumn.js";
|
|
12
|
+
import { registerAlterTableRenameColumnTool } from "./tools/alterTableRenameColumn.js";
|
|
13
|
+
import { registerDescribeTableTool } from "./tools/describeTable.js";
|
|
14
|
+
import { registerExplainSelectTool } from "./tools/explainSelect.js";
|
|
15
|
+
import { registerInsertRowTool } from "./tools/insertRow.js";
|
|
16
|
+
import { registerInsertRowsTool } from "./tools/insertRows.js";
|
|
17
|
+
import { registerListSchemasTool } from "./tools/listSchemas.js";
|
|
18
|
+
import { registerListTablesTool } from "./tools/listTables.js";
|
|
19
|
+
import { registerQuerySelectTool } from "./tools/querySelect.js";
|
|
20
|
+
const server = new McpServer({
|
|
21
|
+
name: "mysql-db-mcp",
|
|
22
|
+
version: "0.1.0"
|
|
23
|
+
});
|
|
24
|
+
const client = new MySqlClient();
|
|
25
|
+
registerListSchemasTool(server, client);
|
|
26
|
+
registerListTablesTool(server, client);
|
|
27
|
+
registerDescribeTableTool(server, client);
|
|
28
|
+
registerQuerySelectTool(server, client);
|
|
29
|
+
registerExplainSelectTool(server, client);
|
|
30
|
+
registerInsertRowTool(server, client);
|
|
31
|
+
registerInsertRowsTool(server, client);
|
|
32
|
+
registerAlterTableAddColumnTool(server, client);
|
|
33
|
+
registerAlterTableModifyColumnTool(server, client);
|
|
34
|
+
registerAlterTableDropColumnTool(server, client);
|
|
35
|
+
registerAlterTableRenameColumnTool(server, client);
|
|
36
|
+
registerAlterTableAddIndexTool(server, client);
|
|
37
|
+
registerAlterTableDropIndexTool(server, client);
|
|
38
|
+
async function shutdown(signal) {
|
|
39
|
+
logger.info("Shutting down mysql-db-mcp", { signal });
|
|
40
|
+
await closePool();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
process.on("SIGINT", () => {
|
|
44
|
+
void shutdown("SIGINT");
|
|
45
|
+
});
|
|
46
|
+
process.on("SIGTERM", () => {
|
|
47
|
+
void shutdown("SIGTERM");
|
|
48
|
+
});
|
|
49
|
+
process.on("uncaughtException", (error) => {
|
|
50
|
+
logger.error("Uncaught exception", { message: error.message, stack: error.stack });
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
process.on("unhandledRejection", (reason) => {
|
|
54
|
+
logger.error("Unhandled rejection", { reason });
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
const transport = new StdioServerTransport();
|
|
58
|
+
await server.connect(transport);
|
|
59
|
+
logger.info("mysql-db-mcp started");
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const logger: {
|
|
2
|
+
debug: (message: string, meta?: Record<string, unknown>) => void;
|
|
3
|
+
info: (message: string, meta?: Record<string, unknown>) => void;
|
|
4
|
+
warn: (message: string, meta?: Record<string, unknown>) => void;
|
|
5
|
+
error: (message: string, meta?: Record<string, unknown>) => void;
|
|
6
|
+
};
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const levels = {
|
|
2
|
+
debug: 10,
|
|
3
|
+
info: 20,
|
|
4
|
+
warn: 30,
|
|
5
|
+
error: 40
|
|
6
|
+
};
|
|
7
|
+
function configuredLevel() {
|
|
8
|
+
const value = process.env.MYSQL_MCP_LOG_LEVEL?.toLowerCase();
|
|
9
|
+
if (value === "debug" || value === "info" || value === "warn" || value === "error") {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
return "info";
|
|
13
|
+
}
|
|
14
|
+
function redact(value) {
|
|
15
|
+
if (typeof value === "string") {
|
|
16
|
+
return value.replace(/(password|pwd|token|secret)=([^&\s]+)/gi, "$1=<redacted>");
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
return value.map((item) => redact(item));
|
|
20
|
+
}
|
|
21
|
+
if (value && typeof value === "object") {
|
|
22
|
+
const result = {};
|
|
23
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
24
|
+
if (/password|pwd|token|secret|connection|string|dsn|url/i.test(key)) {
|
|
25
|
+
result[key] = "<redacted>";
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
result[key] = redact(entry);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function write(level, message, meta) {
|
|
36
|
+
if (levels[level] < levels[configuredLevel()]) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const line = {
|
|
40
|
+
time: new Date().toISOString(),
|
|
41
|
+
level,
|
|
42
|
+
message,
|
|
43
|
+
...(meta ? { meta: redact(meta) } : {})
|
|
44
|
+
};
|
|
45
|
+
process.stderr.write(`${JSON.stringify(line)}\n`);
|
|
46
|
+
}
|
|
47
|
+
export const logger = {
|
|
48
|
+
debug: (message, meta) => write("debug", message, meta),
|
|
49
|
+
info: (message, meta) => write("info", message, meta),
|
|
50
|
+
warn: (message, meta) => write("warn", message, meta),
|
|
51
|
+
error: (message, meta) => write("error", message, meta)
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAA6B;IACvC,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,EAAE;IACR,KAAK,EAAE,EAAE;CACV,CAAC;AAEF,SAAS,eAAe;IACtB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,WAAW,EAAE,CAAC;IAC7D,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACnF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,yCAAyC,EAAE,eAAe,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YAC5E,IAAI,sDAAsD,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrE,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,KAAK,CAAC,KAAe,EAAE,OAAe,EAAE,IAA8B;IAC7E,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC,EAAE,CAAC;QAC9C,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG;QACX,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC9B,KAAK;QACL,OAAO;QACP,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxC,CAAC;IAEF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;IACzF,IAAI,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;IACvF,IAAI,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC;IACvF,KAAK,EAAE,CAAC,OAAe,EAAE,IAA8B,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC;CAC1F,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type SafeSqlValue = string | number | boolean | null | Date;
|
|
2
|
+
export declare function assertIdentifier(identifier: string, label?: string): void;
|
|
3
|
+
export declare function quoteIdentifier(identifier: string): string;
|
|
4
|
+
export declare function quoteQualifiedTable(schema: string, table: string): string;
|
|
5
|
+
export declare function assertSchemaAllowedForWrite(schema: string): void;
|
|
6
|
+
export declare function assertTableAllowedForWrite(table: string): void;
|
|
7
|
+
export declare function assertSchemaAllowedForDDL(schema: string): void;
|
|
8
|
+
export declare function assertTableAllowedForDDL(table: string): void;
|
|
9
|
+
export declare function assertWriteEnabled(confirm?: boolean): void;
|
|
10
|
+
export declare function assertDDLEnabled(confirm?: boolean, forceConfirm?: boolean): void;
|
|
11
|
+
export declare function assertWriteTarget(schema: string, table: string): void;
|
|
12
|
+
export declare function assertDDLTarget(schema: string, table: string): void;
|
|
13
|
+
export declare function validateSqlValue(value: unknown, label?: string): SafeSqlValue;
|
|
14
|
+
export declare function validateColumnType(columnType: string): string;
|
|
15
|
+
export declare function validateComment(comment: string | undefined): string | undefined;
|
|
16
|
+
export declare function assertSafeSelectSql(sql: string): void;
|
|
17
|
+
export declare function assertSafeExplainSql(sql: string): void;
|
package/dist/security.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { config } from "./config.js";
|
|
2
|
+
const identifierPattern = /^[A-Za-z0-9_$]+$/;
|
|
3
|
+
export function assertIdentifier(identifier, label = "identifier") {
|
|
4
|
+
if (identifier.length === 0 || identifier.length > 64 || !identifierPattern.test(identifier)) {
|
|
5
|
+
throw new Error(`${label} must contain only letters, numbers, underscores, and $.`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export function quoteIdentifier(identifier) {
|
|
9
|
+
assertIdentifier(identifier);
|
|
10
|
+
return `\`${identifier}\``;
|
|
11
|
+
}
|
|
12
|
+
export function quoteQualifiedTable(schema, table) {
|
|
13
|
+
return `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;
|
|
14
|
+
}
|
|
15
|
+
function normalizeName(value) {
|
|
16
|
+
return value.toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
function assertNotBlockedSchema(schema) {
|
|
19
|
+
assertIdentifier(schema, "schema");
|
|
20
|
+
const blocked = config.security.blockedSchemas.map(normalizeName);
|
|
21
|
+
if (blocked.includes(normalizeName(schema))) {
|
|
22
|
+
throw new Error(`schema '${schema}' is blocked.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function assertNotBlockedTable(table) {
|
|
26
|
+
assertIdentifier(table, "table");
|
|
27
|
+
const blocked = config.security.blockedTables.map(normalizeName);
|
|
28
|
+
if (blocked.includes(normalizeName(table))) {
|
|
29
|
+
throw new Error(`table '${table}' is blocked.`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function assertAllowedByList(name, allowed, label) {
|
|
33
|
+
if (allowed.length === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const normalizedAllowed = allowed.map(normalizeName);
|
|
37
|
+
if (!normalizedAllowed.includes(normalizeName(name))) {
|
|
38
|
+
throw new Error(`${label} '${name}' is not in the allowed list.`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function assertSchemaAllowedForWrite(schema) {
|
|
42
|
+
assertNotBlockedSchema(schema);
|
|
43
|
+
assertAllowedByList(schema, config.security.writeAllowedSchemas, "schema");
|
|
44
|
+
}
|
|
45
|
+
export function assertTableAllowedForWrite(table) {
|
|
46
|
+
assertNotBlockedTable(table);
|
|
47
|
+
assertAllowedByList(table, config.security.writeAllowedTables, "table");
|
|
48
|
+
}
|
|
49
|
+
export function assertSchemaAllowedForDDL(schema) {
|
|
50
|
+
assertNotBlockedSchema(schema);
|
|
51
|
+
assertAllowedByList(schema, config.security.ddlAllowedSchemas, "schema");
|
|
52
|
+
}
|
|
53
|
+
export function assertTableAllowedForDDL(table) {
|
|
54
|
+
assertNotBlockedTable(table);
|
|
55
|
+
assertAllowedByList(table, config.security.ddlAllowedTables, "table");
|
|
56
|
+
}
|
|
57
|
+
export function assertWriteEnabled(confirm) {
|
|
58
|
+
if (!config.security.enableWrite) {
|
|
59
|
+
throw new Error("Write tools are disabled. Set MYSQL_ENABLE_WRITE=true to enable them.");
|
|
60
|
+
}
|
|
61
|
+
if (config.security.requireConfirmation && confirm !== true) {
|
|
62
|
+
throw new Error("confirm: true is required for write tools.");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function assertDDLEnabled(confirm, forceConfirm = false) {
|
|
66
|
+
if (!config.security.enableDDL) {
|
|
67
|
+
throw new Error("DDL tools are disabled. Set MYSQL_ENABLE_DDL=true to enable them.");
|
|
68
|
+
}
|
|
69
|
+
if ((forceConfirm || config.security.requireConfirmation) && confirm !== true) {
|
|
70
|
+
throw new Error("confirm: true is required for DDL tools.");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function assertWriteTarget(schema, table) {
|
|
74
|
+
assertSchemaAllowedForWrite(schema);
|
|
75
|
+
assertTableAllowedForWrite(table);
|
|
76
|
+
}
|
|
77
|
+
export function assertDDLTarget(schema, table) {
|
|
78
|
+
assertSchemaAllowedForDDL(schema);
|
|
79
|
+
assertTableAllowedForDDL(table);
|
|
80
|
+
}
|
|
81
|
+
export function validateSqlValue(value, label = "value") {
|
|
82
|
+
if (value === undefined) {
|
|
83
|
+
throw new Error(`${label} cannot be undefined.`);
|
|
84
|
+
}
|
|
85
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date) {
|
|
86
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
87
|
+
throw new Error(`${label} must be a finite number.`);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
throw new Error(`${label} must be string, number, boolean, null, or Date.`);
|
|
92
|
+
}
|
|
93
|
+
export function validateColumnType(columnType) {
|
|
94
|
+
const normalized = columnType.trim().toUpperCase().replace(/\s+/g, "");
|
|
95
|
+
if (/^VARCHAR\((\d{1,5})\)$/.test(normalized) || /^CHAR\((\d{1,5})\)$/.test(normalized)) {
|
|
96
|
+
const size = Number(normalized.match(/\((\d+)\)/)?.[1]);
|
|
97
|
+
if (size >= 1 && size <= 65535) {
|
|
98
|
+
return normalized;
|
|
99
|
+
}
|
|
100
|
+
throw new Error("VARCHAR/CHAR length must be between 1 and 65535.");
|
|
101
|
+
}
|
|
102
|
+
if (/^DECIMAL\((\d{1,2}),(\d{1,2})\)$/.test(normalized)) {
|
|
103
|
+
const match = normalized.match(/^DECIMAL\((\d+),(\d+)\)$/);
|
|
104
|
+
const precision = Number(match?.[1]);
|
|
105
|
+
const scale = Number(match?.[2]);
|
|
106
|
+
if (precision >= 1 && precision <= 65 && scale >= 0 && scale <= 30 && scale <= precision) {
|
|
107
|
+
return normalized;
|
|
108
|
+
}
|
|
109
|
+
throw new Error("DECIMAL precision must be 1-65, scale must be 0-30, and scale must be <= precision.");
|
|
110
|
+
}
|
|
111
|
+
const allowedSimpleTypes = new Set([
|
|
112
|
+
"TEXT",
|
|
113
|
+
"MEDIUMTEXT",
|
|
114
|
+
"LONGTEXT",
|
|
115
|
+
"INT",
|
|
116
|
+
"INTEGER",
|
|
117
|
+
"BIGINT",
|
|
118
|
+
"SMALLINT",
|
|
119
|
+
"TINYINT",
|
|
120
|
+
"FLOAT",
|
|
121
|
+
"DOUBLE",
|
|
122
|
+
"DATE",
|
|
123
|
+
"DATETIME",
|
|
124
|
+
"TIMESTAMP",
|
|
125
|
+
"TIME",
|
|
126
|
+
"BOOLEAN",
|
|
127
|
+
"JSON"
|
|
128
|
+
]);
|
|
129
|
+
if (allowedSimpleTypes.has(normalized)) {
|
|
130
|
+
return normalized;
|
|
131
|
+
}
|
|
132
|
+
throw new Error("columnType is not allowed.");
|
|
133
|
+
}
|
|
134
|
+
export function validateComment(comment) {
|
|
135
|
+
if (comment === undefined) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
if (comment.length > 255) {
|
|
139
|
+
throw new Error("comment must be at most 255 characters.");
|
|
140
|
+
}
|
|
141
|
+
return comment;
|
|
142
|
+
}
|
|
143
|
+
export function assertSafeSelectSql(sql) {
|
|
144
|
+
const trimmed = sql.trim();
|
|
145
|
+
if (trimmed.length === 0) {
|
|
146
|
+
throw new Error("sql is required.");
|
|
147
|
+
}
|
|
148
|
+
if (trimmed.includes(";")) {
|
|
149
|
+
throw new Error("Multiple statements and semicolons are not allowed.");
|
|
150
|
+
}
|
|
151
|
+
if (/\/\*|\*\/|--|#/u.test(trimmed)) {
|
|
152
|
+
throw new Error("SQL comments are not allowed.");
|
|
153
|
+
}
|
|
154
|
+
if (!/^(select|with)\b/i.test(trimmed)) {
|
|
155
|
+
throw new Error("Only SELECT queries are allowed.");
|
|
156
|
+
}
|
|
157
|
+
const blocked = /\b(insert|update|delete|alter|create|drop|truncate|replace|merge|call|exec|execute|grant|revoke|commit|rollback|start\s+transaction|begin|lock|unlock|handler|load\s+data|into\s+outfile|into\s+dumpfile)\b/i;
|
|
158
|
+
if (blocked.test(trimmed)) {
|
|
159
|
+
throw new Error("SQL contains a blocked keyword.");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
export function assertSafeExplainSql(sql) {
|
|
163
|
+
assertSafeSelectSql(sql);
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertDDLEnabled, assertIdentifier, quoteIdentifier, validateColumnType } from "../security.js";
|
|
3
|
+
import { assertAndQuoteDDLTarget, buildColumnDefinition, primitiveValueSchema, resolveSchema, runTool } from "./shared.js";
|
|
4
|
+
export function registerAlterTableAddColumnTool(server, client) {
|
|
5
|
+
server.tool("alter_table_add_column", "Add a column to a table with a whitelisted MySQL column type. Disabled unless MYSQL_ENABLE_DDL=true.", {
|
|
6
|
+
schema: z.string().optional(),
|
|
7
|
+
table: z.string(),
|
|
8
|
+
columnName: z.string(),
|
|
9
|
+
columnType: z.string(),
|
|
10
|
+
nullable: z.boolean().optional(),
|
|
11
|
+
defaultValue: primitiveValueSchema.optional(),
|
|
12
|
+
comment: z.string().optional(),
|
|
13
|
+
afterColumn: z.string().optional(),
|
|
14
|
+
confirm: z.boolean().optional()
|
|
15
|
+
}, async (args) => runTool(async () => {
|
|
16
|
+
assertDDLEnabled(args.confirm);
|
|
17
|
+
const schemaName = resolveSchema(args.schema);
|
|
18
|
+
assertIdentifier(args.table, "table");
|
|
19
|
+
const tableSql = assertAndQuoteDDLTarget(schemaName, args.table);
|
|
20
|
+
const safeColumnType = validateColumnType(args.columnType);
|
|
21
|
+
const definition = buildColumnDefinition({
|
|
22
|
+
columnName: args.columnName,
|
|
23
|
+
columnType: safeColumnType,
|
|
24
|
+
nullable: args.nullable,
|
|
25
|
+
defaultValue: args.defaultValue,
|
|
26
|
+
hasDefaultValue: Object.prototype.hasOwnProperty.call(args, "defaultValue"),
|
|
27
|
+
comment: args.comment
|
|
28
|
+
});
|
|
29
|
+
const params = [...definition.params];
|
|
30
|
+
const pieces = [`ALTER TABLE ${tableSql}`, `ADD COLUMN ${definition.sql}`];
|
|
31
|
+
if (args.afterColumn !== undefined) {
|
|
32
|
+
assertIdentifier(args.afterColumn, "afterColumn");
|
|
33
|
+
if (!(await client.columnExists(schemaName, args.table, args.afterColumn))) {
|
|
34
|
+
throw new Error(`afterColumn '${args.afterColumn}' does not exist.`);
|
|
35
|
+
}
|
|
36
|
+
pieces.push(`AFTER ${quoteIdentifier(args.afterColumn)}`);
|
|
37
|
+
}
|
|
38
|
+
const executedSqlTemplate = pieces.join(" ");
|
|
39
|
+
const result = await client.execute(executedSqlTemplate, params);
|
|
40
|
+
return {
|
|
41
|
+
affectedRows: result.affectedRows,
|
|
42
|
+
schemaName,
|
|
43
|
+
tableName: args.table,
|
|
44
|
+
operation: "add_column",
|
|
45
|
+
executedSqlTemplate
|
|
46
|
+
};
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=alterTableAddColumn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alterTableAddColumn.js","sourceRoot":"","sources":["../../src/tools/alterTableAddColumn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertDDLEnabled, assertIdentifier, quoteIdentifier } from "../security.js";
|
|
3
|
+
import { assertAndQuoteDDLTarget, resolveSchema, runTool } from "./shared.js";
|
|
4
|
+
export function registerAlterTableAddIndexTool(server, client) {
|
|
5
|
+
server.tool("alter_table_add_index", "Add a normal or unique index to existing columns. Disabled unless MYSQL_ENABLE_DDL=true.", {
|
|
6
|
+
schema: z.string().optional(),
|
|
7
|
+
table: z.string(),
|
|
8
|
+
indexName: z.string(),
|
|
9
|
+
columns: z.array(z.string()),
|
|
10
|
+
unique: z.boolean().optional(),
|
|
11
|
+
confirm: z.boolean().optional()
|
|
12
|
+
}, async (args) => runTool(async () => {
|
|
13
|
+
assertDDLEnabled(args.confirm);
|
|
14
|
+
const schemaName = resolveSchema(args.schema);
|
|
15
|
+
assertIdentifier(args.table, "table");
|
|
16
|
+
assertIdentifier(args.indexName, "indexName");
|
|
17
|
+
if (args.indexName.toUpperCase() === "PRIMARY") {
|
|
18
|
+
throw new Error("Creating PRIMARY indexes is not allowed.");
|
|
19
|
+
}
|
|
20
|
+
if (args.columns.length === 0 || args.columns.length > 16) {
|
|
21
|
+
throw new Error("columns must contain 1 to 16 column names.");
|
|
22
|
+
}
|
|
23
|
+
for (const column of args.columns) {
|
|
24
|
+
assertIdentifier(column, "column");
|
|
25
|
+
}
|
|
26
|
+
const tableSql = assertAndQuoteDDLTarget(schemaName, args.table);
|
|
27
|
+
if (!(await client.columnsExist(schemaName, args.table, args.columns))) {
|
|
28
|
+
throw new Error("All index columns must exist.");
|
|
29
|
+
}
|
|
30
|
+
if (await client.indexExists(schemaName, args.table, args.indexName)) {
|
|
31
|
+
throw new Error(`index '${args.indexName}' already exists.`);
|
|
32
|
+
}
|
|
33
|
+
const indexKind = args.unique === true ? "ADD UNIQUE INDEX" : "ADD INDEX";
|
|
34
|
+
const columnSql = args.columns.map((column) => quoteIdentifier(column)).join(", ");
|
|
35
|
+
const executedSqlTemplate = `ALTER TABLE ${tableSql} ${indexKind} ${quoteIdentifier(args.indexName)} (${columnSql})`;
|
|
36
|
+
const result = await client.execute(executedSqlTemplate);
|
|
37
|
+
return {
|
|
38
|
+
affectedRows: result.affectedRows,
|
|
39
|
+
schemaName,
|
|
40
|
+
tableName: args.table,
|
|
41
|
+
operation: args.unique === true ? "add_unique_index" : "add_index",
|
|
42
|
+
executedSqlTemplate
|
|
43
|
+
};
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=alterTableAddIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alterTableAddIndex.js","sourceRoot":"","sources":["../../src/tools/alterTableAddIndex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertDDLEnabled, assertIdentifier, quoteIdentifier } from "../security.js";
|
|
3
|
+
import { assertAndQuoteDDLTarget, resolveSchema, runTool } from "./shared.js";
|
|
4
|
+
export function registerAlterTableDropColumnTool(server, client) {
|
|
5
|
+
server.tool("alter_table_drop_column", "Drop a column. This high-risk DDL always requires confirm: true.", {
|
|
6
|
+
schema: z.string().optional(),
|
|
7
|
+
table: z.string(),
|
|
8
|
+
columnName: z.string(),
|
|
9
|
+
confirm: z.boolean()
|
|
10
|
+
}, async (args) => runTool(async () => {
|
|
11
|
+
assertDDLEnabled(args.confirm, true);
|
|
12
|
+
const schemaName = resolveSchema(args.schema);
|
|
13
|
+
assertIdentifier(args.table, "table");
|
|
14
|
+
assertIdentifier(args.columnName, "columnName");
|
|
15
|
+
const tableSql = assertAndQuoteDDLTarget(schemaName, args.table);
|
|
16
|
+
if (!(await client.columnExists(schemaName, args.table, args.columnName))) {
|
|
17
|
+
throw new Error(`column '${args.columnName}' does not exist.`);
|
|
18
|
+
}
|
|
19
|
+
if (await client.isPrimaryKeyColumn(schemaName, args.table, args.columnName)) {
|
|
20
|
+
throw new Error("Refusing to drop primary key columns.");
|
|
21
|
+
}
|
|
22
|
+
if (await client.isUniqueIndexColumn(schemaName, args.table, args.columnName)) {
|
|
23
|
+
throw new Error("Refusing to drop columns that participate in a unique index.");
|
|
24
|
+
}
|
|
25
|
+
const executedSqlTemplate = `ALTER TABLE ${tableSql} DROP COLUMN ${quoteIdentifier(args.columnName)}`;
|
|
26
|
+
const result = await client.execute(executedSqlTemplate);
|
|
27
|
+
return {
|
|
28
|
+
affectedRows: result.affectedRows,
|
|
29
|
+
schemaName,
|
|
30
|
+
tableName: args.table,
|
|
31
|
+
operation: "drop_column",
|
|
32
|
+
executedSqlTemplate
|
|
33
|
+
};
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=alterTableDropColumn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alterTableDropColumn.js","sourceRoot":"","sources":["../../src/tools/alterTableDropColumn.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE9E,MAAM,UAAU,gCAAgC,CAAC,MAAiB,EAAE,MAAmB;IACrF,MAAM,CAAC,IAAI,CACT,yBAAyB,EACzB,kEAAkE,EAClE;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;KACrB,EACD,KAAK,EAAE,IAAI,EAAE,EAAE,CACb,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACtC,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,uBAAuB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACjE,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,UAAU,mBAAmB,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,MAAM,MAAM,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,MAAM,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAClF,CAAC;QAED,MAAM,mBAAmB,GAAG,eAAe,QAAQ,gBAAgB,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACtG,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACzD,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,UAAU;YACV,SAAS,EAAE,IAAI,CAAC,KAAK;YACrB,SAAS,EAAE,aAAa;YACxB,mBAAmB;SACpB,CAAC;IACJ,CAAC,CAAC,CACL,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertDDLEnabled, assertIdentifier, quoteIdentifier } from "../security.js";
|
|
3
|
+
import { assertAndQuoteDDLTarget, resolveSchema, runTool } from "./shared.js";
|
|
4
|
+
export function registerAlterTableDropIndexTool(server, client) {
|
|
5
|
+
server.tool("alter_table_drop_index", "Drop a non-primary index. This high-risk DDL always requires confirm: true.", {
|
|
6
|
+
schema: z.string().optional(),
|
|
7
|
+
table: z.string(),
|
|
8
|
+
indexName: z.string(),
|
|
9
|
+
confirm: z.boolean()
|
|
10
|
+
}, async (args) => runTool(async () => {
|
|
11
|
+
assertDDLEnabled(args.confirm, true);
|
|
12
|
+
const schemaName = resolveSchema(args.schema);
|
|
13
|
+
assertIdentifier(args.table, "table");
|
|
14
|
+
assertIdentifier(args.indexName, "indexName");
|
|
15
|
+
if (args.indexName.toUpperCase() === "PRIMARY") {
|
|
16
|
+
throw new Error("Dropping PRIMARY is not allowed.");
|
|
17
|
+
}
|
|
18
|
+
const tableSql = assertAndQuoteDDLTarget(schemaName, args.table);
|
|
19
|
+
if (!(await client.indexExists(schemaName, args.table, args.indexName))) {
|
|
20
|
+
throw new Error(`index '${args.indexName}' does not exist.`);
|
|
21
|
+
}
|
|
22
|
+
const executedSqlTemplate = `ALTER TABLE ${tableSql} DROP INDEX ${quoteIdentifier(args.indexName)}`;
|
|
23
|
+
const result = await client.execute(executedSqlTemplate);
|
|
24
|
+
return {
|
|
25
|
+
affectedRows: result.affectedRows,
|
|
26
|
+
schemaName,
|
|
27
|
+
tableName: args.table,
|
|
28
|
+
operation: "drop_index",
|
|
29
|
+
executedSqlTemplate
|
|
30
|
+
};
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=alterTableDropIndex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alterTableDropIndex.js","sourceRoot":"","sources":["../../src/tools/alterTableDropIndex.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE9E,MAAM,UAAU,+BAA+B,CAAC,MAAiB,EAAE,MAAmB;IACpF,MAAM,CAAC,IAAI,CACT,wBAAwB,EACxB,6EAA6E,EAC7E;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;KACrB,EACD,KAAK,EAAE,IAAI,EAAE,EAAE,CACb,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrC,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACtC,gBAAgB,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,SAAS,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,QAAQ,GAAG,uBAAuB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACjE,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,SAAS,mBAAmB,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,mBAAmB,GAAG,eAAe,QAAQ,eAAe,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACpG,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACzD,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,UAAU;YACV,SAAS,EAAE,IAAI,CAAC,KAAK;YACrB,SAAS,EAAE,YAAY;YACvB,mBAAmB;SACpB,CAAC;IACJ,CAAC,CAAC,CACL,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { assertDDLEnabled, assertIdentifier, validateColumnType } from "../security.js";
|
|
3
|
+
import { assertAndQuoteDDLTarget, buildColumnDefinition, primitiveValueSchema, resolveSchema, runTool } from "./shared.js";
|
|
4
|
+
export function registerAlterTableModifyColumnTool(server, client) {
|
|
5
|
+
server.tool("alter_table_modify_column", "Modify a column type, nullability, default, and comment with controlled DDL. Disabled unless MYSQL_ENABLE_DDL=true.", {
|
|
6
|
+
schema: z.string().optional(),
|
|
7
|
+
table: z.string(),
|
|
8
|
+
columnName: z.string(),
|
|
9
|
+
columnType: z.string(),
|
|
10
|
+
nullable: z.boolean().optional(),
|
|
11
|
+
defaultValue: primitiveValueSchema.optional(),
|
|
12
|
+
comment: z.string().optional(),
|
|
13
|
+
confirm: z.boolean().optional()
|
|
14
|
+
}, async (args) => runTool(async () => {
|
|
15
|
+
assertDDLEnabled(args.confirm);
|
|
16
|
+
const schemaName = resolveSchema(args.schema);
|
|
17
|
+
assertIdentifier(args.table, "table");
|
|
18
|
+
assertIdentifier(args.columnName, "columnName");
|
|
19
|
+
const tableSql = assertAndQuoteDDLTarget(schemaName, args.table);
|
|
20
|
+
if (!(await client.columnExists(schemaName, args.table, args.columnName))) {
|
|
21
|
+
throw new Error(`column '${args.columnName}' does not exist.`);
|
|
22
|
+
}
|
|
23
|
+
if (await client.isPrimaryKeyColumn(schemaName, args.table, args.columnName)) {
|
|
24
|
+
throw new Error("Refusing to modify primary key columns.");
|
|
25
|
+
}
|
|
26
|
+
const safeColumnType = validateColumnType(args.columnType);
|
|
27
|
+
const definition = buildColumnDefinition({
|
|
28
|
+
columnName: args.columnName,
|
|
29
|
+
columnType: safeColumnType,
|
|
30
|
+
nullable: args.nullable,
|
|
31
|
+
defaultValue: args.defaultValue,
|
|
32
|
+
hasDefaultValue: Object.prototype.hasOwnProperty.call(args, "defaultValue"),
|
|
33
|
+
comment: args.comment
|
|
34
|
+
});
|
|
35
|
+
const executedSqlTemplate = `ALTER TABLE ${tableSql} MODIFY COLUMN ${definition.sql}`;
|
|
36
|
+
const result = await client.execute(executedSqlTemplate, definition.params);
|
|
37
|
+
return {
|
|
38
|
+
affectedRows: result.affectedRows,
|
|
39
|
+
schemaName,
|
|
40
|
+
tableName: args.table,
|
|
41
|
+
operation: "modify_column",
|
|
42
|
+
executedSqlTemplate
|
|
43
|
+
};
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=alterTableModifyColumn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alterTableModifyColumn.js","sourceRoot":"","sources":["../../src/tools/alterTableModifyColumn.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3H,MAAM,UAAU,kCAAkC,CAAC,MAAiB,EAAE,MAAmB;IACvF,MAAM,CAAC,IAAI,CACT,2BAA2B,EAC3B,qHAAqH,EACrH;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;QAChC,YAAY,EAAE,oBAAoB,CAAC,QAAQ,EAAE;QAC7C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;KAChC,EACD,KAAK,EAAE,IAAI,EAAE,EAAE,CACb,OAAO,CAAC,KAAK,IAAI,EAAE;QACjB,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACtC,gBAAgB,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,uBAAuB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACjE,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,UAAU,mBAAmB,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,MAAM,MAAM,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,cAAc,GAAG,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3D,MAAM,UAAU,GAAG,qBAAqB,CAAC;YACvC,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,cAAc;YAC1B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,eAAe,EAAE,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;YAC3E,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;QACH,MAAM,mBAAmB,GAAG,eAAe,QAAQ,kBAAkB,UAAU,CAAC,GAAG,EAAE,CAAC;QACtF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5E,OAAO;YACL,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,UAAU;YACV,SAAS,EAAE,IAAI,CAAC,KAAK;YACrB,SAAS,EAAE,eAAe;YAC1B,mBAAmB;SACpB,CAAC;IACJ,CAAC,CAAC,CACL,CAAC;AACJ,CAAC"}
|