bpo-28518: Start a transaction implicitly before a DML statement (#245)

Patch by Aviv Palivoda.
This commit is contained in:
Berker Peksag 2017-02-26 18:22:38 +03:00 committed by GitHub
parent 46ce7599af
commit 4a926caf8e
5 changed files with 24 additions and 11 deletions

View File

@ -179,6 +179,15 @@ class TransactionalDDL(unittest.TestCase):
result = self.con.execute("select * from test").fetchall() result = self.con.execute("select * from test").fetchall()
self.assertEqual(result, []) self.assertEqual(result, [])
def CheckImmediateTransactionalDDL(self):
# You can achieve transactional DDL by issuing a BEGIN
# statement manually.
self.con.execute("begin immediate")
self.con.execute("create table test(i)")
self.con.rollback()
with self.assertRaises(sqlite.OperationalError):
self.con.execute("select * from test")
def CheckTransactionalDDL(self): def CheckTransactionalDDL(self):
# You can achieve transactional DDL by issuing a BEGIN # You can achieve transactional DDL by issuing a BEGIN
# statement manually. # statement manually.

View File

@ -249,6 +249,9 @@ Extension Modules
Library Library
------- -------
- bpo-28518: Start a transaction implicitly before a DML statement.
Patch by Aviv Palivoda.
- Issue #16285: urrlib.parse.quote is now based on RFC 3986 and hence includes - Issue #16285: urrlib.parse.quote is now based on RFC 3986 and hence includes
'~' in the set of characters that is not quoted by default. Patch by '~' in the set of characters that is not quoted by default. Patch by
Christian Theune and Ratnadeep Debnath. Christian Theune and Ratnadeep Debnath.

View File

@ -511,10 +511,9 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
pysqlite_statement_reset(self->statement); pysqlite_statement_reset(self->statement);
pysqlite_statement_mark_dirty(self->statement); pysqlite_statement_mark_dirty(self->statement);
/* For backwards compatibility reasons, do not start a transaction if a /* We start a transaction implicitly before a DML statement.
DDL statement is encountered. If anybody wants transactional DDL, SELECT is the only exception. See #9924. */
they can issue a BEGIN statement manually. */ if (self->connection->begin_statement && self->statement->is_dml) {
if (self->connection->begin_statement && !sqlite3_stmt_readonly(self->statement->st) && !self->statement->is_ddl) {
if (sqlite3_get_autocommit(self->connection->db)) { if (sqlite3_get_autocommit(self->connection->db)) {
result = _pysqlite_connection_begin(self->connection); result = _pysqlite_connection_begin(self->connection);
if (!result) { if (!result) {
@ -609,7 +608,7 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
} }
} }
if (!sqlite3_stmt_readonly(self->statement->st)) { if (self->statement->is_dml) {
self->rowcount += (long)sqlite3_changes(self->connection->db); self->rowcount += (long)sqlite3_changes(self->connection->db);
} else { } else {
self->rowcount= -1L; self->rowcount= -1L;

View File

@ -73,8 +73,9 @@ int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* con
Py_INCREF(sql); Py_INCREF(sql);
self->sql = sql; self->sql = sql;
/* determine if the statement is a DDL statement */ /* Determine if the statement is a DML statement.
self->is_ddl = 0; SELECT is the only exception. See #9924. */
self->is_dml = 0;
for (p = sql_cstr; *p != 0; p++) { for (p = sql_cstr; *p != 0; p++) {
switch (*p) { switch (*p) {
case ' ': case ' ':
@ -84,9 +85,10 @@ int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* con
continue; continue;
} }
self->is_ddl = (PyOS_strnicmp(p, "create ", 7) == 0) self->is_dml = (PyOS_strnicmp(p, "insert ", 7) == 0)
|| (PyOS_strnicmp(p, "drop ", 5) == 0) || (PyOS_strnicmp(p, "update ", 7) == 0)
|| (PyOS_strnicmp(p, "reindex ", 8) == 0); || (PyOS_strnicmp(p, "delete ", 7) == 0)
|| (PyOS_strnicmp(p, "replace ", 8) == 0);
break; break;
} }

View File

@ -38,7 +38,7 @@ typedef struct
sqlite3_stmt* st; sqlite3_stmt* st;
PyObject* sql; PyObject* sql;
int in_use; int in_use;
int is_ddl; int is_dml;
PyObject* in_weakreflist; /* List of weak references */ PyObject* in_weakreflist; /* List of weak references */
} pysqlite_Statement; } pysqlite_Statement;