feat(postgres): support sequence objects
This commit is contained in:
parent
178a800ce8
commit
befee2fd4c
|
|
@ -17,6 +17,7 @@ import {
|
|||
Eraser,
|
||||
Eye,
|
||||
FileCode,
|
||||
ListTree,
|
||||
Upload,
|
||||
Loader2,
|
||||
Network,
|
||||
|
|
@ -93,7 +94,7 @@ import {
|
|||
type ObjectBrowserSortKey,
|
||||
} from "@/lib/objectBrowserRows";
|
||||
|
||||
type ObjectFilter = "all" | "tables" | "views" | "procedures" | "functions" | "packages";
|
||||
type ObjectFilter = "all" | "tables" | "views" | "procedures" | "functions" | "sequences" | "packages";
|
||||
|
||||
const props = defineProps<{
|
||||
connection: ConnectionConfig;
|
||||
|
|
@ -170,6 +171,7 @@ const tableCount = computed(() => rows.value.filter((row) => row.type === "TABLE
|
|||
const viewCount = computed(() => rows.value.filter((row) => row.type === "VIEW").length);
|
||||
const procedureCount = computed(() => rows.value.filter((row) => row.type === "PROCEDURE").length);
|
||||
const functionCount = computed(() => rows.value.filter((row) => row.type === "FUNCTION").length);
|
||||
const sequenceCount = computed(() => rows.value.filter((row) => row.type === "SEQUENCE").length);
|
||||
const packageCount = computed(
|
||||
() => rows.value.filter((row) => row.type === "PACKAGE" || row.type === "PACKAGE_BODY").length,
|
||||
);
|
||||
|
|
@ -213,6 +215,7 @@ const objectFilters = computed<ObjectFilter[]>(() =>
|
|||
["views", viewCount.value],
|
||||
["procedures", procedureCount.value],
|
||||
["functions", functionCount.value],
|
||||
["sequences", sequenceCount.value],
|
||||
["packages", packageCount.value],
|
||||
] as Array<[ObjectFilter, number]>
|
||||
)
|
||||
|
|
@ -258,6 +261,7 @@ function iconFor(row: ObjectBrowserRow) {
|
|||
if (row.type === "VIEW") return Eye;
|
||||
if (row.type === "PROCEDURE") return ScrollText;
|
||||
if (row.type === "FUNCTION") return Braces;
|
||||
if (row.type === "SEQUENCE") return ListTree;
|
||||
if (row.type === "PACKAGE" || row.type === "PACKAGE_BODY") return Package;
|
||||
return Table2;
|
||||
}
|
||||
|
|
@ -266,6 +270,7 @@ function typeLabel(type: ObjectBrowserRow["type"]) {
|
|||
if (type === "VIEW") return t("objects.view");
|
||||
if (type === "PROCEDURE") return t("objects.procedure");
|
||||
if (type === "FUNCTION") return t("objects.function");
|
||||
if (type === "SEQUENCE") return t("objects.sequence");
|
||||
if (type === "PACKAGE") return t("objects.package");
|
||||
if (type === "PACKAGE_BODY") return t("objects.packageBody");
|
||||
return t("objects.table");
|
||||
|
|
@ -290,6 +295,7 @@ function rowMatchesObjectFilter(row: ObjectBrowserRow) {
|
|||
if (objectFilter.value === "views") return row.type === "VIEW";
|
||||
if (objectFilter.value === "procedures") return row.type === "PROCEDURE";
|
||||
if (objectFilter.value === "functions") return row.type === "FUNCTION";
|
||||
if (objectFilter.value === "sequences") return row.type === "SEQUENCE";
|
||||
if (objectFilter.value === "packages") return row.type === "PACKAGE" || row.type === "PACKAGE_BODY";
|
||||
return true;
|
||||
}
|
||||
|
|
@ -330,6 +336,7 @@ function iconClass(type: ObjectBrowserRow["type"]) {
|
|||
if (type === "VIEW") return "text-purple-500";
|
||||
if (type === "PROCEDURE") return "text-blue-500";
|
||||
if (type === "FUNCTION") return "text-amber-500";
|
||||
if (type === "SEQUENCE") return "text-emerald-500";
|
||||
if (type === "PACKAGE" || type === "PACKAGE_BODY") return "text-cyan-500";
|
||||
return "text-green-500";
|
||||
}
|
||||
|
|
@ -351,6 +358,7 @@ function canOpenSource(row: ObjectBrowserRow) {
|
|||
row.type === "VIEW" ||
|
||||
row.type === "PROCEDURE" ||
|
||||
row.type === "FUNCTION" ||
|
||||
row.type === "SEQUENCE" ||
|
||||
row.type === "PACKAGE" ||
|
||||
row.type === "PACKAGE_BODY"
|
||||
);
|
||||
|
|
@ -405,7 +413,7 @@ async function openSource(row: ObjectBrowserRow) {
|
|||
);
|
||||
sourceContent.value = result.source;
|
||||
sourceDraft.value = result.source;
|
||||
sourceEditing.value = true;
|
||||
sourceEditing.value = row.type !== "SEQUENCE";
|
||||
} catch (e: any) {
|
||||
sourceError.value = e?.message || String(e);
|
||||
} finally {
|
||||
|
|
@ -1145,6 +1153,7 @@ function filterCount(filter: ObjectFilter) {
|
|||
if (filter === "views") return viewCount.value;
|
||||
if (filter === "procedures") return procedureCount.value;
|
||||
if (filter === "functions") return functionCount.value;
|
||||
if (filter === "sequences") return sequenceCount.value;
|
||||
if (filter === "packages") return packageCount.value;
|
||||
return rows.value.length;
|
||||
}
|
||||
|
|
@ -1159,9 +1168,11 @@ function filterLabel(filter: ObjectFilter) {
|
|||
? "objects.procedures"
|
||||
: filter === "functions"
|
||||
? "objects.functions"
|
||||
: filter === "packages"
|
||||
? "objects.packages"
|
||||
: "objects.all";
|
||||
: filter === "sequences"
|
||||
? "objects.sequences"
|
||||
: filter === "packages"
|
||||
? "objects.packages"
|
||||
: "objects.all";
|
||||
return `${t(key)} ${filterCount(filter)}`;
|
||||
}
|
||||
|
||||
|
|
@ -1320,6 +1331,7 @@ function getPackageMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
|||
function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
||||
if (item.type === "TABLE") return getTableMenuItems(item);
|
||||
if (item.type === "VIEW") return getViewMenuItems(item);
|
||||
if (item.type === "SEQUENCE") return getPackageMenuItems(item);
|
||||
if (item.type === "PACKAGE" || item.type === "PACKAGE_BODY") return getPackageMenuItems(item);
|
||||
return getProcFuncMenuItems(item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,6 +270,8 @@ function getIconInfo(node: TreeNode): { icon: any; colorClass: string } | null {
|
|||
return { icon: ScrollText, colorClass: "text-blue-500" };
|
||||
case "function":
|
||||
return { icon: Braces, colorClass: "text-amber-500" };
|
||||
case "sequence":
|
||||
return { icon: ListTree, colorClass: "text-emerald-500" };
|
||||
case "package":
|
||||
return { icon: Package, colorClass: "text-cyan-500" };
|
||||
case "package-body":
|
||||
|
|
@ -282,6 +284,8 @@ function getIconInfo(node: TreeNode): { icon: any; colorClass: string } | null {
|
|||
return { icon: ScrollText, colorClass: "text-blue-500" };
|
||||
case "group-functions":
|
||||
return { icon: Braces, colorClass: "text-amber-500" };
|
||||
case "group-sequences":
|
||||
return { icon: ListTree, colorClass: "text-emerald-500" };
|
||||
case "group-packages":
|
||||
return { icon: Package, colorClass: "text-cyan-500" };
|
||||
case "group-partitions":
|
||||
|
|
@ -300,6 +304,7 @@ const groupTypes: Set<TreeNodeType> = new Set([
|
|||
"group-views",
|
||||
"group-procedures",
|
||||
"group-functions",
|
||||
"group-sequences",
|
||||
"group-packages",
|
||||
"group-partitions",
|
||||
"saved-sql-root",
|
||||
|
|
@ -368,6 +373,7 @@ async function toggle() {
|
|||
node.type === "group-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;
|
||||
|
|
@ -473,6 +479,7 @@ function runRowClickAction() {
|
|||
} else if (
|
||||
node.type === "procedure" ||
|
||||
node.type === "function" ||
|
||||
node.type === "sequence" ||
|
||||
node.type === "package" ||
|
||||
node.type === "package-body"
|
||||
) {
|
||||
|
|
@ -1225,11 +1232,13 @@ function viewObjectSource() {
|
|||
.then(async (result) => {
|
||||
const tabId = queryStore.createTab(node.connectionId!, node.database!, `Source - ${node.label}`);
|
||||
queryStore.updateSql(tabId, result.source);
|
||||
queryStore.setObjectSource(tabId, {
|
||||
schema,
|
||||
name: node.label,
|
||||
objectType,
|
||||
});
|
||||
if (objectType !== "SEQUENCE") {
|
||||
queryStore.setObjectSource(tabId, {
|
||||
schema,
|
||||
name: node.label,
|
||||
objectType,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e: any) => {
|
||||
toast(e?.message || String(e), 5000);
|
||||
|
|
@ -3248,6 +3257,13 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
|||
return items;
|
||||
}
|
||||
|
||||
if (node.type === "sequence") {
|
||||
items.push({ label: t("contextMenu.viewSource"), action: viewObjectSource, icon: Code2 });
|
||||
items.push({ label: "", separator: true });
|
||||
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
|
||||
return items;
|
||||
}
|
||||
|
||||
if (node.type === "package" || node.type === "package-body") {
|
||||
items.push({ label: t("contextMenu.viewSource"), action: viewObjectSource, icon: Code2 });
|
||||
items.push({ label: "", separator: true });
|
||||
|
|
@ -3410,6 +3426,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -1150,6 +1150,7 @@ export default {
|
|||
views: "Views",
|
||||
procedures: "Procedures",
|
||||
functions: "Functions",
|
||||
sequences: "Sequences",
|
||||
packages: "Packages",
|
||||
partitions: "Partitions",
|
||||
objectBrowser: "Browse in Object Browser ({count})",
|
||||
|
|
@ -1207,12 +1208,14 @@ export default {
|
|||
views: "Views",
|
||||
procedures: "Procedures",
|
||||
functions: "Functions",
|
||||
sequences: "Sequences",
|
||||
packages: "Packages",
|
||||
table: "Table",
|
||||
partitions: "{count} partitions",
|
||||
view: "View",
|
||||
procedure: "Procedure",
|
||||
function: "Function",
|
||||
sequence: "Sequence",
|
||||
package: "Package",
|
||||
packageBody: "Package Body",
|
||||
name: "Name",
|
||||
|
|
@ -1228,7 +1231,7 @@ export default {
|
|||
comment: "Comment",
|
||||
loadingSchemas: "Loading schemas...",
|
||||
schema: "Schema",
|
||||
search: "Search tables, views, functions, or procedures...",
|
||||
search: "Search tables, views, functions, sequences, or procedures...",
|
||||
loading: "Loading objects...",
|
||||
empty: "No objects found",
|
||||
selectedTables: "{count} tables selected",
|
||||
|
|
|
|||
|
|
@ -1006,6 +1006,7 @@ export default {
|
|||
views: "Vistas",
|
||||
procedures: "Procedimientos",
|
||||
functions: "Funciones",
|
||||
sequences: "Secuencias",
|
||||
packages: "Paquetes",
|
||||
partitions: "Particiones",
|
||||
objectBrowser: "Explorar en el navegador de objetos ({count})",
|
||||
|
|
@ -1016,12 +1017,14 @@ export default {
|
|||
views: "Vistas",
|
||||
procedures: "Procedimientos",
|
||||
functions: "Funciones",
|
||||
sequences: "Secuencias",
|
||||
packages: "Paquetes",
|
||||
table: "Tabla",
|
||||
partitions: "{count} particiones",
|
||||
view: "Vista",
|
||||
procedure: "Procedimiento",
|
||||
function: "Función",
|
||||
sequence: "Secuencia",
|
||||
package: "Paquete",
|
||||
packageBody: "Cuerpo del paquete",
|
||||
name: "Nombre",
|
||||
|
|
@ -1037,7 +1040,7 @@ export default {
|
|||
comment: "Comentario",
|
||||
loadingSchemas: "Cargando esquemas...",
|
||||
schema: "Esquema",
|
||||
search: "Buscar tablas, vistas, funciones o procedimientos...",
|
||||
search: "Buscar tablas, vistas, funciones, secuencias o procedimientos...",
|
||||
loading: "Cargando objetos...",
|
||||
empty: "No se encontraron objetos",
|
||||
selectedTables: "{count} tablas seleccionadas",
|
||||
|
|
|
|||
|
|
@ -1139,6 +1139,7 @@ export default {
|
|||
views: "Viste",
|
||||
procedures: "Procedure",
|
||||
functions: "Funzioni",
|
||||
sequences: "Sequenze",
|
||||
packages: "Pacchetti",
|
||||
partitions: "Partizioni",
|
||||
objectBrowser: "Sfoglia in Esplora Oggetti ({count})",
|
||||
|
|
@ -1149,12 +1150,14 @@ export default {
|
|||
views: "Viste",
|
||||
procedures: "Procedure",
|
||||
functions: "Funzioni",
|
||||
sequences: "Sequenze",
|
||||
packages: "Pacchetti",
|
||||
table: "Tabella",
|
||||
partitions: "{count} partizioni",
|
||||
view: "Vista",
|
||||
procedure: "Procedura",
|
||||
function: "Funzione",
|
||||
sequence: "Sequenza",
|
||||
package: "Pacchetto",
|
||||
packageBody: "Corpo del Pacchetto",
|
||||
name: "Nome",
|
||||
|
|
@ -1170,7 +1173,7 @@ export default {
|
|||
comment: "Commento",
|
||||
loadingSchemas: "Caricamento schemi...",
|
||||
schema: "Schema",
|
||||
search: "Cerca tabelle, viste, funzioni o procedure...",
|
||||
search: "Cerca tabelle, viste, funzioni, sequenze o procedure...",
|
||||
loading: "Caricamento oggetti...",
|
||||
empty: "Nessun oggetto trovato",
|
||||
selectedTables: "{count} tabelle selezionate",
|
||||
|
|
|
|||
|
|
@ -1134,6 +1134,7 @@ export default {
|
|||
views: "Visões",
|
||||
procedures: "Procedimentos",
|
||||
functions: "Funções",
|
||||
sequences: "Sequências",
|
||||
packages: "Pacotes",
|
||||
partitions: "Partições",
|
||||
objectBrowser: "Navegar no Navegador de Objetos ({count})",
|
||||
|
|
@ -1144,12 +1145,14 @@ export default {
|
|||
views: "Visões",
|
||||
procedures: "Procedimentos",
|
||||
functions: "Funções",
|
||||
sequences: "Sequências",
|
||||
packages: "Pacotes",
|
||||
table: "Tabela",
|
||||
partitions: "{count} partições",
|
||||
view: "Visão",
|
||||
procedure: "Procedimento",
|
||||
function: "Função",
|
||||
sequence: "Sequência",
|
||||
package: "Pacote",
|
||||
packageBody: "Corpo do pacote",
|
||||
name: "Nome",
|
||||
|
|
@ -1165,7 +1168,7 @@ export default {
|
|||
comment: "Comentário",
|
||||
loadingSchemas: "Carregando schemas...",
|
||||
schema: "Schema",
|
||||
search: "Pesquisar tabelas, visões, funções ou procedimentos...",
|
||||
search: "Pesquisar tabelas, visões, funções, sequências ou procedimentos...",
|
||||
loading: "Carregando objetos...",
|
||||
empty: "Nenhum objeto encontrado",
|
||||
selectedTables: "{count} tabelas selecionadas",
|
||||
|
|
|
|||
|
|
@ -1125,6 +1125,7 @@ export default {
|
|||
views: "视图",
|
||||
procedures: "存储过程",
|
||||
functions: "函数",
|
||||
sequences: "序列",
|
||||
packages: "包",
|
||||
partitions: "分区",
|
||||
objectBrowser: "在对象浏览器中查看 ({count})",
|
||||
|
|
@ -1180,12 +1181,14 @@ export default {
|
|||
views: "视图",
|
||||
procedures: "存储过程",
|
||||
functions: "函数",
|
||||
sequences: "序列",
|
||||
packages: "包",
|
||||
table: "表",
|
||||
partitions: "{count} 个分区",
|
||||
view: "视图",
|
||||
procedure: "存储过程",
|
||||
function: "函数",
|
||||
sequence: "序列",
|
||||
package: "包",
|
||||
packageBody: "包体",
|
||||
name: "名称",
|
||||
|
|
@ -1201,7 +1204,7 @@ export default {
|
|||
comment: "注释",
|
||||
loadingSchemas: "加载 Schema...",
|
||||
schema: "Schema",
|
||||
search: "搜索表、视图、函数或存储过程...",
|
||||
search: "搜索表、视图、函数、序列或存储过程...",
|
||||
loading: "正在加载对象...",
|
||||
empty: "没有找到对象",
|
||||
selectedTables: "已选择 {count} 张表",
|
||||
|
|
|
|||
|
|
@ -1071,6 +1071,7 @@ export default {
|
|||
views: "檢視",
|
||||
procedures: "預存程序",
|
||||
functions: "函式",
|
||||
sequences: "序列",
|
||||
packages: "套件",
|
||||
partitions: "分割區",
|
||||
objectBrowser: "在物件瀏覽器中檢視 ({count})",
|
||||
|
|
@ -1081,12 +1082,14 @@ export default {
|
|||
views: "檢視",
|
||||
procedures: "預存程序",
|
||||
functions: "函式",
|
||||
sequences: "序列",
|
||||
packages: "套件",
|
||||
table: "資料表",
|
||||
partitions: "{count} 個分割區",
|
||||
view: "檢視",
|
||||
procedure: "預存程序",
|
||||
function: "函式",
|
||||
sequence: "序列",
|
||||
package: "套件",
|
||||
packageBody: "套件主體",
|
||||
name: "名稱",
|
||||
|
|
@ -1102,7 +1105,7 @@ export default {
|
|||
comment: "註解",
|
||||
loadingSchemas: "載入 Schema……",
|
||||
schema: "Schema",
|
||||
search: "搜尋資料表、檢視、函式或預存程序……",
|
||||
search: "搜尋資料表、檢視、函式、序列或預存程序……",
|
||||
loading: "正在載入物件……",
|
||||
empty: "沒有找到物件",
|
||||
selectedTables: "已選擇 {count} 張資料表",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
export type SidebarObjectKind = "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "PACKAGE" | "PACKAGE_BODY";
|
||||
export type SidebarObjectKind = "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
|
||||
export interface DatabaseObjectCapabilities {
|
||||
sidebarObjects: SidebarObjectKind[];
|
||||
|
|
@ -10,6 +10,7 @@ export interface DatabaseObjectCapabilities {
|
|||
|
||||
const TABLE_VIEW_OBJECTS: SidebarObjectKind[] = ["TABLE", "VIEW"];
|
||||
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 TABLE_VIEW_ONLY_TYPES = new Set<DatabaseType>([
|
||||
|
|
@ -31,6 +32,7 @@ const TABLE_VIEW_ONLY_TYPES = new Set<DatabaseType>([
|
|||
]);
|
||||
|
||||
const ORACLE_PACKAGE_TYPES = new Set<DatabaseType>(["oracle", "oceanbase-oracle"]);
|
||||
const POSTGRES_SEQUENCE_TYPES = new Set<DatabaseType>(["postgres", "gaussdb", "kwdb", "opengauss"]);
|
||||
|
||||
export function databaseObjectCapabilities(dbType?: DatabaseType): DatabaseObjectCapabilities {
|
||||
const sidebarObjects = sidebarObjectKindsForDatabase(dbType);
|
||||
|
|
@ -45,6 +47,7 @@ export function sidebarObjectKindsForDatabase(dbType?: DatabaseType): SidebarObj
|
|||
if (!dbType) return [...TABLE_VIEW_OBJECTS];
|
||||
if (ORACLE_PACKAGE_TYPES.has(dbType)) return [...ORACLE_OBJECTS];
|
||||
if (TABLE_VIEW_ONLY_TYPES.has(dbType)) return [...TABLE_VIEW_OBJECTS];
|
||||
if (POSTGRES_SEQUENCE_TYPES.has(dbType)) return [...POSTGRES_OBJECTS];
|
||||
return [...ROUTINE_OBJECTS];
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +56,7 @@ export function normalizeSidebarObjectKind(type: string): SidebarObjectKind {
|
|||
if (value.includes("PACKAGE BODY") || value.includes("PACKAGE_BODY")) return "PACKAGE_BODY";
|
||||
if (value.includes("PACKAGE")) return "PACKAGE";
|
||||
if (value.includes("VIEW")) return "VIEW";
|
||||
if (value.includes("SEQ")) return "SEQUENCE";
|
||||
if (value.includes("PROC")) return "PROCEDURE";
|
||||
if (value.includes("FUNC")) return "FUNCTION";
|
||||
return "TABLE";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export type ObjectBrowserRow = {
|
|||
id: string;
|
||||
name: string;
|
||||
schema?: string;
|
||||
type: "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "PACKAGE" | "PACKAGE_BODY";
|
||||
type: "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
comment?: string | null;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
|
|
@ -24,6 +24,7 @@ export function normalizeObjectBrowserType(type: string): ObjectBrowserRow["type
|
|||
if (value.includes("PACKAGE BODY") || value.includes("PACKAGE_BODY")) return "PACKAGE_BODY";
|
||||
if (value.includes("PACKAGE")) return "PACKAGE";
|
||||
if (value.includes("VIEW")) return "VIEW";
|
||||
if (value.includes("SEQ")) return "SEQUENCE";
|
||||
if (value.includes("PROC")) return "PROCEDURE";
|
||||
if (value.includes("FUNC")) return "FUNCTION";
|
||||
return "TABLE";
|
||||
|
|
|
|||
|
|
@ -374,7 +374,9 @@ export function buildSimpleObjectTreeNodes({
|
|||
|
||||
for (const obj of objects) {
|
||||
const objectType = normalizeObjectType(obj.object_type);
|
||||
if (!["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "PACKAGE", "PACKAGE_BODY"].includes(objectType)) continue;
|
||||
if (!["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE", "PACKAGE", "PACKAGE_BODY"].includes(objectType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const name = normalizeDatabaseObjectName(obj.name);
|
||||
if (!name) continue;
|
||||
|
|
@ -426,6 +428,7 @@ function simpleObjectNodeType(objectType: DatabaseObjectTreeKind): TreeNodeType
|
|||
if (objectType === "VIEW") return "view";
|
||||
if (objectType === "PROCEDURE") return "procedure";
|
||||
if (objectType === "FUNCTION") return "function";
|
||||
if (objectType === "SEQUENCE") return "sequence";
|
||||
if (objectType === "PACKAGE_BODY") return "package-body";
|
||||
if (objectType === "PACKAGE") return "package";
|
||||
return "table";
|
||||
|
|
@ -458,6 +461,13 @@ const groupDefs: Array<{
|
|||
nodeType: "group-functions",
|
||||
childType: "function",
|
||||
},
|
||||
{
|
||||
key: "__sequences",
|
||||
label: "tree.sequences",
|
||||
objectTypes: ["SEQUENCE"],
|
||||
nodeType: "group-sequences",
|
||||
childType: "sequence",
|
||||
},
|
||||
{
|
||||
key: "__packages",
|
||||
label: "tree.packages",
|
||||
|
|
@ -472,6 +482,7 @@ const objectGroupNodeTypes = new Set<TreeNodeType>([
|
|||
"group-views",
|
||||
"group-procedures",
|
||||
"group-functions",
|
||||
"group-sequences",
|
||||
"group-packages",
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export type SidebarActivation = "single" | "double";
|
|||
const dataNodeTypes = new Set<TreeNodeType>(["table", "view"]);
|
||||
const toggleLeafNodeTypes = new Set<TreeNodeType>(["redis-db", "mongo-collection", "user-admin"]);
|
||||
const objectBrowserNodeTypes = new Set<TreeNodeType>(["database", "schema", "object-browser"]);
|
||||
const sourceNodeTypes = new Set<TreeNodeType>(["procedure", "function", "package", "package-body"]);
|
||||
const sourceNodeTypes = new Set<TreeNodeType>(["procedure", "function", "sequence", "package", "package-body"]);
|
||||
const tableChildGroupNodeTypes = new Set<TreeNodeType>([
|
||||
"group-columns",
|
||||
"group-indexes",
|
||||
|
|
@ -29,6 +29,7 @@ const databaseChildGroupNodeTypes = new Set<TreeNodeType>([
|
|||
"group-views",
|
||||
"group-procedures",
|
||||
"group-functions",
|
||||
"group-sequences",
|
||||
"group-packages",
|
||||
]);
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ export function objectSourceKindForTreeNode(type: TreeNodeType): ObjectSourceKin
|
|||
if (type === "view") return "VIEW";
|
||||
if (type === "procedure") return "PROCEDURE";
|
||||
if (type === "function") return "FUNCTION";
|
||||
if (type === "sequence") return "SEQUENCE";
|
||||
if (type === "package") return "PACKAGE";
|
||||
if (type === "package-body") return "PACKAGE_BODY";
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1593,6 +1593,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
|||
node.type === "group-views" ||
|
||||
node.type === "group-procedures" ||
|
||||
node.type === "group-functions" ||
|
||||
node.type === "group-sequences" ||
|
||||
node.type === "group-packages"
|
||||
) {
|
||||
await loadObjectGroupChildren(node, options);
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ export interface TableInfo {
|
|||
parent_name?: string | null;
|
||||
}
|
||||
|
||||
export type DatabaseObjectType = "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "PACKAGE" | "PACKAGE_BODY";
|
||||
export type DatabaseObjectType = "TABLE" | "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
|
||||
export interface ObjectInfo {
|
||||
name: string;
|
||||
|
|
@ -191,7 +191,7 @@ export interface ObjectInfo {
|
|||
parent_name?: string | null;
|
||||
}
|
||||
|
||||
export type ObjectSourceKind = "VIEW" | "PROCEDURE" | "FUNCTION" | "PACKAGE" | "PACKAGE_BODY";
|
||||
export type ObjectSourceKind = "VIEW" | "PROCEDURE" | "FUNCTION" | "SEQUENCE" | "PACKAGE" | "PACKAGE_BODY";
|
||||
|
||||
export interface ObjectSource {
|
||||
name: string;
|
||||
|
|
@ -288,6 +288,7 @@ export type TreeNodeType =
|
|||
| "view"
|
||||
| "procedure"
|
||||
| "function"
|
||||
| "sequence"
|
||||
| "package"
|
||||
| "package-body"
|
||||
| "group-columns"
|
||||
|
|
@ -298,6 +299,7 @@ export type TreeNodeType =
|
|||
| "group-views"
|
||||
| "group-procedures"
|
||||
| "group-functions"
|
||||
| "group-sequences"
|
||||
| "group-packages"
|
||||
| "group-partitions"
|
||||
| "object-browser"
|
||||
|
|
|
|||
|
|
@ -1151,6 +1151,7 @@ fn list_objects_sql(include_timestamps: bool) -> &'static str {
|
|||
CASE c.relkind \
|
||||
WHEN 'v' THEN 'VIEW' \
|
||||
WHEN 'm' THEN 'VIEW' \
|
||||
WHEN 'S' THEN 'SEQUENCE' \
|
||||
ELSE 'TABLE' \
|
||||
END AS object_type, \
|
||||
obj_description(c.oid) AS object_comment, \
|
||||
|
|
@ -1162,7 +1163,7 @@ fn list_objects_sql(include_timestamps: bool) -> &'static str {
|
|||
) AS updated_at, \
|
||||
CASE WHEN pc.relkind = 'p' THEN pn.nspname ELSE NULL END AS parent_schema, \
|
||||
CASE WHEN pc.relkind = 'p' THEN pc.relname ELSE NULL END AS parent_name, \
|
||||
CASE c.relkind WHEN 'v' THEN 1 WHEN 'm' THEN 1 ELSE 0 END AS sort_order \
|
||||
CASE c.relkind WHEN 'v' THEN 1 WHEN 'm' THEN 1 WHEN 'S' THEN 4 ELSE 0 END AS sort_order \
|
||||
FROM pg_catalog.pg_class c \
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \
|
||||
LEFT JOIN pg_catalog.pg_inherits i ON i.inhrelid = c.oid \
|
||||
|
|
@ -1171,7 +1172,7 @@ fn list_objects_sql(include_timestamps: bool) -> &'static str {
|
|||
LEFT JOIN LATERAL pg_stat_file( \
|
||||
CASE WHEN c.relkind IN ('r','m','f','p') THEN pg_relation_filepath(c.oid) END, true \
|
||||
) stat ON true \
|
||||
WHERE n.nspname = $1 AND c.relkind IN ('r','v','m','f','p') \
|
||||
WHERE n.nspname = $1 AND c.relkind IN ('r','v','m','f','p','S') \
|
||||
UNION ALL \
|
||||
SELECT p.proname AS object_name, \
|
||||
CASE p.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS object_type, \
|
||||
|
|
@ -1192,6 +1193,7 @@ fn list_objects_sql(include_timestamps: bool) -> &'static str {
|
|||
CASE c.relkind \
|
||||
WHEN 'v' THEN 'VIEW' \
|
||||
WHEN 'm' THEN 'VIEW' \
|
||||
WHEN 'S' THEN 'SEQUENCE' \
|
||||
ELSE 'TABLE' \
|
||||
END AS object_type, \
|
||||
obj_description(c.oid) AS object_comment, \
|
||||
|
|
@ -1199,13 +1201,13 @@ fn list_objects_sql(include_timestamps: bool) -> &'static str {
|
|||
NULL::text AS updated_at, \
|
||||
CASE WHEN pc.relkind = 'p' THEN pn.nspname ELSE NULL END AS parent_schema, \
|
||||
CASE WHEN pc.relkind = 'p' THEN pc.relname ELSE NULL END AS parent_name, \
|
||||
CASE c.relkind WHEN 'v' THEN 1 WHEN 'm' THEN 1 ELSE 0 END AS sort_order \
|
||||
CASE c.relkind WHEN 'v' THEN 1 WHEN 'm' THEN 1 WHEN 'S' THEN 4 ELSE 0 END AS sort_order \
|
||||
FROM pg_catalog.pg_class c \
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \
|
||||
LEFT JOIN pg_catalog.pg_inherits i ON i.inhrelid = c.oid \
|
||||
LEFT JOIN pg_catalog.pg_class pc ON pc.oid = i.inhparent \
|
||||
LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace \
|
||||
WHERE n.nspname = $1 AND c.relkind IN ('r','v','m','f','p') \
|
||||
WHERE n.nspname = $1 AND c.relkind IN ('r','v','m','f','p','S') \
|
||||
UNION ALL \
|
||||
SELECT p.proname AS object_name, \
|
||||
CASE p.prokind WHEN 'p' THEN 'PROCEDURE' ELSE 'FUNCTION' END AS object_type, \
|
||||
|
|
@ -2123,6 +2125,8 @@ mod tests {
|
|||
assert!(sql.contains("pg_xact_commit_timestamp"));
|
||||
assert!(sql.contains("'PROCEDURE'"));
|
||||
assert!(sql.contains("'FUNCTION'"));
|
||||
assert!(sql.contains("'SEQUENCE'"));
|
||||
assert!(sql.contains("'S'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ fn object_type_keyword(object_type: &ObjectSourceKind) -> &'static str {
|
|||
ObjectSourceKind::View => "VIEW",
|
||||
ObjectSourceKind::Procedure => "PROCEDURE",
|
||||
ObjectSourceKind::Function => "FUNCTION",
|
||||
ObjectSourceKind::Sequence => "SEQUENCE",
|
||||
ObjectSourceKind::Package => "PACKAGE",
|
||||
ObjectSourceKind::PackageBody => "PACKAGE BODY",
|
||||
}
|
||||
|
|
@ -384,6 +385,8 @@ fn parse_object_source_kind(value: &str) -> Option<ObjectSourceKind> {
|
|||
Some(ObjectSourceKind::Procedure)
|
||||
} else if value.eq_ignore_ascii_case("FUNCTION") {
|
||||
Some(ObjectSourceKind::Function)
|
||||
} else if value.eq_ignore_ascii_case("SEQUENCE") {
|
||||
Some(ObjectSourceKind::Sequence)
|
||||
} else if value.eq_ignore_ascii_case("PACKAGE") {
|
||||
Some(ObjectSourceKind::Package)
|
||||
} else if value.eq_ignore_ascii_case("PACKAGE BODY") || value.eq_ignore_ascii_case("PACKAGE_BODY") {
|
||||
|
|
|
|||
|
|
@ -1534,6 +1534,7 @@ fn sqlite_object_type(kind: &db::ObjectSourceKind) -> &'static str {
|
|||
db::ObjectSourceKind::View => "view",
|
||||
db::ObjectSourceKind::Procedure
|
||||
| db::ObjectSourceKind::Function
|
||||
| db::ObjectSourceKind::Sequence
|
||||
| db::ObjectSourceKind::Package
|
||||
| db::ObjectSourceKind::PackageBody => "routine",
|
||||
}
|
||||
|
|
@ -1544,7 +1545,7 @@ fn sqlserver_object_type_filter(kind: &db::ObjectSourceKind) -> &'static str {
|
|||
db::ObjectSourceKind::View => "'V'",
|
||||
db::ObjectSourceKind::Procedure => "'P'",
|
||||
db::ObjectSourceKind::Function => "'FN','IF','TF','FS','FT'",
|
||||
db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => "''",
|
||||
db::ObjectSourceKind::Sequence | db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => "''",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1586,6 +1587,30 @@ pub fn postgres_object_source_sql(schema: &str, name: &str, kind: &db::ObjectSou
|
|||
prokind
|
||||
)
|
||||
}
|
||||
db::ObjectSourceKind::Sequence => {
|
||||
format!(
|
||||
"SELECT concat_ws(E'\\n\\n', \
|
||||
'-- auto-generated definition' || E'\\n' || \
|
||||
'create sequence ' || quote_ident(c.relname) || E'\\n' || \
|
||||
' as ' || pg_catalog.format_type(s.seqtypid, NULL) || ';', \
|
||||
'alter sequence ' || quote_ident(c.relname) || ' owner to ' || quote_ident(pg_get_userbyid(c.relowner)) || ';', \
|
||||
CASE WHEN owned.relname IS NOT NULL AND a.attname IS NOT NULL \
|
||||
THEN 'alter sequence ' || quote_ident(c.relname) || ' owned by ' || quote_ident(owned.relname) || '.' || quote_ident(a.attname) || ';' \
|
||||
END \
|
||||
) \
|
||||
FROM pg_catalog.pg_class c \
|
||||
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \
|
||||
JOIN pg_catalog.pg_sequence s ON s.seqrelid = c.oid \
|
||||
LEFT JOIN pg_catalog.pg_depend d \
|
||||
ON d.classid = 'pg_class'::regclass AND d.objid = c.oid AND d.deptype = 'a' \
|
||||
LEFT JOIN pg_catalog.pg_class owned ON owned.oid = d.refobjid \
|
||||
LEFT JOIN pg_catalog.pg_attribute a ON a.attrelid = d.refobjid AND a.attnum = d.refobjsubid \
|
||||
WHERE n.nspname = {} AND c.relname = {} AND c.relkind = 'S' \
|
||||
ORDER BY c.oid LIMIT 1",
|
||||
sql_string(schema),
|
||||
sql_string(name)
|
||||
)
|
||||
}
|
||||
db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => "SELECT NULL WHERE FALSE".to_string(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1595,6 +1620,7 @@ pub fn oracle_object_source_sql(schema: &str, name: &str, kind: &db::ObjectSourc
|
|||
db::ObjectSourceKind::View => "VIEW",
|
||||
db::ObjectSourceKind::Procedure => "PROCEDURE",
|
||||
db::ObjectSourceKind::Function => "FUNCTION",
|
||||
db::ObjectSourceKind::Sequence => "SEQUENCE",
|
||||
db::ObjectSourceKind::Package => "PACKAGE",
|
||||
db::ObjectSourceKind::PackageBody => "PACKAGE_BODY",
|
||||
};
|
||||
|
|
@ -1623,7 +1649,9 @@ pub fn mysql_object_source_sql(name: &str, kind: &db::ObjectSourceKind) -> Strin
|
|||
db::ObjectSourceKind::View => format!("SHOW CREATE VIEW {}", mysql_ident(name)),
|
||||
db::ObjectSourceKind::Procedure => format!("SHOW CREATE PROCEDURE {}", mysql_ident(name)),
|
||||
db::ObjectSourceKind::Function => format!("SHOW CREATE FUNCTION {}", mysql_ident(name)),
|
||||
db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => String::new(),
|
||||
db::ObjectSourceKind::Sequence | db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1883,6 +1911,25 @@ mod object_source_tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_postgres_object_source_sql_for_sequences() {
|
||||
let sql = postgres_object_source_sql("tenant's schema", "order id seq", &ObjectSourceKind::Sequence);
|
||||
|
||||
assert!(sql.contains("-- auto-generated definition"));
|
||||
assert!(sql.contains("create sequence"));
|
||||
assert!(sql.contains("alter sequence"));
|
||||
assert!(sql.contains("owner to"));
|
||||
assert!(sql.contains("owned by"));
|
||||
assert!(sql.contains("pg_catalog.pg_sequence"));
|
||||
assert!(sql.contains("n.nspname = 'tenant''s schema'"));
|
||||
assert!(sql.contains("c.relname = 'order id seq'"));
|
||||
assert!(sql.contains("c.relkind = 'S'"));
|
||||
assert!(!sql.contains("MINVALUE"));
|
||||
assert!(!sql.contains("START WITH"));
|
||||
assert!(!sql.contains("CACHE"));
|
||||
assert!(!sql.contains("NO CYCLE"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_postgres_view_source_sql_without_regclass_cast() {
|
||||
let sql = postgres_object_source_sql("tenant's schema", "active users", &ObjectSourceKind::View);
|
||||
|
|
|
|||
|
|
@ -2960,7 +2960,9 @@ where
|
|||
rewrite_postgres_routine_schema(&object.source, &request.target_schema)
|
||||
.unwrap_or_else(|| object.source.clone())
|
||||
}
|
||||
db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => object.source.clone(),
|
||||
db::ObjectSourceKind::Sequence | db::ObjectSourceKind::Package | db::ObjectSourceKind::PackageBody => {
|
||||
object.source.clone()
|
||||
}
|
||||
};
|
||||
let statements = build_executable_object_source_statements(EditableObjectSourceSqlInput {
|
||||
database_type: DatabaseType::Postgres,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ pub enum ObjectSourceKind {
|
|||
View,
|
||||
Procedure,
|
||||
Function,
|
||||
Sequence,
|
||||
Package,
|
||||
PackageBody,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ test("object browser entry follows database tree shape", () => {
|
|||
|
||||
test("sidebar object capability registry describes object groups by database type", () => {
|
||||
assert.deepEqual(sidebarObjectKindsForDatabase("databend"), ["TABLE", "VIEW"]);
|
||||
assert.deepEqual(sidebarObjectKindsForDatabase("postgres"), ["TABLE", "VIEW", "PROCEDURE", "FUNCTION"]);
|
||||
assert.deepEqual(sidebarObjectKindsForDatabase("postgres"), ["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE"]);
|
||||
assert.deepEqual(sidebarObjectKindsForDatabase("oracle"), [
|
||||
"TABLE",
|
||||
"VIEW",
|
||||
|
|
|
|||
|
|
@ -45,6 +45,20 @@ test("object browser rows normalize Oracle package body objects", () => {
|
|||
);
|
||||
});
|
||||
|
||||
test("object browser rows normalize PostgreSQL sequence objects", () => {
|
||||
const rows = buildObjectBrowserRows({
|
||||
objects: [{ name: "order_id_seq", object_type: "SEQUENCE", schema: "public" }],
|
||||
database: "app",
|
||||
fallbackSchema: "public",
|
||||
needsSchema: true,
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
rows.map((row) => ({ id: row.id, type: row.type })),
|
||||
[{ id: "public:order_id_seq:SEQUENCE:0", type: "SEQUENCE" }],
|
||||
);
|
||||
});
|
||||
|
||||
test("object browser search matches names, types, and comments but not schema names", () => {
|
||||
const rows = buildObjectBrowserRows({
|
||||
objects: [
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ test("buildObjectGroupPlaceholderNodes creates capability-driven lazy sidebar gr
|
|||
connectionId: "conn",
|
||||
database: "app",
|
||||
schema: "HR",
|
||||
objectTypes: ["TABLE", "VIEW", "PROCEDURE", "FUNCTION"],
|
||||
objectTypes: ["TABLE", "VIEW", "PROCEDURE", "FUNCTION", "SEQUENCE"],
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
|
|
@ -187,11 +187,12 @@ test("buildObjectGroupPlaceholderNodes creates capability-driven lazy sidebar gr
|
|||
{ label: "tree.views", type: "group-views", count: undefined, children: [] },
|
||||
{ label: "tree.procedures", type: "group-procedures", count: undefined, children: [] },
|
||||
{ label: "tree.functions", type: "group-functions", count: undefined, children: [] },
|
||||
{ label: "tree.sequences", type: "group-sequences", count: undefined, children: [] },
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test("buildSimpleObjectTreeNodes keeps routines and packages visible in flat sidebar mode", () => {
|
||||
test("buildSimpleObjectTreeNodes keeps routines, sequences, and packages visible in flat sidebar mode", () => {
|
||||
const nodes = buildSimpleObjectTreeNodes({
|
||||
nodeId: "conn:app:HR",
|
||||
connectionId: "conn",
|
||||
|
|
@ -202,6 +203,7 @@ test("buildSimpleObjectTreeNodes keeps routines and packages visible in flat sid
|
|||
{ name: "ACTIVE_ORDERS", object_type: "VIEW", schema: "HR" },
|
||||
{ name: "REFRESH_STATS", object_type: "PROCEDURE", schema: "HR" },
|
||||
{ name: "TOTAL_DUE", object_type: "FUNCTION", schema: "HR" },
|
||||
{ name: "ORDER_ID_SEQ", object_type: "SEQUENCE", schema: "HR" },
|
||||
{ name: "PAYROLL", object_type: "PACKAGE", schema: "HR" },
|
||||
{ name: "PAYROLL", object_type: "PACKAGE_BODY", schema: "HR" },
|
||||
],
|
||||
|
|
@ -212,6 +214,7 @@ test("buildSimpleObjectTreeNodes keeps routines and packages visible in flat sid
|
|||
[
|
||||
{ label: "ORDERS", type: "table" },
|
||||
{ label: "ACTIVE_ORDERS", type: "view" },
|
||||
{ label: "ORDER_ID_SEQ", type: "sequence" },
|
||||
{ label: "PAYROLL", type: "package" },
|
||||
{ label: "PAYROLL", type: "package-body" },
|
||||
{ label: "REFRESH_STATS", type: "procedure" },
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ test("maps source-capable sidebar nodes to object source kinds", () => {
|
|||
assert.equal(objectSourceKindForTreeNode("view"), "VIEW");
|
||||
assert.equal(objectSourceKindForTreeNode("procedure"), "PROCEDURE");
|
||||
assert.equal(objectSourceKindForTreeNode("function"), "FUNCTION");
|
||||
assert.equal(objectSourceKindForTreeNode("sequence"), "SEQUENCE");
|
||||
assert.equal(objectSourceKindForTreeNode("package"), "PACKAGE");
|
||||
assert.equal(objectSourceKindForTreeNode("package-body"), "PACKAGE_BODY");
|
||||
assert.equal(objectSourceKindForTreeNode("table"), null);
|
||||
|
|
|
|||
Loading…
Reference in New Issue