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

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