summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBerker Peksag <berker.peksag@gmail.com>2017-02-26 16:09:10 (GMT)
committerGitHub <noreply@github.com>2017-02-26 16:09:10 (GMT)
commit76995cab69d5ef83d31d8a5754cbad11be4038cb (patch)
tree1a3af522b463de5c31b226f7a48797f9bf687be8
parent893e86e9d3c0caeb878ccb1120c7259e022f3b68 (diff)
downloadcpython-76995cab69d5ef83d31d8a5754cbad11be4038cb.zip
cpython-76995cab69d5ef83d31d8a5754cbad11be4038cb.tar.gz
cpython-76995cab69d5ef83d31d8a5754cbad11be4038cb.tar.bz2
bpo-28518: Start a transaction implicitly before a DML statement (#245) (#318)
Patch by Aviv Palivoda. (cherry picked from commit 4a926caf8e5fd8af771b2c34bfb6e91c732331fe)
-rw-r--r--Lib/sqlite3/test/transactions.py9
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_sqlite/cursor.c9
-rw-r--r--Modules/_sqlite/statement.c12
-rw-r--r--Modules/_sqlite/statement.h2
5 files changed, 24 insertions, 11 deletions
diff --git a/Lib/sqlite3/test/transactions.py b/Lib/sqlite3/test/transactions.py
index 45f1b04..b8a13de 100644
--- a/Lib/sqlite3/test/transactions.py
+++ b/Lib/sqlite3/test/transactions.py
@@ -179,6 +179,15 @@ class TransactionalDDL(unittest.TestCase):
result = self.con.execute("select * from test").fetchall()
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):
# You can achieve transactional DDL by issuing a BEGIN
# statement manually.
diff --git a/Misc/NEWS b/Misc/NEWS
index 0f5c279..a48debd 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -69,6 +69,9 @@ Extension Modules
Library
-------
+- bpo-28518: Start a transaction implicitly before a DML statement.
+ Patch by Aviv Palivoda.
+
- bpo-29532: Altering a kwarg dictionary passed to functools.partial()
no longer affects a partial object after creation.
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
index 39f7a65..8341fb8 100644
--- a/Modules/_sqlite/cursor.c
+++ b/Modules/_sqlite/cursor.c
@@ -511,10 +511,9 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
pysqlite_statement_reset(self->statement);
pysqlite_statement_mark_dirty(self->statement);
- /* For backwards compatibility reasons, do not start a transaction if a
- DDL statement is encountered. If anybody wants transactional DDL,
- they can issue a BEGIN statement manually. */
- if (self->connection->begin_statement && !sqlite3_stmt_readonly(self->statement->st) && !self->statement->is_ddl) {
+ /* We start a transaction implicitly before a DML statement.
+ SELECT is the only exception. See #9924. */
+ if (self->connection->begin_statement && self->statement->is_dml) {
if (sqlite3_get_autocommit(self->connection->db)) {
result = _pysqlite_connection_begin(self->connection);
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);
} else {
self->rowcount= -1L;
diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c
index 0df661b..087375b 100644
--- a/Modules/_sqlite/statement.c
+++ b/Modules/_sqlite/statement.c
@@ -73,8 +73,9 @@ int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* con
Py_INCREF(sql);
self->sql = sql;
- /* determine if the statement is a DDL statement */
- self->is_ddl = 0;
+ /* Determine if the statement is a DML statement.
+ SELECT is the only exception. See #9924. */
+ self->is_dml = 0;
for (p = sql_cstr; *p != 0; p++) {
switch (*p) {
case ' ':
@@ -84,9 +85,10 @@ int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* con
continue;
}
- self->is_ddl = (PyOS_strnicmp(p, "create ", 7) == 0)
- || (PyOS_strnicmp(p, "drop ", 5) == 0)
- || (PyOS_strnicmp(p, "reindex ", 8) == 0);
+ self->is_dml = (PyOS_strnicmp(p, "insert ", 7) == 0)
+ || (PyOS_strnicmp(p, "update ", 7) == 0)
+ || (PyOS_strnicmp(p, "delete ", 7) == 0)
+ || (PyOS_strnicmp(p, "replace ", 8) == 0);
break;
}
diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h
index 6eef168..8db10f6 100644
--- a/Modules/_sqlite/statement.h
+++ b/Modules/_sqlite/statement.h
@@ -38,7 +38,7 @@ typedef struct
sqlite3_stmt* st;
PyObject* sql;
int in_use;
- int is_ddl;
+ int is_dml;
PyObject* in_weakreflist; /* List of weak references */
} pysqlite_Statement;