mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 06:00:39 +00:00
fix: distinguish materialized view from regular view
This commit is contained in:
@@ -393,7 +393,7 @@ async function loadDiagram() {
|
||||
await store.ensureConnected(connectionId.value);
|
||||
const querySchema = schema.value || database.value;
|
||||
const tableInfos = await api.listTables(connectionId.value, database.value, querySchema);
|
||||
const baseTables = tableInfos.filter((table) => table.table_type !== "VIEW").sort((a, b) => a.name.localeCompare(b.name));
|
||||
const baseTables = tableInfos.filter((table) => table.table_type !== "VIEW" && table.table_type !== "MATERIALIZED VIEW").sort((a, b) => a.name.localeCompare(b.name));
|
||||
totalTableCount.value = baseTables.length;
|
||||
|
||||
const loadedTables: DiagramTable[] = [];
|
||||
|
||||
@@ -357,7 +357,7 @@ async function loadTables(side: "source" | "target") {
|
||||
const database = side === "source" ? sourceDatabase.value : targetDatabase.value;
|
||||
if (!connectionId || !database) return;
|
||||
const schema = side === "source" ? sourceSchema.value || (await resolveSchema(connectionId, database, props.prefillSchema)) : targetSchema.value || (await resolveSchema(connectionId, database));
|
||||
const tables = (await api.listTables(connectionId, database, schema)).filter((table) => table.table_type !== "VIEW").map((table) => table.name);
|
||||
const tables = (await api.listTables(connectionId, database, schema)).filter((table) => table.table_type !== "VIEW" && table.table_type !== "MATERIALIZED VIEW").map((table) => table.name);
|
||||
|
||||
if (side === "source") {
|
||||
const preferredSelection = props.prefillTable && tables.includes(props.prefillTable) ? [props.prefillTable] : [...selectedSourceTables.value].filter((table) => tables.includes(table));
|
||||
|
||||
@@ -394,7 +394,7 @@ async function ensureTableObjectGroupsLoaded(target: Extract<ActiveTabSidebarTar
|
||||
function findTableObjectGroupNodes(nodes: TreeNode[], target: Extract<ActiveTabSidebarTarget, { type: "table" }>): TreeNode[] {
|
||||
const matches: TreeNode[] = [];
|
||||
for (const node of nodes) {
|
||||
if ((node.type === "group-tables" || node.type === "group-views") && node.connectionId === target.connectionId && sameTreeName(node.database, target.database) && (!target.schema || sameTreeName(node.schema, target.schema))) {
|
||||
if ((node.type === "group-tables" || node.type === "group-views" || node.type === "group-materialized-views") && node.connectionId === target.connectionId && sameTreeName(node.database, target.database) && (!target.schema || sameTreeName(node.schema, target.schema))) {
|
||||
matches.push(node);
|
||||
}
|
||||
if (node.children) {
|
||||
|
||||
@@ -201,6 +201,8 @@ function getIconInfo(node: TreeNode): { icon: any; colorClass: string } | null {
|
||||
return { icon: Table, colorClass: "text-green-500" };
|
||||
case "view":
|
||||
return { icon: Eye, colorClass: "text-purple-500" };
|
||||
case "materialized_view":
|
||||
return { icon: Eye, colorClass: "text-indigo-500" };
|
||||
case "column":
|
||||
if ((node.meta as ColumnInfo).is_primary_key) {
|
||||
return { icon: Columns3, colorClass: "text-orange-400" };
|
||||
@@ -249,6 +251,8 @@ function getIconInfo(node: TreeNode): { icon: any; colorClass: string } | null {
|
||||
return { icon: Table, colorClass: "text-green-500" };
|
||||
case "group-views":
|
||||
return { icon: Eye, colorClass: "text-purple-500" };
|
||||
case "group-materialized-views":
|
||||
return { icon: Eye, colorClass: "text-indigo-500" };
|
||||
case "group-procedures":
|
||||
return { icon: ScrollText, colorClass: "text-blue-500" };
|
||||
case "group-functions":
|
||||
@@ -266,8 +270,8 @@ function getIconInfo(node: TreeNode): { icon: any; colorClass: string } | null {
|
||||
}
|
||||
}
|
||||
|
||||
const groupTypes: Set<TreeNodeType> = new Set(["group-columns", "group-indexes", "group-fkeys", "group-triggers", "group-tables", "group-views", "group-procedures", "group-functions", "group-sequences", "group-packages", "group-partitions"]);
|
||||
const pinnableTypes: Set<TreeNodeType> = new Set(["connection-group", "database", "schema", "table", "view", "redis-db", "mongo-db", "mongo-collection", "elasticsearch-index"]);
|
||||
const groupTypes: Set<TreeNodeType> = new Set(["group-columns", "group-indexes", "group-fkeys", "group-triggers", "group-tables", "group-views", "group-materialized-views", "group-procedures", "group-functions", "group-sequences", "group-packages", "group-partitions"]);
|
||||
const pinnableTypes: Set<TreeNodeType> = new Set(["connection-group", "database", "schema", "table", "view", "materialized_view", "redis-db", "mongo-db", "mongo-collection", "elasticsearch-index"]);
|
||||
|
||||
function isGroupLabel(node: TreeNode): boolean {
|
||||
return groupTypes.has(node.type);
|
||||
@@ -282,7 +286,7 @@ function displayLabel(node: TreeNode): string {
|
||||
}
|
||||
|
||||
function visibleLabel(node: TreeNode): string {
|
||||
if (node.type === "table" || node.type === "view" || node.type === "mongo-collection" || node.type === "elasticsearch-index") {
|
||||
if (node.type === "table" || node.type === "view" || node.type === "materialized_view" || node.type === "mongo-collection" || node.type === "elasticsearch-index") {
|
||||
return sidebarDisplayTableName(node.label, settingsStore.editorSettings.sidebarHiddenTablePrefixes);
|
||||
}
|
||||
return displayLabel(node);
|
||||
@@ -326,7 +330,7 @@ async function toggle() {
|
||||
return;
|
||||
}
|
||||
|
||||
const databaseObjectGroup = node.type === "group-tables" || node.type === "group-views" || node.type === "group-procedures" || node.type === "group-functions" || node.type === "group-sequences" || node.type === "group-packages";
|
||||
const databaseObjectGroup = node.type === "group-tables" || node.type === "group-views" || node.type === "group-materialized-views" || node.type === "group-procedures" || node.type === "group-functions" || node.type === "group-sequences" || node.type === "group-packages";
|
||||
if (databaseObjectGroup && connectionStore.isTreeNodeChildrenLoaded(node.id)) {
|
||||
node.isExpanded = !node.isExpanded;
|
||||
emit("node-toggled", node, wasExpanded);
|
||||
@@ -382,7 +386,7 @@ async function toggle() {
|
||||
}
|
||||
} else if (node.type === "schema" && node.connectionId && hasTreeNodeDatabaseContext(node) && node.schema) {
|
||||
await connectionStore.loadTables(node.connectionId, node.database, node.schema);
|
||||
} else if ((node.type === "table" || node.type === "view") && node.connectionId && hasTreeNodeDatabaseContext(node)) {
|
||||
} else if ((node.type === "table" || node.type === "view" || node.type === "materialized_view") && node.connectionId && hasTreeNodeDatabaseContext(node)) {
|
||||
await connectionStore.loadTableGroups(node.connectionId, node.database, node.label, node.schema, node.id);
|
||||
} else if (node.type === "group-columns" && node.connectionId && hasTreeNodeDatabaseContext(node) && node.tableName) {
|
||||
await connectionStore.loadColumns(node.connectionId, node.database, node.tableName, node.schema, node.id);
|
||||
@@ -685,7 +689,7 @@ async function openUserAdmin() {
|
||||
|
||||
async function openData() {
|
||||
const node = props.node;
|
||||
if (!(node.type === "table" || node.type === "view") || !hasNodeDatabaseContext(node)) return;
|
||||
if (!(node.type === "table" || node.type === "view" || node.type === "materialized_view") || !hasNodeDatabaseContext(node)) return;
|
||||
const config = connectionStore.getConfig(node.connectionId);
|
||||
const traceId = uuid().slice(0, 8);
|
||||
const startedAt = performance.now();
|
||||
@@ -730,7 +734,7 @@ async function openData() {
|
||||
queryStore.setTableMeta(tabId, {
|
||||
schema: tableSchema,
|
||||
tableName: node.label,
|
||||
tableType: node.type === "view" ? "VIEW" : "TABLE",
|
||||
tableType: node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : "TABLE",
|
||||
columns: [],
|
||||
primaryKeys: [],
|
||||
});
|
||||
@@ -761,7 +765,7 @@ async function openData() {
|
||||
});
|
||||
columns = await api.getColumns(node.connectionId, node.database, querySchema, node.label);
|
||||
if (!isActive()) return;
|
||||
primaryKeys = editablePrimaryKeys(effectiveDbType, columns, node.type === "view" ? "VIEW" : "TABLE");
|
||||
primaryKeys = editablePrimaryKeys(effectiveDbType, columns, node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : "TABLE");
|
||||
console.info("[DBX][openData:get-columns:done]", {
|
||||
traceId,
|
||||
columnCount: columns.length,
|
||||
@@ -771,7 +775,7 @@ async function openData() {
|
||||
queryStore.setTableMeta(tabId, {
|
||||
schema: tableSchema,
|
||||
tableName: node.label,
|
||||
tableType: node.type === "view" ? "VIEW" : "TABLE",
|
||||
tableType: node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : "TABLE",
|
||||
columns,
|
||||
primaryKeys,
|
||||
});
|
||||
@@ -822,7 +826,7 @@ async function newQuery() {
|
||||
if (hasTreeNodeDatabaseContext(node)) {
|
||||
const tabId = queryStore.createTab(node.connectionId, node.database, undefined, "query", node.schema);
|
||||
// For table/view nodes, generate SELECT * FROM table_name SQL
|
||||
if (node.type === "table" || node.type === "view") {
|
||||
if (node.type === "table" || node.type === "view" || node.type === "materialized_view") {
|
||||
const config = connectionStore.getConfig(node.connectionId);
|
||||
const dbType = config ? effectiveDatabaseTypeForConnection(config) : undefined;
|
||||
const tableSchema = node.schema || node.database;
|
||||
@@ -1234,10 +1238,10 @@ function dropObjectSqlOptions(): DropObjectSqlOptions | null {
|
||||
}
|
||||
|
||||
function dropObjectSqlOptionsForNode(node: TreeNode): DropObjectSqlOptions | null {
|
||||
if (node.type !== "view" && node.type !== "procedure" && node.type !== "function") return null;
|
||||
if (node.type !== "view" && node.type !== "materialized_view" && node.type !== "procedure" && node.type !== "function") return null;
|
||||
return {
|
||||
databaseType: tableStructureDatabaseTypeForNode(node),
|
||||
objectType: node.type === "view" ? "VIEW" : node.type === "procedure" ? "PROCEDURE" : "FUNCTION",
|
||||
objectType: node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : node.type === "procedure" ? "PROCEDURE" : "FUNCTION",
|
||||
schema: node.schema,
|
||||
name: node.label,
|
||||
};
|
||||
@@ -1292,6 +1296,7 @@ function canDropTableChildObjectNode(node: TreeNode): boolean {
|
||||
|
||||
function dropObjectMenuLabel(): string {
|
||||
if (props.node.type === "view") return t("contextMenu.dropView");
|
||||
if (props.node.type === "materialized_view") return t("contextMenu.dropView");
|
||||
if (props.node.type === "procedure") return t("contextMenu.dropProcedure");
|
||||
if (props.node.type === "function") return t("contextMenu.dropFunction");
|
||||
return t("contextMenu.dropObject");
|
||||
@@ -1299,6 +1304,7 @@ function dropObjectMenuLabel(): string {
|
||||
|
||||
function dropObjectConfirmTitle(): string {
|
||||
if (props.node.type === "view") return t("contextMenu.confirmDropViewTitle");
|
||||
if (props.node.type === "materialized_view") return t("contextMenu.confirmDropViewTitle");
|
||||
if (props.node.type === "procedure") return t("contextMenu.confirmDropProcedureTitle");
|
||||
if (props.node.type === "function") return t("contextMenu.confirmDropFunctionTitle");
|
||||
return t("contextMenu.confirmDropObjectTitle");
|
||||
@@ -1306,6 +1312,7 @@ function dropObjectConfirmTitle(): string {
|
||||
|
||||
function dropObjectConfirmMessage(): string {
|
||||
if (props.node.type === "view") return t("contextMenu.confirmDropViewMessage", { name: props.node.label });
|
||||
if (props.node.type === "materialized_view") return t("contextMenu.confirmDropViewMessage", { name: props.node.label });
|
||||
if (props.node.type === "procedure") return t("contextMenu.confirmDropProcedureMessage", { name: props.node.label });
|
||||
if (props.node.type === "function") return t("contextMenu.confirmDropFunctionMessage", { name: props.node.label });
|
||||
return t("contextMenu.confirmDropObjectMessage", { name: props.node.label });
|
||||
@@ -1376,13 +1383,14 @@ function viewObjectSource() {
|
||||
|
||||
function viewObjectDdl() {
|
||||
const node = props.node;
|
||||
if (node.type !== "view" || !node.connectionId || !node.database) return;
|
||||
if ((node.type !== "view" && node.type !== "materialized_view") || !node.connectionId || !node.database) return;
|
||||
const schema = node.schema || node.database;
|
||||
const objectType = node.type === "materialized_view" ? "MATERIALIZED_VIEW" : "VIEW";
|
||||
connectionStore
|
||||
.ensureConnected(node.connectionId)
|
||||
.then(() => {
|
||||
connectionStore.activeConnectionId = node.connectionId!;
|
||||
return api.getObjectSource(node.connectionId!, node.database!, schema, node.label, "VIEW");
|
||||
return api.getObjectSource(node.connectionId!, node.database!, schema, node.label, objectType);
|
||||
})
|
||||
.then(async (result) => {
|
||||
const connection = connectionStore.getConfig(node.connectionId!);
|
||||
@@ -1434,7 +1442,7 @@ function requestDropTableChildObject() {
|
||||
|
||||
function canDropTreeNode(node: TreeNode): boolean {
|
||||
if (node.type === "table") return !!node.connectionId && !!node.database;
|
||||
if (node.type === "view" || node.type === "procedure" || node.type === "function") {
|
||||
if (node.type === "view" || node.type === "materialized_view" || node.type === "procedure" || node.type === "function") {
|
||||
return !!node.connectionId && !!node.database && !!dropObjectSqlOptionsForNode(node);
|
||||
}
|
||||
return canDropTableChildObjectNode(node);
|
||||
@@ -1523,6 +1531,7 @@ function requestDropSelectedNode(): boolean {
|
||||
function nodeRenameObjectType(): RenameableObjectType | null {
|
||||
if (props.node.type === "table") return "TABLE";
|
||||
if (props.node.type === "view") return "VIEW";
|
||||
if (props.node.type === "materialized_view") return "MATERIALIZED_VIEW";
|
||||
if (props.node.type === "procedure") return "PROCEDURE";
|
||||
if (props.node.type === "function") return "FUNCTION";
|
||||
return null;
|
||||
@@ -1622,9 +1631,9 @@ async function confirmDropObject() {
|
||||
await connectionStore.ensureConnected(node.connectionId);
|
||||
const sql = dropObjectPreviewSql.value || (await buildDropObjectSql(options));
|
||||
await api.executeQuery(node.connectionId, node.database, sql, node.schema);
|
||||
const msgKey = node.type === "view" ? "contextMenu.dropViewSuccess" : node.type === "procedure" ? "contextMenu.dropProcedureSuccess" : "contextMenu.dropFunctionSuccess";
|
||||
const msgKey = node.type === "view" ? "contextMenu.dropViewSuccess" : node.type === "materialized_view" ? "contextMenu.dropViewSuccess" : node.type === "procedure" ? "contextMenu.dropProcedureSuccess" : "contextMenu.dropFunctionSuccess";
|
||||
toast(t(msgKey, { name: node.label }), 3000);
|
||||
if (node.type === "view") {
|
||||
if (node.type === "view" || node.type === "materialized_view") {
|
||||
connectionStore.removeTreeNode(node.id);
|
||||
} else {
|
||||
await refreshTableList(node);
|
||||
@@ -2528,8 +2537,8 @@ function openDatabaseExport() {
|
||||
connectionStore.databaseExportSource = {
|
||||
connectionId: node.connectionId,
|
||||
database: node.database,
|
||||
schema: node.type === "schema" || node.type === "table" || node.type === "view" ? node.schema : undefined,
|
||||
tableName: node.type === "table" || node.type === "view" ? node.label : undefined,
|
||||
schema: node.type === "schema" || node.type === "table" || node.type === "view" || node.type === "materialized_view" ? node.schema : undefined,
|
||||
tableName: node.type === "table" || node.type === "view" || node.type === "materialized_view" ? node.label : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2782,7 +2791,7 @@ const TABLE_REFERENCE_DRAG_THRESHOLD = 5;
|
||||
const TABLE_REFERENCE_DRAGGING_CLASS = "dbx-table-reference-dragging";
|
||||
const canDragTableReference = computed(() => {
|
||||
if (props.dragDisabled || !props.node.connectionId || props.node.database == null) return false;
|
||||
if (props.node.type === "table" || props.node.type === "view") return true;
|
||||
if (props.node.type === "table" || props.node.type === "view" || props.node.type === "materialized_view") return true;
|
||||
return props.node.type === "column" && !!props.node.tableName;
|
||||
});
|
||||
|
||||
@@ -3209,8 +3218,8 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
return items;
|
||||
}
|
||||
|
||||
// 6. Table / View
|
||||
if (node.type === "table" || node.type === "view") {
|
||||
// 6. Table / View / Materialized View
|
||||
if (node.type === "table" || node.type === "view" || node.type === "materialized_view") {
|
||||
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
|
||||
items.push({ label: "", separator: true });
|
||||
items.push({ label: t("contextMenu.viewData"), action: openData, icon: TableProperties });
|
||||
@@ -3224,7 +3233,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
icon: FileCode,
|
||||
});
|
||||
}
|
||||
if (node.type === "view") {
|
||||
if (node.type === "view" || node.type === "materialized_view") {
|
||||
items.push({ label: t("contextMenu.editView"), action: viewObjectSource, icon: Pencil });
|
||||
items.push({ label: t("contextMenu.viewSource"), action: viewObjectSource, icon: Code2 });
|
||||
items.push({ label: t("contextMenu.viewDdl"), action: viewObjectDdl, icon: FileCode });
|
||||
@@ -3240,7 +3249,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
shortcut: shortcutRename,
|
||||
});
|
||||
}
|
||||
if (node.type === "view") {
|
||||
if (node.type === "view" || node.type === "materialized_view") {
|
||||
items.push({
|
||||
label: deleteMenuLabel(t("contextMenu.dropView")),
|
||||
action: deleteMenuAction(requestDropObject),
|
||||
@@ -3475,7 +3484,10 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
/>
|
||||
<span v-else ref="labelRef" :class="labelWidthClass">{{ visibleLabel(node) }}</span>
|
||||
<span
|
||||
v-if="(node.type === 'group-tables' || node.type === 'group-views' || node.type === 'group-procedures' || node.type === 'group-functions' || node.type === 'group-sequences' || node.type === 'group-packages' || node.type === 'group-partitions') && node.objectCount != null"
|
||||
v-if="
|
||||
(node.type === 'group-tables' || node.type === 'group-views' || node.type === 'group-materialized-views' || node.type === 'group-procedures' || node.type === 'group-functions' || node.type === 'group-sequences' || node.type === 'group-packages' || node.type === 'group-partitions') &&
|
||||
node.objectCount != null
|
||||
"
|
||||
class="text-muted-foreground text-[10px] shrink-0"
|
||||
>{{ node.objectCount }}</span
|
||||
>
|
||||
|
||||
@@ -1220,6 +1220,7 @@
|
||||
triggers: "Triggers",
|
||||
tables: "Tables",
|
||||
views: "Views",
|
||||
materializedViews: "Materialized Views",
|
||||
procedures: "Procedures",
|
||||
functions: "Functions",
|
||||
sequences: "Sequences",
|
||||
|
||||
@@ -1219,6 +1219,7 @@
|
||||
triggers: "触发器",
|
||||
tables: "表",
|
||||
views: "视图",
|
||||
materializedViews: "物化视图",
|
||||
procedures: "存储过程",
|
||||
functions: "函数",
|
||||
sequences: "序列",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
export type SidebarObjectKind = "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
export type SidebarObjectKind = "TABLE" | "VIEW" | "MATERIALIZED_VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
|
||||
export interface DatabaseObjectCapabilities {
|
||||
sidebarObjects: SidebarObjectKind[];
|
||||
@@ -13,8 +13,9 @@ const TABLE_VIEW_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW"];
|
||||
const TABLE_VIEW_PROCEDURE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "PROCEDURE"];
|
||||
const TABLE_FUNCTION_OBJECTS: SidebarObjectKind[] = ["TABLE", "FUNCTION"];
|
||||
const ROUTINE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "PROCEDURE", "FUNCTION"];
|
||||
const POSTGRES_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE"];
|
||||
const ORACLE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "PACKAGE", "PACKAGE_BODY"];
|
||||
const POSTGRES_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE"];
|
||||
const POSTGRES_LIKE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION"];
|
||||
const ORACLE_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "PACKAGE", "PACKAGE_BODY"];
|
||||
|
||||
const TABLE_ONLY_TYPES = new Set<DatabaseType>(["influxdb"]);
|
||||
const TABLE_FUNCTION_TYPES = new Set<DatabaseType>(["manticoresearch"]);
|
||||
@@ -23,6 +24,7 @@ const TABLE_VIEW_ONLY_TYPES = new Set<DatabaseType>(["sqlite", "rqlite", "turso"
|
||||
|
||||
const ORACLE_PACKAGE_TYPES = new Set<DatabaseType>(["oracle", "oceanbase-oracle"]);
|
||||
const POSTGRES_SEQUENCE_TYPES = new Set<DatabaseType>(["postgres", "gaussdb", "kwdb", "opengauss"]);
|
||||
const POSTGRES_LIKE_TYPES = new Set<DatabaseType>(["kingbase", "highgo", "vastbase", "redshift"]);
|
||||
|
||||
export function databaseObjectCapabilities(dbType?: DatabaseType): DatabaseObjectCapabilities {
|
||||
const sidebarObjects = sidebarObjectKindsForDatabase(dbType);
|
||||
@@ -41,6 +43,7 @@ export function sidebarObjectKindsForDatabase(dbType?: DatabaseType): SidebarObj
|
||||
if (TABLE_VIEW_PROCEDURE_TYPES.has(dbType)) return [...TABLE_VIEW_PROCEDURE_OBJECTS];
|
||||
if (TABLE_VIEW_ONLY_TYPES.has(dbType)) return [...TABLE_VIEW_OBJECTS];
|
||||
if (POSTGRES_SEQUENCE_TYPES.has(dbType)) return [...POSTGRES_OBJECTS];
|
||||
if (POSTGRES_LIKE_TYPES.has(dbType)) return [...POSTGRES_LIKE_OBJECTS];
|
||||
return [...ROUTINE_OBJECTS];
|
||||
}
|
||||
|
||||
@@ -48,6 +51,7 @@ export function normalizeSidebarObjectKind(type: string): SidebarObjectKind {
|
||||
const value = type.toUpperCase();
|
||||
if (value.includes("PACKAGE BODY") || value.includes("PACKAGE_BODY")) return "PACKAGE_BODY";
|
||||
if (value.includes("PACKAGE")) return "PACKAGE";
|
||||
if (value.includes("MATERIALIZED") && value.includes("VIEW")) return "MATERIALIZED_VIEW";
|
||||
if (value.includes("VIEW")) return "VIEW";
|
||||
if (value.includes("SEQ")) return "SEQUENCE";
|
||||
if (value.includes("PROC")) return "PROCEDURE";
|
||||
|
||||
@@ -6,7 +6,7 @@ export type ObjectBrowserRow = {
|
||||
id: string;
|
||||
name: string;
|
||||
schema?: string;
|
||||
type: "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
type: "TABLE" | "VIEW" | "MATERIALIZED_VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
comment?: string | null;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
@@ -23,6 +23,7 @@ export function normalizeObjectBrowserType(type: string): ObjectBrowserRow["type
|
||||
const value = type.toUpperCase();
|
||||
if (value.includes("PACKAGE BODY") || value.includes("PACKAGE_BODY")) return "PACKAGE_BODY";
|
||||
if (value.includes("PACKAGE")) return "PACKAGE";
|
||||
if (value.includes("MATERIALIZED") && value.includes("VIEW")) return "MATERIALIZED_VIEW";
|
||||
if (value.includes("VIEW")) return "VIEW";
|
||||
if (value.includes("SEQ")) return "SEQUENCE";
|
||||
if (value.includes("PROC")) return "PROCEDURE";
|
||||
|
||||
@@ -23,8 +23,8 @@ export function supportsObjectRename(databaseType: DatabaseType | undefined, obj
|
||||
}
|
||||
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso") return objectType === "TABLE";
|
||||
if (databaseType === "mysql" || databaseType === "goldendb") return objectType === "TABLE" || objectType === "VIEW";
|
||||
if (postgresLikeRenameTypes.has(databaseType)) return objectType === "TABLE" || objectType === "VIEW";
|
||||
if (oracleLikeRenameTypes.has(databaseType)) return objectType === "TABLE" || objectType === "VIEW";
|
||||
if (postgresLikeRenameTypes.has(databaseType)) return objectType === "TABLE" || objectType === "VIEW" || objectType === "MATERIALIZED_VIEW";
|
||||
if (oracleLikeRenameTypes.has(databaseType)) return objectType === "TABLE" || objectType === "VIEW" || objectType === "MATERIALIZED_VIEW";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ export function matchesTarget(node: TreeNode, target: ActiveTabSidebarTarget): b
|
||||
return node.type === "saved-sql-file" && node.savedSqlId === target.savedSqlId;
|
||||
}
|
||||
|
||||
return (node.type === "table" || node.type === "view") && node.connectionId === target.connectionId && node.database === target.database && schemaMatches(node, target.schema) && node.label === target.tableName;
|
||||
return (node.type === "table" || node.type === "view" || node.type === "materialized_view") && node.connectionId === target.connectionId && node.database === target.database && schemaMatches(node, target.schema) && node.label === target.tableName;
|
||||
}
|
||||
|
||||
export function findSidebarNodeForActiveTab(tab: QueryTab | undefined | null, flatNodes: readonly FlatTreeNode[]): FlatTreeNode | null {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { TreeNodeType } from "@/types/database";
|
||||
|
||||
const leafTypes: Set<TreeNodeType> = new Set(["column", "index", "fkey", "trigger", "procedure", "function", "package", "package-body", "object-browser", "redis-db", "mongo-collection", "elasticsearch-index", "user-admin", "saved-sql-file", "load-more"]);
|
||||
|
||||
const fullWidthLabelTypes: Set<TreeNodeType> = new Set(["table", "view", "mongo-collection", "elasticsearch-index"]);
|
||||
const fullWidthLabelTypes: Set<TreeNodeType> = new Set(["table", "view", "materialized_view", "mongo-collection", "elasticsearch-index"]);
|
||||
|
||||
const emptyContainerTypes: Set<TreeNodeType> = new Set(["saved-sql-root", "saved-sql-folder"]);
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ function makeTableTreeEntry({
|
||||
const node: TreeNode = {
|
||||
id: `${nodeId}:${nodeIdSchemaSuffix}${name}`,
|
||||
label: name,
|
||||
type: objectType === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
type: objectType === "VIEW" ? ("view" as const) : objectType === "MATERIALIZED_VIEW" ? ("materialized_view" as const) : ("table" as const),
|
||||
comment,
|
||||
connectionId,
|
||||
database,
|
||||
@@ -213,7 +213,7 @@ export function mergeTableInfosIntoObjects(objects: readonly ObjectInfo[], table
|
||||
|
||||
for (const table of tables) {
|
||||
const objectType = normalizeObjectType(table.table_type);
|
||||
if (objectType !== "TABLE" && objectType !== "VIEW") continue;
|
||||
if (objectType !== "TABLE" && objectType !== "VIEW" && objectType !== "MATERIALIZED_VIEW") continue;
|
||||
const name = normalizeDatabaseObjectName(table.name);
|
||||
if (!name) continue;
|
||||
const matchingObject = objects.find((obj) => {
|
||||
@@ -307,7 +307,7 @@ export function hasTablePartitionGroups(node: TreeNode): boolean {
|
||||
|
||||
export type DatabaseObjectTreeKind = SidebarObjectKind;
|
||||
|
||||
function buildObjectTreeEntries({ nodeId, connectionId, database, schema, objects, objectType }: { nodeId: string; connectionId: string; database: string; schema?: string; objects: ObjectInfo[]; objectType: "TABLE" | "VIEW" }): TreeNode[] {
|
||||
function buildObjectTreeEntries({ nodeId, connectionId, database, schema, objects, objectType }: { nodeId: string; connectionId: string; database: string; schema?: string; objects: ObjectInfo[]; objectType: "TABLE" | "VIEW" | "MATERIALIZED_VIEW" }): TreeNode[] {
|
||||
const entries = objects.flatMap((obj) => {
|
||||
const name = normalizeDatabaseObjectName(obj.name);
|
||||
if (!name) return [];
|
||||
@@ -337,7 +337,7 @@ export function buildSimpleObjectTreeNodes({ nodeId, connectionId, database, sch
|
||||
|
||||
for (const obj of objects) {
|
||||
const objectType = normalizeObjectType(obj.object_type);
|
||||
if (!["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE", "PACKAGE", "PACKAGE_BODY"].includes(objectType)) {
|
||||
if (!["TABLE", "VIEW", "MATERIALIZED_VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE", "PACKAGE", "PACKAGE_BODY"].includes(objectType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ export function buildSimpleObjectTreeNodes({ nodeId, connectionId, database, sch
|
||||
} else {
|
||||
const simpleNodeType = simpleObjectNodeType(objectType);
|
||||
objectNodes.push({
|
||||
id: objectType === "VIEW" ? entry.node.id : `${nodeId}:${childSchema ? `${childSchema}:` : ""}${name}:${objectType}`,
|
||||
id: objectType === "VIEW" || objectType === "MATERIALIZED_VIEW" ? entry.node.id : `${nodeId}:${childSchema ? `${childSchema}:` : ""}${name}:${objectType}`,
|
||||
label: name,
|
||||
type: simpleNodeType,
|
||||
comment: obj.comment,
|
||||
@@ -383,6 +383,7 @@ export function buildSimpleObjectTreeNodes({ nodeId, connectionId, database, sch
|
||||
|
||||
function simpleObjectNodeType(objectType: DatabaseObjectTreeKind): TreeNodeType {
|
||||
if (objectType === "VIEW") return "view";
|
||||
if (objectType === "MATERIALIZED_VIEW") return "materialized_view";
|
||||
if (objectType === "PROCEDURE") return "procedure";
|
||||
if (objectType === "FUNCTION") return "function";
|
||||
if (objectType === "SEQUENCE") return "sequence";
|
||||
@@ -404,6 +405,13 @@ const groupDefs: Array<{
|
||||
}> = [
|
||||
{ key: "__tables", label: "tree.tables", objectTypes: ["TABLE"], nodeType: "group-tables", childType: "table" },
|
||||
{ key: "__views", label: "tree.views", objectTypes: ["VIEW"], nodeType: "group-views", childType: "view" },
|
||||
{
|
||||
key: "__materialized_views",
|
||||
label: "tree.materializedViews",
|
||||
objectTypes: ["MATERIALIZED_VIEW"],
|
||||
nodeType: "group-materialized-views",
|
||||
childType: "materialized_view",
|
||||
},
|
||||
{
|
||||
key: "__procedures",
|
||||
label: "tree.procedures",
|
||||
@@ -434,7 +442,7 @@ const groupDefs: Array<{
|
||||
},
|
||||
];
|
||||
|
||||
const objectGroupNodeTypes = new Set<TreeNodeType>(["group-tables", "group-views", "group-procedures", "group-functions", "group-sequences", "group-packages"]);
|
||||
const objectGroupNodeTypes = new Set<TreeNodeType>(["group-tables", "group-views", "group-materialized-views", "group-procedures", "group-functions", "group-sequences", "group-packages"]);
|
||||
|
||||
export function buildObjectGroupPlaceholderNodes({ nodeId, connectionId, database, schema, objectTypes }: { nodeId: string; connectionId: string; database: string; schema?: string; objectTypes: DatabaseObjectTreeKind[] }): TreeNode[] {
|
||||
const supported = new Set(objectTypes);
|
||||
@@ -483,7 +491,7 @@ export function buildGroupedObjectTreeNodes({ nodeId, connectionId, database, sc
|
||||
for (const def of groupDefs) {
|
||||
const items = def.objectTypes.flatMap((objectType) => buckets.get(objectType) ?? []);
|
||||
if (!items?.length) continue;
|
||||
const isExpandable = def.nodeType === "group-tables" || def.nodeType === "group-views";
|
||||
const isExpandable = def.nodeType === "group-tables" || def.nodeType === "group-views" || def.nodeType === "group-materialized-views";
|
||||
const children = isExpandable
|
||||
? buildObjectTreeEntries({
|
||||
nodeId: `${nodeId}:${def.key}`,
|
||||
|
||||
@@ -6,16 +6,17 @@ export type TreeNodeRowDoubleClickAction = "open-data" | "open-object-browser" |
|
||||
export type SidebarSelectionCopyAction = "copy-name" | "none";
|
||||
export type SidebarActivation = "single" | "double";
|
||||
|
||||
const dataNodeTypes = new Set<TreeNodeType>(["table", "view"]);
|
||||
const dataNodeTypes = new Set<TreeNodeType>(["table", "view", "materialized_view"]);
|
||||
const toggleLeafNodeTypes = new Set<TreeNodeType>(["redis-db", "mongo-collection", "elasticsearch-index", "user-admin"]);
|
||||
const objectBrowserNodeTypes = new Set<TreeNodeType>(["database", "schema", "object-browser"]);
|
||||
const sourceNodeTypes = new Set<TreeNodeType>(["procedure", "function", "sequence", "package", "package-body"]);
|
||||
const sourceNodeTypes = new Set<TreeNodeType>(["materialized_view", "procedure", "function", "sequence", "package", "package-body"]);
|
||||
const savedSqlNodeTypes = new Set<TreeNodeType>(["saved-sql-file"]);
|
||||
const tableChildGroupNodeTypes = new Set<TreeNodeType>(["group-columns", "group-indexes", "group-fkeys", "group-triggers", "group-partitions"]);
|
||||
const databaseChildGroupNodeTypes = new Set<TreeNodeType>(["group-tables", "group-views", "group-procedures", "group-functions", "group-sequences", "group-packages"]);
|
||||
const databaseChildGroupNodeTypes = new Set<TreeNodeType>(["group-tables", "group-views", "group-materialized-views", "group-procedures", "group-functions", "group-sequences", "group-packages"]);
|
||||
|
||||
export function objectSourceKindForTreeNode(type: TreeNodeType): ObjectSourceKind | null {
|
||||
if (type === "view") return "VIEW";
|
||||
if (type === "materialized_view") return "MATERIALIZED_VIEW";
|
||||
if (type === "procedure") return "PROCEDURE";
|
||||
if (type === "function") return "FUNCTION";
|
||||
if (type === "sequence") return "SEQUENCE";
|
||||
|
||||
@@ -1275,7 +1275,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
}
|
||||
|
||||
const wantsOnlyTablesOrViews = objectTypes.every((objectType) => objectType === "TABLE" || objectType === "VIEW");
|
||||
const wantsOnlyTablesOrViews = objectTypes.every((objectType) => objectType === "TABLE" || objectType === "VIEW" || objectType === "MATERIALIZED_VIEW");
|
||||
let children: TreeNode[];
|
||||
if (wantsOnlyTablesOrViews) {
|
||||
const page = await loadPagedTableGroupChildren({
|
||||
@@ -1323,7 +1323,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
const objectTypes = objectTypesForGroupNode(parent.type);
|
||||
const parentNodeId = objectGroupRefreshParentId(parent);
|
||||
if (!objectTypes || !parentNodeId) return;
|
||||
if (!objectTypes.every((objectType) => objectType === "TABLE" || objectType === "VIEW")) return;
|
||||
if (!objectTypes.every((objectType) => objectType === "TABLE" || objectType === "VIEW" || objectType === "MATERIALIZED_VIEW")) return;
|
||||
|
||||
const config = getConfig(parent.connectionId);
|
||||
const querySchema = connectionObjectTreeQuerySchema(config, parent.database, parent.schema);
|
||||
@@ -1605,7 +1605,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
} else if (node.type === "schema" && node.connectionId && hasTreeNodeDatabaseContext(node) && node.schema) {
|
||||
await loadTables(node.connectionId, node.database, node.schema, options);
|
||||
} else if ((node.type === "table" || node.type === "view") && node.connectionId && hasTreeNodeDatabaseContext(node)) {
|
||||
} else if ((node.type === "table" || node.type === "view" || node.type === "materialized_view") && node.connectionId && hasTreeNodeDatabaseContext(node)) {
|
||||
await loadTableGroups(node.connectionId, node.database, node.label, node.schema, node.id);
|
||||
} else if (node.type === "group-columns" && node.connectionId && hasTreeNodeDatabaseContext(node) && node.tableName) {
|
||||
await loadColumns(node.connectionId, node.database, node.tableName, node.schema, node.id);
|
||||
@@ -1615,7 +1615,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
await loadForeignKeys(node.connectionId, node.database, node.tableName, node.schema, node.id);
|
||||
} else if (node.type === "group-triggers" && node.connectionId && hasTreeNodeDatabaseContext(node) && node.tableName) {
|
||||
await loadTriggers(node.connectionId, node.database, node.tableName, node.schema, node.id);
|
||||
} else if (node.type === "group-tables" || node.type === "group-views" || node.type === "group-procedures" || node.type === "group-functions" || node.type === "group-sequences" || node.type === "group-packages") {
|
||||
} else if (node.type === "group-tables" || node.type === "group-views" || node.type === "group-materialized-views" || node.type === "group-procedures" || node.type === "group-functions" || node.type === "group-sequences" || node.type === "group-packages") {
|
||||
await loadObjectGroupChildren(node, options);
|
||||
} else if (node.type === "group-partitions") {
|
||||
node.isExpanded = true;
|
||||
@@ -1962,7 +1962,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
return tables.map((table) => ({
|
||||
name: table.name,
|
||||
schema: s,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED VIEW" ? ("view" as const) : ("table" as const),
|
||||
})) as SqlCompletionTable[];
|
||||
} catch {
|
||||
return [] as SqlCompletionTable[];
|
||||
@@ -1984,7 +1984,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
return tables.map((table) => ({
|
||||
name: table.name,
|
||||
schema: s,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED VIEW" ? ("view" as const) : ("table" as const),
|
||||
})) as SqlCompletionTable[];
|
||||
} catch {
|
||||
return [] as SqlCompletionTable[];
|
||||
@@ -2011,7 +2011,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
return tables.map((table) => ({
|
||||
name: table.name,
|
||||
schema,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED VIEW" ? ("view" as const) : ("table" as const),
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
@@ -2030,7 +2030,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
completionTablesCache.value[cacheKey] = tables.map((table) => ({
|
||||
name: table.name,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED VIEW" ? ("view" as const) : ("table" as const),
|
||||
}));
|
||||
completionTablesCache.value[cacheKey] = limit ? completionTablesCache.value[cacheKey].slice(0, limit) : completionTablesCache.value[cacheKey];
|
||||
indexCompletionTables(connectionId, database, schema, completionTablesCache.value[cacheKey]);
|
||||
|
||||
@@ -211,7 +211,7 @@ export interface TableInfo {
|
||||
parent_name?: string | null;
|
||||
}
|
||||
|
||||
export type DatabaseObjectType = "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
export type DatabaseObjectType = "TABLE" | "VIEW" | "MATERIALIZED_VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
|
||||
export interface ObjectInfo {
|
||||
name: string;
|
||||
@@ -224,7 +224,7 @@ export interface ObjectInfo {
|
||||
parent_name?: string | null;
|
||||
}
|
||||
|
||||
export type ObjectSourceKind = "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
export type ObjectSourceKind = "VIEW" | "MATERIALIZED_VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
|
||||
export interface ObjectSource {
|
||||
name: string;
|
||||
@@ -390,6 +390,7 @@ export type TreeNodeType =
|
||||
| "schema"
|
||||
| "table"
|
||||
| "view"
|
||||
| "materialized_view"
|
||||
| "procedure"
|
||||
| "function"
|
||||
| "sequence"
|
||||
@@ -401,6 +402,7 @@ export type TreeNodeType =
|
||||
| "group-triggers"
|
||||
| "group-tables"
|
||||
| "group-views"
|
||||
| "group-materialized-views"
|
||||
| "group-procedures"
|
||||
| "group-functions"
|
||||
| "group-sequences"
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::sql_dialect::{is_schema_aware, qualified_table_name, quote_table_iden
|
||||
pub enum DatabaseObjectType {
|
||||
Table,
|
||||
View,
|
||||
MaterializedView,
|
||||
Procedure,
|
||||
Function,
|
||||
}
|
||||
@@ -325,7 +326,10 @@ pub fn supports_object_rename(database_type: Option<DatabaseType>, object_type:
|
||||
return matches!(object_type, DatabaseObjectType::Table | DatabaseObjectType::View);
|
||||
}
|
||||
if is_postgres_like_rename(database_type) || is_oracle_like_rename(database_type) {
|
||||
return matches!(object_type, DatabaseObjectType::Table | DatabaseObjectType::View);
|
||||
return matches!(
|
||||
object_type,
|
||||
DatabaseObjectType::Table | DatabaseObjectType::View | DatabaseObjectType::MaterializedView
|
||||
);
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -462,6 +466,7 @@ fn object_type_keyword(object_type: DatabaseObjectType) -> &'static str {
|
||||
match object_type {
|
||||
DatabaseObjectType::Table => "TABLE",
|
||||
DatabaseObjectType::View => "VIEW",
|
||||
DatabaseObjectType::MaterializedView => "MATERIALIZED VIEW",
|
||||
DatabaseObjectType::Procedure => "PROCEDURE",
|
||||
DatabaseObjectType::Function => "FUNCTION",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user