aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Modules/_sqlite/connection.c
diff options
context:
space:
mode:
authorAlexSm <alex@ydb.tech>2024-03-05 10:40:59 +0100
committerGitHub <noreply@github.com>2024-03-05 12:40:59 +0300
commit1ac13c847b5358faba44dbb638a828e24369467b (patch)
tree07672b4dd3604ad3dee540a02c6494cb7d10dc3d /contrib/tools/python3/Modules/_sqlite/connection.c
parentffcca3e7f7958ddc6487b91d3df8c01054bd0638 (diff)
downloadydb-1ac13c847b5358faba44dbb638a828e24369467b.tar.gz
Library import 16 (#2433)
Co-authored-by: robot-piglet <robot-piglet@yandex-team.com> Co-authored-by: deshevoy <deshevoy@yandex-team.com> Co-authored-by: robot-contrib <robot-contrib@yandex-team.com> Co-authored-by: thegeorg <thegeorg@yandex-team.com> Co-authored-by: robot-ya-builder <robot-ya-builder@yandex-team.com> Co-authored-by: svidyuk <svidyuk@yandex-team.com> Co-authored-by: shadchin <shadchin@yandex-team.com> Co-authored-by: robot-ratatosk <robot-ratatosk@yandex-team.com> Co-authored-by: innokentii <innokentii@yandex-team.com> Co-authored-by: arkady-e1ppa <arkady-e1ppa@yandex-team.com> Co-authored-by: snermolaev <snermolaev@yandex-team.com> Co-authored-by: dimdim11 <dimdim11@yandex-team.com> Co-authored-by: kickbutt <kickbutt@yandex-team.com> Co-authored-by: abdullinsaid <abdullinsaid@yandex-team.com> Co-authored-by: korsunandrei <korsunandrei@yandex-team.com> Co-authored-by: petrk <petrk@yandex-team.com> Co-authored-by: miroslav2 <miroslav2@yandex-team.com> Co-authored-by: serjflint <serjflint@yandex-team.com> Co-authored-by: akhropov <akhropov@yandex-team.com> Co-authored-by: prettyboy <prettyboy@yandex-team.com> Co-authored-by: ilikepugs <ilikepugs@yandex-team.com> Co-authored-by: hiddenpath <hiddenpath@yandex-team.com> Co-authored-by: mikhnenko <mikhnenko@yandex-team.com> Co-authored-by: spreis <spreis@yandex-team.com> Co-authored-by: andreyshspb <andreyshspb@yandex-team.com> Co-authored-by: dimaandreev <dimaandreev@yandex-team.com> Co-authored-by: rashid <rashid@yandex-team.com> Co-authored-by: robot-ydb-importer <robot-ydb-importer@yandex-team.com> Co-authored-by: r-vetrov <r-vetrov@yandex-team.com> Co-authored-by: ypodlesov <ypodlesov@yandex-team.com> Co-authored-by: zaverden <zaverden@yandex-team.com> Co-authored-by: vpozdyayev <vpozdyayev@yandex-team.com> Co-authored-by: robot-cozmo <robot-cozmo@yandex-team.com> Co-authored-by: v-korovin <v-korovin@yandex-team.com> Co-authored-by: arikon <arikon@yandex-team.com> Co-authored-by: khoden <khoden@yandex-team.com> Co-authored-by: psydmm <psydmm@yandex-team.com> Co-authored-by: robot-javacom <robot-javacom@yandex-team.com> Co-authored-by: dtorilov <dtorilov@yandex-team.com> Co-authored-by: sennikovmv <sennikovmv@yandex-team.com> Co-authored-by: hcpp <hcpp@ydb.tech>
Diffstat (limited to 'contrib/tools/python3/Modules/_sqlite/connection.c')
-rw-r--r--contrib/tools/python3/Modules/_sqlite/connection.c2659
1 files changed, 2659 insertions, 0 deletions
diff --git a/contrib/tools/python3/Modules/_sqlite/connection.c b/contrib/tools/python3/Modules/_sqlite/connection.c
new file mode 100644
index 0000000000..12e5c135aa
--- /dev/null
+++ b/contrib/tools/python3/Modules/_sqlite/connection.c
@@ -0,0 +1,2659 @@
+/* connection.c - the connection type
+ *
+ * Copyright (C) 2004-2010 Gerhard Häring <gh@ghaering.de>
+ *
+ * This file is part of pysqlite.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "module.h"
+#include "structmember.h" // PyMemberDef
+#include "connection.h"
+#include "statement.h"
+#include "cursor.h"
+#include "blob.h"
+#include "prepare_protocol.h"
+#include "util.h"
+
+#include <stdbool.h>
+
+#if SQLITE_VERSION_NUMBER >= 3014000
+#define HAVE_TRACE_V2
+#endif
+
+#if SQLITE_VERSION_NUMBER >= 3025000
+#define HAVE_WINDOW_FUNCTIONS
+#endif
+
+static const char *
+get_isolation_level(const char *level)
+{
+ assert(level != NULL);
+ static const char *const allowed_levels[] = {
+ "",
+ "DEFERRED",
+ "IMMEDIATE",
+ "EXCLUSIVE",
+ NULL
+ };
+ for (int i = 0; allowed_levels[i] != NULL; i++) {
+ const char *candidate = allowed_levels[i];
+ if (sqlite3_stricmp(level, candidate) == 0) {
+ return candidate;
+ }
+ }
+ PyErr_SetString(PyExc_ValueError,
+ "isolation_level string must be '', 'DEFERRED', "
+ "'IMMEDIATE', or 'EXCLUSIVE'");
+ return NULL;
+}
+
+static int
+isolation_level_converter(PyObject *str_or_none, const char **result)
+{
+ if (Py_IsNone(str_or_none)) {
+ *result = NULL;
+ }
+ else if (PyUnicode_Check(str_or_none)) {
+ Py_ssize_t sz;
+ const char *str = PyUnicode_AsUTF8AndSize(str_or_none, &sz);
+ if (str == NULL) {
+ return 0;
+ }
+ if (strlen(str) != (size_t)sz) {
+ PyErr_SetString(PyExc_ValueError, "embedded null character");
+ return 0;
+ }
+
+ const char *level = get_isolation_level(str);
+ if (level == NULL) {
+ return 0;
+ }
+ *result = level;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "isolation_level must be str or None");
+ return 0;
+ }
+ return 1;
+}
+
+static int
+autocommit_converter(PyObject *val, enum autocommit_mode *result)
+{
+ if (Py_IsTrue(val)) {
+ *result = AUTOCOMMIT_ENABLED;
+ return 1;
+ }
+ if (Py_IsFalse(val)) {
+ *result = AUTOCOMMIT_DISABLED;
+ return 1;
+ }
+ if (PyLong_Check(val) &&
+ PyLong_AsLong(val) == LEGACY_TRANSACTION_CONTROL)
+ {
+ *result = AUTOCOMMIT_LEGACY;
+ return 1;
+ }
+
+ PyErr_SetString(PyExc_ValueError,
+ "autocommit must be True, False, or "
+ "sqlite3.LEGACY_TRANSACTION_CONTROL");
+ return 0;
+}
+
+static int
+sqlite3_int64_converter(PyObject *obj, sqlite3_int64 *result)
+{
+ if (!PyLong_Check(obj)) {
+ PyErr_SetString(PyExc_TypeError, "expected 'int'");
+ return 0;
+ }
+ *result = _pysqlite_long_as_int64(obj);
+ if (PyErr_Occurred()) {
+ return 0;
+ }
+ return 1;
+}
+
+#define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
+#include "clinic/connection.c.h"
+#undef clinic_state
+
+/*[clinic input]
+module _sqlite3
+class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionType"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=67369db2faf80891]*/
+
+static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self);
+static void free_callback_context(callback_context *ctx);
+static void set_callback_context(callback_context **ctx_pp,
+ callback_context *ctx);
+static int connection_close(pysqlite_Connection *self);
+PyObject *_pysqlite_query_execute(pysqlite_Cursor *, int, PyObject *, PyObject *);
+
+static PyObject *
+new_statement_cache(pysqlite_Connection *self, pysqlite_state *state,
+ int maxsize)
+{
+ PyObject *args[] = { NULL, PyLong_FromLong(maxsize), };
+ if (args[1] == NULL) {
+ return NULL;
+ }
+ PyObject *lru_cache = state->lru_cache;
+ size_t nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
+ PyObject *inner = PyObject_Vectorcall(lru_cache, args + 1, nargsf, NULL);
+ Py_DECREF(args[1]);
+ if (inner == NULL) {
+ return NULL;
+ }
+
+ args[1] = (PyObject *)self; // Borrowed ref.
+ nargsf = 1 | PY_VECTORCALL_ARGUMENTS_OFFSET;
+ PyObject *res = PyObject_Vectorcall(inner, args + 1, nargsf, NULL);
+ Py_DECREF(inner);
+ return res;
+}
+
+static inline int
+connection_exec_stmt(pysqlite_Connection *self, const char *sql)
+{
+ int rc;
+ Py_BEGIN_ALLOW_THREADS
+ int len = (int)strlen(sql) + 1;
+ sqlite3_stmt *stmt;
+ rc = sqlite3_prepare_v2(self->db, sql, len, &stmt, NULL);
+ if (rc == SQLITE_OK) {
+ (void)sqlite3_step(stmt);
+ rc = sqlite3_finalize(stmt);
+ }
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ (void)_pysqlite_seterror(self->state, self->db);
+ return -1;
+ }
+ return 0;
+}
+
+/*[python input]
+class IsolationLevel_converter(CConverter):
+ type = "const char *"
+ converter = "isolation_level_converter"
+
+class Autocommit_converter(CConverter):
+ type = "enum autocommit_mode"
+ converter = "autocommit_converter"
+
+class sqlite3_int64_converter(CConverter):
+ type = "sqlite3_int64"
+ converter = "sqlite3_int64_converter"
+
+[python start generated code]*/
+/*[python end generated code: output=da39a3ee5e6b4b0d input=dff8760fb1eba6a1]*/
+
+// NB: This needs to be in sync with the sqlite3.connect docstring
+/*[clinic input]
+_sqlite3.Connection.__init__ as pysqlite_connection_init
+
+ database: object
+ timeout: double = 5.0
+ detect_types: int = 0
+ isolation_level: IsolationLevel = ""
+ check_same_thread: bool = True
+ factory: object(c_default='(PyObject*)clinic_state()->ConnectionType') = ConnectionType
+ cached_statements as cache_size: int = 128
+ uri: bool = False
+ *
+ autocommit: Autocommit(c_default='LEGACY_TRANSACTION_CONTROL') = sqlite3.LEGACY_TRANSACTION_CONTROL
+[clinic start generated code]*/
+
+static int
+pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
+ double timeout, int detect_types,
+ const char *isolation_level,
+ int check_same_thread, PyObject *factory,
+ int cache_size, int uri,
+ enum autocommit_mode autocommit)
+/*[clinic end generated code: output=cba057313ea7712f input=9b0ab6c12f674fa3]*/
+{
+ if (PySys_Audit("sqlite3.connect", "O", database) < 0) {
+ return -1;
+ }
+
+ PyObject *bytes;
+ if (!PyUnicode_FSConverter(database, &bytes)) {
+ return -1;
+ }
+
+ if (self->initialized) {
+ self->initialized = 0;
+
+ PyTypeObject *tp = Py_TYPE(self);
+ tp->tp_clear((PyObject *)self);
+ if (connection_close(self) < 0) {
+ return -1;
+ }
+ }
+
+ // Create and configure SQLite database object.
+ sqlite3 *db;
+ int rc;
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_open_v2(PyBytes_AS_STRING(bytes), &db,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
+ (uri ? SQLITE_OPEN_URI : 0), NULL);
+ if (rc == SQLITE_OK) {
+ (void)sqlite3_busy_timeout(db, (int)(timeout*1000));
+ }
+ Py_END_ALLOW_THREADS
+
+ Py_DECREF(bytes);
+ if (db == NULL && rc == SQLITE_NOMEM) {
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ pysqlite_state *state = pysqlite_get_state_by_type(Py_TYPE(self));
+ if (rc != SQLITE_OK) {
+ _pysqlite_seterror(state, db);
+ goto error;
+ }
+
+ // Create LRU statement cache; returns a new reference.
+ PyObject *statement_cache = new_statement_cache(self, state, cache_size);
+ if (statement_cache == NULL) {
+ goto error;
+ }
+
+ /* Create lists of weak references to cursors and blobs */
+ PyObject *cursors = PyList_New(0);
+ if (cursors == NULL) {
+ Py_DECREF(statement_cache);
+ goto error;
+ }
+
+ PyObject *blobs = PyList_New(0);
+ if (blobs == NULL) {
+ Py_DECREF(statement_cache);
+ Py_DECREF(cursors);
+ goto error;
+ }
+
+ // Init connection state members.
+ self->db = db;
+ self->state = state;
+ self->detect_types = detect_types;
+ self->isolation_level = isolation_level;
+ self->autocommit = autocommit;
+ self->check_same_thread = check_same_thread;
+ self->thread_ident = PyThread_get_thread_ident();
+ self->statement_cache = statement_cache;
+ self->cursors = cursors;
+ self->blobs = blobs;
+ self->created_cursors = 0;
+ self->row_factory = Py_NewRef(Py_None);
+ self->text_factory = Py_NewRef(&PyUnicode_Type);
+ self->trace_ctx = NULL;
+ self->progress_ctx = NULL;
+ self->authorizer_ctx = NULL;
+
+ // Borrowed refs
+ self->Warning = state->Warning;
+ self->Error = state->Error;
+ self->InterfaceError = state->InterfaceError;
+ self->DatabaseError = state->DatabaseError;
+ self->DataError = state->DataError;
+ self->OperationalError = state->OperationalError;
+ self->IntegrityError = state->IntegrityError;
+ self->InternalError = state->InternalError;
+ self->ProgrammingError = state->ProgrammingError;
+ self->NotSupportedError = state->NotSupportedError;
+
+ if (PySys_Audit("sqlite3.connect/handle", "O", self) < 0) {
+ return -1; // Don't goto error; at this point, dealloc will clean up.
+ }
+
+ self->initialized = 1;
+
+ if (autocommit == AUTOCOMMIT_DISABLED) {
+ if (connection_exec_stmt(self, "BEGIN") < 0) {
+ return -1;
+ }
+ }
+ return 0;
+
+error:
+ // There are no statements or other SQLite objects attached to the
+ // database, so sqlite3_close() should always return SQLITE_OK.
+ rc = sqlite3_close(db);
+ assert(rc == SQLITE_OK);
+ return -1;
+}
+
+#define VISIT_CALLBACK_CONTEXT(ctx) \
+do { \
+ if (ctx) { \
+ Py_VISIT(ctx->callable); \
+ Py_VISIT(ctx->module); \
+ } \
+} while (0)
+
+static int
+connection_traverse(pysqlite_Connection *self, visitproc visit, void *arg)
+{
+ Py_VISIT(Py_TYPE(self));
+ Py_VISIT(self->statement_cache);
+ Py_VISIT(self->cursors);
+ Py_VISIT(self->blobs);
+ Py_VISIT(self->row_factory);
+ Py_VISIT(self->text_factory);
+ VISIT_CALLBACK_CONTEXT(self->trace_ctx);
+ VISIT_CALLBACK_CONTEXT(self->progress_ctx);
+ VISIT_CALLBACK_CONTEXT(self->authorizer_ctx);
+#undef VISIT_CALLBACK_CONTEXT
+ return 0;
+}
+
+static inline void
+clear_callback_context(callback_context *ctx)
+{
+ if (ctx != NULL) {
+ Py_CLEAR(ctx->callable);
+ Py_CLEAR(ctx->module);
+ }
+}
+
+static int
+connection_clear(pysqlite_Connection *self)
+{
+ Py_CLEAR(self->statement_cache);
+ Py_CLEAR(self->cursors);
+ Py_CLEAR(self->blobs);
+ Py_CLEAR(self->row_factory);
+ Py_CLEAR(self->text_factory);
+ clear_callback_context(self->trace_ctx);
+ clear_callback_context(self->progress_ctx);
+ clear_callback_context(self->authorizer_ctx);
+ return 0;
+}
+
+static void
+free_callback_contexts(pysqlite_Connection *self)
+{
+ set_callback_context(&self->trace_ctx, NULL);
+ set_callback_context(&self->progress_ctx, NULL);
+ set_callback_context(&self->authorizer_ctx, NULL);
+}
+
+static void
+remove_callbacks(sqlite3 *db)
+{
+ assert(db != NULL);
+ /* None of these APIs can fail, as long as they are given a valid
+ * database pointer. */
+ int rc;
+#ifdef HAVE_TRACE_V2
+ rc = sqlite3_trace_v2(db, SQLITE_TRACE_STMT, 0, 0);
+ assert(rc == SQLITE_OK), (void)rc;
+#else
+ sqlite3_trace(db, 0, (void*)0);
+#endif
+
+ sqlite3_progress_handler(db, 0, 0, (void *)0);
+
+ rc = sqlite3_set_authorizer(db, NULL, NULL);
+ assert(rc == SQLITE_OK), (void)rc;
+}
+
+static int
+connection_close(pysqlite_Connection *self)
+{
+ if (self->db == NULL) {
+ return 0;
+ }
+
+ int rc = 0;
+ if (self->autocommit == AUTOCOMMIT_DISABLED &&
+ !sqlite3_get_autocommit(self->db))
+ {
+ if (connection_exec_stmt(self, "ROLLBACK") < 0) {
+ rc = -1;
+ }
+ }
+
+ sqlite3 *db = self->db;
+ self->db = NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ /* The v2 close call always returns SQLITE_OK if given a valid database
+ * pointer (which we do), so we can safely ignore the return value */
+ (void)sqlite3_close_v2(db);
+ Py_END_ALLOW_THREADS
+
+ free_callback_contexts(self);
+ return rc;
+}
+
+static void
+connection_finalize(PyObject *self)
+{
+ pysqlite_Connection *con = (pysqlite_Connection *)self;
+ PyObject *exc = PyErr_GetRaisedException();
+
+ /* If close is implicitly called as a result of interpreter
+ * tear-down, we must not call back into Python. */
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ int teardown = _Py_IsInterpreterFinalizing(interp);
+ if (teardown && con->db) {
+ remove_callbacks(con->db);
+ }
+
+ /* Clean up if user has not called .close() explicitly. */
+ if (connection_close(con) < 0) {
+ if (teardown) {
+ PyErr_Clear();
+ }
+ else {
+ PyErr_WriteUnraisable((PyObject *)self);
+ }
+ }
+
+ PyErr_SetRaisedException(exc);
+}
+
+static void
+connection_dealloc(PyObject *self)
+{
+ if (PyObject_CallFinalizerFromDealloc(self) < 0) {
+ return;
+ }
+ PyTypeObject *tp = Py_TYPE(self);
+ PyObject_GC_UnTrack(self);
+ tp->tp_clear(self);
+ tp->tp_free(self);
+ Py_DECREF(tp);
+}
+
+/*[clinic input]
+_sqlite3.Connection.cursor as pysqlite_connection_cursor
+
+ factory: object = NULL
+
+Return a cursor for the connection.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_cursor_impl(pysqlite_Connection *self, PyObject *factory)
+/*[clinic end generated code: output=562432a9e6af2aa1 input=4127345aa091b650]*/
+{
+ PyObject* cursor;
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (factory == NULL) {
+ factory = (PyObject *)self->state->CursorType;
+ }
+
+ cursor = PyObject_CallOneArg(factory, (PyObject *)self);
+ if (cursor == NULL)
+ return NULL;
+ if (!PyObject_TypeCheck(cursor, self->state->CursorType)) {
+ PyErr_Format(PyExc_TypeError,
+ "factory must return a cursor, not %.100s",
+ Py_TYPE(cursor)->tp_name);
+ Py_DECREF(cursor);
+ return NULL;
+ }
+
+ _pysqlite_drop_unused_cursor_references(self);
+
+ if (cursor && self->row_factory != Py_None) {
+ Py_INCREF(self->row_factory);
+ Py_XSETREF(((pysqlite_Cursor *)cursor)->row_factory, self->row_factory);
+ }
+
+ return cursor;
+}
+
+/*[clinic input]
+_sqlite3.Connection.blobopen as blobopen
+
+ table: str
+ Table name.
+ column as col: str
+ Column name.
+ row: sqlite3_int64
+ Row index.
+ /
+ *
+ readonly: bool = False
+ Open the BLOB without write permissions.
+ name: str = "main"
+ Database name.
+
+Open and return a BLOB object.
+[clinic start generated code]*/
+
+static PyObject *
+blobopen_impl(pysqlite_Connection *self, const char *table, const char *col,
+ sqlite3_int64 row, int readonly, const char *name)
+/*[clinic end generated code: output=6a02d43efb885d1c input=23576bd1108d8774]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ int rc;
+ sqlite3_blob *blob;
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_blob_open(self->db, name, table, col, row, !readonly, &blob);
+ Py_END_ALLOW_THREADS
+
+ if (rc == SQLITE_MISUSE) {
+ PyErr_Format(self->state->InterfaceError, sqlite3_errstr(rc));
+ return NULL;
+ }
+ else if (rc != SQLITE_OK) {
+ _pysqlite_seterror(self->state, self->db);
+ return NULL;
+ }
+
+ pysqlite_Blob *obj = PyObject_GC_New(pysqlite_Blob, self->state->BlobType);
+ if (obj == NULL) {
+ goto error;
+ }
+
+ obj->connection = (pysqlite_Connection *)Py_NewRef(self);
+ obj->blob = blob;
+ obj->offset = 0;
+ obj->in_weakreflist = NULL;
+
+ PyObject_GC_Track(obj);
+
+ // Add our blob to connection blobs list
+ PyObject *weakref = PyWeakref_NewRef((PyObject *)obj, NULL);
+ if (weakref == NULL) {
+ goto error;
+ }
+ rc = PyList_Append(self->blobs, weakref);
+ Py_DECREF(weakref);
+ if (rc < 0) {
+ goto error;
+ }
+
+ return (PyObject *)obj;
+
+error:
+ Py_XDECREF(obj);
+ return NULL;
+}
+
+/*[clinic input]
+_sqlite3.Connection.close as pysqlite_connection_close
+
+Close the database connection.
+
+Any pending transaction is not committed implicitly.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_close_impl(pysqlite_Connection *self)
+/*[clinic end generated code: output=a546a0da212c9b97 input=b3ed5b74f6fefc06]*/
+{
+ if (!pysqlite_check_thread(self)) {
+ return NULL;
+ }
+
+ if (!self->initialized) {
+ PyTypeObject *tp = Py_TYPE(self);
+ pysqlite_state *state = pysqlite_get_state_by_type(tp);
+ PyErr_SetString(state->ProgrammingError,
+ "Base Connection.__init__ not called.");
+ return NULL;
+ }
+
+ pysqlite_close_all_blobs(self);
+ Py_CLEAR(self->statement_cache);
+ if (connection_close(self) < 0) {
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+/*
+ * Checks if a connection object is usable (i. e. not closed).
+ *
+ * 0 => error; 1 => ok
+ */
+int pysqlite_check_connection(pysqlite_Connection* con)
+{
+ if (!con->initialized) {
+ pysqlite_state *state = pysqlite_get_state_by_type(Py_TYPE(con));
+ PyErr_SetString(state->ProgrammingError,
+ "Base Connection.__init__ not called.");
+ return 0;
+ }
+
+ if (!con->db) {
+ PyErr_SetString(con->state->ProgrammingError,
+ "Cannot operate on a closed database.");
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/*[clinic input]
+_sqlite3.Connection.commit as pysqlite_connection_commit
+
+Commit any pending transaction to the database.
+
+If there is no open transaction, this method is a no-op.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_commit_impl(pysqlite_Connection *self)
+/*[clinic end generated code: output=3da45579e89407f2 input=c8793c97c3446065]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (self->autocommit == AUTOCOMMIT_LEGACY) {
+ if (!sqlite3_get_autocommit(self->db)) {
+ if (connection_exec_stmt(self, "COMMIT") < 0) {
+ return NULL;
+ }
+ }
+ }
+ else if (self->autocommit == AUTOCOMMIT_DISABLED) {
+ if (connection_exec_stmt(self, "COMMIT") < 0) {
+ return NULL;
+ }
+ if (connection_exec_stmt(self, "BEGIN") < 0) {
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+_sqlite3.Connection.rollback as pysqlite_connection_rollback
+
+Roll back to the start of any pending transaction.
+
+If there is no open transaction, this method is a no-op.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_rollback_impl(pysqlite_Connection *self)
+/*[clinic end generated code: output=b66fa0d43e7ef305 input=7f60a2f1076f16b3]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (self->autocommit == AUTOCOMMIT_LEGACY) {
+ if (!sqlite3_get_autocommit(self->db)) {
+ if (connection_exec_stmt(self, "ROLLBACK") < 0) {
+ return NULL;
+ }
+ }
+ }
+ else if (self->autocommit == AUTOCOMMIT_DISABLED) {
+ if (connection_exec_stmt(self, "ROLLBACK") < 0) {
+ return NULL;
+ }
+ if (connection_exec_stmt(self, "BEGIN") < 0) {
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+static int
+_pysqlite_set_result(sqlite3_context* context, PyObject* py_val)
+{
+ if (py_val == Py_None) {
+ sqlite3_result_null(context);
+ } else if (PyLong_Check(py_val)) {
+ sqlite_int64 value = _pysqlite_long_as_int64(py_val);
+ if (value == -1 && PyErr_Occurred())
+ return -1;
+ sqlite3_result_int64(context, value);
+ } else if (PyFloat_Check(py_val)) {
+ double value = PyFloat_AsDouble(py_val);
+ if (value == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ sqlite3_result_double(context, value);
+ } else if (PyUnicode_Check(py_val)) {
+ Py_ssize_t sz;
+ const char *str = PyUnicode_AsUTF8AndSize(py_val, &sz);
+ if (str == NULL) {
+ return -1;
+ }
+ if (sz > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "string is longer than INT_MAX bytes");
+ return -1;
+ }
+ sqlite3_result_text(context, str, (int)sz, SQLITE_TRANSIENT);
+ } else if (PyObject_CheckBuffer(py_val)) {
+ Py_buffer view;
+ if (PyObject_GetBuffer(py_val, &view, PyBUF_SIMPLE) != 0) {
+ return -1;
+ }
+ if (view.len > INT_MAX) {
+ PyErr_SetString(PyExc_OverflowError,
+ "BLOB longer than INT_MAX bytes");
+ PyBuffer_Release(&view);
+ return -1;
+ }
+ sqlite3_result_blob(context, view.buf, (int)view.len, SQLITE_TRANSIENT);
+ PyBuffer_Release(&view);
+ } else {
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ PyErr_Format(ctx->state->ProgrammingError,
+ "User-defined functions cannot return '%s' values to "
+ "SQLite",
+ Py_TYPE(py_val)->tp_name);
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+_pysqlite_build_py_params(sqlite3_context *context, int argc,
+ sqlite3_value **argv)
+{
+ PyObject* args;
+ int i;
+ sqlite3_value* cur_value;
+ PyObject* cur_py_value;
+
+ args = PyTuple_New(argc);
+ if (!args) {
+ return NULL;
+ }
+
+ for (i = 0; i < argc; i++) {
+ cur_value = argv[i];
+ switch (sqlite3_value_type(argv[i])) {
+ case SQLITE_INTEGER:
+ cur_py_value = PyLong_FromLongLong(sqlite3_value_int64(cur_value));
+ break;
+ case SQLITE_FLOAT:
+ cur_py_value = PyFloat_FromDouble(sqlite3_value_double(cur_value));
+ break;
+ case SQLITE_TEXT: {
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ const char *text = (const char *)sqlite3_value_text(cur_value);
+
+ if (text == NULL && sqlite3_errcode(db) == SQLITE_NOMEM) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ Py_ssize_t size = sqlite3_value_bytes(cur_value);
+ cur_py_value = PyUnicode_FromStringAndSize(text, size);
+ break;
+ }
+ case SQLITE_BLOB: {
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ const void *blob = sqlite3_value_blob(cur_value);
+
+ if (blob == NULL && sqlite3_errcode(db) == SQLITE_NOMEM) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ Py_ssize_t size = sqlite3_value_bytes(cur_value);
+ cur_py_value = PyBytes_FromStringAndSize(blob, size);
+ break;
+ }
+ case SQLITE_NULL:
+ default:
+ cur_py_value = Py_NewRef(Py_None);
+ }
+
+ if (!cur_py_value) {
+ goto error;
+ }
+
+ PyTuple_SET_ITEM(args, i, cur_py_value);
+ }
+
+ return args;
+
+error:
+ Py_DECREF(args);
+ return NULL;
+}
+
+static void
+print_or_clear_traceback(callback_context *ctx)
+{
+ assert(ctx != NULL);
+ assert(ctx->state != NULL);
+ if (ctx->state->enable_callback_tracebacks) {
+ PyErr_WriteUnraisable(ctx->callable);
+ }
+ else {
+ PyErr_Clear();
+ }
+}
+
+// Checks the Python exception and sets the appropriate SQLite error code.
+static void
+set_sqlite_error(sqlite3_context *context, const char *msg)
+{
+ assert(PyErr_Occurred());
+ if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
+ sqlite3_result_error_nomem(context);
+ }
+ else if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
+ sqlite3_result_error_toobig(context);
+ }
+ else {
+ sqlite3_result_error(context, msg, -1);
+ }
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ print_or_clear_traceback(ctx);
+}
+
+static void
+func_callback(sqlite3_context *context, int argc, sqlite3_value **argv)
+{
+ PyGILState_STATE threadstate = PyGILState_Ensure();
+
+ PyObject* args;
+ PyObject* py_retval = NULL;
+ int ok;
+
+ args = _pysqlite_build_py_params(context, argc, argv);
+ if (args) {
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+ py_retval = PyObject_CallObject(ctx->callable, args);
+ Py_DECREF(args);
+ }
+
+ ok = 0;
+ if (py_retval) {
+ ok = _pysqlite_set_result(context, py_retval) == 0;
+ Py_DECREF(py_retval);
+ }
+ if (!ok) {
+ set_sqlite_error(context, "user-defined function raised exception");
+ }
+
+ PyGILState_Release(threadstate);
+}
+
+static void
+step_callback(sqlite3_context *context, int argc, sqlite3_value **params)
+{
+ PyGILState_STATE threadstate = PyGILState_Ensure();
+
+ PyObject* args;
+ PyObject* function_result = NULL;
+ PyObject** aggregate_instance;
+ PyObject* stepmethod = NULL;
+
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+
+ aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, sizeof(PyObject*));
+ if (*aggregate_instance == NULL) {
+ *aggregate_instance = PyObject_CallNoArgs(ctx->callable);
+ if (!*aggregate_instance) {
+ set_sqlite_error(context,
+ "user-defined aggregate's '__init__' method raised error");
+ goto error;
+ }
+ }
+
+ stepmethod = PyObject_GetAttr(*aggregate_instance, ctx->state->str_step);
+ if (!stepmethod) {
+ set_sqlite_error(context,
+ "user-defined aggregate's 'step' method not defined");
+ goto error;
+ }
+
+ args = _pysqlite_build_py_params(context, argc, params);
+ if (!args) {
+ goto error;
+ }
+
+ function_result = PyObject_CallObject(stepmethod, args);
+ Py_DECREF(args);
+
+ if (!function_result) {
+ set_sqlite_error(context,
+ "user-defined aggregate's 'step' method raised error");
+ }
+
+error:
+ Py_XDECREF(stepmethod);
+ Py_XDECREF(function_result);
+
+ PyGILState_Release(threadstate);
+}
+
+static void
+final_callback(sqlite3_context *context)
+{
+ PyGILState_STATE threadstate = PyGILState_Ensure();
+
+ PyObject* function_result;
+ PyObject** aggregate_instance;
+ int ok;
+
+ aggregate_instance = (PyObject**)sqlite3_aggregate_context(context, 0);
+ if (aggregate_instance == NULL) {
+ /* No rows matched the query; the step handler was never called. */
+ goto error;
+ }
+ else 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, value, or inverse
+ PyObject *exc = PyErr_GetRaisedException();
+
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+ function_result = PyObject_CallMethodNoArgs(*aggregate_instance,
+ ctx->state->str_finalize);
+ Py_DECREF(*aggregate_instance);
+
+ ok = 0;
+ if (function_result) {
+ ok = _pysqlite_set_result(context, function_result) == 0;
+ Py_DECREF(function_result);
+ }
+ if (!ok) {
+ int attr_err = PyErr_ExceptionMatches(PyExc_AttributeError);
+ _PyErr_ChainExceptions1(exc);
+
+ /* Note: contrary to the step, value, and inverse callbacks, SQLite
+ * does _not_, as of SQLite 3.38.0, propagate errors to sqlite3_step()
+ * from the finalize callback. This implies that execute*() will not
+ * raise OperationalError, as it normally would. */
+ set_sqlite_error(context, attr_err
+ ? "user-defined aggregate's 'finalize' method not defined"
+ : "user-defined aggregate's 'finalize' method raised error");
+ }
+ else {
+ PyErr_SetRaisedException(exc);
+ }
+
+error:
+ PyGILState_Release(threadstate);
+}
+
+static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self)
+{
+ PyObject* new_list;
+ PyObject* weakref;
+ int i;
+
+ /* we only need to do this once in a while */
+ if (self->created_cursors++ < 200) {
+ return;
+ }
+
+ self->created_cursors = 0;
+
+ new_list = PyList_New(0);
+ if (!new_list) {
+ return;
+ }
+
+ for (i = 0; i < PyList_Size(self->cursors); i++) {
+ weakref = PyList_GetItem(self->cursors, i);
+ if (PyWeakref_GetObject(weakref) != Py_None) {
+ if (PyList_Append(new_list, weakref) != 0) {
+ Py_DECREF(new_list);
+ return;
+ }
+ }
+ }
+
+ Py_SETREF(self->cursors, new_list);
+}
+
+/* Allocate a UDF/callback context structure. In order to ensure that the state
+ * pointer always outlives the callback context, we make sure it owns a
+ * reference to the module itself. create_callback_context() is always called
+ * from connection methods, so we use the defining class to fetch the module
+ * pointer.
+ */
+static callback_context *
+create_callback_context(PyTypeObject *cls, PyObject *callable)
+{
+ callback_context *ctx = PyMem_Malloc(sizeof(callback_context));
+ if (ctx != NULL) {
+ PyObject *module = PyType_GetModule(cls);
+ ctx->callable = Py_NewRef(callable);
+ ctx->module = Py_NewRef(module);
+ ctx->state = pysqlite_get_state(module);
+ }
+ return ctx;
+}
+
+static void
+free_callback_context(callback_context *ctx)
+{
+ assert(ctx != NULL);
+ Py_XDECREF(ctx->callable);
+ Py_XDECREF(ctx->module);
+ PyMem_Free(ctx);
+}
+
+static void
+set_callback_context(callback_context **ctx_pp, callback_context *ctx)
+{
+ assert(ctx_pp != NULL);
+ callback_context *tmp = *ctx_pp;
+ *ctx_pp = ctx;
+ if (tmp != NULL) {
+ free_callback_context(tmp);
+ }
+}
+
+static void
+destructor_callback(void *ctx)
+{
+ if (ctx != NULL) {
+ // This function may be called without the GIL held, so we need to
+ // ensure that we destroy 'ctx' with the GIL held.
+ PyGILState_STATE gstate = PyGILState_Ensure();
+ free_callback_context((callback_context *)ctx);
+ PyGILState_Release(gstate);
+ }
+}
+
+/*[clinic input]
+_sqlite3.Connection.create_function as pysqlite_connection_create_function
+
+ cls: defining_class
+ /
+ name: str
+ narg: int
+ func: object
+ *
+ deterministic: bool = False
+
+Creates a new function.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_create_function_impl(pysqlite_Connection *self,
+ PyTypeObject *cls, const char *name,
+ int narg, PyObject *func,
+ int deterministic)
+/*[clinic end generated code: output=8a811529287ad240 input=b3e8e1d8ddaffbef]*/
+{
+ int rc;
+ int flags = SQLITE_UTF8;
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (deterministic) {
+#if SQLITE_VERSION_NUMBER < 3008003
+ PyErr_SetString(self->NotSupportedError,
+ "deterministic=True requires SQLite 3.8.3 or higher");
+ return NULL;
+#else
+ if (sqlite3_libversion_number() < 3008003) {
+ PyErr_SetString(self->NotSupportedError,
+ "deterministic=True requires SQLite 3.8.3 or higher");
+ return NULL;
+ }
+ flags |= SQLITE_DETERMINISTIC;
+#endif
+ }
+ callback_context *ctx = create_callback_context(cls, func);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_create_function_v2(self->db, name, narg, flags, ctx,
+ func_callback,
+ NULL,
+ NULL,
+ &destructor_callback); // will decref func
+
+ if (rc != SQLITE_OK) {
+ /* Workaround for SQLite bug: no error code or string is available here */
+ PyErr_SetString(self->OperationalError, "Error creating function");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+#ifdef HAVE_WINDOW_FUNCTIONS
+/*
+ * Regarding the 'inverse' aggregate callback:
+ * This method is only required by window aggregate functions, not
+ * ordinary aggregate function implementations. It is invoked to remove
+ * a row from the current window. The function arguments, if any,
+ * correspond to the row being removed.
+ */
+static void
+inverse_callback(sqlite3_context *context, int argc, sqlite3_value **params)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+
+ int size = sizeof(PyObject *);
+ PyObject **cls = (PyObject **)sqlite3_aggregate_context(context, size);
+ assert(cls != NULL);
+ assert(*cls != NULL);
+
+ PyObject *method = PyObject_GetAttr(*cls, ctx->state->str_inverse);
+ if (method == NULL) {
+ set_sqlite_error(context,
+ "user-defined aggregate's 'inverse' method not defined");
+ goto exit;
+ }
+
+ PyObject *args = _pysqlite_build_py_params(context, argc, params);
+ if (args == NULL) {
+ set_sqlite_error(context,
+ "unable to build arguments for user-defined aggregate's "
+ "'inverse' method");
+ goto exit;
+ }
+
+ PyObject *res = PyObject_CallObject(method, args);
+ Py_DECREF(args);
+ if (res == NULL) {
+ set_sqlite_error(context,
+ "user-defined aggregate's 'inverse' method raised error");
+ goto exit;
+ }
+ Py_DECREF(res);
+
+exit:
+ Py_XDECREF(method);
+ PyGILState_Release(gilstate);
+}
+
+/*
+ * Regarding the 'value' aggregate callback:
+ * This method is only required by window aggregate functions, not
+ * ordinary aggregate function implementations. It is invoked to return
+ * the current value of the aggregate.
+ */
+static void
+value_callback(sqlite3_context *context)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ callback_context *ctx = (callback_context *)sqlite3_user_data(context);
+ assert(ctx != NULL);
+
+ int size = sizeof(PyObject *);
+ PyObject **cls = (PyObject **)sqlite3_aggregate_context(context, size);
+ assert(cls != NULL);
+ assert(*cls != NULL);
+
+ PyObject *res = PyObject_CallMethodNoArgs(*cls, ctx->state->str_value);
+ if (res == NULL) {
+ int attr_err = PyErr_ExceptionMatches(PyExc_AttributeError);
+ set_sqlite_error(context, attr_err
+ ? "user-defined aggregate's 'value' method not defined"
+ : "user-defined aggregate's 'value' method raised error");
+ }
+ else {
+ int rc = _pysqlite_set_result(context, res);
+ Py_DECREF(res);
+ if (rc < 0) {
+ set_sqlite_error(context,
+ "unable to set result from user-defined aggregate's "
+ "'value' method");
+ }
+ }
+
+ PyGILState_Release(gilstate);
+}
+
+/*[clinic input]
+_sqlite3.Connection.create_window_function as create_window_function
+
+ cls: defining_class
+ name: str
+ The name of the SQL aggregate window function to be created or
+ redefined.
+ num_params: int
+ The number of arguments the step and inverse methods takes.
+ aggregate_class: object
+ A class with step(), finalize(), value(), and inverse() methods.
+ Set to None to clear the window function.
+ /
+
+Creates or redefines an aggregate window function. Non-standard.
+[clinic start generated code]*/
+
+static PyObject *
+create_window_function_impl(pysqlite_Connection *self, PyTypeObject *cls,
+ const char *name, int num_params,
+ PyObject *aggregate_class)
+/*[clinic end generated code: output=5332cd9464522235 input=46d57a54225b5228]*/
+{
+ if (sqlite3_libversion_number() < 3025000) {
+ PyErr_SetString(self->NotSupportedError,
+ "create_window_function() requires "
+ "SQLite 3.25.0 or higher");
+ return NULL;
+ }
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ int flags = SQLITE_UTF8;
+ int rc;
+ if (Py_IsNone(aggregate_class)) {
+ rc = sqlite3_create_window_function(self->db, name, num_params, flags,
+ 0, 0, 0, 0, 0, 0);
+ }
+ else {
+ callback_context *ctx = create_callback_context(cls, aggregate_class);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_create_window_function(self->db, name, num_params, flags,
+ ctx,
+ &step_callback,
+ &final_callback,
+ &value_callback,
+ &inverse_callback,
+ &destructor_callback);
+ }
+
+ if (rc != SQLITE_OK) {
+ // Errors are not set on the database connection, so we cannot
+ // use _pysqlite_seterror().
+ PyErr_SetString(self->ProgrammingError, sqlite3_errstr(rc));
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+#endif
+
+/*[clinic input]
+_sqlite3.Connection.create_aggregate as pysqlite_connection_create_aggregate
+
+ cls: defining_class
+ /
+ name: str
+ n_arg: int
+ aggregate_class: object
+
+Creates a new aggregate.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_create_aggregate_impl(pysqlite_Connection *self,
+ PyTypeObject *cls,
+ const char *name, int n_arg,
+ PyObject *aggregate_class)
+/*[clinic end generated code: output=1b02d0f0aec7ff96 input=68a2a26366d4c686]*/
+{
+ int rc;
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ callback_context *ctx = create_callback_context(cls, aggregate_class);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_create_function_v2(self->db, name, n_arg, SQLITE_UTF8, ctx,
+ 0,
+ &step_callback,
+ &final_callback,
+ &destructor_callback); // will decref func
+ if (rc != SQLITE_OK) {
+ /* Workaround for SQLite bug: no error code or string is available here */
+ PyErr_SetString(self->OperationalError, "Error creating aggregate");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static int
+authorizer_callback(void *ctx, int action, const char *arg1,
+ const char *arg2 , const char *dbname,
+ const char *access_attempt_source)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject *ret;
+ int rc = SQLITE_DENY;
+
+ assert(ctx != NULL);
+ PyObject *callable = ((callback_context *)ctx)->callable;
+ ret = PyObject_CallFunction(callable, "issss", action, arg1, arg2, dbname,
+ access_attempt_source);
+
+ if (ret == NULL) {
+ print_or_clear_traceback(ctx);
+ rc = SQLITE_DENY;
+ }
+ else {
+ if (PyLong_Check(ret)) {
+ rc = _PyLong_AsInt(ret);
+ if (rc == -1 && PyErr_Occurred()) {
+ print_or_clear_traceback(ctx);
+ rc = SQLITE_DENY;
+ }
+ }
+ else {
+ rc = SQLITE_DENY;
+ }
+ Py_DECREF(ret);
+ }
+
+ PyGILState_Release(gilstate);
+ return rc;
+}
+
+static int
+progress_callback(void *ctx)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ int rc;
+ PyObject *ret;
+
+ assert(ctx != NULL);
+ PyObject *callable = ((callback_context *)ctx)->callable;
+ ret = PyObject_CallNoArgs(callable);
+ if (!ret) {
+ /* abort query if error occurred */
+ rc = -1;
+ }
+ else {
+ rc = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+ if (rc < 0) {
+ print_or_clear_traceback(ctx);
+ }
+
+ PyGILState_Release(gilstate);
+ return rc;
+}
+
+#ifdef HAVE_TRACE_V2
+/*
+ * From https://sqlite.org/c3ref/trace_v2.html:
+ * The integer return value from the callback is currently ignored, though this
+ * may change in future releases. Callback implementations should return zero
+ * to ensure future compatibility.
+ */
+static int
+trace_callback(unsigned int type, void *ctx, void *stmt, void *sql)
+#else
+static void
+trace_callback(void *ctx, const char *sql)
+#endif
+{
+#ifdef HAVE_TRACE_V2
+ if (type != SQLITE_TRACE_STMT) {
+ return 0;
+ }
+#endif
+
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ assert(ctx != NULL);
+ pysqlite_state *state = ((callback_context *)ctx)->state;
+ assert(state != NULL);
+
+ PyObject *py_statement = NULL;
+#ifdef HAVE_TRACE_V2
+ const char *expanded_sql = sqlite3_expanded_sql((sqlite3_stmt *)stmt);
+ if (expanded_sql == NULL) {
+ sqlite3 *db = sqlite3_db_handle((sqlite3_stmt *)stmt);
+ if (sqlite3_errcode(db) == SQLITE_NOMEM) {
+ (void)PyErr_NoMemory();
+ goto exit;
+ }
+
+ PyErr_SetString(state->DataError,
+ "Expanded SQL string exceeds the maximum string length");
+ print_or_clear_traceback((callback_context *)ctx);
+
+ // Fall back to unexpanded sql
+ py_statement = PyUnicode_FromString((const char *)sql);
+ }
+ else {
+ py_statement = PyUnicode_FromString(expanded_sql);
+ sqlite3_free((void *)expanded_sql);
+ }
+#else
+ if (sql == NULL) {
+ PyErr_SetString(state->DataError,
+ "Expanded SQL string exceeds the maximum string length");
+ print_or_clear_traceback((callback_context *)ctx);
+ goto exit;
+ }
+ py_statement = PyUnicode_FromString(sql);
+#endif
+ if (py_statement) {
+ PyObject *callable = ((callback_context *)ctx)->callable;
+ PyObject *ret = PyObject_CallOneArg(callable, py_statement);
+ Py_DECREF(py_statement);
+ Py_XDECREF(ret);
+ }
+ if (PyErr_Occurred()) {
+ print_or_clear_traceback((callback_context *)ctx);
+ }
+
+exit:
+ PyGILState_Release(gilstate);
+#ifdef HAVE_TRACE_V2
+ return 0;
+#endif
+}
+
+/*[clinic input]
+_sqlite3.Connection.set_authorizer as pysqlite_connection_set_authorizer
+
+ cls: defining_class
+ /
+ authorizer_callback as callable: object
+
+Sets authorizer callback.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_set_authorizer_impl(pysqlite_Connection *self,
+ PyTypeObject *cls,
+ PyObject *callable)
+/*[clinic end generated code: output=75fa60114fc971c3 input=605d32ba92dd3eca]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ int rc;
+ if (callable == Py_None) {
+ rc = sqlite3_set_authorizer(self->db, NULL, NULL);
+ set_callback_context(&self->authorizer_ctx, NULL);
+ }
+ else {
+ callback_context *ctx = create_callback_context(cls, callable);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_set_authorizer(self->db, authorizer_callback, ctx);
+ set_callback_context(&self->authorizer_ctx, ctx);
+ }
+ if (rc != SQLITE_OK) {
+ PyErr_SetString(self->OperationalError,
+ "Error setting authorizer callback");
+ set_callback_context(&self->authorizer_ctx, NULL);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+_sqlite3.Connection.set_progress_handler as pysqlite_connection_set_progress_handler
+
+ cls: defining_class
+ /
+ progress_handler as callable: object
+ n: int
+
+Sets progress handler callback.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_set_progress_handler_impl(pysqlite_Connection *self,
+ PyTypeObject *cls,
+ PyObject *callable, int n)
+/*[clinic end generated code: output=0739957fd8034a50 input=f7c1837984bd86db]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (callable == Py_None) {
+ /* None clears the progress handler previously set */
+ sqlite3_progress_handler(self->db, 0, 0, (void*)0);
+ set_callback_context(&self->progress_ctx, NULL);
+ }
+ else {
+ callback_context *ctx = create_callback_context(cls, callable);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ sqlite3_progress_handler(self->db, n, progress_callback, ctx);
+ set_callback_context(&self->progress_ctx, ctx);
+ }
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+_sqlite3.Connection.set_trace_callback as pysqlite_connection_set_trace_callback
+
+ cls: defining_class
+ /
+ trace_callback as callable: object
+
+Sets a trace callback called for each SQL statement (passed as unicode).
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_set_trace_callback_impl(pysqlite_Connection *self,
+ PyTypeObject *cls,
+ PyObject *callable)
+/*[clinic end generated code: output=d91048c03bfcee05 input=351a94210c5f81bb]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (callable == Py_None) {
+ /*
+ * None clears the trace callback previously set
+ *
+ * Ref.
+ * - https://sqlite.org/c3ref/c_trace.html
+ * - https://sqlite.org/c3ref/trace_v2.html
+ */
+#ifdef HAVE_TRACE_V2
+ sqlite3_trace_v2(self->db, SQLITE_TRACE_STMT, 0, 0);
+#else
+ sqlite3_trace(self->db, 0, (void*)0);
+#endif
+ set_callback_context(&self->trace_ctx, NULL);
+ }
+ else {
+ callback_context *ctx = create_callback_context(cls, callable);
+ if (ctx == NULL) {
+ return NULL;
+ }
+#ifdef HAVE_TRACE_V2
+ sqlite3_trace_v2(self->db, SQLITE_TRACE_STMT, trace_callback, ctx);
+#else
+ sqlite3_trace(self->db, trace_callback, ctx);
+#endif
+ set_callback_context(&self->trace_ctx, ctx);
+ }
+
+ Py_RETURN_NONE;
+}
+
+#ifdef PY_SQLITE_ENABLE_LOAD_EXTENSION
+/*[clinic input]
+_sqlite3.Connection.enable_load_extension as pysqlite_connection_enable_load_extension
+
+ enable as onoff: bool
+ /
+
+Enable dynamic loading of SQLite extension modules.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_enable_load_extension_impl(pysqlite_Connection *self,
+ int onoff)
+/*[clinic end generated code: output=9cac37190d388baf input=2a1e87931486380f]*/
+{
+ int rc;
+
+ if (PySys_Audit("sqlite3.enable_load_extension",
+ "OO", self, onoff ? Py_True : Py_False) < 0) {
+ return NULL;
+ }
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ rc = sqlite3_enable_load_extension(self->db, onoff);
+
+ if (rc != SQLITE_OK) {
+ PyErr_SetString(self->OperationalError,
+ "Error enabling load extension");
+ return NULL;
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+/*[clinic input]
+_sqlite3.Connection.load_extension as pysqlite_connection_load_extension
+
+ name as extension_name: str
+ /
+ *
+ entrypoint: str(accept={str, NoneType}) = None
+
+Load SQLite extension module.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_load_extension_impl(pysqlite_Connection *self,
+ const char *extension_name,
+ const char *entrypoint)
+/*[clinic end generated code: output=7e61a7add9de0286 input=c36b14ea702e04f5]*/
+{
+ int rc;
+ char* errmsg;
+
+ if (PySys_Audit("sqlite3.load_extension", "Os", self, extension_name) < 0) {
+ return NULL;
+ }
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ rc = sqlite3_load_extension(self->db, extension_name, entrypoint, &errmsg);
+ if (rc != 0) {
+ PyErr_SetString(self->OperationalError, errmsg);
+ return NULL;
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+#endif
+
+int pysqlite_check_thread(pysqlite_Connection* self)
+{
+ if (self->check_same_thread) {
+ if (PyThread_get_thread_ident() != self->thread_ident) {
+ PyErr_Format(self->ProgrammingError,
+ "SQLite objects created in a thread can only be used in that same thread. "
+ "The object was created in thread id %lu and this is thread id %lu.",
+ self->thread_ident, PyThread_get_thread_ident());
+ return 0;
+ }
+
+ }
+ return 1;
+}
+
+static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* self, void* unused)
+{
+ if (!pysqlite_check_connection(self)) {
+ return NULL;
+ }
+ if (self->isolation_level != NULL) {
+ return PyUnicode_FromString(self->isolation_level);
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject* pysqlite_connection_get_total_changes(pysqlite_Connection* self, void* unused)
+{
+ if (!pysqlite_check_connection(self)) {
+ return NULL;
+ }
+ return PyLong_FromLong(sqlite3_total_changes(self->db));
+}
+
+static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* self, void* unused)
+{
+ if (!pysqlite_check_connection(self)) {
+ return NULL;
+ }
+ if (!sqlite3_get_autocommit(self->db)) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static int
+pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored))
+{
+ if (isolation_level == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
+ return -1;
+ }
+ if (Py_IsNone(isolation_level)) {
+ self->isolation_level = NULL;
+
+ // Execute a COMMIT to re-enable autocommit mode
+ PyObject *res = pysqlite_connection_commit_impl(self);
+ if (res == NULL) {
+ return -1;
+ }
+ Py_DECREF(res);
+ return 0;
+ }
+ if (!isolation_level_converter(isolation_level, &self->isolation_level)) {
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+pysqlite_connection_call(pysqlite_Connection *self, PyObject *args,
+ PyObject *kwargs)
+{
+ PyObject* sql;
+ pysqlite_Statement* statement;
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (!_PyArg_NoKeywords(MODULE_NAME ".Connection", kwargs))
+ return NULL;
+
+ if (!PyArg_ParseTuple(args, "U", &sql))
+ return NULL;
+
+ statement = pysqlite_statement_create(self, sql);
+ if (statement == NULL) {
+ return NULL;
+ }
+
+ return (PyObject*)statement;
+}
+
+/*[clinic input]
+_sqlite3.Connection.execute as pysqlite_connection_execute
+
+ sql: unicode
+ parameters: object = NULL
+ /
+
+Executes an SQL statement.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_execute_impl(pysqlite_Connection *self, PyObject *sql,
+ PyObject *parameters)
+/*[clinic end generated code: output=5be05ae01ee17ee4 input=27aa7792681ddba2]*/
+{
+ PyObject* result = 0;
+
+ PyObject *cursor = pysqlite_connection_cursor_impl(self, NULL);
+ if (!cursor) {
+ goto error;
+ }
+
+ result = _pysqlite_query_execute((pysqlite_Cursor *)cursor, 0, sql, parameters);
+ if (!result) {
+ Py_CLEAR(cursor);
+ }
+
+error:
+ Py_XDECREF(result);
+
+ return cursor;
+}
+
+/*[clinic input]
+_sqlite3.Connection.executemany as pysqlite_connection_executemany
+
+ sql: unicode
+ parameters: object
+ /
+
+Repeatedly executes an SQL statement.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_executemany_impl(pysqlite_Connection *self,
+ PyObject *sql, PyObject *parameters)
+/*[clinic end generated code: output=776cd2fd20bfe71f input=495be76551d525db]*/
+{
+ PyObject* result = 0;
+
+ PyObject *cursor = pysqlite_connection_cursor_impl(self, NULL);
+ if (!cursor) {
+ goto error;
+ }
+
+ result = _pysqlite_query_execute((pysqlite_Cursor *)cursor, 1, sql, parameters);
+ if (!result) {
+ Py_CLEAR(cursor);
+ }
+
+error:
+ Py_XDECREF(result);
+
+ return cursor;
+}
+
+/*[clinic input]
+_sqlite3.Connection.executescript as pysqlite_connection_executescript
+
+ sql_script as script_obj: object
+ /
+
+Executes multiple SQL statements at once.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_executescript(pysqlite_Connection *self,
+ PyObject *script_obj)
+/*[clinic end generated code: output=4c4f9d77aa0ae37d input=f6e5f1ccfa313db4]*/
+{
+ PyObject* result = 0;
+
+ PyObject *cursor = pysqlite_connection_cursor_impl(self, NULL);
+ if (!cursor) {
+ goto error;
+ }
+
+ PyObject *meth = self->state->str_executescript; // borrowed ref.
+ result = PyObject_CallMethodObjArgs(cursor, meth, script_obj, NULL);
+ if (!result) {
+ Py_CLEAR(cursor);
+ }
+
+error:
+ Py_XDECREF(result);
+
+ return cursor;
+}
+
+/* ------------------------- COLLATION CODE ------------------------ */
+
+static int
+collation_callback(void *context, int text1_length, const void *text1_data,
+ int text2_length, const void *text2_data)
+{
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ PyObject* string1 = 0;
+ PyObject* string2 = 0;
+ PyObject* retval = NULL;
+ long longval;
+ int result = 0;
+
+ /* This callback may be executed multiple times per sqlite3_step(). Bail if
+ * the previous call failed */
+ if (PyErr_Occurred()) {
+ goto finally;
+ }
+
+ string1 = PyUnicode_FromStringAndSize((const char*)text1_data, text1_length);
+ if (string1 == NULL) {
+ goto finally;
+ }
+ string2 = PyUnicode_FromStringAndSize((const char*)text2_data, text2_length);
+ if (string2 == NULL) {
+ goto finally;
+ }
+
+ callback_context *ctx = (callback_context *)context;
+ assert(ctx != NULL);
+ PyObject *args[] = { NULL, string1, string2 }; // Borrowed refs.
+ size_t nargsf = 2 | PY_VECTORCALL_ARGUMENTS_OFFSET;
+ retval = PyObject_Vectorcall(ctx->callable, args + 1, nargsf, NULL);
+ if (retval == NULL) {
+ /* execution failed */
+ goto finally;
+ }
+
+ longval = PyLong_AsLongAndOverflow(retval, &result);
+ if (longval == -1 && PyErr_Occurred()) {
+ PyErr_Clear();
+ result = 0;
+ }
+ else if (!result) {
+ if (longval > 0)
+ result = 1;
+ else if (longval < 0)
+ result = -1;
+ }
+
+finally:
+ Py_XDECREF(string1);
+ Py_XDECREF(string2);
+ Py_XDECREF(retval);
+ PyGILState_Release(gilstate);
+ return result;
+}
+
+/*[clinic input]
+_sqlite3.Connection.interrupt as pysqlite_connection_interrupt
+
+Abort any pending database operation.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_interrupt_impl(pysqlite_Connection *self)
+/*[clinic end generated code: output=f193204bc9e70b47 input=75ad03ade7012859]*/
+{
+ PyObject* retval = NULL;
+
+ if (!pysqlite_check_connection(self)) {
+ goto finally;
+ }
+
+ sqlite3_interrupt(self->db);
+
+ retval = Py_NewRef(Py_None);
+
+finally:
+ return retval;
+}
+
+/* Function author: Paul Kippes <kippesp@gmail.com>
+ * Class method of Connection to call the Python function _iterdump
+ * of the sqlite3 module.
+ */
+/*[clinic input]
+_sqlite3.Connection.iterdump as pysqlite_connection_iterdump
+
+Returns iterator to the dump of the database in an SQL text format.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_iterdump_impl(pysqlite_Connection *self)
+/*[clinic end generated code: output=586997aaf9808768 input=1911ca756066da89]*/
+{
+ if (!pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ PyObject *iterdump = _PyImport_GetModuleAttrString(MODULE_NAME ".dump", "_iterdump");
+ if (!iterdump) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(self->OperationalError,
+ "Failed to obtain _iterdump() reference");
+ }
+ return NULL;
+ }
+
+ PyObject *retval = PyObject_CallOneArg(iterdump, (PyObject *)self);
+ Py_DECREF(iterdump);
+ return retval;
+}
+
+/*[clinic input]
+_sqlite3.Connection.backup as pysqlite_connection_backup
+
+ target: object(type='pysqlite_Connection *', subclass_of='clinic_state()->ConnectionType')
+ *
+ pages: int = -1
+ progress: object = None
+ name: str = "main"
+ sleep: double = 0.250
+
+Makes a backup of the database.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_backup_impl(pysqlite_Connection *self,
+ pysqlite_Connection *target, int pages,
+ PyObject *progress, const char *name,
+ double sleep)
+/*[clinic end generated code: output=306a3e6a38c36334 input=c6519d0f59d0fd7f]*/
+{
+ int rc;
+ int sleep_ms = (int)(sleep * 1000.0);
+ sqlite3 *bck_conn;
+ sqlite3_backup *bck_handle;
+
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ if (!pysqlite_check_connection(target)) {
+ return NULL;
+ }
+
+ if (target == self) {
+ PyErr_SetString(PyExc_ValueError, "target cannot be the same connection instance");
+ return NULL;
+ }
+
+#if SQLITE_VERSION_NUMBER < 3008008
+ /* Since 3.8.8 this is already done, per commit
+ https://www.sqlite.org/src/info/169b5505498c0a7e */
+ if (!sqlite3_get_autocommit(target->db)) {
+ PyErr_SetString(self->OperationalError, "target is in transaction");
+ return NULL;
+ }
+#endif
+
+ if (progress != Py_None && !PyCallable_Check(progress)) {
+ PyErr_SetString(PyExc_TypeError, "progress argument must be a callable");
+ return NULL;
+ }
+
+ if (pages == 0) {
+ pages = -1;
+ }
+
+ bck_conn = target->db;
+
+ Py_BEGIN_ALLOW_THREADS
+ bck_handle = sqlite3_backup_init(bck_conn, "main", self->db, name);
+ Py_END_ALLOW_THREADS
+
+ if (bck_handle == NULL) {
+ _pysqlite_seterror(self->state, bck_conn);
+ return NULL;
+ }
+
+ do {
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_backup_step(bck_handle, pages);
+ Py_END_ALLOW_THREADS
+
+ if (progress != Py_None) {
+ int remaining = sqlite3_backup_remaining(bck_handle);
+ int pagecount = sqlite3_backup_pagecount(bck_handle);
+ PyObject *res = PyObject_CallFunction(progress, "iii", rc,
+ remaining, pagecount);
+ if (res == NULL) {
+ /* Callback failed: abort backup and bail. */
+ Py_BEGIN_ALLOW_THREADS
+ sqlite3_backup_finish(bck_handle);
+ Py_END_ALLOW_THREADS
+ return NULL;
+ }
+ Py_DECREF(res);
+ }
+
+ /* Sleep for a while if there are still further pages to copy and
+ the engine could not make any progress */
+ if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
+ Py_BEGIN_ALLOW_THREADS
+ sqlite3_sleep(sleep_ms);
+ Py_END_ALLOW_THREADS
+ }
+ } while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
+
+ Py_BEGIN_ALLOW_THREADS
+ rc = sqlite3_backup_finish(bck_handle);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ _pysqlite_seterror(self->state, bck_conn);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+_sqlite3.Connection.create_collation as pysqlite_connection_create_collation
+
+ cls: defining_class
+ name: str
+ callback as callable: object
+ /
+
+Creates a collation function.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
+ PyTypeObject *cls,
+ const char *name,
+ PyObject *callable)
+/*[clinic end generated code: output=32d339e97869c378 input=f67ecd2e31e61ad3]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ callback_context *ctx = NULL;
+ int rc;
+ int flags = SQLITE_UTF8;
+ if (callable == Py_None) {
+ rc = sqlite3_create_collation_v2(self->db, name, flags,
+ NULL, NULL, NULL);
+ }
+ else {
+ if (!PyCallable_Check(callable)) {
+ PyErr_SetString(PyExc_TypeError, "parameter must be callable");
+ return NULL;
+ }
+ ctx = create_callback_context(cls, callable);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ rc = sqlite3_create_collation_v2(self->db, name, flags, ctx,
+ &collation_callback,
+ &destructor_callback);
+ }
+
+ if (rc != SQLITE_OK) {
+ /* Unlike other sqlite3_* functions, the destructor callback is _not_
+ * called if sqlite3_create_collation_v2() fails, so we have to free
+ * the context before returning.
+ */
+ if (callable != Py_None) {
+ free_callback_context(ctx);
+ }
+ _pysqlite_seterror(self->state, self->db);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+#ifdef PY_SQLITE_HAVE_SERIALIZE
+/*[clinic input]
+_sqlite3.Connection.serialize as serialize
+
+ *
+ name: str = "main"
+ Which database to serialize.
+
+Serialize a database into a byte string.
+
+For an ordinary on-disk database file, the serialization is just a copy of the
+disk file. For an in-memory database or a "temp" database, the serialization is
+the same sequence of bytes which would be written to disk if that database
+were backed up to disk.
+[clinic start generated code]*/
+
+static PyObject *
+serialize_impl(pysqlite_Connection *self, const char *name)
+/*[clinic end generated code: output=97342b0e55239dd3 input=d2eb5194a65abe2b]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ /* If SQLite has a contiguous memory representation of the database, we can
+ * avoid memory allocations, so we try with the no-copy flag first.
+ */
+ sqlite3_int64 size;
+ unsigned int flags = SQLITE_SERIALIZE_NOCOPY;
+ const char *data;
+
+ Py_BEGIN_ALLOW_THREADS
+ data = (const char *)sqlite3_serialize(self->db, name, &size, flags);
+ if (data == NULL) {
+ flags &= ~SQLITE_SERIALIZE_NOCOPY;
+ data = (const char *)sqlite3_serialize(self->db, name, &size, flags);
+ }
+ Py_END_ALLOW_THREADS
+
+ if (data == NULL) {
+ PyErr_Format(self->OperationalError, "unable to serialize '%s'",
+ name);
+ return NULL;
+ }
+ PyObject *res = PyBytes_FromStringAndSize(data, (Py_ssize_t)size);
+ if (!(flags & SQLITE_SERIALIZE_NOCOPY)) {
+ sqlite3_free((void *)data);
+ }
+ return res;
+}
+
+/*[clinic input]
+_sqlite3.Connection.deserialize as deserialize
+
+ data: Py_buffer(accept={buffer, str})
+ The serialized database content.
+ /
+ *
+ name: str = "main"
+ Which database to reopen with the deserialization.
+
+Load a serialized database.
+
+The deserialize interface causes the database connection to disconnect from the
+target database, and then reopen it as an in-memory database based on the given
+serialized data.
+
+The deserialize interface will fail with SQLITE_BUSY if the database is
+currently in a read transaction or is involved in a backup operation.
+[clinic start generated code]*/
+
+static PyObject *
+deserialize_impl(pysqlite_Connection *self, Py_buffer *data,
+ const char *name)
+/*[clinic end generated code: output=e394c798b98bad89 input=1be4ca1faacf28f2]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ /* Transfer ownership of the buffer to SQLite:
+ * - Move buffer from Py to SQLite
+ * - Tell SQLite to free buffer memory
+ * - Tell SQLite that it is permitted to grow the resulting database
+ *
+ * Make sure we don't overflow sqlite3_deserialize(); it accepts a signed
+ * 64-bit int as its data size argument.
+ *
+ * We can safely use sqlite3_malloc64 here, since it was introduced before
+ * the serialize APIs.
+ */
+ if (data->len > 9223372036854775807) { // (1 << 63) - 1
+ PyErr_SetString(PyExc_OverflowError, "'data' is too large");
+ return NULL;
+ }
+
+ sqlite3_int64 size = (sqlite3_int64)data->len;
+ unsigned char *buf = sqlite3_malloc64(size);
+ if (buf == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ const unsigned int flags = SQLITE_DESERIALIZE_FREEONCLOSE |
+ SQLITE_DESERIALIZE_RESIZEABLE;
+ int rc;
+ Py_BEGIN_ALLOW_THREADS
+ (void)memcpy(buf, data->buf, data->len);
+ rc = sqlite3_deserialize(self->db, name, buf, size, size, flags);
+ Py_END_ALLOW_THREADS
+
+ if (rc != SQLITE_OK) {
+ (void)_pysqlite_seterror(self->state, self->db);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+#endif // PY_SQLITE_HAVE_SERIALIZE
+
+
+/*[clinic input]
+_sqlite3.Connection.__enter__ as pysqlite_connection_enter
+
+Called when the connection is used as a context manager.
+
+Returns itself as a convenience to the caller.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_enter_impl(pysqlite_Connection *self)
+/*[clinic end generated code: output=457b09726d3e9dcd input=127d7a4f17e86d8f]*/
+{
+ if (!pysqlite_check_connection(self)) {
+ return NULL;
+ }
+ return Py_NewRef((PyObject *)self);
+}
+
+/*[clinic input]
+_sqlite3.Connection.__exit__ as pysqlite_connection_exit
+
+ type as exc_type: object
+ value as exc_value: object
+ traceback as exc_tb: object
+ /
+
+Called when the connection is used as a context manager.
+
+If there was any exception, a rollback takes place; otherwise we commit.
+[clinic start generated code]*/
+
+static PyObject *
+pysqlite_connection_exit_impl(pysqlite_Connection *self, PyObject *exc_type,
+ PyObject *exc_value, PyObject *exc_tb)
+/*[clinic end generated code: output=0705200e9321202a input=bd66f1532c9c54a7]*/
+{
+ int commit = 0;
+ PyObject* result;
+
+ if (exc_type == Py_None && exc_value == Py_None && exc_tb == Py_None) {
+ commit = 1;
+ result = pysqlite_connection_commit_impl(self);
+ }
+ else {
+ result = pysqlite_connection_rollback_impl(self);
+ }
+
+ if (result == NULL) {
+ if (commit) {
+ /* Commit failed; try to rollback in order to unlock the database.
+ * If rollback also fails, chain the exceptions. */
+ PyObject *exc = PyErr_GetRaisedException();
+ result = pysqlite_connection_rollback_impl(self);
+ if (result == NULL) {
+ _PyErr_ChainExceptions1(exc);
+ }
+ else {
+ Py_DECREF(result);
+ PyErr_SetRaisedException(exc);
+ }
+ }
+ return NULL;
+ }
+ Py_DECREF(result);
+
+ Py_RETURN_FALSE;
+}
+
+/*[clinic input]
+_sqlite3.Connection.setlimit as setlimit
+
+ category: int
+ The limit category to be set.
+ limit: int
+ The new limit. If the new limit is a negative number, the limit is
+ unchanged.
+ /
+
+Set connection run-time limits.
+
+Attempts to increase a limit above its hard upper bound are silently truncated
+to the hard upper bound. Regardless of whether or not the limit was changed,
+the prior value of the limit is returned.
+[clinic start generated code]*/
+
+static PyObject *
+setlimit_impl(pysqlite_Connection *self, int category, int limit)
+/*[clinic end generated code: output=0d208213f8d68ccd input=9bd469537e195635]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+
+ int old_limit = sqlite3_limit(self->db, category, limit);
+ if (old_limit < 0) {
+ PyErr_SetString(self->ProgrammingError, "'category' is out of bounds");
+ return NULL;
+ }
+ return PyLong_FromLong(old_limit);
+}
+
+/*[clinic input]
+_sqlite3.Connection.getlimit as getlimit
+
+ category: int
+ The limit category to be queried.
+ /
+
+Get connection run-time limits.
+[clinic start generated code]*/
+
+static PyObject *
+getlimit_impl(pysqlite_Connection *self, int category)
+/*[clinic end generated code: output=7c3f5d11f24cecb1 input=61e0849fb4fb058f]*/
+{
+ return setlimit_impl(self, category, -1);
+}
+
+static inline bool
+is_int_config(const int op)
+{
+ switch (op) {
+ case SQLITE_DBCONFIG_ENABLE_FKEY:
+ case SQLITE_DBCONFIG_ENABLE_TRIGGER:
+#if SQLITE_VERSION_NUMBER >= 3012002
+ case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3013000
+ case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3016000
+ case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3020000
+ case SQLITE_DBCONFIG_ENABLE_QPSG:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3022000
+ case SQLITE_DBCONFIG_TRIGGER_EQP:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3024000
+ case SQLITE_DBCONFIG_RESET_DATABASE:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3026000
+ case SQLITE_DBCONFIG_DEFENSIVE:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3028000
+ case SQLITE_DBCONFIG_WRITABLE_SCHEMA:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3029000
+ case SQLITE_DBCONFIG_DQS_DDL:
+ case SQLITE_DBCONFIG_DQS_DML:
+ case SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3030000
+ case SQLITE_DBCONFIG_ENABLE_VIEW:
+#endif
+#if SQLITE_VERSION_NUMBER >= 3031000
+ case SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
+ case SQLITE_DBCONFIG_TRUSTED_SCHEMA:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*[clinic input]
+_sqlite3.Connection.setconfig as setconfig
+
+ op: int
+ The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.
+ enable: bool = True
+ /
+
+Set a boolean connection configuration option.
+[clinic start generated code]*/
+
+static PyObject *
+setconfig_impl(pysqlite_Connection *self, int op, int enable)
+/*[clinic end generated code: output=c60b13e618aff873 input=a10f1539c2d7da6b]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+ if (!is_int_config(op)) {
+ return PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
+ }
+
+ int actual;
+ int rc = sqlite3_db_config(self->db, op, enable, &actual);
+ if (rc != SQLITE_OK) {
+ (void)_pysqlite_seterror(self->state, self->db);
+ return NULL;
+ }
+ if (enable != actual) {
+ PyErr_SetString(self->state->OperationalError, "Unable to set config");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+/*[clinic input]
+_sqlite3.Connection.getconfig as getconfig -> bool
+
+ op: int
+ The configuration verb; one of the sqlite3.SQLITE_DBCONFIG codes.
+ /
+
+Query a boolean connection configuration option.
+[clinic start generated code]*/
+
+static int
+getconfig_impl(pysqlite_Connection *self, int op)
+/*[clinic end generated code: output=25ac05044c7b78a3 input=b0526d7e432e3f2f]*/
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return -1;
+ }
+ if (!is_int_config(op)) {
+ PyErr_Format(PyExc_ValueError, "unknown config 'op': %d", op);
+ return -1;
+ }
+
+ int current;
+ int rc = sqlite3_db_config(self->db, op, -1, &current);
+ if (rc != SQLITE_OK) {
+ (void)_pysqlite_seterror(self->state, self->db);
+ return -1;
+ }
+ return current;
+}
+
+static PyObject *
+get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx))
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return NULL;
+ }
+ if (self->autocommit == AUTOCOMMIT_ENABLED) {
+ Py_RETURN_TRUE;
+ }
+ if (self->autocommit == AUTOCOMMIT_DISABLED) {
+ Py_RETURN_FALSE;
+ }
+ return PyLong_FromLong(LEGACY_TRANSACTION_CONTROL);
+}
+
+static int
+set_autocommit(pysqlite_Connection *self, PyObject *val, void *Py_UNUSED(ctx))
+{
+ if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
+ return -1;
+ }
+ if (!autocommit_converter(val, &self->autocommit)) {
+ return -1;
+ }
+ if (self->autocommit == AUTOCOMMIT_ENABLED) {
+ if (!sqlite3_get_autocommit(self->db)) {
+ if (connection_exec_stmt(self, "COMMIT") < 0) {
+ return -1;
+ }
+ }
+ }
+ else if (self->autocommit == AUTOCOMMIT_DISABLED) {
+ if (sqlite3_get_autocommit(self->db)) {
+ if (connection_exec_stmt(self, "BEGIN") < 0) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static const char connection_doc[] =
+PyDoc_STR("SQLite database connection object.");
+
+static PyGetSetDef connection_getset[] = {
+ {"isolation_level", (getter)pysqlite_connection_get_isolation_level, (setter)pysqlite_connection_set_isolation_level},
+ {"total_changes", (getter)pysqlite_connection_get_total_changes, (setter)0},
+ {"in_transaction", (getter)pysqlite_connection_get_in_transaction, (setter)0},
+ {"autocommit", (getter)get_autocommit, (setter)set_autocommit},
+ {NULL}
+};
+
+static PyMethodDef connection_methods[] = {
+ PYSQLITE_CONNECTION_BACKUP_METHODDEF
+ PYSQLITE_CONNECTION_CLOSE_METHODDEF
+ PYSQLITE_CONNECTION_COMMIT_METHODDEF
+ PYSQLITE_CONNECTION_CREATE_AGGREGATE_METHODDEF
+ PYSQLITE_CONNECTION_CREATE_COLLATION_METHODDEF
+ PYSQLITE_CONNECTION_CREATE_FUNCTION_METHODDEF
+ PYSQLITE_CONNECTION_CURSOR_METHODDEF
+ PYSQLITE_CONNECTION_ENABLE_LOAD_EXTENSION_METHODDEF
+ PYSQLITE_CONNECTION_ENTER_METHODDEF
+ PYSQLITE_CONNECTION_EXECUTEMANY_METHODDEF
+ PYSQLITE_CONNECTION_EXECUTESCRIPT_METHODDEF
+ PYSQLITE_CONNECTION_EXECUTE_METHODDEF
+ PYSQLITE_CONNECTION_EXIT_METHODDEF
+ PYSQLITE_CONNECTION_INTERRUPT_METHODDEF
+ PYSQLITE_CONNECTION_ITERDUMP_METHODDEF
+ PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
+ PYSQLITE_CONNECTION_ROLLBACK_METHODDEF
+ PYSQLITE_CONNECTION_SET_AUTHORIZER_METHODDEF
+ PYSQLITE_CONNECTION_SET_PROGRESS_HANDLER_METHODDEF
+ PYSQLITE_CONNECTION_SET_TRACE_CALLBACK_METHODDEF
+ SETLIMIT_METHODDEF
+ GETLIMIT_METHODDEF
+ SERIALIZE_METHODDEF
+ DESERIALIZE_METHODDEF
+ CREATE_WINDOW_FUNCTION_METHODDEF
+ BLOBOPEN_METHODDEF
+ SETCONFIG_METHODDEF
+ GETCONFIG_METHODDEF
+ {NULL, NULL}
+};
+
+static struct PyMemberDef connection_members[] =
+{
+ {"Warning", T_OBJECT, offsetof(pysqlite_Connection, Warning), READONLY},
+ {"Error", T_OBJECT, offsetof(pysqlite_Connection, Error), READONLY},
+ {"InterfaceError", T_OBJECT, offsetof(pysqlite_Connection, InterfaceError), READONLY},
+ {"DatabaseError", T_OBJECT, offsetof(pysqlite_Connection, DatabaseError), READONLY},
+ {"DataError", T_OBJECT, offsetof(pysqlite_Connection, DataError), READONLY},
+ {"OperationalError", T_OBJECT, offsetof(pysqlite_Connection, OperationalError), READONLY},
+ {"IntegrityError", T_OBJECT, offsetof(pysqlite_Connection, IntegrityError), READONLY},
+ {"InternalError", T_OBJECT, offsetof(pysqlite_Connection, InternalError), READONLY},
+ {"ProgrammingError", T_OBJECT, offsetof(pysqlite_Connection, ProgrammingError), READONLY},
+ {"NotSupportedError", T_OBJECT, offsetof(pysqlite_Connection, NotSupportedError), READONLY},
+ {"row_factory", T_OBJECT, offsetof(pysqlite_Connection, row_factory)},
+ {"text_factory", T_OBJECT, offsetof(pysqlite_Connection, text_factory)},
+ {NULL}
+};
+
+static PyType_Slot connection_slots[] = {
+ {Py_tp_finalize, connection_finalize},
+ {Py_tp_dealloc, connection_dealloc},
+ {Py_tp_doc, (void *)connection_doc},
+ {Py_tp_methods, connection_methods},
+ {Py_tp_members, connection_members},
+ {Py_tp_getset, connection_getset},
+ {Py_tp_init, pysqlite_connection_init},
+ {Py_tp_call, pysqlite_connection_call},
+ {Py_tp_traverse, connection_traverse},
+ {Py_tp_clear, connection_clear},
+ {0, NULL},
+};
+
+static PyType_Spec connection_spec = {
+ .name = MODULE_NAME ".Connection",
+ .basicsize = sizeof(pysqlite_Connection),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
+ Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
+ .slots = connection_slots,
+};
+
+int
+pysqlite_connection_setup_types(PyObject *module)
+{
+ PyObject *type = PyType_FromModuleAndSpec(module, &connection_spec, NULL);
+ if (type == NULL) {
+ return -1;
+ }
+ pysqlite_state *state = pysqlite_get_state(module);
+ state->ConnectionType = (PyTypeObject *)type;
+ return 0;
+}