Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ba457fe

Browse files
author
Erlend Egeberg Aasland
authored
[3.10] bpo-43853: Expand test suite for SQLite UDF's (GH-27642) (GH-31030)
* [3.10] bpo-43853: Expand test suite for SQLite UDF's (GH-27642). (cherry picked from commit 3eb3b4f) Co-authored-by: Erlend Egeberg Aasland <[email protected]> * Fix test_func_return_too_large_int GH-27613 (bpo 44839) was not backported, so exceptions differ between main (3.11) and older versions.
1 parent f1916cd commit ba457fe

File tree

3 files changed

+79
-66
lines changed

3 files changed

+79
-66
lines changed

Lib/sqlite3/test/userfunctions.py

+65-63
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323

2424
import unittest
2525
import unittest.mock
26-
import gc
2726
import sqlite3 as sqlite
2827

28+
from test.support import gc_collect
29+
30+
2931
def func_returntext():
3032
return "foo"
3133
def func_returntextwithnull():
@@ -45,22 +47,6 @@ def func_returnlonglong():
4547
def func_raiseexception():
4648
5/0
4749

48-
def func_isstring(v):
49-
return type(v) is str
50-
def func_isint(v):
51-
return type(v) is int
52-
def func_isfloat(v):
53-
return type(v) is float
54-
def func_isnone(v):
55-
return type(v) is type(None)
56-
def func_isblob(v):
57-
return isinstance(v, (bytes, memoryview))
58-
def func_islonglong(v):
59-
return isinstance(v, int) and v >= 1<<31
60-
61-
def func(*args):
62-
return len(args)
63-
6450
class AggrNoStep:
6551
def __init__(self):
6652
pass
@@ -161,15 +147,13 @@ def setUp(self):
161147
self.con.create_function("returnnull", 0, func_returnnull)
162148
self.con.create_function("returnblob", 0, func_returnblob)
163149
self.con.create_function("returnlonglong", 0, func_returnlonglong)
150+
self.con.create_function("returnnan", 0, lambda: float("nan"))
151+
self.con.create_function("returntoolargeint", 0, lambda: 1 << 65)
164152
self.con.create_function("raiseexception", 0, func_raiseexception)
165153

166-
self.con.create_function("isstring", 1, func_isstring)
167-
self.con.create_function("isint", 1, func_isint)
168-
self.con.create_function("isfloat", 1, func_isfloat)
169-
self.con.create_function("isnone", 1, func_isnone)
170-
self.con.create_function("isblob", 1, func_isblob)
171-
self.con.create_function("islonglong", 1, func_islonglong)
172-
self.con.create_function("spam", -1, func)
154+
self.con.create_function("isblob", 1, lambda x: isinstance(x, bytes))
155+
self.con.create_function("isnone", 1, lambda x: x is None)
156+
self.con.create_function("spam", -1, lambda *x: len(x))
173157
self.con.execute("create table test(t text)")
174158

175159
def tearDown(self):
@@ -246,51 +230,23 @@ def test_func_return_long_long(self):
246230
val = cur.fetchone()[0]
247231
self.assertEqual(val, 1<<31)
248232

233+
def test_func_return_nan(self):
234+
cur = self.con.cursor()
235+
cur.execute("select returnnan()")
236+
self.assertIsNone(cur.fetchone()[0])
237+
238+
def test_func_return_too_large_int(self):
239+
cur = self.con.cursor()
240+
with self.assertRaises(sqlite.OperationalError):
241+
self.con.execute("select returntoolargeint()")
242+
249243
def test_func_exception(self):
250244
cur = self.con.cursor()
251245
with self.assertRaises(sqlite.OperationalError) as cm:
252246
cur.execute("select raiseexception()")
253247
cur.fetchone()
254248
self.assertEqual(str(cm.exception), 'user-defined function raised exception')
255249

256-
def test_param_string(self):
257-
cur = self.con.cursor()
258-
for text in ["foo", str()]:
259-
with self.subTest(text=text):
260-
cur.execute("select isstring(?)", (text,))
261-
val = cur.fetchone()[0]
262-
self.assertEqual(val, 1)
263-
264-
def test_param_int(self):
265-
cur = self.con.cursor()
266-
cur.execute("select isint(?)", (42,))
267-
val = cur.fetchone()[0]
268-
self.assertEqual(val, 1)
269-
270-
def test_param_float(self):
271-
cur = self.con.cursor()
272-
cur.execute("select isfloat(?)", (3.14,))
273-
val = cur.fetchone()[0]
274-
self.assertEqual(val, 1)
275-
276-
def test_param_none(self):
277-
cur = self.con.cursor()
278-
cur.execute("select isnone(?)", (None,))
279-
val = cur.fetchone()[0]
280-
self.assertEqual(val, 1)
281-
282-
def test_param_blob(self):
283-
cur = self.con.cursor()
284-
cur.execute("select isblob(?)", (memoryview(b"blob"),))
285-
val = cur.fetchone()[0]
286-
self.assertEqual(val, 1)
287-
288-
def test_param_long_long(self):
289-
cur = self.con.cursor()
290-
cur.execute("select islonglong(?)", (1<<42,))
291-
val = cur.fetchone()[0]
292-
self.assertEqual(val, 1)
293-
294250
def test_any_arguments(self):
295251
cur = self.con.cursor()
296252
cur.execute("select spam(?, ?)", (1, 2))
@@ -301,6 +257,52 @@ def test_empty_blob(self):
301257
cur = self.con.execute("select isblob(x'')")
302258
self.assertTrue(cur.fetchone()[0])
303259

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

358360
del x,y
359-
gc.collect()
361+
gc_collect()
360362

361363
class AggregateTests(unittest.TestCase):
362364
def setUp(self):

Modules/_sqlite/connection.c

+5-1
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,11 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
552552
return -1;
553553
sqlite3_result_int64(context, value);
554554
} else if (PyFloat_Check(py_val)) {
555-
sqlite3_result_double(context, PyFloat_AsDouble(py_val));
555+
double value = PyFloat_AsDouble(py_val);
556+
if (value == -1 && PyErr_Occurred()) {
557+
return -1;
558+
}
559+
sqlite3_result_double(context, value);
556560
} else if (PyUnicode_Check(py_val)) {
557561
Py_ssize_t sz;
558562
const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);

Modules/_sqlite/statement.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,16 @@ int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObjec
172172
rc = sqlite3_bind_int64(self->st, pos, value);
173173
break;
174174
}
175-
case TYPE_FLOAT:
176-
rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter));
175+
case TYPE_FLOAT: {
176+
double value = PyFloat_AsDouble(parameter);
177+
if (value == -1 && PyErr_Occurred()) {
178+
rc = -1;
179+
}
180+
else {
181+
rc = sqlite3_bind_double(self->st, pos, value);
182+
}
177183
break;
184+
}
178185
case TYPE_UNICODE:
179186
string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
180187
if (string == NULL)

0 commit comments

Comments
 (0)