From 3ea174ad26c6de450af5cc2874bb12efce7be05d Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Tue, 3 Jul 2018 14:51:41 +0500 Subject: [PATCH 01/14] bpo-34041: Added *deterministic* parameter to sqlite3.Connection.create_function(). --- Doc/library/sqlite3.rst | 11 +++++++++-- Lib/sqlite3/test/userfunctions.py | 9 +++++++++ Modules/_sqlite/connection.c | 25 +++++++++++++++++++++---- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index efc74a6bab8ca5..d1abcd87085006 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 `_, which + allows SQLite to perform additional optimizations. This flag is supported by + SQLite 3.8.3 or higher and has no effect on 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 4075045b727e89..b9a805265d2fee 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,14 @@ def CheckAnyArguments(self): val = cur.fetchone()[0] self.assertEqual(val, 2) + def CheckFuncDeterministic(self): + mock = unittest.mock.Mock() + mock.return_value = None + self.con.create_function("deterministic", 0, mock, deterministic=True) + cur = self.con.cursor() + cur.execute("select deterministic(), deterministic()") + self.assertEqual(mock.call_count, 2 if sqlite.sqlite_version_info < (3, 8, 3) else 1) + class AggregateTests(unittest.TestCase): def setUp(self): diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index ef2daeb0c25763..1cb449fb689ea2 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -808,26 +808,43 @@ static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self) Py_SETREF(self->cursors, new_list); } +#ifndef SQLITE_DETERMINISTIC +#define SQLITE_DETERMINISTIC 0x800 +#endif + 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 eTextRep = 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) { + eTextRep |= SQLITE_DETERMINISTIC; + } + + rc = sqlite3_create_function(self->db, + name, + narg, + eTextRep, + (void*)func, + _pysqlite_func_callback, + NULL, + NULL); if (rc != SQLITE_OK) { /* Workaround for SQLite bug: no error code or string is available here */ From d854d9152e3323f9451b429a3b7558af58f78f2a Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Wed, 4 Jul 2018 15:52:58 +0500 Subject: [PATCH 02/14] Made parameter keyword only. --- Modules/_sqlite/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 1cb449fb689ea2..847ffdef15497e 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -827,7 +827,7 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec return NULL; } - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO|p", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO|$p", kwlist, &name, &narg, &func, &deterministic)) { return NULL; From 5c8a67b528f96bda9c135aaf1951b2357293e438 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Wed, 4 Jul 2018 15:53:27 +0500 Subject: [PATCH 03/14] Removed redundant line in doc. --- Doc/library/sqlite3.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index d1abcd87085006..b44e225d649115 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -352,7 +352,6 @@ Connection Objects float and ``None``. .. versionchanged:: 3.8 - The *deterministic* parameter was added. Example: From dcb8e5194026eca9e5977b0ebf3bf250e1e611ab Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Wed, 4 Jul 2018 16:07:09 +0500 Subject: [PATCH 04/14] Changed test for SQLite 3.11. --- Lib/sqlite3/test/userfunctions.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index b9a805265d2fee..6bc1ae45a33179 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -281,8 +281,11 @@ def CheckFuncDeterministic(self): mock.return_value = None self.con.create_function("deterministic", 0, mock, deterministic=True) cur = self.con.cursor() - cur.execute("select deterministic(), deterministic()") - self.assertEqual(mock.call_count, 2 if sqlite.sqlite_version_info < (3, 8, 3) else 1) + cur.execute("select deterministic() = deterministic()") + self.assertEqual( + mock.call_count, + 2 if sqlite.sqlite_version_info < (3, 8, 3) else 1, + ) class AggregateTests(unittest.TestCase): From 25952a3c96967f580227428f74ad687f7940f5f5 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Wed, 4 Jul 2018 18:03:01 +0500 Subject: [PATCH 05/14] Do not pass SQLITE_DETERMINISTIC when it's not supported. --- Modules/_sqlite/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 847ffdef15497e..b84e2ee036a9bf 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -833,7 +833,7 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec return NULL; } - if (deterministic) { + if (deterministic && sqlite3_libversion_number() >= 3008003) { eTextRep |= SQLITE_DETERMINISTIC; } From 14736891dcb8c16e812db3e1573419319d7af777 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Wed, 4 Jul 2018 20:19:10 +0500 Subject: [PATCH 06/14] Documented parameter as keyword only. --- Doc/library/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index b44e225d649115..052d2aa0f139d3 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -337,7 +337,7 @@ Connection Objects :meth:`~Cursor.executescript` method with the given *sql_script*, and returns the cursor. - .. method:: create_function(name, num_params, func, deterministic=False) + .. 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 From faf9b6b89be567113d5bc6dff94cdce07bdd9b4b Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Wed, 4 Jul 2018 21:25:17 +0500 Subject: [PATCH 07/14] Added news entry. --- .../next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst diff --git a/Misc/NEWS.d/next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst b/Misc/NEWS.d/next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst new file mode 100644 index 00000000000000..0fa8c572d40fe9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst @@ -0,0 +1,2 @@ +Add the parameter *deterministic* to the +sqlite3.Connection.create_function(). From 0bfe04a12965b2db07db2dc2899ed1c551b5413a Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Wed, 4 Jul 2018 21:27:37 +0500 Subject: [PATCH 08/14] Raise exception on old SQLite versions. --- Lib/sqlite3/test/userfunctions.py | 11 +++++++---- Modules/_sqlite/connection.c | 21 +++++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index 6bc1ae45a33179..a1522c20e80d34 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -276,16 +276,19 @@ def CheckAnyArguments(self): val = cur.fetchone()[0] self.assertEqual(val, 2) + @unittest.skipIf(sqlite.sqlite_version_info < (3, 8, 3), "deterministic parameter not supported") def CheckFuncDeterministic(self): mock = unittest.mock.Mock() mock.return_value = None self.con.create_function("deterministic", 0, mock, deterministic=True) cur = self.con.cursor() cur.execute("select deterministic() = deterministic()") - self.assertEqual( - mock.call_count, - 2 if sqlite.sqlite_version_info < (3, 8, 3) else 1, - ) + 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) class AggregateTests(unittest.TestCase): diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index b84e2ee036a9bf..19fc598eb3b0ed 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -808,10 +808,6 @@ static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self) Py_SETREF(self->cursors, new_list); } -#ifndef SQLITE_DETERMINISTIC -#define SQLITE_DETERMINISTIC 0x800 -#endif - PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) { static char *kwlist[] = {"name", "narg", "func", "deterministic", NULL}; @@ -833,8 +829,21 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec return NULL; } - if (deterministic && sqlite3_libversion_number() >= 3008003) { - eTextRep |= SQLITE_DETERMINISTIC; + if (deterministic) { +#if SQLITE_VERSION_NUMBER < 3008003 + PyErr_SetString(pysqlite_NotSupportedError, + "should be built with SQLite 3.8.3 or higher"); + return NULL; +#else + if (sqlite3_libversion_number() < 3008003) { + PyErr_SetString(pysqlite_NotSupportedError, + "requires SQLite 3.8.3 or higher"); + return NULL; + } + else { + eTextRep |= SQLITE_DETERMINISTIC; + } +#endif } rc = sqlite3_create_function(self->db, From 33f8e642630127fa55a7707296739bf10a9cb2b2 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 6 Jul 2018 15:30:54 +0500 Subject: [PATCH 09/14] Made requested changes. --- Doc/library/sqlite3.rst | 5 +++-- Lib/sqlite3/test/userfunctions.py | 4 ++++ .../next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst | 2 -- .../next/Library/2018-07-06-15-06-32.bpo-34041.0zrKLh.rst | 2 ++ Modules/_sqlite/connection.c | 6 +++--- 5 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst create mode 100644 Misc/NEWS.d/next/Library/2018-07-06-15-06-32.bpo-34041.0zrKLh.rst diff --git a/Doc/library/sqlite3.rst b/Doc/library/sqlite3.rst index 052d2aa0f139d3..d30e4d4157996d 100644 --- a/Doc/library/sqlite3.rst +++ b/Doc/library/sqlite3.rst @@ -346,13 +346,14 @@ Connection Objects called as the SQL function. If *deterministic* is true, the created function is marked as `deterministic `_, which allows SQLite to perform additional optimizations. This flag is supported by - SQLite 3.8.3 or higher and has no effect on older versions. + 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. + The *deterministic* parameter was added. Example: diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index a1522c20e80d34..872b8e4cc3cd89 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -290,6 +290,10 @@ def CheckFuncDeterministicNotSupported(self): with self.assertRaises(sqlite.NotSupportedError): self.con.create_function("deterministic", 0, int, deterministic=True) + def CheckFuncDeterministicKwOnly(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-04-21-24-58.bpo-34041.aKk8t5.rst b/Misc/NEWS.d/next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst deleted file mode 100644 index 0fa8c572d40fe9..00000000000000 --- a/Misc/NEWS.d/next/Library/2018-07-04-21-24-58.bpo-34041.aKk8t5.rst +++ /dev/null @@ -1,2 +0,0 @@ -Add the parameter *deterministic* to the -sqlite3.Connection.create_function(). 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 00000000000000..c41876bc60cbb4 --- /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 19fc598eb3b0ed..e7feab06a08031 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -817,7 +817,7 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec int narg; int rc; int deterministic = 0; - int eTextRep = SQLITE_UTF8; + int flags = SQLITE_UTF8; if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { return NULL; @@ -841,7 +841,7 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec return NULL; } else { - eTextRep |= SQLITE_DETERMINISTIC; + flags |= SQLITE_DETERMINISTIC; } #endif } @@ -849,7 +849,7 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec rc = sqlite3_create_function(self->db, name, narg, - eTextRep, + flags, (void*)func, _pysqlite_func_callback, NULL, From 0dcc22cfcd4d07f4f05a0a6f7a64446a019e48eb Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 6 Jul 2018 15:42:41 +0500 Subject: [PATCH 10/14] Fixed indentation. --- Lib/sqlite3/test/userfunctions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index 872b8e4cc3cd89..58eb634881821c 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -291,7 +291,7 @@ def CheckFuncDeterministicNotSupported(self): self.con.create_function("deterministic", 0, int, deterministic=True) def CheckFuncDeterministicKwOnly(self): - with self.assertRaises(TypeError): + with self.assertRaises(TypeError): self.con.create_function("deterministic", 0, int, True) From dc8ca8d2cdd32443f5570c8f7560574594eb31cb Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 6 Jul 2018 20:55:51 +0500 Subject: [PATCH 11/14] Made requested changes. --- Modules/_sqlite/connection.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index e7feab06a08031..b8470df7fb8ffd 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -832,17 +832,15 @@ PyObject* pysqlite_connection_create_function(pysqlite_Connection* self, PyObjec if (deterministic) { #if SQLITE_VERSION_NUMBER < 3008003 PyErr_SetString(pysqlite_NotSupportedError, - "should be built with SQLite 3.8.3 or higher"); + "deterministic=True requires SQLite 3.8.3 or higher"); return NULL; #else if (sqlite3_libversion_number() < 3008003) { PyErr_SetString(pysqlite_NotSupportedError, - "requires SQLite 3.8.3 or higher"); + "deterministic=True requires SQLite 3.8.3 or higher"); return NULL; } - else { - flags |= SQLITE_DETERMINISTIC; - } + flags |= SQLITE_DETERMINISTIC; #endif } From b7d928a78bbc7d775d08600783e4c878e439fb8a Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 6 Jul 2018 23:01:56 +0500 Subject: [PATCH 12/14] Added test for deterministic=False. --- Lib/sqlite3/test/userfunctions.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index 58eb634881821c..b779e4b686b558 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -280,9 +280,15 @@ def CheckAnyArguments(self): def CheckFuncDeterministic(self): mock = unittest.mock.Mock() mock.return_value = None + query = "select deterministic() = deterministic()" + + self.con.create_function("deterministic", 0, mock, deterministic=False) + self.con.execute(query) + self.assertEqual(mock.call_count, 2) + + mock.reset_mock() self.con.create_function("deterministic", 0, mock, deterministic=True) - cur = self.con.cursor() - cur.execute("select deterministic() = deterministic()") + self.con.execute(query) self.assertEqual(mock.call_count, 1) @unittest.skipIf(sqlite.sqlite_version_info >= (3, 8, 3), "SQLite < 3.8.3 needed") From 31153248824d1edda66df0958ff4a5b274c925a1 Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 6 Jul 2018 23:18:31 +0500 Subject: [PATCH 13/14] Run test for deterministic=False even on SQLite < 3.8.3. --- Lib/sqlite3/test/userfunctions.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index b779e4b686b558..2a68271b992000 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -276,7 +276,6 @@ def CheckAnyArguments(self): val = cur.fetchone()[0] self.assertEqual(val, 2) - @unittest.skipIf(sqlite.sqlite_version_info < (3, 8, 3), "deterministic parameter not supported") def CheckFuncDeterministic(self): mock = unittest.mock.Mock() mock.return_value = None @@ -286,15 +285,14 @@ def CheckFuncDeterministic(self): self.con.execute(query) self.assertEqual(mock.call_count, 2) - mock.reset_mock() - self.con.create_function("deterministic", 0, mock, deterministic=True) - self.con.execute(query) - 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) + if sqlite.sqlite_version_info < (3, 8, 3): + with self.assertRaises(sqlite.NotSupportedError): + self.con.create_function("deterministic", 0, mock, deterministic=True) + else: + self.con.create_function("deterministic", 0, mock, deterministic=True) + mock.reset_mock() + self.con.execute(query) + self.assertEqual(mock.call_count, 1) def CheckFuncDeterministicKwOnly(self): with self.assertRaises(TypeError): From 130cb5e304ac2b5a1e6ece46f9b676a9fa36c0ad Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Sun, 8 Jul 2018 10:06:32 +0500 Subject: [PATCH 14/14] Moar changes. --- Lib/sqlite3/test/userfunctions.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Lib/sqlite3/test/userfunctions.py b/Lib/sqlite3/test/userfunctions.py index 2a68271b992000..9501f535c49999 100644 --- a/Lib/sqlite3/test/userfunctions.py +++ b/Lib/sqlite3/test/userfunctions.py @@ -276,25 +276,25 @@ def CheckAnyArguments(self): val = cur.fetchone()[0] self.assertEqual(val, 2) - def CheckFuncDeterministic(self): - mock = unittest.mock.Mock() - mock.return_value = None - query = "select deterministic() = deterministic()" - + def CheckFuncNonDeterministic(self): + mock = unittest.mock.Mock(return_value=None) self.con.create_function("deterministic", 0, mock, deterministic=False) - self.con.execute(query) + self.con.execute("select deterministic() = deterministic()") self.assertEqual(mock.call_count, 2) - if sqlite.sqlite_version_info < (3, 8, 3): - with self.assertRaises(sqlite.NotSupportedError): - self.con.create_function("deterministic", 0, mock, deterministic=True) - else: - self.con.create_function("deterministic", 0, mock, deterministic=True) - mock.reset_mock() - self.con.execute(query) - self.assertEqual(mock.call_count, 1) + @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 CheckFuncDeterministicKwOnly(self): + def CheckFuncDeterministicKeywordOnly(self): with self.assertRaises(TypeError): self.con.create_function("deterministic", 0, int, True)