mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-34041: Allow creating deterministic functions in Connection.create_function() (GH-8086)
This commit is contained in:
		
							parent
							
								
									8d41278045
								
							
						
					
					
						commit
						0830858aee
					
				
					 4 changed files with 62 additions and 6 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
|  | @ -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): | ||||
|  |  | |||
|  | @ -0,0 +1,2 @@ | |||
| Add the parameter *deterministic* to the | ||||
| :meth:`sqlite3.Connection.create_function` method. Patch by Sergey Fedoseev. | ||||
|  | @ -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 */ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sergey Fedoseev
						Sergey Fedoseev