diff options
author | Berker Peksag <berker.peksag@gmail.com> | 2016-09-11 09:57:15 (GMT) |
---|---|---|
committer | Berker Peksag <berker.peksag@gmail.com> | 2016-09-11 09:57:15 (GMT) |
commit | ab994ed8b97e1b0dac151ec827c857f5e7277565 (patch) | |
tree | d497b41e76a441931ac06430f44332497eaaa083 /Modules/_sqlite | |
parent | bd48d27944453ad83d3ce37b2c867fa0d59a1c15 (diff) | |
download | cpython-ab994ed8b97e1b0dac151ec827c857f5e7277565.zip cpython-ab994ed8b97e1b0dac151ec827c857f5e7277565.tar.gz cpython-ab994ed8b97e1b0dac151ec827c857f5e7277565.tar.bz2 |
Issue #10740: sqlite3 no longer implicitly commit an open transaction before DDL statements
This commit contains the following commits from ghaering/pysqlite:
* https://github.com/ghaering/pysqlite/commit/f254c534948c41c0ceb8cbabf0d4a2f547754739
* https://github.com/ghaering/pysqlite/commit/796b3afe38cfdac5d7d5ec260826b0a596554631
* https://github.com/ghaering/pysqlite/commit/cae87ee68613697a5f4947b4a0941f59a28da1b6
* https://github.com/ghaering/pysqlite/commit/3567b31bb5e5b226ba006213a9c69dde3f155faf
With the following additions:
* Fixed a refcount error
* Fixed a compiler warning
* Made the string comparison a little more robust
* Added a whatsnew entry
Diffstat (limited to 'Modules/_sqlite')
-rw-r--r-- | Modules/_sqlite/cursor.c | 125 | ||||
-rw-r--r-- | Modules/_sqlite/cursor.h | 6 | ||||
-rw-r--r-- | Modules/_sqlite/statement.c | 18 | ||||
-rw-r--r-- | Modules/_sqlite/statement.h | 1 |
4 files changed, 45 insertions, 105 deletions
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index e2c7a34..020f931 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -29,44 +29,6 @@ PyObject* pysqlite_cursor_iternext(pysqlite_Cursor* self); static const char errmsg_fetch_across_rollback[] = "Cursor needed to be reset because of commit/rollback and can no longer be fetched from."; -static pysqlite_StatementKind detect_statement_type(const char* statement) -{ - char buf[20]; - const char* src; - char* dst; - - src = statement; - /* skip over whitepace */ - while (*src == '\r' || *src == '\n' || *src == ' ' || *src == '\t') { - src++; - } - - if (*src == 0) - return STATEMENT_INVALID; - - dst = buf; - *dst = 0; - while (Py_ISALPHA(*src) && (dst - buf) < ((Py_ssize_t)sizeof(buf) - 2)) { - *dst++ = Py_TOLOWER(*src++); - } - - *dst = 0; - - if (!strcmp(buf, "select")) { - return STATEMENT_SELECT; - } else if (!strcmp(buf, "insert")) { - return STATEMENT_INSERT; - } else if (!strcmp(buf, "update")) { - return STATEMENT_UPDATE; - } else if (!strcmp(buf, "delete")) { - return STATEMENT_DELETE; - } else if (!strcmp(buf, "replace")) { - return STATEMENT_REPLACE; - } else { - return STATEMENT_OTHER; - } -} - static int pysqlite_cursor_init(pysqlite_Cursor* self, PyObject* args, PyObject* kwargs) { pysqlite_Connection* connection; @@ -427,9 +389,9 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* PyObject* func_args; PyObject* result; int numcols; - int statement_type; PyObject* descriptor; PyObject* second_argument = NULL; + sqlite_int64 lastrowid; if (!check_cursor(self)) { goto error; @@ -510,7 +472,7 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* /* reset description and rowcount */ Py_INCREF(Py_None); Py_SETREF(self->description, Py_None); - self->rowcount = -1L; + self->rowcount = 0L; func_args = PyTuple_New(1); if (!func_args) { @@ -549,43 +511,19 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* pysqlite_statement_reset(self->statement); pysqlite_statement_mark_dirty(self->statement); - statement_type = detect_statement_type(operation_cstr); - if (self->connection->begin_statement) { - switch (statement_type) { - case STATEMENT_UPDATE: - case STATEMENT_DELETE: - case STATEMENT_INSERT: - case STATEMENT_REPLACE: - if (!self->connection->inTransaction) { - result = _pysqlite_connection_begin(self->connection); - if (!result) { - goto error; - } - Py_DECREF(result); - } - break; - case STATEMENT_OTHER: - /* it's a DDL statement or something similar - - we better COMMIT first so it works for all cases */ - if (self->connection->inTransaction) { - result = pysqlite_connection_commit(self->connection, NULL); - if (!result) { - goto error; - } - Py_DECREF(result); - } - break; - case STATEMENT_SELECT: - if (multiple) { - PyErr_SetString(pysqlite_ProgrammingError, - "You cannot execute SELECT statements in executemany()."); - goto error; - } - break; + /* 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) { + if (sqlite3_get_autocommit(self->connection->db)) { + result = _pysqlite_connection_begin(self->connection); + if (!result) { + goto error; + } + Py_DECREF(result); } } - while (1) { parameters = PyIter_Next(parameters_iter); if (!parameters) { @@ -671,6 +609,20 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* } } + if (!sqlite3_stmt_readonly(self->statement->st)) { + self->rowcount += (long)sqlite3_changes(self->connection->db); + } else { + self->rowcount= -1L; + } + + if (!multiple) { + Py_DECREF(self->lastrowid); + Py_BEGIN_ALLOW_THREADS + lastrowid = sqlite3_last_insert_rowid(self->connection->db); + Py_END_ALLOW_THREADS + self->lastrowid = _pysqlite_long_from_int64(lastrowid); + } + if (rc == SQLITE_ROW) { if (multiple) { PyErr_SetString(pysqlite_ProgrammingError, "executemany() can only execute DML statements."); @@ -685,31 +637,6 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* Py_CLEAR(self->statement); } - switch (statement_type) { - case STATEMENT_UPDATE: - case STATEMENT_DELETE: - case STATEMENT_INSERT: - case STATEMENT_REPLACE: - if (self->rowcount == -1L) { - self->rowcount = 0L; - } - self->rowcount += (long)sqlite3_changes(self->connection->db); - } - - Py_DECREF(self->lastrowid); - if (!multiple && - /* REPLACE is an alias for INSERT OR REPLACE */ - (statement_type == STATEMENT_INSERT || statement_type == STATEMENT_REPLACE)) { - sqlite_int64 lastrowid; - Py_BEGIN_ALLOW_THREADS - lastrowid = sqlite3_last_insert_rowid(self->connection->db); - Py_END_ALLOW_THREADS - self->lastrowid = _pysqlite_long_from_int64(lastrowid); - } else { - Py_INCREF(Py_None); - self->lastrowid = Py_None; - } - if (multiple) { pysqlite_statement_reset(self->statement); } diff --git a/Modules/_sqlite/cursor.h b/Modules/_sqlite/cursor.h index 118ba38..28bbd5f 100644 --- a/Modules/_sqlite/cursor.h +++ b/Modules/_sqlite/cursor.h @@ -51,12 +51,6 @@ typedef struct PyObject* in_weakreflist; /* List of weak references */ } pysqlite_Cursor; -typedef enum { - STATEMENT_INVALID, STATEMENT_INSERT, STATEMENT_DELETE, - STATEMENT_UPDATE, STATEMENT_REPLACE, STATEMENT_SELECT, - STATEMENT_OTHER -} pysqlite_StatementKind; - extern PyTypeObject pysqlite_CursorType; PyObject* pysqlite_cursor_execute(pysqlite_Cursor* self, PyObject* args); diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c index e870633..7b98013 100644 --- a/Modules/_sqlite/statement.c +++ b/Modules/_sqlite/statement.c @@ -54,6 +54,7 @@ int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* con int rc; const char* sql_cstr; Py_ssize_t sql_cstr_len; + const char* p; self->st = NULL; self->in_use = 0; @@ -72,6 +73,23 @@ 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; + for (p = sql_cstr; *p != 0; p++) { + switch (*p) { + case ' ': + case '\r': + case '\n': + case '\t': + continue; + } + + self->is_ddl = (PyOS_strnicmp(p, "create ", 7) == 0) + || (PyOS_strnicmp(p, "drop ", 5) == 0) + || (PyOS_strnicmp(p, "reindex ", 8) == 0); + break; + } + Py_BEGIN_ALLOW_THREADS rc = sqlite3_prepare(connection->db, sql_cstr, diff --git a/Modules/_sqlite/statement.h b/Modules/_sqlite/statement.h index 4681443..6eef168 100644 --- a/Modules/_sqlite/statement.h +++ b/Modules/_sqlite/statement.h @@ -38,6 +38,7 @@ typedef struct sqlite3_stmt* st; PyObject* sql; int in_use; + int is_ddl; PyObject* in_weakreflist; /* List of weak references */ } pysqlite_Statement; |