From e161ec5dd7ba9355eb06757b9304019ac53cdf69 Mon Sep 17 00:00:00 2001 From: Erlend Egeberg Aasland Date: Thu, 4 Mar 2021 10:50:25 +0100 Subject: bpo-43369: sqlite3_column_{text,blob} failures now raise MemoryError (GH-24723) --- Lib/sqlite3/test/types.py | 23 ++++++++++++++- .../2021-03-02-15-25-28.bpo-43369.F4knlQ.rst | 3 ++ Modules/_sqlite/cursor.c | 33 +++++++++++++++------- 3 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-03-02-15-25-28.bpo-43369.F4knlQ.rst diff --git a/Lib/sqlite3/test/types.py b/Lib/sqlite3/test/types.py index 92ec634..2370dd1 100644 --- a/Lib/sqlite3/test/types.py +++ b/Lib/sqlite3/test/types.py @@ -110,7 +110,20 @@ class DeclTypesTests(unittest.TestCase): def setUp(self): self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES) self.cur = self.con.cursor() - self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob, n1 number, n2 number(5), bad bad)") + self.cur.execute(""" + create table test( + i int, + s str, + f float, + b bool, + u unicode, + foo foo, + bin blob, + n1 number, + n2 number(5), + bad bad, + cbin cblob) + """) # override float, make them always return the same number sqlite.converters["FLOAT"] = lambda x: 47.2 @@ -121,6 +134,7 @@ class DeclTypesTests(unittest.TestCase): sqlite.converters["BAD"] = DeclTypesTests.BadConform sqlite.converters["WRONG"] = lambda x: "WRONG" sqlite.converters["NUMBER"] = float + sqlite.converters["CBLOB"] = lambda x: b"blobish" def tearDown(self): del sqlite.converters["FLOAT"] @@ -129,6 +143,7 @@ class DeclTypesTests(unittest.TestCase): del sqlite.converters["BAD"] del sqlite.converters["WRONG"] del sqlite.converters["NUMBER"] + del sqlite.converters["CBLOB"] self.cur.close() self.con.close() @@ -237,6 +252,12 @@ class DeclTypesTests(unittest.TestCase): # if the converter is not used, it's an int instead of a float self.assertEqual(type(value), float) + def test_convert_zero_sized_blob(self): + self.con.execute("insert into test(cbin) values (?)", (b"",)) + cur = self.con.execute("select cbin from test") + self.assertEqual(cur.fetchone()[0], b"blobish") + + class ColNamesTests(unittest.TestCase): def setUp(self): self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES) diff --git a/Misc/NEWS.d/next/Library/2021-03-02-15-25-28.bpo-43369.F4knlQ.rst b/Misc/NEWS.d/next/Library/2021-03-02-15-25-28.bpo-43369.F4knlQ.rst new file mode 100644 index 0000000..f88a707 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-03-02-15-25-28.bpo-43369.F4knlQ.rst @@ -0,0 +1,3 @@ +Improve :mod:`sqlite3` error handling: If ``sqlite3_column_text()`` and +``sqlite3_column_blob()`` set ``SQLITE_NOMEM``, :exc:`MemoryError` is now +raised. Patch by Erlend E. Aasland. diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 764eec5..dfaa557 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -262,6 +262,7 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self) if (!row) return NULL; + sqlite3 *db = self->connection->db; for (i = 0; i < numcols; i++) { if (self->connection->detect_types && self->row_cast_map != NULL @@ -280,17 +281,19 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self) * See https://sqlite.org/c3ref/column_blob.html for details. */ if (converter != Py_None) { - const char *blob = (const char*)sqlite3_column_blob(self->statement->st, i); + const void *blob = sqlite3_column_blob(self->statement->st, i); + if (blob == NULL && sqlite3_errcode(db) == SQLITE_NOMEM) { + PyErr_NoMemory(); + goto error; + } + nbytes = sqlite3_column_bytes(self->statement->st, i); - if (!blob) { - converted = Py_NewRef(Py_None); - } else { - item = PyBytes_FromStringAndSize(blob, nbytes); - if (!item) - goto error; - converted = PyObject_CallOneArg(converter, item); - Py_DECREF(item); + item = PyBytes_FromStringAndSize(blob, nbytes); + if (item == NULL) { + goto error; } + converted = PyObject_CallOneArg(converter, item); + Py_DECREF(item); } else { Py_BEGIN_ALLOW_THREADS coltype = sqlite3_column_type(self->statement->st, i); @@ -303,6 +306,11 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self) converted = PyFloat_FromDouble(sqlite3_column_double(self->statement->st, i)); } else if (coltype == SQLITE_TEXT) { const char *text = (const char*)sqlite3_column_text(self->statement->st, i); + if (text == NULL && sqlite3_errcode(db) == SQLITE_NOMEM) { + PyErr_NoMemory(); + goto error; + } + nbytes = sqlite3_column_bytes(self->statement->st, i); if (self->connection->text_factory == (PyObject*)&PyUnicode_Type) { converted = PyUnicode_FromStringAndSize(text, nbytes); @@ -332,7 +340,12 @@ _pysqlite_fetch_one_row(pysqlite_Cursor* self) } } else { /* coltype == SQLITE_BLOB */ - const char *blob = sqlite3_column_blob(self->statement->st, i); + const void *blob = sqlite3_column_blob(self->statement->st, i); + if (blob == NULL && sqlite3_errcode(db) == SQLITE_NOMEM) { + PyErr_NoMemory(); + goto error; + } + nbytes = sqlite3_column_bytes(self->statement->st, i); converted = PyBytes_FromStringAndSize(blob, nbytes); } -- cgit v0.12