feat(mysql): add routine and trigger creation templates

This commit is contained in:
zipg authored and GitHub committed 2026-07-20 11:03:08 +08:00
1 parent bec6514fee
commit 71ce46e5fd
10 files changed
+200 -1

No files matched your search

@@ -66,6 +66,7 @@ import { resolveDefaultDatabase } from "@/lib/database/defaultDatabase";
import { canTreeNodePin, canTreeNodeShowExpander } from "@/lib/sidebar/sidebarTreeItemLayout";
import { objectTypesForGroupNode } from "@/lib/table/tableTree";
import { loadSidebarObjectGroup } from "@/lib/sidebar/sidebarObjectGroupRouting";
import { mysqlObjectTemplateForGroup } from "@/lib/sidebar/mysqlObjectTemplates";
import { buildTableDeleteTemplate, buildTableInsertTemplate, buildTableSelectTemplate, buildTableUpdateTemplate } from "@/lib/table/tableSqlTemplates";
import { driverStoreFocusForInstallError } from "@/lib/connection/agentDriverInstallHint";
import {
@@ -2865,6 +2866,16 @@ function createView() {
});
}
function createMysqlObjectTemplate() {
const node = activeNode.value;
if (!node.connectionId || !node.database) return;
const template = mysqlObjectTemplateForGroup(connectionStore.getConfig(node.connectionId), node);
if (!template) return;
connectionStore.activeConnectionId = node.connectionId;
const tabId = queryStore.createTab(node.connectionId, node.database, t(template.titleKey), "query", node.schema);
queryStore.updateSql(tabId, template.sql);
}
const canExpand = computed(() =>
canTreeNodeShowExpander({
type: activeNode.value.type,
@@ -4024,7 +4035,8 @@ function buildObjectGroupSidebarMenu(context: SidebarMenuFactoryContext): boolea
const { node, items } = context;
// 9. Group Labels (group-columns, group-tables, etc.)
if (isGroupLabel(node)) {
const hasGroupCreateAction = (node.type === "group-tables" && canCreateTable.value) || (node.type === "group-views" && !!node.connectionId && !!node.database);
const mysqlObjectTemplate = node.connectionId ? mysqlObjectTemplateForGroup(connectionStore.getConfig(node.connectionId), node) : null;
const hasGroupCreateAction = (node.type === "group-tables" && canCreateTable.value) || (node.type === "group-views" && !!node.connectionId && !!node.database) || !!mysqlObjectTemplate;
const canLoadAllObjectGroup = node.type === "group-tables" || node.type === "group-views" || node.type === "group-materialized-views";
if (node.type === "group-tables" && canCreateTable.value) {
items.push({ label: t("contextMenu.createTable"), action: createTable, icon: Plus });
@@ -4038,6 +4050,9 @@ function buildObjectGroupSidebarMenu(context: SidebarMenuFactoryContext): boolea
if (node.type === "group-views" && node.connectionId && node.database) {
items.push({ label: t("contextMenu.createView"), action: createView, icon: Plus });
}
if (mysqlObjectTemplate) {
items.push({ label: t(mysqlObjectTemplate.titleKey), action: createMysqlObjectTemplate, icon: Plus });
}
if (hasGroupCreateAction) {
items.push({ label: "", separator: true });
}
+3
View File
@@ -1644,6 +1644,9 @@ export default {
exportStructureCopied: "Structure copied",
createTable: "Create Table",
createView: "Create View",
createProcedure: "Create Procedure",
createFunction: "Create Function",
createTrigger: "Create Trigger",
dropTable: "Drop Table",
emptyTable: "Empty Table",
truncateTable: "Truncate Table",
+3
View File
@@ -1761,6 +1761,9 @@ export default withEnglishFallback({
dropExtension: "Eliminar extensión",
processList: "Lista de procesos",
serverDashboard: "Panel de control del servidor",
createProcedure: "Nuevo procedimiento almacenado",
createFunction: "Nueva función",
createTrigger: "Nuevo disparador",
},
visibleDatabases: {
title: "Bases de datos visibles",
+3
View File
@@ -1759,6 +1759,9 @@ export default withEnglishFallback({
dropExtension: "Elimina estensione",
processList: "Elenco processi",
serverDashboard: "Dashboard del server",
createProcedure: "Nuovo stored procedure",
createFunction: "Nuova funzione",
createTrigger: "Nuovo trigger",
},
visibleDatabases: {
title: "Database Visibili",
+3
View File
@@ -1760,6 +1760,9 @@ export default withEnglishFallback({
dropExtension: "拡張機能を削除",
processList: "プロセス一覧",
serverDashboard: "サーバーダッシュボード",
createProcedure: "新規ストアドプロシージャ",
createFunction: "新規関数",
createTrigger: "新規トリガー",
},
visibleDatabases: {
title: "表示するデータベース",
+3
View File
@@ -1761,6 +1761,9 @@ export default withEnglishFallback({
dropExtension: "Remover extensão",
processList: "Lista de Processos",
serverDashboard: "Painel do Servidor",
createProcedure: "Novo procedimento armazenado",
createFunction: "Nova função",
createTrigger: "Novo gatilho",
},
visibleDatabases: {
title: "Bancos de dados visíveis",
+3
View File
@@ -1644,6 +1644,9 @@ export default withEnglishFallback({
exportStructureCopied: "表结构已复制",
createTable: "新建表",
createView: "新建视图",
createProcedure: "新建存储过程",
createFunction: "新建函数",
createTrigger: "新建触发器",
dropTable: "删除表",
emptyTable: "清空数据",
truncateTable: "截断表",
+3
View File
@@ -1761,6 +1761,9 @@ export default withEnglishFallback({
dropExtension: "刪除擴展",
processList: "處理程序清單",
serverDashboard: "伺服器儀表板",
createProcedure: "新增儲存程序",
createFunction: "新增函數",
createTrigger: "新增觸發器",
},
visibleDatabases: {
title: "顯示資料庫",
@@ -0,0 +1,89 @@
import type { ConnectionConfig, TreeNode, TreeNodeType } from "@/types/database";
export type MysqlObjectTemplateKind = "procedure" | "function" | "trigger";
export interface MysqlObjectTemplate {
kind: MysqlObjectTemplateKind;
titleKey: "contextMenu.createProcedure" | "contextMenu.createFunction" | "contextMenu.createTrigger";
sql: string;
}
type MysqlTemplateConnection = Pick<ConnectionConfig, "db_type" | "driver_profile">;
type MysqlTemplateNode = Pick<TreeNode, "type" | "database" | "tableName">;
const templateKindByGroup: Partial<Record<TreeNodeType, MysqlObjectTemplateKind>> = {
"group-procedures": "procedure",
"group-functions": "function",
"group-triggers": "trigger",
};
export function supportsMysqlObjectTemplates(connection?: MysqlTemplateConnection): boolean {
if (connection?.db_type !== "mysql") return false;
const profile = connection.driver_profile?.trim().toLowerCase();
return !profile || profile === "mysql" || profile === "custom_mysql";
}
export function mysqlObjectTemplateForGroup(connection: MysqlTemplateConnection | undefined, node: MysqlTemplateNode): MysqlObjectTemplate | null {
if (!supportsMysqlObjectTemplates(connection) || !node.database) return null;
const kind = templateKindByGroup[node.type];
if (!kind) return null;
return buildMysqlObjectTemplate(kind, node.database, node.tableName);
}
export function buildMysqlObjectTemplate(kind: MysqlObjectTemplateKind, database: string, tableName?: string): MysqlObjectTemplate {
const qualifiedDatabase = quoteMysqlIdentifier(database);
if (kind === "procedure") {
const name = "new_procedure";
return {
kind,
titleKey: "contextMenu.createProcedure",
sql: `DELIMITER $$
CREATE PROCEDURE ${qualifiedDatabase}.${quoteMysqlIdentifier(name)}()
BEGIN
SELECT 1;
END$$
DELIMITER ;`,
};
}
if (kind === "function") {
const name = "new_function";
return {
kind,
titleKey: "contextMenu.createFunction",
sql: `DELIMITER $$
CREATE FUNCTION ${qualifiedDatabase}.${quoteMysqlIdentifier(name)}()
RETURNS INT
DETERMINISTIC
BEGIN
RETURN 0;
END$$
DELIMITER ;`,
};
}
const name = "new_trigger";
return {
kind,
titleKey: "contextMenu.createTrigger",
sql: `DELIMITER $$
CREATE TRIGGER ${qualifiedDatabase}.${quoteMysqlIdentifier(name)}
BEFORE INSERT ON ${qualifiedDatabase}.${quoteMysqlIdentifier(tableName || "table_name")}
FOR EACH ROW
BEGIN
SET @dbx_trigger_placeholder = 1;
END$$
DELIMITER ;`,
};
}
function quoteMysqlIdentifier(identifier: string): string {
return `\`${identifier.replaceAll("`", "``")}\``;
}