diff --git a/agents/drivers/oracle-go/main.go b/agents/drivers/oracle-go/main.go index 618fd17b36..9802f64172 100644 --- a/agents/drivers/oracle-go/main.go +++ b/agents/drivers/oracle-go/main.go @@ -3401,6 +3401,15 @@ func (s *server) getViewSource(schema, name string) (string, error) { return "", err } viewName := strings.TrimSpace(name) + var source string + viewsErr := db.QueryRow( + "SELECT TEXT FROM ALL_VIEWS WHERE OWNER = :1 AND VIEW_NAME = :2", + schema, viewName, + ).Scan(&source) + if viewsErr == nil && strings.TrimSpace(source) != "" { + return strings.TrimSpace(source), nil + } + var ddl string metadataErr := db.QueryRow( "SELECT DBMS_METADATA.GET_DDL('VIEW', :1, :2) FROM DUAL", @@ -3410,22 +3419,11 @@ func (s *server) getViewSource(schema, name string) (string, error) { return strings.TrimSpace(ddl), nil } - var source string - fallbackErr := db.QueryRow( - "SELECT TEXT FROM ALL_VIEWS WHERE OWNER = :1 AND VIEW_NAME = :2", - schema, viewName, - ).Scan(&source) - if fallbackErr == nil && strings.TrimSpace(source) != "" { - return strings.TrimSpace(source), nil - } - if fallbackErr != nil && !errors.Is(fallbackErr, sql.ErrNoRows) { - if metadataErr != nil { - return "", fmt.Errorf( - "failed to load view source for %s.%s: DBMS_METADATA: %v; ALL_VIEWS: %w", - schema, viewName, metadataErr, fallbackErr, - ) - } - return "", fmt.Errorf("failed to load view source for %s.%s from ALL_VIEWS: %w", schema, viewName, fallbackErr) + if viewsErr != nil && !errors.Is(viewsErr, sql.ErrNoRows) && metadataErr != nil { + return "", fmt.Errorf( + "failed to load view source for %s.%s: ALL_VIEWS: %v; DBMS_METADATA: %w", + schema, viewName, viewsErr, metadataErr, + ) } return "", fmt.Errorf("view source not found: %s.%s", schema, viewName) } diff --git a/agents/drivers/oracle-go/main_test.go b/agents/drivers/oracle-go/main_test.go index bb1275dcd8..5c3668fc2f 100644 --- a/agents/drivers/oracle-go/main_test.go +++ b/agents/drivers/oracle-go/main_test.go @@ -2757,15 +2757,15 @@ func fakeOracleColumnLoader(columns []oracleColumnMeta) oracleColumnMetaLoader { } } -func TestGetObjectSourceUsesOriginalViewNameWithDBMSMetadata(t *testing.T) { +func TestGetObjectSourceUsesOriginalViewNameWithAllViews(t *testing.T) { for _, viewName := range []string{"vEnginWJZ", "V_ENGINE_WJZ"} { t.Run(viewName, func(t *testing.T) { - ddl := `CREATE OR REPLACE FORCE VIEW "ZTZS_ERP2"."` + viewName + `" AS SELECT source_id FROM "ZTZS_ERP2"."SOURCE_TABLE"` + source := `SELECT source_id FROM "ZTZS_ERP2"."SOURCE_TABLE"` db, scripted := openOracleViewSourceTestDB(t, []oracleViewSourceQueryStep{ { - queryContains: "DBMS_METADATA.GET_DDL('VIEW'", - args: []driver.Value{viewName, "ZTZS_ERP2"}, - rows: [][]driver.Value{{ddl}}, + queryContains: "FROM ALL_VIEWS", + args: []driver.Value{"ZTZS_ERP2", viewName}, + rows: [][]driver.Value{{source}}, }, }) s := newServer() @@ -2775,7 +2775,7 @@ func TestGetObjectSourceUsesOriginalViewNameWithDBMSMetadata(t *testing.T) { if err != nil { t.Fatal(err) } - if result["source"] != ddl { + if result["source"] != source { t.Fatalf("unexpected view source: %#v", result["source"]) } if scripted.next != len(scripted.steps) { @@ -2982,19 +2982,19 @@ func TestGetObjectSourcePreservesQuotedSynonymName(t *testing.T) { } } -func TestGetObjectSourceFallsBackToAllViewsWithOriginalName(t *testing.T) { +func TestGetObjectSourceFallsBackToDBMSMetadataWithOriginalName(t *testing.T) { const viewName = "vEnginWJZ" - const source = `SELECT source_id FROM "ZTZS_ERP2"."SOURCE_TABLE"` + const ddl = `CREATE OR REPLACE FORCE VIEW "ZTZS_ERP2"."vEnginWJZ" AS SELECT source_id FROM "ZTZS_ERP2"."SOURCE_TABLE"` db, scripted := openOracleViewSourceTestDB(t, []oracleViewSourceQueryStep{ - { - queryContains: "DBMS_METADATA.GET_DDL('VIEW'", - args: []driver.Value{viewName, "ZTZS_ERP2"}, - err: errors.New("ORA-31603: object not found"), - }, { queryContains: "FROM ALL_VIEWS", args: []driver.Value{"ZTZS_ERP2", viewName}, - rows: [][]driver.Value{{source}}, + err: errors.New("ORA-00942: table or view does not exist"), + }, + { + queryContains: "DBMS_METADATA.GET_DDL('VIEW'", + args: []driver.Value{viewName, "ZTZS_ERP2"}, + rows: [][]driver.Value{{ddl}}, }, }) s := newServer() @@ -3004,7 +3004,7 @@ func TestGetObjectSourceFallsBackToAllViewsWithOriginalName(t *testing.T) { if err != nil { t.Fatal(err) } - if result["source"] != source { + if result["source"] != ddl { t.Fatalf("unexpected fallback source: %#v", result["source"]) } if scripted.next != len(scripted.steps) { @@ -3014,16 +3014,16 @@ func TestGetObjectSourceFallsBackToAllViewsWithOriginalName(t *testing.T) { func TestGetObjectSourceRejectsMissingViewSource(t *testing.T) { db, scripted := openOracleViewSourceTestDB(t, []oracleViewSourceQueryStep{ - { - queryContains: "DBMS_METADATA.GET_DDL('VIEW'", - args: []driver.Value{"vEnginWJZ", "ZTZS_ERP2"}, - err: errors.New("ORA-31603: object not found"), - }, { queryContains: "FROM ALL_VIEWS", args: []driver.Value{"ZTZS_ERP2", "vEnginWJZ"}, rows: nil, }, + { + queryContains: "DBMS_METADATA.GET_DDL('VIEW'", + args: []driver.Value{"vEnginWJZ", "ZTZS_ERP2"}, + err: errors.New("ORA-31603: object not found"), + }, }) s := newServer() s.db = db diff --git a/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue b/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue index 3679e7d21d..0aa87ec9f4 100644 --- a/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue +++ b/apps/desktop/src/components/sidebar/SidebarTreeRuntimeHost.vue @@ -352,7 +352,7 @@ const settingsStore = useSettingsStore(); const savedSqlStore = useSavedSqlStore(); -const { toast } = useToast(); +const { toast, message: toastMessage, dismissToast } = useToast(); const installedPlugins = ref([]); const sidebarPluginRegistry = computed(() => createFrontendPluginRegistry(installedPlugins.value, appLocale.value)); @@ -2528,6 +2528,12 @@ function openObjectSourceDialog(initialEditing: boolean, viewPackageBody = false if (!sourceTarget) return; const openMode = settingsStore.editorSettings.routineSourceOpenMode; if (openMode === "query-tab") { + const loadingMessage = t("common.loading"); + const loadingToastTimer = window.setTimeout(() => toast(loadingMessage, 30000), 350); + const finishLoadingToast = () => { + window.clearTimeout(loadingToastTimer); + if (toastMessage.value === loadingMessage) dismissToast(); + }; void connectionStore .ensureConnected(connectionId) .then(async () => { @@ -2571,7 +2577,8 @@ function openObjectSourceDialog(initialEditing: boolean, viewPackageBody = false }) .catch((e: any) => { toast(e?.message || String(e), 5000); - }); + }) + .finally(finishLoadingToast); return; } void connectionStore