Skip to content

Commit 10d3a65

Browse files
Add sqlite3.Connection.autocommit for PEP 249 compliant behaviour
1 parent ad90d49 commit 10d3a65

File tree

7 files changed

+183
-55
lines changed

7 files changed

+183
-55
lines changed

Modules/_sqlite/clinic/connection.c.h

Lines changed: 18 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_sqlite/clinic/module.c.h

Lines changed: 18 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_sqlite/connection.c

Lines changed: 115 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,30 @@ isolation_level_converter(PyObject *str_or_none, const char **result)
9292
return 1;
9393
}
9494

95+
static int
96+
autocommit_converter(PyObject *val, enum autocommit_mode *result)
97+
{
98+
if (Py_IsTrue(val)) {
99+
*result = AUTOCOMMIT_ENABLED;
100+
return 1;
101+
}
102+
if (Py_IsFalse(val)) {
103+
*result = AUTOCOMMIT_DISABLED;
104+
return 1;
105+
}
106+
if (PyLong_Check(val) &&
107+
PyLong_AsLong(val) == DEPRECATED_TRANSACTION_CONTROL)
108+
{
109+
*result = AUTOCOMMIT_COMPAT;
110+
return 1;
111+
}
112+
113+
PyErr_SetString(PyExc_ValueError,
114+
"autocommit must be True, False, or "
115+
"sqlite3.DEPRECATED_TRANSACTION_CONTROL");
116+
return 0;
117+
}
118+
95119
#define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
96120
#include "clinic/connection.c.h"
97121
#undef clinic_state
@@ -132,13 +156,37 @@ new_statement_cache(pysqlite_Connection *self, pysqlite_state *state,
132156
return res;
133157
}
134158

159+
static inline int
160+
connection_txn_stmt(pysqlite_Connection *self, const char *sql)
161+
{
162+
int rc;
163+
Py_BEGIN_ALLOW_THREADS
164+
sqlite3_stmt *stmt;
165+
rc = sqlite3_prepare_v2(self->db, sql, -1, &stmt, NULL);
166+
if (rc == SQLITE_OK) {
167+
(void)sqlite3_step(stmt);
168+
rc = sqlite3_finalize(stmt);
169+
}
170+
Py_END_ALLOW_THREADS
171+
172+
if (rc != SQLITE_OK) {
173+
(void)_pysqlite_seterror(self->state, self->db);
174+
return -1;
175+
}
176+
return 0;
177+
}
178+
135179
/*[python input]
136180
class IsolationLevel_converter(CConverter):
137181
type = "const char *"
138182
converter = "isolation_level_converter"
139183
184+
class Autocommit_converter(CConverter):
185+
type = "enum autocommit_mode"
186+
converter = "autocommit_converter"
187+
140188
[python start generated code]*/
141-
/*[python end generated code: output=da39a3ee5e6b4b0d input=cbcfe85b253061c2]*/
189+
/*[python end generated code: output=da39a3ee5e6b4b0d input=bc2aa6c7ba0c5f8f]*/
142190

143191
/*[clinic input]
144192
_sqlite3.Connection.__init__ as pysqlite_connection_init
@@ -151,15 +199,17 @@ _sqlite3.Connection.__init__ as pysqlite_connection_init
151199
factory: object(c_default='(PyObject*)clinic_state()->ConnectionType') = ConnectionType
152200
cached_statements as cache_size: int = 128
153201
uri: bool = False
202+
autocommit: Autocommit(c_default='-1') = sqlite3.DEPRECATED_TRANSACTION_CONTROL
154203
[clinic start generated code]*/
155204

