You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2021/06/10 22:21:38 UTC

[superset] branch master updated: fix: show custom errors in SQL Lab (#14959)

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

beto 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 cc2b4fe  fix: show custom errors in SQL Lab (#14959)
cc2b4fe is described below

commit cc2b4fe3f471814fb75c1c8bce97867c96caaec7
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Thu Jun 10 15:20:31 2021 -0700

    fix: show custom errors in SQL Lab (#14959)
    
    * fix: show custom errors in SQL Lab
    
    * Fix lint
    
    * Fix test
    
    * Update superset/views/base.py
    
    Co-authored-by: ʈᵃᵢ <ta...@apache.org>
    
    * Fix lint
    
    * Debug failing test
    
    * Remove print()
    
    * Debug flaky tests
    
    * Fix test
    
    Co-authored-by: ʈᵃᵢ <ta...@apache.org>
---
 superset/db_engine_specs/postgres.py | 14 +++++++++++++-
 superset/views/base.py               |  3 +++
 superset/views/core.py               | 11 ++++++++++-
 tests/celery_tests.py                |  5 ++++-
 tests/sqllab_tests.py                |  6 +++++-
 5 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/superset/db_engine_specs/postgres.py b/superset/db_engine_specs/postgres.py
index 513e01e..03c955a 100644
--- a/superset/db_engine_specs/postgres.py
+++ b/superset/db_engine_specs/postgres.py
@@ -80,6 +80,10 @@ CONNECTION_HOST_DOWN_REGEX = re.compile(
 CONNECTION_UNKNOWN_DATABASE_REGEX = re.compile(
     'database "(?P<database>.*?)" does not exist'
 )
+COLUMN_DOES_NOT_EXIST_REGEX = re.compile(
+    r'postgresql error: column "(?P<column_name>.+?)" '
+    r"does not exist\s+LINE (?P<location>\d+?)"
+)
 
 
 class PostgresBaseEngineSpec(BaseEngineSpec):
@@ -100,7 +104,7 @@ class PostgresBaseEngineSpec(BaseEngineSpec):
         "P1Y": "DATE_TRUNC('year', {col})",
     }
 
-    custom_errors = {
+    custom_errors: Dict[Pattern[str], Tuple[str, SupersetErrorType, Dict[str, Any]]] = {
         CONNECTION_INVALID_USERNAME_REGEX: (
             __('The username "%(username)s" does not exist.'),
             SupersetErrorType.CONNECTION_INVALID_USERNAME_ERROR,
@@ -139,6 +143,14 @@ class PostgresBaseEngineSpec(BaseEngineSpec):
             SupersetErrorType.CONNECTION_UNKNOWN_DATABASE_ERROR,
             {"invalid": ["database"]},
         ),
+        COLUMN_DOES_NOT_EXIST_REGEX: (
+            __(
+                'We can\'t seem to resolve the column "%(column_name)s" at '
+                "line %(location)s.",
+            ),
+            SupersetErrorType.COLUMN_DOES_NOT_EXIST_ERROR,
+            {},
+        ),
     }
 
     @classmethod
diff --git a/superset/views/base.py b/superset/views/base.py
index 949406a..c318726 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -207,6 +207,9 @@ def handle_api_exception(
             return json_errors_response(
                 errors=[ex.error], status=ex.status, payload=ex.payload
             )
+        except SupersetErrorsException as ex:
+            logger.warning(ex, exc_info=True)
+            return json_errors_response(errors=ex.errors, status=ex.status)
         except SupersetErrorException as ex:
             logger.warning(ex)
             return json_errors_response(errors=[ex.error], status=ex.status)
diff --git a/superset/views/core.py b/superset/views/core.py
index 622b117..219e81e 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -78,6 +78,7 @@ from superset.exceptions import (
     CertificateException,
     DatabaseNotFound,
     SerializationError,
+    SupersetErrorsException,
     SupersetException,
     SupersetGenericDBErrorException,
     SupersetSecurityException,
@@ -2426,7 +2427,15 @@ class Superset(BaseSupersetView):  # pylint: disable=too-many-public-methods
             raise SupersetGenericDBErrorException(utils.error_msg_from_exception(ex))
 
         if data.get("status") == QueryStatus.FAILED:
-            raise SupersetGenericDBErrorException(data["error"])
+            msg = data["error"]
+            query = _session.query(Query).filter_by(id=query_id).one()
+            database = query.database
+            db_engine_spec = database.db_engine_spec
+            errors = db_engine_spec.extract_errors(msg)
+            _session.close()
+            if errors:
+                raise SupersetErrorsException(errors)
+            raise SupersetGenericDBErrorException(msg)
         return json_success(payload)
 
     @has_access_api
diff --git a/tests/celery_tests.py b/tests/celery_tests.py
index 13c7ac3..ac0d754 100644
--- a/tests/celery_tests.py
+++ b/tests/celery_tests.py
@@ -141,6 +141,8 @@ def get_select_star(table: str, schema: Optional[str] = None):
 
 @pytest.mark.parametrize("ctas_method", [CtasMethod.TABLE, CtasMethod.VIEW])
 def test_run_sync_query_dont_exist(setup_sqllab, ctas_method):
+    examples_db = get_example_database()
+    engine_name = examples_db.db_engine_spec.engine_name
     sql_dont_exist = "SELECT name FROM table_dont_exist"
     result = run_sql(sql_dont_exist, cta=True, ctas_method=ctas_method)
     if backend() == "sqlite" and ctas_method == CtasMethod.VIEW:
@@ -157,7 +159,8 @@ def test_run_sync_query_dont_exist(setup_sqllab, ctas_method):
                     "code": 1002,
                     "message": "Issue 1002 - The database returned an unexpected error.",
                 }
-            ]
+            ],
+            "engine_name": engine_name,
         }
 
 
diff --git a/tests/sqllab_tests.py b/tests/sqllab_tests.py
index 8e1e88d..24b931b 100644
--- a/tests/sqllab_tests.py
+++ b/tests/sqllab_tests.py
@@ -75,6 +75,9 @@ class TestSqlLab(SupersetTestCase):
 
     @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")
     def test_sql_json(self):
+        examples_db = get_example_database()
+        engine_name = examples_db.db_engine_spec.engine_name
+
         self.login("admin")
 
         data = self.run_sql("SELECT * FROM birth_names LIMIT 10", "1")
@@ -91,7 +94,8 @@ class TestSqlLab(SupersetTestCase):
                     "code": 1002,
                     "message": "Issue 1002 - The database returned an unexpected error.",
                 }
-            ]
+            ],
+            "engine_name": engine_name,
         }
 
     @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices")