Skip to content

Commit c352412

Browse files
author
Erlend Egeberg Aasland
authored
[3.9] bpo-44822: Don't truncate strs with embedded NULL chars returned by sqlite3 UDF callbacks (GH-27588). (GH-27639)
(cherry picked from commit 8f010dc)
1 parent 62bce24 commit c352412

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
@@ -27,6 +27,8 @@
2727

2828
def func_returntext():
2929
return "foo"
30+
def func_returntextwithnull():
31+
return "1\x002"
3032
def func_returnunicode():
3133
return "bar"
3234
def func_returnint():
@@ -137,11 +139,21 @@ def step(self, val):
137139
def finalize(self):
138140
return self.val
139141

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

144155
self.con.create_function("returntext", 0, func_returntext)
156+
self.con.create_function("returntextwithnull", 0, func_returntextwithnull)
145157
self.con.create_function("returnunicode", 0, func_returnunicode)
146158
self.con.create_function("returnint", 0, func_returnint)
147159
self.con.create_function("returnfloat", 0, func_returnfloat)
@@ -185,6 +197,12 @@ def CheckFuncReturnText(self):
185197
self.assertEqual(type(val), str)
186198
self.assertEqual(val, "foo")
187199

200+
def CheckFuncReturnTextWithNullChar(self):
201+
cur = self.con.cursor()
202+
res = cur.execute("select returntextwithnull()").fetchone()[0]
203+
self.assertEqual(type(res), str)
204+
self.assertEqual(res, "1\x002")
205+
188206
def CheckFuncReturnUnicode(self):
189207
cur = self.con.cursor()
190208
cur.execute("select returnunicode()")
@@ -343,6 +361,7 @@ def setUp(self):
343361
self.con.create_aggregate("checkType", 2, AggrCheckType)
344362
self.con.create_aggregate("checkTypes", -1, AggrCheckTypes)
345363
self.con.create_aggregate("mysum", 1, AggrSum)
364+
self.con.create_aggregate("aggtxt", 1, AggrText)
346365

347366
def tearDown(self):
348367
#self.cur.close()
@@ -431,6 +450,15 @@ def CheckAggrCheckAggrSum(self):
431450
val = cur.fetchone()[0]
432451
self.assertEqual(val, 60)
433452

453+
def CheckAggrText(self):
454+
cur = self.con.cursor()
455+
for txt in ["foo", "1\x002"]:
456+
with self.subTest(txt=txt):
457+
cur.execute("select aggtxt(?) from test", (txt,))
458+
val = cur.fetchone()[0]
459+
self.assertEqual(val, txt)
460+
461+
434462
class AuthorizerTests(unittest.TestCase):
435463
@staticmethod
436464
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
@@ -515,10 +515,17 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
515515
} else if (PyFloat_Check(py_val)) {
516516
sqlite3_result_double(context, PyFloat_AsDouble(py_val));
517517
} else if (PyUnicode_Check(py_val)) {
518-
const char *str = PyUnicode_AsUTF8(py_val);
519-
if (str == NULL)
518+
Py_ssize_t sz;
519+
const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);
520+
if (str == NULL) {
520521
return -1;
521-
sqlite3_result_text(context, str, -1, SQLITE_TRANSIENT);
522+
}
523+
if (sz > INT_MAX) {
524+
PyErr_SetString(PyExc_OverflowError,
525+
"string is longer than INT_MAX bytes");
526+
return -1;
527+
}
528+
sqlite3_result_text(context, str, (int)sz, SQLITE_TRANSIENT);
522529
} else if (PyObject_CheckBuffer(py_val)) {
523530
Py_buffer view;
524531
if (PyObject_GetBuffer(py_val, &view, PyBUF_SIMPLE) != 0) {

0 commit comments

Comments
 (0)