diff --git a/apps/desktop/src/components/diagram/SchemaDiagramDialog.vue b/apps/desktop/src/components/diagram/SchemaDiagramDialog.vue index 8094ffff8..290430bb2 100644 --- a/apps/desktop/src/components/diagram/SchemaDiagramDialog.vue +++ b/apps/desktop/src/components/diagram/SchemaDiagramDialog.vue @@ -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" && table.table_type !== "MATERIALIZED 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[] = []; diff --git a/apps/desktop/src/components/diff/DataCompareDialog.vue b/apps/desktop/src/components/diff/DataCompareDialog.vue index b27267404..7ad992c20 100644 --- a/apps/desktop/src/components/diff/DataCompareDialog.vue +++ b/apps/desktop/src/components/diff/DataCompareDialog.vue @@ -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" && table.table_type !== "MATERIALIZED 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)); diff --git a/apps/desktop/src/components/objects/ObjectBrowser.vue b/apps/desktop/src/components/objects/ObjectBrowser.vue index 819337014..119faacac 100644 --- a/apps/desktop/src/components/objects/ObjectBrowser.vue +++ b/apps/desktop/src/components/objects/ObjectBrowser.vue @@ -144,7 +144,7 @@ const { addTask: addExportTask } = useExportTracker(); const needsSchema = computed(() => isSchemaAware(props.connection.db_type) && !connectionUsesDatabaseObjectTreeMode(props.connection)); const tableCount = computed(() => rows.value.filter((row) => row.type === "TABLE").length); -const viewCount = computed(() => rows.value.filter((row) => row.type === "VIEW").length); +const viewCount = computed(() => rows.value.filter((row) => row.type === "VIEW" || row.type === "MATERIALIZED_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); @@ -220,7 +220,7 @@ const selectedTableCount = computed(() => selectedTableRows.value.length); const allVisibleTablesSelected = computed(() => visibleSelectableRows.value.length > 0 && visibleSelectableRows.value.every((row) => selectedTableIds.value.has(row.id))); function iconFor(row: ObjectBrowserRow) { - if (row.type === "VIEW") return Eye; + if (row.type === "VIEW" || row.type === "MATERIALIZED_VIEW") return Eye; if (row.type === "PROCEDURE") return ScrollText; if (row.type === "FUNCTION") return Braces; if (row.type === "SEQUENCE") return ListTree; @@ -229,7 +229,7 @@ function iconFor(row: ObjectBrowserRow) { } function typeLabel(type: ObjectBrowserRow["type"]) { - if (type === "VIEW") return t("objects.view"); + if (type === "VIEW" || type === "MATERIALIZED_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"); @@ -254,7 +254,7 @@ function toggleSort(key: ObjectBrowserSortKey) { function rowMatchesObjectFilter(row: ObjectBrowserRow) { if (objectFilter.value === "tables") return row.type === "TABLE"; - if (objectFilter.value === "views") return row.type === "VIEW"; + if (objectFilter.value === "views") return row.type === "VIEW" || row.type === "MATERIALIZED_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"; @@ -292,7 +292,7 @@ function groupedFilteredRows() { } function iconClass(type: ObjectBrowserRow["type"]) { - if (type === "VIEW") return "text-purple-500"; + if (type === "VIEW" || type === "MATERIALIZED_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"; @@ -313,7 +313,7 @@ function togglePartitionParent(row: ObjectBrowserRow) { } function canOpenSource(row: ObjectBrowserRow) { - return row.type === "VIEW" || row.type === "PROCEDURE" || row.type === "FUNCTION" || row.type === "SEQUENCE" || row.type === "PACKAGE" || row.type === "PACKAGE_BODY"; + return row.type === "VIEW" || row.type === "MATERIALIZED_VIEW" || row.type === "PROCEDURE" || row.type === "FUNCTION" || row.type === "SEQUENCE" || row.type === "PACKAGE" || row.type === "PACKAGE_BODY"; } function canRename(row: ObjectBrowserRow) { @@ -372,7 +372,7 @@ async function openSource(row: ObjectBrowserRow) { } async function openViewDdl(row: ObjectBrowserRow) { - if (row.type !== "VIEW") return; + if (row.type !== "VIEW" && row.type !== "MATERIALIZED_VIEW") return; try { const result = await api.getObjectSource(props.connection.id, props.database, row.schema || selectedSchema.value || props.database, row.name, "VIEW"); const ddl = await buildViewDdl({ @@ -533,7 +533,7 @@ async function confirmDrop() { function dropConfirmTitle(): string { if (!dropTarget.value) return ""; const type = dropTarget.value.type; - if (type === "VIEW") return t("contextMenu.confirmDropViewTitle"); + if (type === "VIEW" || type === "MATERIALIZED_VIEW") return t("contextMenu.confirmDropViewTitle"); if (type === "PROCEDURE") return t("contextMenu.confirmDropProcedureTitle"); if (type === "FUNCTION") return t("contextMenu.confirmDropFunctionTitle"); return t("contextMenu.confirmDropTableTitle"); @@ -543,7 +543,7 @@ function dropConfirmMessage(): string { if (!dropTarget.value) return ""; const name = dropTarget.value.name; const type = dropTarget.value.type; - if (type === "VIEW") return t("contextMenu.confirmDropViewMessage", { name }); + if (type === "VIEW" || type === "MATERIALIZED_VIEW") return t("contextMenu.confirmDropViewMessage", { name }); if (type === "PROCEDURE") return t("contextMenu.confirmDropProcedureMessage", { name }); if (type === "FUNCTION") return t("contextMenu.confirmDropFunctionMessage", { name }); return t("contextMenu.confirmDropTableMessage", { name }); @@ -620,7 +620,7 @@ function openDatabaseExport(row: ObjectBrowserRow) { connectionId: props.connection.id, database: props.database, schema: row.schema || selectedSchema.value, - tableName: row.type === "TABLE" || row.type === "VIEW" ? row.name : undefined, + tableName: row.type === "TABLE" || row.type === "VIEW" || row.type === "MATERIALIZED_VIEW" ? row.name : undefined, }; } @@ -728,7 +728,7 @@ async function confirmBatchDropTables() { async function exportStructure(row: ObjectBrowserRow) { try { const schema = row.schema || selectedSchema.value || props.database; - const ddl = await api.getTableDdl(props.connection.id, props.database, schema, row.name, row.type === "VIEW" ? "VIEW" : undefined); + const ddl = await api.getTableDdl(props.connection.id, props.database, schema, row.name, row.type === "VIEW" || row.type === "MATERIALIZED_VIEW" ? "VIEW" : undefined); await saveFileContent(ddl + "\n", `${row.name}.sql`, "SQL", "sql"); } catch (e: any) { console.error("Export structure failed:", e); @@ -1233,7 +1233,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 === "VIEW" || item.type === "MATERIALIZED_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); diff --git a/apps/desktop/src/lib/databaseObjectCapabilities.ts b/apps/desktop/src/lib/databaseObjectCapabilities.ts index e3e6ce774..f2557f6c3 100644 --- a/apps/desktop/src/lib/databaseObjectCapabilities.ts +++ b/apps/desktop/src/lib/databaseObjectCapabilities.ts @@ -8,24 +8,50 @@ export interface DatabaseObjectCapabilities { executable: SidebarObjectKind[]; } -const TABLE_OBJECTS: SidebarObjectKind[] = ["TABLE"]; 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", "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(["influxdb"]); -const TABLE_FUNCTION_TYPES = new Set(["manticoresearch"]); -const TABLE_VIEW_PROCEDURE_TYPES = new Set(["databend"]); -const TABLE_VIEW_ONLY_TYPES = new Set(["sqlite", "rqlite", "turso", "duckdb", "clickhouse", "doris", "starrocks", "hive", "trino", "cassandra", "bigquery", "kylin", "tdengine", "iotdb", "neo4j", "questdb"]); - -const ORACLE_PACKAGE_TYPES = new Set(["oracle", "oceanbase-oracle"]); -const POSTGRES_SEQUENCE_TYPES = new Set(["postgres", "gaussdb", "kwdb", "opengauss"]); -const POSTGRES_LIKE_TYPES = new Set(["kingbase", "highgo", "vastbase", "redshift"]); - +const DATABASE_TYPE_OBJECTS = new Map([ + // postgres + ["postgres", POSTGRES_OBJECTS], + ["gaussdb", POSTGRES_OBJECTS], + ["kwdb", POSTGRES_OBJECTS], + ["opengauss", POSTGRES_OBJECTS], + // postgres like + ["kingbase", POSTGRES_LIKE_OBJECTS], + ["highgo", POSTGRES_LIKE_OBJECTS], + ["vastbase", POSTGRES_LIKE_OBJECTS], + ["redshift", POSTGRES_LIKE_OBJECTS], + // oracle + ["oracle", ORACLE_OBJECTS], + ["oceanbase-oracle", ORACLE_OBJECTS], + // table and view + ["sqlite", TABLE_VIEW_OBJECTS], + ["rqlite", TABLE_VIEW_OBJECTS], + ["turso", TABLE_VIEW_OBJECTS], + ["duckdb", TABLE_VIEW_OBJECTS], + ["clickhouse", TABLE_VIEW_OBJECTS], + ["doris", TABLE_VIEW_OBJECTS], + ["starrocks", TABLE_VIEW_OBJECTS], + ["hive", TABLE_VIEW_OBJECTS], + ["trino", TABLE_VIEW_OBJECTS], + ["cassandra", TABLE_VIEW_OBJECTS], + ["bigquery", TABLE_VIEW_OBJECTS], + ["kylin", TABLE_VIEW_OBJECTS], + ["tdengine", TABLE_VIEW_OBJECTS], + ["iotdb", TABLE_VIEW_OBJECTS], + ["neo4j", TABLE_VIEW_OBJECTS], + // others + ["influxdb", ["TABLE"]], + ["questdb", ["TABLE", "VIEW", "MATERIALIZED_VIEW"]], + ["manticoresearch", ["TABLE", "FUNCTION"]], + ["databend", ["TABLE", "VIEW", "PROCEDURE"]], +]); export function databaseObjectCapabilities(dbType?: DatabaseType): DatabaseObjectCapabilities { const sidebarObjects = sidebarObjectKindsForDatabase(dbType); return { @@ -37,21 +63,14 @@ export function databaseObjectCapabilities(dbType?: DatabaseType): DatabaseObjec export function sidebarObjectKindsForDatabase(dbType?: DatabaseType): SidebarObjectKind[] { if (!dbType) return [...TABLE_VIEW_OBJECTS]; - if (ORACLE_PACKAGE_TYPES.has(dbType)) return [...ORACLE_OBJECTS]; - if (TABLE_ONLY_TYPES.has(dbType)) return [...TABLE_OBJECTS]; - if (TABLE_FUNCTION_TYPES.has(dbType)) return [...TABLE_FUNCTION_OBJECTS]; - 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]; + return DATABASE_TYPE_OBJECTS.get(dbType) ?? [...ROUTINE_OBJECTS]; } 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("MATERIALIZED_VIEW")) return "MATERIALIZED_VIEW"; if (value.includes("VIEW")) return "VIEW"; if (value.includes("SEQ")) return "SEQUENCE"; if (value.includes("PROC")) return "PROCEDURE"; diff --git a/apps/desktop/src/lib/objectBrowserRows.ts b/apps/desktop/src/lib/objectBrowserRows.ts index 4a7ab0dd9..c2bb2ad44 100644 --- a/apps/desktop/src/lib/objectBrowserRows.ts +++ b/apps/desktop/src/lib/objectBrowserRows.ts @@ -23,7 +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("MATERIALIZED_VIEW")) return "MATERIALIZED_VIEW"; if (value.includes("VIEW")) return "VIEW"; if (value.includes("SEQ")) return "SEQUENCE"; if (value.includes("PROC")) return "PROCEDURE"; diff --git a/apps/desktop/src/stores/connectionStore.ts b/apps/desktop/src/stores/connectionStore.ts index f3a7f4a4b..b0f682321 100644 --- a/apps/desktop/src/stores/connectionStore.ts +++ b/apps/desktop/src/stores/connectionStore.ts @@ -2043,7 +2043,7 @@ export const useConnectionStore = defineStore("connection", () => { return tables.map((table) => ({ name: table.name, schema: s, - type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED 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[]; @@ -2065,7 +2065,7 @@ export const useConnectionStore = defineStore("connection", () => { return tables.map((table) => ({ name: table.name, schema: s, - type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED 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[]; @@ -2092,7 +2092,7 @@ export const useConnectionStore = defineStore("connection", () => { return tables.map((table) => ({ name: table.name, schema, - type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED 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 []; @@ -2111,7 +2111,7 @@ export const useConnectionStore = defineStore("connection", () => { } completionTablesCache.value[cacheKey] = tables.map((table) => ({ name: table.name, - type: table.table_type === "VIEW" || table.table_type === "MATERIALIZED 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]); diff --git a/crates/dbx-core/assets/database-drivers.manifest.json b/crates/dbx-core/assets/database-drivers.manifest.json index a134f6496..2fa17f1bb 100644 --- a/crates/dbx-core/assets/database-drivers.manifest.json +++ b/crates/dbx-core/assets/database-drivers.manifest.json @@ -939,7 +939,7 @@ "fieldLineage": false, "sqlExplain": true, "userAdmin": false, - "driverManagement": true + "driverManagement": false } }, { diff --git a/crates/dbx-core/src/db/postgres.rs b/crates/dbx-core/src/db/postgres.rs index c49e87ffc..934c48e0f 100644 --- a/crates/dbx-core/src/db/postgres.rs +++ b/crates/dbx-core/src/db/postgres.rs @@ -946,7 +946,7 @@ pub async fn list_tables_filtered( fn postgres_tables_sql() -> &'static str { "SELECT c.relname AS table_name, \ CASE c.relkind WHEN 'r' THEN 'BASE TABLE' WHEN 'v' THEN 'VIEW' \ - WHEN 'm' THEN 'MATERIALIZED VIEW' WHEN 'f' THEN 'FOREIGN TABLE' \ + WHEN 'm' THEN 'MATERIALIZED_VIEW' WHEN 'f' THEN 'FOREIGN TABLE' \ WHEN 'p' THEN 'BASE TABLE' END AS table_type, \ obj_description(c.oid) AS table_comment, \ CASE WHEN pc.relkind = 'p' THEN pn.nspname ELSE NULL END AS parent_schema, \ @@ -984,7 +984,7 @@ fn list_objects_sql(include_timestamps: bool) -> &'static str { return "SELECT c.relname AS object_name, \ CASE c.relkind \ WHEN 'v' THEN 'VIEW' \ - WHEN 'm' THEN 'VIEW' \ + WHEN 'm' THEN 'MATERIALIZED_VIEW' \ WHEN 'S' THEN 'SEQUENCE' \ ELSE 'TABLE' \ END AS object_type, \ @@ -1026,7 +1026,7 @@ fn list_objects_sql(include_timestamps: bool) -> &'static str { "SELECT c.relname AS object_name, \ CASE c.relkind \ WHEN 'v' THEN 'VIEW' \ - WHEN 'm' THEN 'VIEW' \ + WHEN 'm' THEN 'MATERIALIZED_VIEW' \ WHEN 'S' THEN 'SEQUENCE' \ ELSE 'TABLE' \ END AS object_type, \ @@ -1678,7 +1678,7 @@ pub async fn list_owners(pool: &Pool, schema: &str) -> Result, St let object_type = match relkind.as_str() { "r" => "TABLE", "v" => "VIEW", - "m" => "MATERIALIZED VIEW", + "m" => "MATERIALIZED_VIEW", "S" => "SEQUENCE", "f" => "FOREIGN TABLE", "p" => "PARTITIONED TABLE", @@ -2098,7 +2098,7 @@ mod tests { assert!(sql.contains("$1")); assert!(sql.contains("BASE TABLE")); assert!(sql.contains("VIEW")); - assert!(sql.contains("MATERIALIZED VIEW")); + assert!(sql.contains("MATERIALIZED_VIEW")); assert!(sql.contains("FOREIGN TABLE")); } diff --git a/crates/dbx-core/src/db/questdb.rs b/crates/dbx-core/src/db/questdb.rs index 11d974f72..a64314a6b 100644 --- a/crates/dbx-core/src/db/questdb.rs +++ b/crates/dbx-core/src/db/questdb.rs @@ -21,23 +21,34 @@ pub async fn list_objects(pool: &Pool, schema: &str) -> Result, .collect()) } +/// try query `table`, `view` and `materialized view` using statement supported by the newer version. +/// if there is an error, rollback to the previous version of the statement pub async fn list_tables(pool: &Pool, _schema: &str) -> Result, String> { + match list_tables_new_version(pool, _schema).await { + Ok(ddl) => Ok(ddl), + Err(_) => list_tables_older_version(pool, _schema).await, + } +} + +async fn list_tables_new_version(pool: &Pool, _schema: &str) -> Result, String> { let client = pool.get().await.map_err(|e| e.to_string())?; - let stmt = client.prepare_cached(questdb_tables_sql()).await.map_err(|e| e.to_string())?; + let stmt = client.prepare_cached(questdb_tables_sql_new_version()).await.map_err(|e| e.to_string())?; let rows = client.query(&stmt, &[]).await.map_err(|e| e.to_string())?; Ok(rows .iter() .map(|row| { let table_type_col = row.get::<_, String>(1); - let table_type = if table_type_col.eq_ignore_ascii_case("T") { "TABLE" } else { "VIEW" }; - let comment = - if table_type_col.eq_ignore_ascii_case("M") { Some("Materialized".to_string()) } else { None }; + let table_type = match table_type_col.as_ref() { + "V" => "VIEW", + "M" => "MATERIALIZED_VIEW", + _ => "TABLE", + }; TableInfo { name: row.get::<_, String>(0), table_type: table_type.to_string(), - comment, + comment: None, parent_schema: None, parent_name: None, } @@ -45,10 +56,36 @@ pub async fn list_tables(pool: &Pool, _schema: &str) -> Result, S .collect()) } -fn questdb_tables_sql() -> &'static str { +fn questdb_tables_sql_new_version() -> &'static str { "SELECT table_name, table_type FROM tables" } +async fn list_tables_older_version(pool: &Pool, _schema: &str) -> Result, String> { + let client = pool.get().await.map_err(|e| e.to_string())?; + + let stmt = client.prepare_cached(questdb_tables_sql_older_version()).await.map_err(|e| e.to_string())?; + let rows = client.query(&stmt, &[]).await.map_err(|e| e.to_string())?; + + Ok(rows + .iter() + .map(|row| { + let mat_view_col = row.get::<_, bool>(1); + let table_type = if mat_view_col { "MATERIALIZED_VIEW" } else { "TABLE" }; + TableInfo { + name: row.get::<_, String>(0), + table_type: table_type.to_string(), + comment: None, + parent_schema: None, + parent_name: None, + } + }) + .collect()) +} + +fn questdb_tables_sql_older_version() -> &'static str { + "SELECT table_name, matView FROM tables" +} + pub async fn get_columns(pool: &Pool, _schema: &str, table: &str) -> Result, String> { let client = pool.get().await.map_err(|e| e.to_string())?; let sql = format!("SHOW COLUMNS FROM {}", quote_table_identifier(Some(DatabaseType::Questdb), table)); @@ -109,9 +146,9 @@ pub async fn questdb_object_source(pool: &Pool, name: &str) -> Result Result { - match questdb_table_ddl(pool, table_or_view).await { + match questdb_view_ddl(pool, table_or_view).await { Ok(ddl) => Ok(ddl), - Err(_) => questdb_view_ddl(pool, table_or_view).await, + Err(_) => questdb_table_ddl(pool, table_or_view).await, } } diff --git a/crates/dbx-core/src/db_admin_sql.rs b/crates/dbx-core/src/db_admin_sql.rs index b6ae0401e..93fdb8645 100644 --- a/crates/dbx-core/src/db_admin_sql.rs +++ b/crates/dbx-core/src/db_admin_sql.rs @@ -466,7 +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::MaterializedView => "MATERIALIZED_VIEW", DatabaseObjectType::Procedure => "PROCEDURE", DatabaseObjectType::Function => "FUNCTION", } diff --git a/crates/dbx-core/src/schema/normalization.rs b/crates/dbx-core/src/schema/normalization.rs index 2742be54d..5bec0df9c 100644 --- a/crates/dbx-core/src/schema/normalization.rs +++ b/crates/dbx-core/src/schema/normalization.rs @@ -236,7 +236,7 @@ mod tests { }, db::ObjectInfo { name: "active_orders".to_string(), - object_type: "MATERIALIZED VIEW".to_string(), + object_type: "MATERIALIZED_VIEW".to_string(), schema: None, comment: None, created_at: None,