mirror of https://github.com/python/cpython.git
[3.9] bpo-43853: Expand test suite for SQLite UDF's (GH-27642) (GH-31030) (GH-31586)
(cherry picked from commit 3eb3b4f270
)
This commit is contained in:
parent
7190617b56
commit
3ea2a8f425
|
@ -44,22 +44,6 @@ def func_returnlonglong():
|
||||||
def func_raiseexception():
|
def func_raiseexception():
|
||||||
5/0
|
5/0
|
||||||
|
|
||||||
def func_isstring(v):
|
|
||||||
return type(v) is str
|
|
||||||
def func_isint(v):
|
|
||||||
return type(v) is int
|
|
||||||
def func_isfloat(v):
|
|
||||||
return type(v) is float
|
|
||||||
def func_isnone(v):
|
|
||||||
return type(v) is type(None)
|
|
||||||
def func_isblob(v):
|
|
||||||
return isinstance(v, (bytes, memoryview))
|
|
||||||
def func_islonglong(v):
|
|
||||||
return isinstance(v, int) and v >= 1<<31
|
|
||||||
|
|
||||||
def func(*args):
|
|
||||||
return len(args)
|
|
||||||
|
|
||||||
class AggrNoStep:
|
class AggrNoStep:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
@ -160,15 +144,13 @@ def setUp(self):
|
||||||
self.con.create_function("returnnull", 0, func_returnnull)
|
self.con.create_function("returnnull", 0, func_returnnull)
|
||||||
self.con.create_function("returnblob", 0, func_returnblob)
|
self.con.create_function("returnblob", 0, func_returnblob)
|
||||||
self.con.create_function("returnlonglong", 0, func_returnlonglong)
|
self.con.create_function("returnlonglong", 0, func_returnlonglong)
|
||||||
|
self.con.create_function("returnnan", 0, lambda: float("nan"))
|
||||||
|
self.con.create_function("returntoolargeint", 0, lambda: 1 << 65)
|
||||||
self.con.create_function("raiseexception", 0, func_raiseexception)
|
self.con.create_function("raiseexception", 0, func_raiseexception)
|
||||||
|
|
||||||
self.con.create_function("isstring", 1, func_isstring)
|
self.con.create_function("isblob", 1, lambda x: isinstance(x, bytes))
|
||||||
self.con.create_function("isint", 1, func_isint)
|
self.con.create_function("isnone", 1, lambda x: x is None)
|
||||||
self.con.create_function("isfloat", 1, func_isfloat)
|
self.con.create_function("spam", -1, lambda *x: len(x))
|
||||||
self.con.create_function("isnone", 1, func_isnone)
|
|
||||||
self.con.create_function("isblob", 1, func_isblob)
|
|
||||||
self.con.create_function("islonglong", 1, func_islonglong)
|
|
||||||
self.con.create_function("spam", -1, func)
|
|
||||||
self.con.execute("create table test(t text)")
|
self.con.execute("create table test(t text)")
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
@ -245,6 +227,16 @@ def CheckFuncReturnLongLong(self):
|
||||||
val = cur.fetchone()[0]
|
val = cur.fetchone()[0]
|
||||||
self.assertEqual(val, 1<<31)
|
self.assertEqual(val, 1<<31)
|
||||||
|
|
||||||
|
def CheckFuncReturnNaN(self):
|
||||||
|
cur = self.con.cursor()
|
||||||
|
cur.execute("select returnnan()")
|
||||||
|
self.assertIsNone(cur.fetchone()[0])
|
||||||
|
|
||||||
|
def CheckFuncReturnTooLargeInt(self):
|
||||||
|
cur = self.con.cursor()
|
||||||
|
with self.assertRaises(sqlite.OperationalError):
|
||||||
|
self.con.execute("select returntoolargeint()")
|
||||||
|
|
||||||
def CheckFuncException(self):
|
def CheckFuncException(self):
|
||||||
cur = self.con.cursor()
|
cur = self.con.cursor()
|
||||||
with self.assertRaises(sqlite.OperationalError) as cm:
|
with self.assertRaises(sqlite.OperationalError) as cm:
|
||||||
|
@ -252,50 +244,62 @@ def CheckFuncException(self):
|
||||||
cur.fetchone()
|
cur.fetchone()
|
||||||
self.assertEqual(str(cm.exception), 'user-defined function raised exception')
|
self.assertEqual(str(cm.exception), 'user-defined function raised exception')
|
||||||
|
|
||||||
def CheckParamString(self):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
for text in ["foo", str()]:
|
|
||||||
with self.subTest(text=text):
|
|
||||||
cur.execute("select isstring(?)", (text,))
|
|
||||||
val = cur.fetchone()[0]
|
|
||||||
self.assertEqual(val, 1)
|
|
||||||
|
|
||||||
def CheckParamInt(self):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
cur.execute("select isint(?)", (42,))
|
|
||||||
val = cur.fetchone()[0]
|
|
||||||
self.assertEqual(val, 1)
|
|
||||||
|
|
||||||
def CheckParamFloat(self):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
cur.execute("select isfloat(?)", (3.14,))
|
|
||||||
val = cur.fetchone()[0]
|
|
||||||
self.assertEqual(val, 1)
|
|
||||||
|
|
||||||
def CheckParamNone(self):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
cur.execute("select isnone(?)", (None,))
|
|
||||||
val = cur.fetchone()[0]
|
|
||||||
self.assertEqual(val, 1)
|
|
||||||
|
|
||||||
def CheckParamBlob(self):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
cur.execute("select isblob(?)", (memoryview(b"blob"),))
|
|
||||||
val = cur.fetchone()[0]
|
|
||||||
self.assertEqual(val, 1)
|
|
||||||
|
|
||||||
def CheckParamLongLong(self):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
cur.execute("select islonglong(?)", (1<<42,))
|
|
||||||
val = cur.fetchone()[0]
|
|
||||||
self.assertEqual(val, 1)
|
|
||||||
|
|
||||||
def CheckAnyArguments(self):
|
def CheckAnyArguments(self):
|
||||||
cur = self.con.cursor()
|
cur = self.con.cursor()
|
||||||
cur.execute("select spam(?, ?)", (1, 2))
|
cur.execute("select spam(?, ?)", (1, 2))
|
||||||
val = cur.fetchone()[0]
|
val = cur.fetchone()[0]
|
||||||
self.assertEqual(val, 2)
|
self.assertEqual(val, 2)
|
||||||
|
|
||||||
|
def CheckEmptyBlob(self):
|
||||||
|
cur = self.con.execute("select isblob(x'')")
|
||||||
|
self.assertTrue(cur.fetchone()[0])
|
||||||
|
|
||||||
|
def CheckNaNFloat(self):
|
||||||
|
cur = self.con.execute("select isnone(?)", (float("nan"),))
|
||||||
|
# SQLite has no concept of nan; it is converted to NULL
|
||||||
|
self.assertTrue(cur.fetchone()[0])
|
||||||
|
|
||||||
|
def CheckTooLargeInt(self):
|
||||||
|
err = "Python int too large to convert to SQLite INTEGER"
|
||||||
|
self.assertRaisesRegex(OverflowError, err, self.con.execute,
|
||||||
|
"select spam(?)", (1 << 65,))
|
||||||
|
|
||||||
|
def CheckNonContiguousBlob(self):
|
||||||
|
self.assertRaisesRegex(ValueError, "could not convert BLOB to buffer",
|
||||||
|
self.con.execute, "select spam(?)",
|
||||||
|
(memoryview(b"blob")[::2],))
|
||||||
|
|
||||||
|
def CheckParamSurrogates(self):
|
||||||
|
self.assertRaisesRegex(UnicodeEncodeError, "surrogates not allowed",
|
||||||
|
self.con.execute, "select spam(?)",
|
||||||
|
("\ud803\ude6d",))
|
||||||
|
|
||||||
|
def CheckFuncParams(self):
|
||||||
|
results = []
|
||||||
|
def append_result(arg):
|
||||||
|
results.append((arg, type(arg)))
|
||||||
|
self.con.create_function("test_params", 1, append_result)
|
||||||
|
|
||||||
|
dataset = [
|
||||||
|
(42, int),
|
||||||
|
(-1, int),
|
||||||
|
(1234567890123456789, int),
|
||||||
|
(4611686018427387905, int), # 63-bit int with non-zero low bits
|
||||||
|
(3.14, float),
|
||||||
|
(float('inf'), float),
|
||||||
|
("text", str),
|
||||||
|
("1\x002", str),
|
||||||
|
("\u02e2q\u02e1\u2071\u1d57\u1d49", str),
|
||||||
|
(b"blob", bytes),
|
||||||
|
(bytearray(range(2)), bytes),
|
||||||
|
(memoryview(b"blob"), bytes),
|
||||||
|
(None, type(None)),
|
||||||
|
]
|
||||||
|
for val, _ in dataset:
|
||||||
|
cur = self.con.execute("select test_params(?)", (val,))
|
||||||
|
cur.fetchone()
|
||||||
|
self.assertEqual(dataset, results)
|
||||||
|
|
||||||
# Regarding deterministic functions:
|
# Regarding deterministic functions:
|
||||||
#
|
#
|
||||||
# Between 3.8.3 and 3.15.0, deterministic functions were only used to
|
# Between 3.8.3 and 3.15.0, deterministic functions were only used to
|
||||||
|
|
|
@ -518,7 +518,11 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
|
||||||
return -1;
|
return -1;
|
||||||
sqlite3_result_int64(context, value);
|
sqlite3_result_int64(context, value);
|
||||||
} else if (PyFloat_Check(py_val)) {
|
} else if (PyFloat_Check(py_val)) {
|
||||||
sqlite3_result_double(context, PyFloat_AsDouble(py_val));
|
double value = PyFloat_AsDouble(py_val);
|
||||||
|
if (value == -1 && PyErr_Occurred()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sqlite3_result_double(context, value);
|
||||||
} else if (PyUnicode_Check(py_val)) {
|
} else if (PyUnicode_Check(py_val)) {
|
||||||
Py_ssize_t sz;
|
Py_ssize_t sz;
|
||||||
const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);
|
const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);
|
||||||
|
|
|
@ -152,9 +152,16 @@ int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObjec
|
||||||
rc = sqlite3_bind_int64(self->st, pos, value);
|
rc = sqlite3_bind_int64(self->st, pos, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TYPE_FLOAT:
|
case TYPE_FLOAT: {
|
||||||
rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter));
|
double value = PyFloat_AsDouble(parameter);
|
||||||
|
if (value == -1 && PyErr_Occurred()) {
|
||||||
|
rc = -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rc = sqlite3_bind_double(self->st, pos, value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TYPE_UNICODE:
|
case TYPE_UNICODE:
|
||||||
string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
|
string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
|
||||||
if (string == NULL)
|
if (string == NULL)
|
||||||
|
|
Loading…
Reference in New Issue