Imported Upstream version 0.2.0 upstream/0.2.0
authorHyunjee Kim <hj0426.kim@samsung.com>
Fri, 10 Apr 2020 05:17:34 +0000 (14:17 +0900)
committerHyunjee Kim <hj0426.kim@samsung.com>
Fri, 10 Apr 2020 05:17:39 +0000 (14:17 +0900)
Change-Id: I967a353cf08bd976ee15c06cba4775709de34195
Signed-off-by: Hyunjee Kim <hj0426.kim@samsung.com>
PKG-INFO
README.rst
pysqlite3.egg-info/PKG-INFO
setup.py
src/connection.c

index 7333bd45c8bbf3847ab20b703bfa1169472f3762..a57c84c2a837107fb166c31fd55ea7e70631eaa1 100644 (file)
--- 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
index 7a4400b4db7a05b0965aa721c3f549138d33d0e5..d6a4b5b5710dbd097c1405a24a51236cc6de3117 100644 (file)
@@ -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
index 7333bd45c8bbf3847ab20b703bfa1169472f3762..a57c84c2a837107fb166c31fd55ea7e70631eaa1 100644 (file)
@@ -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
index 3adf1ec12d168221be28d795249b9418f9aa54f9..e7c520395da79bb59fbb026a9c8fd0a55b2a5843 100644 (file)
--- 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)
index 58c8d3650512244dfbcb0af9af7bb9b8ae031ec1..1a9098ef607e414a5e9b7d47103c75035d58bf6f 100644 (file)
 #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