8000 bpo-44859: Raise more accurate exceptions in `sqlite3` (GH-27695) · python/cpython@4674fd4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4674fd4

Browse files
author
Erlend Egeberg Aasland
authored
bpo-44859: Raise more accurate exceptions in sqlite3 (GH-27695)
* Improve exception compliance with PEP 249 * Raise InterfaceError instead of ProgrammingError for SQLITE_MISUSE. If SQLITE_MISUSE is raised, it is a sqlite3 module bug. Users of the sqlite3 module are not responsible for using the SQLite C API correctly. * Don't overwrite BufferError with ValueError when conversion to BLOB fails. * Raise ProgrammingError instead of Warning if user tries to execute() more than one SQL statement. * Raise ProgrammingError instead of ValueError if an SQL query contains null characters. * Make sure `_pysqlite_set_result` raises an exception if it returns -1.
1 parent 96568e9 commit 4674fd4

File tree

6 files changed

+45
-14
lines changed
  • Misc/NEWS.d/next/Library
  • Modules/_sqlite
  • 6 files changed

    +45
    -14
    lines changed

    Lib/test/test_sqlite3/test_dbapi.py

    Lines changed: 3 additions & 2 deletions
    Original file line numberDiff line numberDiff line change
    @@ -652,8 +652,9 @@ def test_execute_illegal_sql(self):
    652652
    self.cu.execute("select asdf")
    653653

    654654
    def test_execute_too_much_sql(self):
    655-
    with self.assertRaises(sqlite.Warning):
    656-
    self.cu.execute("select 5+4; select 4+5")
    655+
    self.assertRaisesRegex(sqlite.ProgrammingError,
    656+
    "You can only execute one statement at a time",
    657+
    self.cu.execute, "select 5+4; select 4+5")
    657658

    658659
    def test_execute_too_much_sql2(self):
    659660
    self.cu.execute("select 5+4; -- foo bar")

    Lib/test/test_sqlite3/test_regression.py

    Lines changed: 9 additions & 6 deletions
    Original file line numberDiff line numberDiff line change
    @@ -319,12 +319,15 @@ def test_invalid_isolation_level_type(self):
    319319

    320320
    def test_null_character(self):
    321321
    # Issue #21147
    322-
    con = sqlite.connect(":memory:")
    323-
    self.assertRaises(ValueError, con, "\0select 1")
    324-
    self.assertRaises(ValueError, con, "select 1\0")
    325-
    cur = con.cursor()
    326-
    self.assertRaises(ValueError, cur.execute, " \0select 2")
    327-
    self.assertRaises(ValueError, cur.execute, "select 2\0")
    322+
    cur = self.con.cursor()
    323+
    queries = ["\0select 1", "select 1\0"]
    324+
    for query in queries:
    325+
    with self.subTest(query=query):
    326+
    self.assertRaisesRegex(sqlite.ProgrammingError, "null char",
    327+
    self.con.execute, query)
    328+
    with self.subTest(query=query):
    329+
    self.assertRaisesRegex(sqlite.ProgrammingError, "null char",
    330+
    cur.execute, query)
    328331

    329332
    def test_surrogates(self):
    330333
    con = sqlite.connect(":memory:")

    Lib/test/test_sqlite3/test_userfunctions.py

    Lines changed: 16 additions & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -196,6 +196,8 @@ def setUp(self):
    196196
    self.con.create_function("returnlonglong", 0, func_returnlonglong)
    197197
    self.con.create_function("returnnan", 0, lambda: float("nan"))
    198198
    self.con.create_function("returntoolargeint", 0, lambda: 1 << 65)
    199+
    self.con.create_function("return_noncont_blob", 0,
    200+
    lambda: memoryview(b"blob")[::2])
    199201
    self.con.create_function("raiseexception", 0, func_raiseexception)
    200202
    self.con.create_function("memoryerror", 0, func_memoryerror)
    201203
    self.con.create_function("overflowerror", 0, func_overflowerror)
    @@ -340,10 +342,17 @@ def test_too_large_int(self):
    340342
    "select spam(?)", (1 << 65,))
    341343

    342344
    def test_non_contiguous_blob(self):
    343-
    self.assertRaisesRegex(ValueError, "could not convert BLOB to buffer",
    345+
    self.assertRaisesRegex(BufferError,
    346+
    "underlying buffer is not C-contiguous",
    344347
    self.con.execute, "select spam(?)",
    345348
    (memoryview(b"blob")[::2],))
    346349

    350+
    @with_tracebacks(BufferError, regex="buffer.*contiguous")
    351+
    def test_return_non_contiguous_blob(self):
    352+
    with self.assertRaises(sqlite.OperationalError):
    353+
    cur = self.con.execute("select return_noncont_blob()")
    354+
    cur.fetchone()
    355+
    347356
    def test_param_surrogates(self):
    348357
    self.assertRaisesRegex(UnicodeEncodeError, "surrogates not allowed",
    349358
    self.con.execute, "select spam(?)",
    @@ -466,6 +475,12 @@ def test_func_return_too_large_blob(self, size):
    466475
    with self.assertRaises(sqlite.DataError):
    467476
    cur.execute("select largeblob()")
    468477

    478+
    def test_func_return_illegal_value(self):
    479+
    self.con.create_function("badreturn", 0, lambda: self)
    480+
    msg = "user-defined function raised exception"
    481+
    self.assertRaisesRegex(sqlite.OperationalError, msg,
    482+
    self.con.execute, "select badreturn()")
    483+
    469484

    470485
    class AggregateTests(unittest.TestCase):
    471486
    def setUp(self):
    Lines changed: 10 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,10 @@
    1+
    Raise more accurate and :pep:`249` compatible exceptions in :mod:`sqlite3`.
    2+
    3+
    * Raise :exc:`~sqlite3.InterfaceError` instead of
    4+
    :exc:`~sqlite3.ProgrammingError` for ``SQLITE_MISUSE`` errors.
    5+
    * Don't overwrite :exc:`BufferError` with :exc:`ValueError` when conversion to
    6+
    BLOB fails.
    7+
    * Raise :exc:`~sqlite3.ProgrammingError` instead of :exc:`~sqlite3.Warning` if
    8+
    user tries to :meth:`~sqlite3.Cursor.execute()` more than one SQL statement.
    9+
    * Raise :exc:`~sqlite3.ProgrammingError` instead of :exc:`ValueError` if an SQL
    10+
    query contains null characters.

    Modules/_sqlite/connection.c

    Lines changed: 5 additions & 2 deletions
    Original file line numberDiff line numberDiff line change
    @@ -578,8 +578,6 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
    578578
    } else if (PyObject_CheckBuffer(py_val)) {
    579579
    Py_buffer view;
    580580
    if (PyObject_GetBuffer(py_val, &view, PyBUF_SIMPLE) != 0) {
    581-
    PyErr_SetString(PyExc_ValueError,
    582-
    "could not convert BLOB to buffer");
    583581
    return -1;
    584582
    }
    585583
    if (view.len > INT_MAX) {
    @@ -591,6 +589,11 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
    591589
    sqlite3_result_blob(context, view.buf, (int)view.len, SQLITE_TRANSIENT);
    592590
    PyBuffer_Release(&view);
    593591
    } else {
    592+
    callback_context *ctx = (callback_context *)sqlite3_user_data(context);
    593+
    PyErr_Format(ctx->state->ProgrammingError,
    594+
    "User-defined functions cannot return '%s' values to "
    595+
    "SQLite",
    596+
    Py_TYPE(py_val)->tp_name);
    594597
    return -1;
    595598
    }
    596599
    return 0;

    Modules/_sqlite/statement.c

    Lines changed: 2 additions & 3 deletions
    Original file line numberDiff line numberDiff line change
    @@ -67,7 +67,7 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
    6767
    return NULL;
    6868
    }
    6969
    if (strlen(sql_cstr) != (size_t)size) {
    70-
    PyErr_SetString(PyExc_ValueError,
    70+
    PyErr_SetString(connection->ProgrammingError,
    7171
    "the query contains a null character");
    7272
    return NULL;
    7373
    }
    @@ -85,7 +85,7 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
    8585
    }
    8686

    8787
    if (pysqlite_check_remaining_sql(tail)) {
    88-
    PyErr_SetString(connection->Warning,
    88+
    PyErr_SetString(connection->ProgrammingError,
    8989
    "You can only execute one statement at a time.");
    9090
    goto error;
    9191
    }
    @@ -190,7 +190,6 @@ int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObjec
    190190
    case TYPE_BUFFER: {
    191191
    Py_buffer view;
    192192
    if (PyObject_GetBuffer(parameter, &view, PyBUF_SIMPLE) != 0) {
    193-
    PyErr_SetString(PyExc_ValueError, "could not convert BLOB to buffer");
    194193
    return -1;
    195194
    }
    196195
    if (view.len > INT_MAX) {

    0 commit comments

    Comments
     (0)
    0