Skip to content

Commit 2b1e713

Browse files
miss-islingtonErlend Egeberg Aasland
and
Erlend Egeberg Aasland
authored
bpo-44822: Don't truncate strs with embedded NULL chars returned by sqlite3 UDF callbacks (GH-27588)
(cherry picked from commit 8f010dc) Co-authored-by: Erlend Egeberg Aasland <[email protected]>
1 parent a5d9963 commit 2b1e713

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

Lib/sqlite3/test/userfunctions.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
def func_returntext():
3030
return "foo"
31+
def func_returntextwithnull():
32+
return "1\x002"
3133
def func_returnunicode():
3234
return "bar"
3335
def func_returnint():
@@ -138,11 +140,21 @@ def step(self, val):
138140
def finalize(self):
139141
return self.val
140142

143+
class AggrText:
144+
def __init__(self):
145+
self.txt = ""
146+
def step(self, txt):
147+
self.txt = self.txt + txt
148+
def finalize(self):
149+
return self.txt
150+
151+
141152
class FunctionTests(unittest.TestCase):
142153
def setUp(self):
143154
self.con = sqlite.connect(":memory:")
144155

145156
self.con.create_function("returntext", 0, func_returntext)
157+
self.con.create_function("returntextwithnull", 0, func_returntextwithnull)
146158
self.con.create_function("returnunicode", 0, func_returnunicode)
147159
self.con.create_function("returnint", 0, func_returnint)
148160
self.con.create_function("returnfloat", 0, func_returnfloat)
@@ -186,6 +198,12 @@ def test_func_return_text(self):
186198
self.assertEqual(type(val), str)
187199
self.assertEqual(val, "foo")
188200

201+
def test_func_return_text_with_null_char(self):
202+
cur = self.con.cursor()
203+
res = cur.execute("select returntextwithnull()").fetchone()[0]
204+
self.assertEqual(type(res), str)
205+
self.assertEqual(res, "1\x002")
206+
189207
def test_func_return_unicode(self):
190208
cur = self.con.cursor()
191209
cur.execute("select returnunicode()")
@@ -364,6 +382,7 @@ def setUp(self):
364382
self.con.create_aggregate("checkType", 2, AggrCheckType)
365383
self.con.create_aggregate("checkTypes", -1, AggrCheckTypes)
366384
self.con.create_aggregate("mysum", 1, AggrSum)
385+
self.con.create_aggregate("aggtxt", 1, AggrText)
367386

368387
def tearDown(self):
369388
#self.cur.close()
@@ -457,6 +476,15 @@ def test_aggr_no_match(self):
457476
val = cur.fetchone()[0]
458477
self.assertIsNone(val)
459478

479+
def test_aggr_text(self):
480+
cur = self.con.cursor()
481+
for txt in ["foo", "1\x002"]:
482+
with self.subTest(txt=txt):
483+
cur.execute("select aggtxt(?) from test", (txt,))
484+
val = cur.fetchone()[0]
485+
self.assertEqual(val, txt)
486+
487+
460488
class AuthorizerTests(unittest.TestCase):
461489
@staticmethod
462490
def authorizer_cb(action, arg1, arg2, dbname, source):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`sqlite3` user-defined functions and aggregators returning
2+
:class:`strings <str>` with embedded NUL characters are no longer
3+
truncated. Patch by Erlend E. Aasland.

Modules/_sqlite/connection.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -549,10 +549,17 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
549549
} else if (PyFloat_Check(py_val)) {
550550
sqlite3_result_double(context, PyFloat_AsDouble(py_val));
551551
} else if (PyUnicode_Check(py_val)) {
552-
const char *str = PyUnicode_AsUTF8(py_val);
553-
if (str == NULL)
552+
Py_ssize_t sz;
553+
const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);
554+
if (str == NULL) {
554555
return -1;
555-
sqlite3_result_text(context, str, -1, SQLITE_TRANSIENT);
556+
}
557+
if (sz > INT_MAX) {
558+
PyErr_SetString(PyExc_OverflowError,
559+
"string is longer than INT_MAX bytes");
560+
return -1;
561+
}
562+
sqlite3_result_text(context, str, (int)sz, SQLITE_TRANSIENT);
556563
} else if (PyObject_CheckBuffer(py_val)) {
557564
Py_buffer view;
558565
if (PyObject_GetBuffer(py_val, &view, PyBUF_SIMPLE) != 0) {

0 commit comments

Comments
 (0)