This is an automated email from the ASF dual-hosted git repository.

kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 5566eb8dd6 fix: Undefined error when viewing query in Explore + visual 
fixes (#34869)
5566eb8dd6 is described below

commit 5566eb8dd625d79227cb0bb9e2f6284852993bc3
Author: Kamil Gabryjelski <kamil.gabryjel...@gmail.com>
AuthorDate: Thu Aug 28 12:40:29 2025 +0200

    fix: Undefined error when viewing query in Explore + visual fixes (#34869)
---
 .../explore/components/controls/ViewQuery.test.tsx | 74 ++++++++++++++++++++++
 .../src/explore/components/controls/ViewQuery.tsx  | 61 ++++++++++--------
 .../components/controls/ViewQueryModalFooter.tsx   |  7 +-
 .../src/features/queries/QueryPreviewModal.tsx     |  8 +--
 .../features/queries/SavedQueryPreviewModal.tsx    |  5 +-
 .../src/features/queries/SyntaxHighlighterCopy.tsx |  1 -
 .../src/pages/QueryHistoryList/index.tsx           | 11 ++--
 7 files changed, 128 insertions(+), 39 deletions(-)

diff --git 
a/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx 
b/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx
index a7f655b3b4..1340c1af10 100644
--- a/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQuery.test.tsx
@@ -191,3 +191,77 @@ test('hides View in SQL Lab button when user does not have 
SQL Lab access', () =
   expect(screen.queryByText('View in SQL Lab')).not.toBeInTheDocument();
   expect(screen.getByText('Copy')).toBeInTheDocument(); // Copy button should 
still be visible
 });
+
+test('handles undefined datasource without crashing', () => {
+  const propsWithUndefinedDatasource = {
+    ...mockProps,
+    datasource: undefined as any,
+  };
+
+  expect(() => setup(propsWithUndefinedDatasource)).not.toThrow();
+});
+
+test('handles dataset API error gracefully when no exploreBackend', async () 
=> {
+  const stateWithoutBackend = {
+    ...mockState(),
+    explore: undefined,
+  };
+
+  fetchMock.get(
+    datasetApiEndpoint,
+    { throws: new Error('API Error') },
+    { overwriteRoutes: true },
+  );
+
+  setup(mockProps, stateWithoutBackend);
+
+  await waitFor(() => {
+    expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
+  });
+
+  expect(fetchMock.calls(formatSqlEndpoint)).toHaveLength(0);
+});
+
+test('handles SQL formatting API error gracefully', async () => {
+  const stateWithoutBackend = {
+    ...mockState(),
+    explore: undefined,
+  };
+
+  fetchMock.post(
+    formatSqlEndpoint,
+    { throws: new Error('Format Error') },
+    { overwriteRoutes: true },
+  );
+
+  setup(mockProps, stateWithoutBackend);
+
+  await waitFor(() => {
+    expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
+  });
+});
+
+test('uses exploreBackend from Redux state when available', async () => {
+  const stateWithBackend = {
+    ...mockState(),
+    explore: {
+      datasource: {
+        database: {
+          backend: 'postgresql',
+        },
+      },
+    },
+  };
+
+  setup(mockProps, stateWithBackend);
+
+  await waitFor(() => {
+    expect(fetchMock.calls(formatSqlEndpoint)).toHaveLength(1);
+  });
+
+  const formatCallBody = JSON.parse(
+    fetchMock.lastCall(formatSqlEndpoint)?.[1]?.body as string,
+  );
+  expect(formatCallBody.engine).toBe('postgresql');
+  expect(fetchMock.calls(datasetApiEndpoint)).toHaveLength(0);
+});
diff --git a/superset-frontend/src/explore/components/controls/ViewQuery.tsx 
b/superset-frontend/src/explore/components/controls/ViewQuery.tsx
index 048413af46..ed9cd7d9de 100644
--- a/superset-frontend/src/explore/components/controls/ViewQuery.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQuery.tsx
@@ -43,6 +43,7 @@ import CodeSyntaxHighlighter, {
   preloadLanguages,
 } from '@superset-ui/core/components/CodeSyntaxHighlighter';
 import { useHistory } from 'react-router-dom';
+import { ExplorePageState } from 'src/explore/types';
 
 export interface ViewQueryProps {
   sql: string;
@@ -74,7 +75,10 @@ const DATASET_BACKEND_QUERY = {
 const ViewQuery: FC<ViewQueryProps> = props => {
   const { sql, language = 'sql', datasource } = props;
   const theme = useTheme();
-  const datasetId = datasource.split('__')[0];
+  const datasetId = datasource?.split('__')[0];
+  const exploreBackend = useSelector(
+    (state: ExplorePageState) => state.explore?.datasource?.database?.backend,
+  );
   const [formattedSQL, setFormattedSQL] = useState<string>();
   const [showFormatSQL, setShowFormatSQL] = useState(true);
   const history = useHistory();
@@ -88,31 +92,37 @@ const ViewQuery: FC<ViewQueryProps> = props => {
     preloadLanguages([language]);
   }, [language]);
 
-  const formatCurrentQuery = useCallback(() => {
+  const formatCurrentQuery = useCallback(async () => {
     if (formattedSQL) {
       setShowFormatSQL(val => !val);
-    } else {
-      const queryParams = rison.encode(DATASET_BACKEND_QUERY);
-      SupersetClient.get({
-        endpoint: `/api/v1/dataset/${datasetId}?q=${queryParams}`,
-      })
-        .then(({ json }) =>
-          SupersetClient.post({
-            endpoint: `/api/v1/sqllab/format_sql/`,
-            body: JSON.stringify({
-              sql,
-              engine: json.result.database.backend,
-            }),
-            headers: { 'Content-Type': 'application/json' },
-          }),
-        )
-        .then(({ json }) => {
-          setFormattedSQL(json.result);
-          setShowFormatSQL(true);
-        })
-        .catch(() => {
-          setShowFormatSQL(true);
+      return;
+    }
+    try {
+      let backend = exploreBackend;
+
+      // Fetch backend info if not available in Redux state
+      if (!backend) {
+        const queryParams = rison.encode(DATASET_BACKEND_QUERY);
+        const response = await SupersetClient.get({
+          endpoint: `/api/v1/dataset/${datasetId}?q=${queryParams}`,
         });
+        backend = response.json.result.database;
+      }
+
+      // Format the SQL query
+      const formatResponse = await SupersetClient.post({
+        endpoint: `/api/v1/sqllab/format_sql/`,
+        body: JSON.stringify({
+          sql,
+          engine: backend,
+        }),
+        headers: { 'Content-Type': 'application/json' },
+      });
+
+      setFormattedSQL(formatResponse.json.result);
+      setShowFormatSQL(true);
+    } catch (error) {
+      setShowFormatSQL(false);
     }
   }, [sql, datasetId, formattedSQL]);
 
@@ -142,8 +152,9 @@ const ViewQuery: FC<ViewQueryProps> = props => {
   return (
     <Card bodyStyle={{ padding: theme.sizeUnit * 4 }}>
       <StyledSyntaxContainer key={sql}>
-        {!formattedSQL && <Skeleton active />}
-        {formattedSQL && (
+        {!formattedSQL && showFormatSQL ? (
+          <Skeleton active />
+        ) : (
           <StyledThemedSyntaxHighlighter
             language={language}
             customStyle={{ flex: 1, marginBottom: theme.sizeUnit * 3 }}
diff --git 
a/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx 
b/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx
index 3fa59a9a5e..5834cbd16e 100644
--- a/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx
+++ b/superset-frontend/src/explore/components/controls/ViewQueryModalFooter.tsx
@@ -76,6 +76,7 @@ const ViewQueryModalFooter: FC<ViewQueryModalFooterProps> = 
(props: {
   return (
     <div>
       <Button
+        buttonStyle="secondary"
         onClick={() => {
           props?.closeModal?.();
           props?.changeDatasource?.();
@@ -83,11 +84,13 @@ const ViewQueryModalFooter: FC<ViewQueryModalFooterProps> = 
(props: {
       >
         {SAVE_AS_DATASET}
       </Button>
-      <Button onClick={({ metaKey }) => openSQL(Boolean(metaKey))}>
+      <Button
+        buttonStyle="secondary"
+        onClick={({ metaKey }) => openSQL(Boolean(metaKey))}
+      >
         {OPEN_IN_SQL_LAB}
       </Button>
       <Button
-        buttonStyle="primary"
         onClick={() => {
           props?.closeModal?.();
         }}
diff --git a/superset-frontend/src/features/queries/QueryPreviewModal.tsx 
b/superset-frontend/src/features/queries/QueryPreviewModal.tsx
index 447b3e7d8a..1f7f7a0db9 100644
--- a/superset-frontend/src/features/queries/QueryPreviewModal.tsx
+++ b/superset-frontend/src/features/queries/QueryPreviewModal.tsx
@@ -40,11 +40,10 @@ const QueryLabel = styled.div`
 `;
 
 const QueryViewToggle = styled.div`
-  margin: 0 0 ${({ theme }) => theme.sizeUnit * 6}px 0;
+  display: flex;
 `;
 
 const TabButton = styled.div`
-  display: inline;
   font-size: ${({ theme }) => theme.fontSizeSM}px;
   padding: ${({ theme }) => theme.sizeUnit * 2}px
     ${({ theme }) => theme.sizeUnit * 4}px;
@@ -55,9 +54,7 @@ const TabButton = styled.div`
   &:focus,
   &:hover {
     background: ${({ theme }) => theme.colorPrimaryBg};
-    border-bottom: none;
     border-radius: ${({ theme }) => theme.borderRadius}px;
-    margin-bottom: ${({ theme }) => theme.sizeUnit * 2}px;
   }
 
   &:hover:not(.active) {
@@ -110,6 +107,7 @@ function QueryPreviewModal({
             <Button
               data-test="previous-query"
               key="previous-query"
+              buttonStyle="secondary"
               disabled={disablePrevious}
               onClick={() => handleDataChange(true)}
             >
@@ -118,6 +116,7 @@ function QueryPreviewModal({
             <Button
               data-test="next-query"
               key="next-query"
+              buttonStyle="secondary"
               disabled={disableNext}
               onClick={() => handleDataChange(false)}
             >
@@ -126,7 +125,6 @@ function QueryPreviewModal({
             <Button
               data-test="open-in-sql-lab"
               key="open-in-sql-lab"
-              buttonStyle="primary"
               onClick={() => openInSqlLab(id)}
             >
               {t('Open in SQL Lab')}
diff --git a/superset-frontend/src/features/queries/SavedQueryPreviewModal.tsx 
b/superset-frontend/src/features/queries/SavedQueryPreviewModal.tsx
index ce888945e0..9e6b69a6d6 100644
--- a/superset-frontend/src/features/queries/SavedQueryPreviewModal.tsx
+++ b/superset-frontend/src/features/queries/SavedQueryPreviewModal.tsx
@@ -34,7 +34,7 @@ const QueryTitle = styled.div`
 const QueryLabel = styled.div`
   color: ${({ theme }) => theme.colorTextLabel};
   font-size: ${({ theme }) => theme.fontSize}px;
-  padding: 4px 0 16px 0;
+  padding-top: ${({ theme }) => theme.sizeUnit}px;
 `;
 
 const StyledModal = styled(Modal)`
@@ -89,6 +89,7 @@ const SavedQueryPreviewModal: FunctionComponent<
             <Button
               data-test="previous-saved-query"
               key="previous-saved-query"
+              buttonStyle="secondary"
               disabled={disablePrevious}
               onClick={() => handleDataChange(true)}
             >
@@ -97,6 +98,7 @@ const SavedQueryPreviewModal: FunctionComponent<
             <Button
               data-test="next-saved-query"
               key="next-saved-query"
+              buttonStyle="secondary"
               disabled={disableNext}
               onClick={() => handleDataChange(false)}
             >
@@ -105,7 +107,6 @@ const SavedQueryPreviewModal: FunctionComponent<
             <Button
               data-test="open-in-sql-lab"
               key="open-in-sql-lab"
-              buttonStyle="primary"
               onClick={({ metaKey }) =>
                 openInSqlLab(savedQuery.id, Boolean(metaKey))
               }
diff --git a/superset-frontend/src/features/queries/SyntaxHighlighterCopy.tsx 
b/superset-frontend/src/features/queries/SyntaxHighlighterCopy.tsx
index 4b38e381a3..5262548f48 100644
--- a/superset-frontend/src/features/queries/SyntaxHighlighterCopy.tsx
+++ b/superset-frontend/src/features/queries/SyntaxHighlighterCopy.tsx
@@ -29,7 +29,6 @@ import copyTextToClipboard from 'src/utils/copy';
 
 const SyntaxHighlighterWrapper = styled.div`
   position: relative;
-  margin-top: -24px;
 
   &:hover {
     .copy-button {
diff --git a/superset-frontend/src/pages/QueryHistoryList/index.tsx 
b/superset-frontend/src/pages/QueryHistoryList/index.tsx
index dc4de5b309..e2291678ff 100644
--- a/superset-frontend/src/pages/QueryHistoryList/index.tsx
+++ b/superset-frontend/src/pages/QueryHistoryList/index.tsx
@@ -190,7 +190,7 @@ function QueryList({ addDangerToast }: QueryListProps) {
           ) {
             statusConfig.name = (
               <Icons.CloseOutlined
-                iconSize="xs"
+                iconSize="m"
                 iconColor={
                   status === QueryState.Failed
                     ? theme.colorError
@@ -201,19 +201,22 @@ function QueryList({ addDangerToast }: QueryListProps) {
             statusConfig.label = t('Failed');
           } else if (status === QueryState.Running) {
             statusConfig.name = (
-              <Icons.Running iconColor={theme.colorPrimary} />
+              <Icons.LoadingOutlined
+                iconSize="m"
+                iconColor={theme.colorPrimary}
+              />
             );
             statusConfig.label = t('Running');
           } else if (status === QueryState.TimedOut) {
             statusConfig.name = (
-              <Icons.CircleSolid iconColor={theme.colorIcon} />
+              <Icons.CircleSolid iconSize="m" iconColor={theme.colorIcon} />
             );
             statusConfig.label = t('Offline');
           } else if (
             status === QueryState.Scheduled ||
             status === QueryState.Pending
           ) {
-            statusConfig.name = <Icons.Queued />;
+            statusConfig.name = <Icons.Queued iconSize="m" />;
             statusConfig.label = t('Scheduled');
           }
           return (

Reply via email to