Report on syntax errors without crashing
authorKarl O. Pinc <kop@karlpinc.com>
Sun, 18 Aug 2024 22:37:15 +0000 (17:37 -0500)
committerKarl O. Pinc <kop@karlpinc.com>
Sun, 18 Aug 2024 22:37:15 +0000 (17:37 -0500)
src/pgwui_sql/exceptions.py
src/pgwui_sql/views/sql.py

index 1256b18679a76f74be276d34ab9c4be1c4516ca5..e90679387a644846c6f7278cb686859db59d4a63 100644 (file)
@@ -42,20 +42,17 @@ class ExampleDetailedError(ExampleSetupError):
         super().__init__('Detailed error', descr=descr, detail=detail)
 
 
-# Data error related exception
+# SQL error related exception
 
-class ExampleTooManyRowsError(core_ex.DataLineError):
+class SQLError(core_ex.UploadError):
     '''
-    Module exception rasied while line-by-line processing the uploaded
-    data.
+    Error raised when processing SQL
 
-    lineno The line number
-    e      The error message
+    e      The error severity
     descr  More description of the error
     detail Extra HTML describing the error
     data   The uploaded data
     '''
-    def __init__(self, lineno, e, descr='', detail='', data=''):
-        if descr is None:
-            descr = 'Too many rows of uploaded data'
-        super().__init__(e, lineno, descr, detail, data)
+    def __init__(self, e, lineno, descr='', detail=''):
+        super().__init__(f'{e} when executing SQL',
+                         lineno, descr=descr, detail=detail)
index 2d1eda50d3ee78dad6914b3d6c5c6b16d1266c9f..7307050f926e244f72b97db6265718a87c502ad6 100644 (file)
@@ -21,6 +21,8 @@ from pyramid.view import view_config
 from wtforms.fields import TextAreaField
 import attrs
 import logging
+import markupsafe
+import psycopg.errors
 
 import pgwui_core.core
 import pgwui_core.forms
@@ -148,6 +150,32 @@ class SQLHandler(pgwui_core.core.SessionDBHandler):
 
         return response
 
+    def execute(self, cur, stmt_text):
+        try:
+            cur.execute(stmt_text)
+        except psycopg.errors.Error as err:
+            lineno = stmt_text.count(
+                '\n', 0, int(err.diag.statement_position) - 1) + 1
+
+            detail = []
+            if err.diag.message_detail is not None:
+                detail.append(markupsafe.escape(err.diag.message_detail))
+            if err.diag.message_hint is not None:
+                detail.append(
+                    f'Hint: {markupsafe.escape(err.diag.message_hint)}')
+            detail = '<br>\n'.join(detail)
+
+            numbered_stmts = []
+            numbered_stmts.append('<br><ol>')
+            for line in stmt_text.rstrip().split('\n'):
+                numbered_stmts.append(f'<li>{markupsafe.escape(line)}</li>')
+            numbered_stmts.append('</ol>')
+
+            raise sql_ex.SQLError(err.diag.severity,
+                                  lineno,
+                                  descr=err.diag.message_primary,
+                                  detail=detail + '\n'.join(numbered_stmts))
+
     def get_result_rows(self, cur, sql_results):
         first = True
         while (row := cur.fetchone()) is not None:
@@ -163,7 +191,7 @@ class SQLHandler(pgwui_core.core.SessionDBHandler):
         interleaving errors with output.
         '''
         cur = self.cur
-        cur.execute(self.uf['sql'])
+        self.execute(cur, self.uf['sql'])
 
         sql_results = self.sql_results
         nextset = True