Skip to content

Commit 3ea2a8f

Browse files
author
Erlend Egeberg Aasland
authored
[3.9] bpo-43853: Expand test suite for SQLite UDF's (GH-27642) (GH-31030) (GH-31586)
(cherry picked from commit 3eb3b4f)
1 parent 7190617 commit 3ea2a8f

File tree

3 files changed

+79
-64
lines changed

3 files changed

+79
-64
lines changed

Lib/sqlite3/test/userfunctions.py

+65-61
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,6 @@ def func_returnlonglong():
4444
def func_raiseexception():
4545
5/0
4646

47-
def func_isstring(v):
48-
return type(v) is str
49-
def func_isint(v):
50-
return type(v) is int
51-
def func_isfloat(v):
52-
return type(v) is float
53-
def func_isnone(v):
54-
return type(v) is type(None)
55-
def func_isblob(v):
56-
return isinstance(v, (bytes, memoryview))
57-
def func_islonglong(v):
58-
return isinstance(v, int) and v >= 1<<31
59-
60-
def func(*args):
61-
return len(args)
62-
6347
class AggrNoStep:
6448
def __init__(self):
6549
pass
@@ -160,15 +144,13 @@ def setUp(self):
160144
self.con.create_function("returnnull", 0, func_returnnull)
161145
self.con.create_function("returnblob", 0, func_returnblob)
162146
self.con.create_function("returnlonglong", 0, func_returnlonglong)
147+
self.con.create_function("returnnan", 0, lambda: float("nan"))
148+
self.con.create_function("returntoolargeint", 0, lambda: 1 << 65)
163149
self.con.create_function("raiseexception", 0, func_raiseexception)
164150

165-
self.con.create_function("isstring", 1, func_isstring)
166-
self.con.create_function("isint", 1, func_isint)
167-
self.con.create_function("isfloat", 1, func_isfloat)
168-
self.con.create_function("isnone", 1, func_isnone)
169-
self.con.create_function("isblob", 1, func_isblob)
170-
self.con.create_function("islonglong", 1, func_islonglong)
171-
self.con.create_function("spam", -1, func)
151+
self.con.create_function("isblob", 1, lambda x: isinstance(x, bytes))
152+
self.con.create_function("isnone", 1, lambda x: x is None)
153+
self.con.create_function("spam", -1, lambda *x: len(x))
172154
self.con.execute("create table test(t text)")
173155

174156
def tearDown(self):
@@ -245,57 +227,79 @@ def CheckFuncReturnLongLong(self):
245227
val = cur.fetchone()[0]
246228
self.assertEqual(val, 1<<31)
247229

230+
def CheckFuncReturnNaN(self):
231+
cur = self.con.cursor()
232+
cur.execute("select returnnan()")
233+
self.assertIsNone(cur.fetchone()[0])
234+
235+
def CheckFuncReturnTooLargeInt(self):
236+
cur = self.con.cursor()
237+
with self.assertRaises(sqlite.OperationalError):
238+
self.con.execute("select returntoolargeint()")
239+
248240
def CheckFuncException(self):
249241
cur = self.con.cursor()
250242
with self.assertRaises(sqlite.OperationalError) as cm:
251243
cur.execute("select raiseexception()")
252244
cur.fetchone()
253245
self.assertEqual(str(cm.exception), 'user-defined function raised exception')
254246

255-
def CheckParamString(self):
256-
cur = self.con.cursor()
257-
for text in ["foo", str()]:
258-
with self.subTest(text=text):
259-
cur.execute("select isstring(?)", (text,))
260-
val = cur.fetchone()[0]
261-
self.assertEqual(val, 1)
262-
263-
def CheckParamInt(self):
264-
cur = self.con.cursor()
265-
cur.execute("select isint(?)", (42,))
266-
val = cur.fetchone()[0]
267-
self.assertEqual(val, 1)
268-
269-
def CheckParamFloat(self):
270-
cur = self.con.cursor()
271-
cur.execute("select isfloat(?)", (3.14,))
272-
val = cur.fetchone()[0]
273-
self.assertEqual(val, 1)
274-
275-
def CheckParamNone(self):
276-
cur = self.con.cursor()
277-
cur.execute("select isnone(?)", (None,))
278-
val = cur.fetchone()[0]
279-
self.assertEqual(val, 1)
280-
281-
def CheckParamBlob(self):
282-
cur = self.con.cursor()
283-
cur.execute("select isblob(?)", (memoryview(b"blob"),))
284-
val = cur.fetchone()[0]
285-
self.assertEqual(val, 1)
286-
287-
def CheckParamLongLong(self):
288-
cur = self.con.cursor()
289-
cur.execute("select islonglong(?)", (1<<42,))
290-
val = cur.fetchone()[0]
291-
self.assertEqual(val, 1)
292-
293247
def CheckAnyArguments(self):
294248
cur = self.con.cursor()
295249
cur.execute("select spam(?, ?)", (1, 2))
296250
val = cur.fetchone()[0]
297251
self.assertEqual(val, 2)
298252

