[Python-checkins] bpo-34041: Allow creating deterministic functions in Connection.create_function() (GH-8086)

Berker Peksag webhook-mailer at python.org
Sun Jul 8 03:09:23 EDT 2018


https://github.com/python/cpython/commit/0830858aeedecc9ece60349f8c31c2690d1a99f8
commit: 0830858aeedecc9ece60349f8c31c2690d1a99f8
branch: master
author: Sergey Fedoseev <fedoseev.sergey at gmail.com>
committer: Berker Peksag <berker.peksag at gmail.com>
date: 2018年07月08日T10:09:20+03:00
summary:
bpo-34041: Allow creating deterministic functions in Connection.create_function() (GH-8086)
files:
A Misc/NEWS.d/next/Library/2018-07-06-15-06-32.bpo-34041.0zrKLh.rst
M Doc/library/sqlite3.rst
M Lib/sqlite3/test/userfunctions.py
M Modules/_sqlite/connection.c
diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst
index efc74a6bab8c..d30e4d415799 100644
--- a/Doc/library/sqlite3.rst
+++ b/Doc/library/sqlite3.rst
@@ -337,17 +337,24 @@ Connection Objects
 :meth:`~Cursor.executescript` method with the given *sql_script*, and
 returns the cursor.
 
- .. method:: create_function(name, num_params, func)
+ .. method:: create_function(name, num_params, func, *, deterministic=False)
 
 Creates a user-defined function that you can later use from within SQL
 statements under the function name *name*. *num_params* is the number of
 parameters the function accepts (if *num_params* is -1, the function may
 take any number of arguments), and *func* is a Python callable that is
- called as the SQL function.
+ called as the SQL function. If *deterministic* is true, the created function
+ is marked as `deterministic <https://sqlite.org/deterministic.html>`_, which
+ allows SQLite to perform additional optimizations. This flag is supported by
+ SQLite 3.8.3 or higher, ``sqlite3.NotSupportedError`` will be raised if used
+ with older versions.
 
 The function can return any of the types supported by SQLite: bytes, str, int,
 float and ``None``.
 
+ .. versionchanged:: 3.8
+ The *deterministic* parameter was added.
+
 Example:
 
 .. literalinclude:: ../includes/sqlite3/md5func.py
diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py
index 4075045b727e..9501f535c499 100644
--- a/Lib/sqlite3/test/userfunctions.py
+++ b/Lib/sqlite3/test/userfunctions.py
@@ -23,6 +23,7 @@
 # 3. This notice may not be removed or altered from any source distribution.
 
 import unittest
+import unittest.mock
 import sqlite3 as sqlite
 
 def func_returntext():
@@ -275,6 +276,28 @@ def CheckAnyArguments(self):
 val = cur.fetchone()[0]
 self.assertEqual(val, 2)
 
+ def CheckFuncNonDeterministic(self):
+ mock = unittest.mock.Mock(return_value=None)
+ self.con.create_function("deterministic", 0, mock, deterministic=False)
+ self.con.execute("select deterministic() = deterministic()")
+ self.assertEqual(mock.call_count, 2)
+
+ @unittest.skipIf(sqlite.sqlite_version_info < (3, 8, 3), "deterministic parameter not supported")
+ def CheckFuncDeterministic(self):
+ mock = unittest.mock.Mock(return_value=None)
+ self.con.create_function("deterministic", 0, mock, deterministic=True)
+ self.con.execute("select deterministic() = deterministic()")
+ self.assertEqual(mock.call_count, 1)
+
+ @unittest.skipIf(sqlite.sqlite_version_info >= (3, 8, 3), "SQLite < 3.8.3 needed")
+ def CheckFuncDeterministicNotSupported(self):
+ with self.assertRaises(sqlite.NotSupportedError):
+ self.con.create_function("deterministic", 0, int, deterministic=True)
+
+ def CheckFuncDeterministicKeywordOnly(self):
+ with self.assertRaises(TypeError):
+ self.con.create_function("deterministic", 0, int, True)
+
 
 class AggregateTests(unittest.TestCase):
 def setUp(self):
diff --git a/Misc/NEWS.d/next/Library/2018-07-06-15-06-32.bpo-34041.0zrKLh.rst b/Misc/NEWS.d/next/Library/2018-07-06-15-06-32.bpo-34041.0zrKLh.rst
new file mode 100644
index 000000000000..c41876bc60cb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-07-06-15-06-32.bpo-34041.0zrKLh.rst
@@ -0,0 +1,2 @@
+Add the parameter *deterministic* to the
+:meth:`sqlite3.Connection.create_function` method. Patch by Sergey Fedoseev.
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
index ef2daeb0c257..b8470df7fb8f 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -810,24 +810,48 @@ static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self)
 
 PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObject* args, PyObject* kwargs)
 {
- static char *kwlist[] = {"name", "narg", "func", NULL, NULL};
+ static char *kwlist[] = {"name", "narg", "func", "deterministic", NULL};
 
 PyObject* func;
 char* name;
 int narg;
 int rc;
+ int deterministic = 0;
+ int flags = SQLITE_UTF8;
 
 if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
 return NULL;
 }
 
- if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO", kwlist,
- &name, &narg, &func))
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO|$p", kwlist,
+ &name, &narg, &func, &deterministic))
 {
 return NULL;
 }
 
- rc = sqlite3_create_function(self->db, name, narg, SQLITE_UTF8, (void*)func, _pysqlite_func_callback, NULL, NULL);
+ if (deterministic) {
+#if SQLITE_VERSION_NUMBER < 3008003
+ PyErr_SetString(pysqlite_NotSupportedError,
+ "deterministic=True requires SQLite 3.8.3 or higher");
+ return NULL;
+#else
+ if (sqlite3_libversion_number() < 3008003) {
+ PyErr_SetString(pysqlite_NotSupportedError,
+ "deterministic=True requires SQLite 3.8.3 or higher");
+ return NULL;
+ }
+ flags |= SQLITE_DETERMINISTIC;
+#endif
+ }
+
+ rc = sqlite3_create_function(self->db,
+ name,
+ narg,
+ flags,
+ (void*)func,
+ _pysqlite_func_callback,
+ NULL,
+ NULL);
 
 if (rc != SQLITE_OK) {
 /* Workaround for SQLite bug: no error code or string is available here */


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /