From 5e073163cea78019f14d12efd7a542b2ecf38a11 Mon Sep 17 00:00:00 2001 From: Hyunjee Kim Date: Fri, 10 Apr 2020 14:17:34 +0900 Subject: [PATCH] Imported Upstream version 0.2.0 Change-Id: I967a353cf08bd976ee15c06cba4775709de34195 Signed-off-by: Hyunjee Kim --- PKG-INFO | 2 +- README.rst | 6 ++ pysqlite3.egg-info/PKG-INFO | 2 +- setup.py | 2 +- src/connection.c | 155 ++++++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 3 deletions(-) diff --git a/PKG-INFO b/PKG-INFO index 7333bd4..a57c84c 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pysqlite3 -Version: 0.1.4 +Version: 0.2.0 Summary: DB-API 2.0 interface for Sqlite 3.x Home-page: https://github.com/rigglemania/pysqlcipher3 Author: Charles Leifer diff --git a/README.rst b/README.rst index 7a4400b..d6a4b5b 100644 --- a/README.rst +++ b/README.rst @@ -7,4 +7,10 @@ separately-installable module. This may be useful for creating SQLite modules capable of working with other versions of SQLite (via the amalgamation option). +Additional features: + +* Support for user-defined window functions (requires SQLite >= 3.25) +* Support specifying flags when opening connection +* Support specifying VFS when opening connection + Original code (c) Gerhard Häring diff --git a/pysqlite3.egg-info/PKG-INFO b/pysqlite3.egg-info/PKG-INFO index 7333bd4..a57c84c 100644 --- a/pysqlite3.egg-info/PKG-INFO +++ b/pysqlite3.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pysqlite3 -Version: 0.1.4 +Version: 0.2.0 Summary: DB-API 2.0 interface for Sqlite 3.x Home-page: https://github.com/rigglemania/pysqlcipher3 Author: Charles Leifer diff --git a/setup.py b/setup.py index 3adf1ec..e7c5203 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ from setuptools import Extension # If you need to change anything, it should be enough to change setup.cfg. PACKAGE_NAME = 'pysqlite3' -VERSION = '0.1.4' +VERSION = '0.2.0' # define sqlite sources sources = [os.path.join('src', source) diff --git a/src/connection.c b/src/connection.c index 58c8d36..1a9098e 100644 --- a/src/connection.c +++ b/src/connection.c @@ -45,6 +45,10 @@ #define HAVE_BACKUP_API #endif +#if SQLITE_VERSION_NUMBER >= 3025000 +#define HAVE_WINDOW_FUNCTION +#endif + _Py_IDENTIFIER(cursor); static const char * const begin_statements[] = { @@ -747,6 +751,109 @@ error: PyGILState_Release(threadstate); } +#ifdef HAVE_WINDOW_FUNCTION + +void _pysqlite_value_callback(sqlite3_context* context) +{ + PyObject* function_result; + PyObject** aggregate_instance; + _Py_IDENTIFIER(value); + int ok; + PyObject *exception, *val, *tb; + int restore; + + PyGILState_STATE threadstate; + + threadstate = PyGILState_Ensure(); + + aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*)); + if (!*aggregate_instance) { + /* this branch is executed if there was an exception in the aggregate's + * __init__ */ + + goto error; + } + + /* Keep the exception (if any) of the last call to step() */ + PyErr_Fetch(&exception, &val, &tb); + restore = 1; + + function_result = _PyObject_CallMethodId(*aggregate_instance, &PyId_value, NULL); + + ok = 0; + if (function_result) { + ok = _pysqlite_set_result(context, function_result) == 0; + Py_DECREF(function_result); + } + if (!ok) { + if (_enable_callback_tracebacks) { + PyErr_Print(); + } else { + PyErr_Clear(); + } + _sqlite3_result_error(context, "user-defined window function's 'value' method raised error", -1); + } + + if (restore) { + /* Restore the exception (if any) of the last call to step(), + but clear also the current exception if finalize() failed */ + PyErr_Restore(exception, val, tb); + } + +error: + PyGILState_Release(threadstate); +} + +static void _pysqlite_inverse_callback(sqlite3_context *context, int argc, sqlite3_value** params) +{ + PyObject* args; + PyObject* function_result = NULL; + PyObject** aggregate_instance; + PyObject* invmethod = NULL; + + PyGILState_STATE threadstate; + + threadstate = PyGILState_Ensure(); + + aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*)); + if (!*aggregate_instance) { + /* this branch is executed if there was an exception in the aggregate's + * __init__ */ + + goto error; + } + + invmethod = PyObject_GetAttrString(*aggregate_instance, "inverse"); + if (!invmethod) { + goto error; + } + + args = _pysqlite_build_py_params(context, argc, params); + if (!args) { + goto error; + } + + function_result = PyObject_CallObject(invmethod, args); + Py_DECREF(args); + + if (!function_result) { + if (_enable_callback_tracebacks) { + PyErr_Print(); + } else { + PyErr_Clear(); + } + _sqlite3_result_error(context, "user-defined aggregate's 'inverse' method raised error", -1); + } + +error: + Py_XDECREF(invmethod); + Py_XDECREF(function_result); + + PyGILState_Release(threadstate); +} + +#endif + static void _pysqlite_drop_unused_statement_references(pysqlite_Connection* self) { PyObject* new_list; @@ -873,6 +980,50 @@ PyObject* pysqlite_connection_create_aggregate(pysqlite_Connection* self, PyObje } } +#ifdef HAVE_WINDOW_FUNCTION +PyObject* pysqlite_connection_create_window_function(pysqlite_Connection* self, PyObject* args, PyObject* kwargs) +{ + PyObject* window_function_class; + + int n_arg; + char* name; + static char *kwlist[] = { "name", "n_arg", "window_function_class", NULL }; + int rc; + + if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) { + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "siO:create_window_function", + kwlist, &name, &n_arg, &window_function_class)) { + return NULL; + } + + rc = sqlite3_create_window_function( + self->db, + name, + n_arg, + SQLITE_UTF8, + (void*)window_function_class, + &_pysqlite_step_callback, + &_pysqlite_final_callback, + &_pysqlite_value_callback, + &_pysqlite_inverse_callback, + NULL); + + if (rc != SQLITE_OK) { + /* Workaround for SQLite bug: no error code or string is available here */ + PyErr_SetString(pysqlite_OperationalError, "Error creating window function"); + return NULL; + } else { + if (PyDict_SetItem(self->function_pinboard, window_function_class, Py_None) == -1) + return NULL; + + Py_RETURN_NONE; + } +} +#endif + static int _authorizer_callback(void* user_arg, int action, const char* arg1, const char* arg2 , const char* dbname, const char* access_attempt_source) { PyObject *ret; @@ -1747,6 +1898,10 @@ static PyMethodDef connection_methods[] = { PyDoc_STR("Creates a new function. Non-standard.")}, {"create_aggregate", (PyCFunction)pysqlite_connection_create_aggregate, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Creates a new aggregate. Non-standard.")}, + #ifdef HAVE_WINDOW_FUNCTION + {"create_window_function", (PyCFunction)pysqlite_connection_create_window_function, METH_VARARGS|METH_KEYWORDS, + PyDoc_STR("Creates a new window function. Non-standard.")}, + #endif {"set_authorizer", (PyCFunction)pysqlite_connection_set_authorizer, METH_VARARGS|METH_KEYWORDS, PyDoc_STR("Sets authorizer callback. Non-standard.")}, #ifdef HAVE_LOAD_EXTENSION -- 2.34.1