253+
def CheckEmptyBlob(self):
254+
cur = self.con.execute("select isblob(x'')")
255+
self.assertTrue(cur.fetchone()[0])
256+
257+
def CheckNaNFloat(self):
258+
cur = self.con.execute("select isnone(?)", (float("nan"),))
259+
# SQLite has no concept of nan; it is converted to NULL
260+
self.assertTrue(cur.fetchone()[0])
261+
262+
def CheckTooLargeInt(self):
263+
err = "Python int too large to convert to SQLite INTEGER"
264+
self.assertRaisesRegex(OverflowError, err, self.con.execute,
265+
"select spam(?)", (1 << 65,))
266+
267+
def CheckNonContiguousBlob(self):
268+
self.assertRaisesRegex(ValueError, "could not convert BLOB to buffer",
269+
self.con.execute, "select spam(?)",
270+
(memoryview(b"blob")[::2],))
271+
272+
def CheckParamSurrogates(self):
273+
self.assertRaisesRegex(UnicodeEncodeError, "surrogates not allowed",
274+
self.con.execute, "select spam(?)",
275+
("\ud803\ude6d",))
276+
277+
def CheckFuncParams(self):
278+
results = []
279+
def append_result(arg):
280+
results.append((arg, type(arg)))
281+
self.con.create_function("test_params", 1, append_result)
282+
283+
dataset = [
284+
(42, int),
285+
(-1, int),
286+
(1234567890123456789, int),
287+
(4611686018427387905, int), # 63-bit int with non-zero low bits
288+
(3.14, float),
289+
(float('inf'), float),
290+
("text", str),
291+
("1\x002", str),
292+
("\u02e2q\u02e1\u2071\u1d57\u1d49", str),
293+
(b"blob", bytes),
294+
(bytearray(range(2)), bytes),
295+
(memoryview(b"blob"), bytes),
296+
(None, type(None)),
297+
]
298+
for val, _ in dataset:
299+
cur = self.con.execute("select test_params(?)", (val,))
300+
cur.fetchone()
301+
self.assertEqual(dataset, results)
302+
299303
# Regarding deterministic functions:
300304
#
301305
# Between 3.8.3 and 3.15.0, deterministic functions were only used to

Modules/_sqlite/connection.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,11 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
518518
return -1;
519519
sqlite3_result_int64(context, value);
520520
} else if (PyFloat_Check(py_val)) {
521-
sqlite3_result_double(context, PyFloat_AsDouble(py_val));
521+
double value = PyFloat_AsDouble(py_val);
522+
if (value == -1 && PyErr_Occurred()) {
523+
return -1;
524+
}
525+
sqlite3_result_double(context, value);
522526
} else if (PyUnicode_Check(py_val)) {
523527
Py_ssize_t sz;
524528
const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);

Modules/_sqlite/statement.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,16 @@ int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObjec
152152
rc = sqlite3_bind_int64(self->st, pos, value);
153153
break;
154154
}
155-
case TYPE_FLOAT:
156-
rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter));
155+
case TYPE_FLOAT: {
156+
double value = PyFloat_AsDouble(parameter);
157+
if (value == -1 && PyErr_Occurred()) {
158+
rc = -1;
159+
}
160+
else {
161+
rc = sqlite3_bind_double(self->st, pos, value);
162+
}
157163
break;
164+
}
158165
case TYPE_UNICODE:
159166
string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
160167
if (string == NULL)

0 commit comments

Comments
 (0)