: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
# 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():
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):
--- /dev/null
+Add the parameter *deterministic* to the
+:meth:`sqlite3.Connection.create_function` method. Patch by Sergey Fedoseev.
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 */