[Python-checkins] [3.9] bpo-43853: Expand test suite for SQLite UDF's (GH-27642) (GH-31030) (GH-31586)

miss-islington webhook-mailer at python.org
Tue Mar 1 23:46:20 EST 2022


https://github.com/python/cpython/commit/3ea2a8f425d26e81d914c54d477e9d56eb27ac98
commit: 3ea2a8f425d26e81d914c54d477e9d56eb27ac98
branch: 3.9
author: Erlend Egeberg Aasland <erlend.aasland at innova.no>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2022-03-01T20:46:16-08:00
summary:

[3.9] bpo-43853: Expand test suite for SQLite UDF's (GH-27642) (GH-31030) (GH-31586)



(cherry picked from commit 3eb3b4f270757f66c7fb6dcf5afa416ee1582a4b)

files:
M Lib/sqlite3/test/userfunctions.py
M Modules/_sqlite/connection.c
M Modules/_sqlite/statement.c

diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py
index 1bceefe8e69d9..8fc8b4c3abb5a 100644
--- a/Lib/sqlite3/test/userfunctions.py
+++ b/Lib/sqlite3/test/userfunctions.py
@@ -44,22 +44,6 @@ def func_returnlonglong():
 def func_raiseexception():
     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:
     def __init__(self):
         pass
@@ -160,15 +144,13 @@ def setUp(self):
         self.con.create_function("returnnull", 0, func_returnnull)
         self.con.create_function("returnblob", 0, func_returnblob)
         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("isstring", 1, func_isstring)
-        self.con.create_function("isint", 1, func_isint)
-        self.con.create_function("isfloat", 1, func_isfloat)
-        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.create_function("isblob", 1, lambda x: isinstance(x, bytes))
+        self.con.create_function("isnone", 1, lambda x: x is None)
+        self.con.create_function("spam", -1, lambda *x: len(x))
         self.con.execute("create table test(t text)")
 
     def tearDown(self):
@@ -245,6 +227,16 @@ def CheckFuncReturnLongLong(self):
         val = cur.fetchone()[0]
         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):
         cur = self.con.cursor()
         with self.assertRaises(sqlite.OperationalError) as cm:
@@ -252,50 +244,62 @@ def CheckFuncException(self):
             cur.fetchone()
         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):
         cur = self.con.cursor()
         cur.execute("select spam(?, ?)", (1, 2))
         val = cur.fetchone()[0]
         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:
     #
     # Between 3.8.3 and 3.15.0, deterministic functions were only used to
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
index 30e333a4b86d8..90327376cc0d4 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -518,7 +518,11 @@ _pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
             return -1;
         sqlite3_result_int64(context, value);
     } 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)) {
         Py_ssize_t sz;
         const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);
diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c
index 23c204e7521f0..0272ce11207f1 100644
--- a/Modules/_sqlite/statement.c
+++ b/Modules/_sqlite/statement.c
@@ -152,9 +152,16 @@ int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObjec
                 rc = sqlite3_bind_int64(self->st, pos, value);
             break;
         }
-        case TYPE_FLOAT:
-            rc = sqlite3_bind_double(self->st, pos, PyFloat_AsDouble(parameter));
+        case TYPE_FLOAT: {
+            double value = PyFloat_AsDouble(parameter);
+            if (value == -1 && PyErr_Occurred()) {
+                rc = -1;
+            }
+            else {
+                rc = sqlite3_bind_double(self->st, pos, value);
+            }
             break;
+        }
         case TYPE_UNICODE:
             string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
             if (string == NULL)



More information about the Python-checkins mailing list