156205
static int
157206
pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
158207
double timeout, int detect_types,
159208
const char *isolation_level,
160209
int check_same_thread, PyObject *factory,
161-
int cache_size, int uri)
162-
/*[clinic end generated code: output=839eb2fee4293bda input=b8ce63dc6f70a383]*/
210+
int cache_size, int uri,
211+
enum autocommit_mode autocommit)
212+
/*[clinic end generated code: output=cba057313ea7712f input=82b8f749d645f63d]*/
163213
{
164214
if (PySys_Audit("sqlite3.connect", "O", database) < 0) {
165215
return -1;
@@ -226,6 +276,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
226276
self->state = state;
227277
self->detect_types = detect_types;
228278
self->isolation_level = isolation_level;
279+
self->autocommit = autocommit;
229280
self->check_same_thread = check_same_thread;
230281
self->thread_ident = PyThread_get_thread_ident();
231282
self->statement_cache = statement_cache;
@@ -255,6 +306,10 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database,
255306
}
256307

257308
self->initialized = 1;
309+
310+
if (autocommit == AUTOCOMMIT_DISABLED) {
311+
(void)connection_txn_stmt(self, "BEGIN");
312+
}
258313
return 0;
259314

260315
error:
@@ -324,6 +379,12 @@ static void
324379
connection_close(pysqlite_Connection *self)
325380
{
326381
if (self->db) {
382+
if (self->autocommit == AUTOCOMMIT_DISABLED &&
383+
!sqlite3_get_autocommit(self->db))
384+
{
385+
(void)connection_txn_stmt(self, "ROLLBACK");
386+
}
387+
327388
free_callback_contexts(self);
328389

329390
sqlite3 *db = self->db;
@@ -533,24 +594,21 @@ pysqlite_connection_commit_impl(pysqlite_Connection *self)
533594
return NULL;
534595
}
535596

536-
if (!sqlite3_get_autocommit(self->db)) {
537-
int rc;
538-
539-
Py_BEGIN_ALLOW_THREADS
540-
sqlite3_stmt *statement;
541-
rc = sqlite3_prepare_v2(self->db, "COMMIT", 7, &statement, NULL);
542-
if (rc == SQLITE_OK) {
543-
(void)sqlite3_step(statement);
544-
rc = sqlite3_finalize(statement);
597+
if (self->autocommit == AUTOCOMMIT_COMPAT) {
598+
if (!sqlite3_get_autocommit(self->db)) {
599+
if (connection_txn_stmt(self, "COMMIT") < 0) {
600+
return NULL;
601+
}
545602
}
546-
Py_END_ALLOW_THREADS
547-
548-
if (rc != SQLITE_OK) {
549-
(void)_pysqlite_seterror(self->state, self->db);
603+
}
604+
else if (self->autocommit == AUTOCOMMIT_DISABLED) {
605+
if (connection_txn_stmt(self, "COMMIT") < 0) {
606+
return NULL;
607+
}
608+
if (connection_txn_stmt(self, "BEGIN") < 0) {
550609
return NULL;
551610
}
552611
}
553-
554612
Py_RETURN_NONE;
555613
}
556614

@@ -568,25 +626,21 @@ pysqlite_connection_rollback_impl(pysqlite_Connection *self)
568626
return NULL;
569627
}
570628

571-
if (!sqlite3_get_autocommit(self->db)) {
572-
int rc;
573-
574-
Py_BEGIN_ALLOW_THREADS
575-
sqlite3_stmt *statement;
576-
rc = sqlite3_prepare_v2(self->db, "ROLLBACK", 9, &statement, NULL);
577-
if (rc == SQLITE_OK) {
578-
(void)sqlite3_step(statement);
579-
rc = sqlite3_finalize(statement);
629+
if (self->autocommit == AUTOCOMMIT_COMPAT) {
630+
if (!sqlite3_get_autocommit(self->db)) {
631+
if (connection_txn_stmt(self, "ROLLBACK") < 0) {
632+
return NULL;
633+
}
580634
}
581-
Py_END_ALLOW_THREADS
582-
583-
if (rc != SQLITE_OK) {
584-
(void)_pysqlite_seterror(self->state, self->db);
635+
}
636+
else if (self->autocommit == AUTOCOMMIT_DISABLED) {
637+
if (connection_txn_stmt(self, "ROLLBACK") < 0) {
638+
return NULL;
639+
}
640+
if (connection_txn_stmt(self, "BEGIN") < 0) {
585641
return NULL;
586642
}
587-
588643
}
589-
590644
Py_RETURN_NONE;
591645
}
592646

@@ -2264,13 +2318,42 @@ getlimit_impl(pysqlite_Connection *self, int category)
22642318
}
22652319

22662320

2321+
static PyObject *
2322+
get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx))
2323+
{
2324+
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
2325+
return NULL;
2326+
}
2327+
if (self->autocommit == AUTOCOMMIT_ENABLED) {
2328+
Py_RETURN_TRUE;
2329+
}
2330+
if (self->autocommit == AUTOCOMMIT_DISABLED) {
2331+
Py_RETURN_FALSE;
2332+
}
2333+
return PyLong_FromLong(DEPRECATED_TRANSACTION_CONTROL);
2334+
}
2335+
2336+
static int
2337+
set_autocommit(pysqlite_Connection *self, PyObject *val, void *Py_UNUSED(ctx))
2338+
{
2339+
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
2340+
return -1;
2341+
}
2342+
if (!autocommit_converter(val, &self->autocommit)) {
2343+
return -1;
2344+
}
2345+
return 0;
2346+
}
2347+
2348+
22672349
static const char connection_doc[] =
22682350
PyDoc_STR("SQLite database connection object.");
22692351

22702352
static PyGetSetDef connection_getset[] = {
22712353
{"isolation_level", (getter)pysqlite_connection_get_isolation_level, (setter)pysqlite_connection_set_isolation_level},
22722354
{"total_changes", (getter)pysqlite_connection_get_total_changes, (setter)0},
22732355
{"in_transaction", (getter)pysqlite_connection_get_in_transaction, (setter)0},
2356+
{"autocommit", (getter)get_autocommit, (setter)set_autocommit},
22742357
{NULL}
22752358
};
22762359

Modules/_sqlite/connection.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ typedef struct _callback_context
3939
pysqlite_state *state;
4040
} callback_context;
4141

42+
enum autocommit_mode {
43+
AUTOCOMMIT_COMPAT,
44+
AUTOCOMMIT_ENABLED,
45+
AUTOCOMMIT_DISABLED,
46+
};
47+
4248
typedef struct
4349
{
4450
PyObject_HEAD
@@ -51,6 +57,7 @@ typedef struct
5157

5258
/* NULL for autocommit, otherwise a string with the isolation level */
5359
const char *isolation_level;
60+
enum autocommit_mode autocommit;
5461

5562
/* 1 if a check should be performed for each API call if the connection is
5663
* used from the same thread it was created in */

0 commit comments

Comments
 (0)