]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-89022: Improve sqlite3 exceptions related to binding params and API misuse (#91572)
authorErlend Egeberg Aasland <erlend.aasland@protonmail.com>
Wed, 4 May 2022 13:16:01 +0000 (07:16 -0600)
committerGitHub <noreply@github.com>
Wed, 4 May 2022 13:16:01 +0000 (07:16 -0600)
* Map SQLITE_MISUSE to sqlite3.InterfaceError

SQLITE_MISUSE implies misuse of the SQLite C API, which, if it happens,
is _not_ a user error; it is an sqlite3 extension module error.

* Raise better errors when binding parameters fail.

Instead of always raising InterfaceError, guessing what went wrong,
raise accurate exceptions with more accurate error messages.

Lib/test/test_sqlite3/test_dbapi.py
Lib/test/test_sqlite3/test_types.py
Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst [new file with mode: 0644]
Modules/_sqlite/cursor.c
Modules/_sqlite/util.c

index 7e675a91b5d1fdbd31471e9c8215a74a8fd249f0..e132fcdfb0e65803fbca1ae2d1c371c0e4fa598a 100644 (file)
@@ -743,7 +743,7 @@ class CursorTests(unittest.TestCase):
         self.assertEqual(row[0], "Hu\x00go")
 
     def test_execute_non_iterable(self):
-        with self.assertRaises(ValueError) as cm:
+        with self.assertRaises(sqlite.ProgrammingError) as cm:
             self.cu.execute("insert into test(id) values (?)", 42)
         self.assertEqual(str(cm.exception), 'parameters are of unsupported type')
 
index 0cfb72c5f0999e16bca9a9b2f16b8b27221f1e4f..177cd102350397553596c5b2aedab2c73bdabc02 100644 (file)
@@ -255,9 +255,9 @@ class DeclTypesTests(unittest.TestCase):
 
     def test_error_in_conform(self):
         val = DeclTypesTests.BadConform(TypeError)
-        with self.assertRaises(sqlite.InterfaceError):
+        with self.assertRaises(sqlite.ProgrammingError):
             self.cur.execute("insert into test(bad) values (?)", (val,))
-        with self.assertRaises(sqlite.InterfaceError):
+        with self.assertRaises(sqlite.ProgrammingError):
             self.cur.execute("insert into test(bad) values (:val)", {"val": val})
 
         val = DeclTypesTests.BadConform(KeyboardInterrupt)
@@ -269,13 +269,13 @@ class DeclTypesTests(unittest.TestCase):
     def test_unsupported_seq(self):
         class Bar: pass
         val = Bar()
-        with self.assertRaises(sqlite.InterfaceError):
+        with self.assertRaises(sqlite.ProgrammingError):
             self.cur.execute("insert into test(f) values (?)", (val,))
 
     def test_unsupported_dict(self):
         class Bar: pass
         val = Bar()
-        with self.assertRaises(sqlite.InterfaceError):
+        with self.assertRaises(sqlite.ProgrammingError):
             self.cur.execute("insert into test(f) values (:val)", {"val": val})
 
     def test_blob(self):
diff --git a/Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst b/Misc/NEWS.d/next/Library/2022-04-15-17-06-09.gh-issue-89022.DgdQCa.rst
new file mode 100644 (file)
index 0000000..4392f29
--- /dev/null
@@ -0,0 +1,4 @@
+In :mod:`sqlite3`, ``SQLITE_MISUSE`` result codes are now mapped to
+:exc:`~sqlite3.InterfaceError` instead of :exc:`~sqlite3.ProgrammingError`.
+Also, more accurate exceptions are raised when binding parameters fail.
+Patch by Erlend E. Aasland.
index f72bf30024065e6ea8749148879400e46ca8d760..c58def5f0362f15f940e7e05af6a97266e5018c8 100644 (file)
@@ -527,7 +527,8 @@ stmt_step(sqlite3_stmt *statement)
 }
 
 static int
-bind_param(pysqlite_Statement *self, int pos, PyObject *parameter)
+bind_param(pysqlite_state *state, pysqlite_Statement *self, int pos,
+           PyObject *parameter)
 {
     int rc = SQLITE_OK;
     const char *string;
@@ -603,6 +604,9 @@ bind_param(pysqlite_Statement *self, int pos, PyObject *parameter)
             break;
         }
         case TYPE_UNKNOWN:
+            PyErr_Format(state->ProgrammingError,
+                    "Error binding parameter %d: type '%s' is not supported",
+                    pos, Py_TYPE(parameter)->tp_name);
             rc = -1;
     }
 
@@ -688,15 +692,15 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
                 }
             }
 
-            rc = bind_param(self, i + 1, adapted);
+            rc = bind_param(state, self, i + 1, adapted);
             Py_DECREF(adapted);
 
             if (rc != SQLITE_OK) {
-                if (!PyErr_Occurred()) {
-                    PyErr_Format(state->InterfaceError,
-                                 "Error binding parameter %d - "
-                                 "probably unsupported type.", i);
-                }
+                PyObject *exc, *val, *tb;
+                PyErr_Fetch(&exc, &val, &tb);
+                sqlite3 *db = sqlite3_db_handle(self->st);
+                _pysqlite_seterror(state, db);
+                _PyErr_ChainExceptions(exc, val, tb);
                 return;
             }
         }
@@ -748,20 +752,21 @@ bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
                 }
             }
 
-            rc = bind_param(self, i, adapted);
+            rc = bind_param(state, self, i, adapted);
             Py_DECREF(adapted);
 
             if (rc != SQLITE_OK) {
-                if (!PyErr_Occurred()) {
-                    PyErr_Format(state->InterfaceError,
-                                 "Error binding parameter :%s - "
-                                 "probably unsupported type.", binding_name);
-                }
+                PyObject *exc, *val, *tb;
+                PyErr_Fetch(&exc, &val, &tb);
+                sqlite3 *db = sqlite3_db_handle(self->st);
+                _pysqlite_seterror(state, db);
+                _PyErr_ChainExceptions(exc, val, tb);
                 return;
            }
         }
     } else {
-        PyErr_SetString(PyExc_ValueError, "parameters are of unsupported type");
+        PyErr_SetString(state->ProgrammingError,
+                        "parameters are of unsupported type");
     }
 }
 
index 37b2dd6cb29f4e4874bdc26177a5dec4b99ac628..2b3bbfefa3cf5fc0410b91b74c2fa64d1529b603 100644 (file)
@@ -59,7 +59,6 @@ get_exception_class(pysqlite_state *state, int errorcode)
         case SQLITE_MISMATCH:
             return state->IntegrityError;
         case SQLITE_MISUSE:
-            return state->ProgrammingError;
         case SQLITE_RANGE:
             return state->InterfaceError;
         default: