aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Modules/_sqlite/statement.c
diff options
context:
space:
mode:
authorErlend Egeberg Aasland <erlend.aasland@innova.no>2021-06-01 12:47:37 +0200
committerGitHub <noreply@github.com>2021-06-01 12:47:37 +0200
commitfffa0f92adaaed0bcb3907d982506f78925e9052 (patch)
tree585aa05ef6e6f3d72ed652e2beb3ecb79c448b4e /Modules/_sqlite/statement.c
parenta6a20658814e8668966fc86de0e80a4772864781 (diff)
downloadcpython-fffa0f92adaaed0bcb3907d982506f78925e9052.tar.gz
cpython-fffa0f92adaaed0bcb3907d982506f78925e9052.zip
bpo-42972: Track sqlite3 statement objects (GH-26475)
Allocate and track statement objects in pysqlite_statement_create. By allocating and tracking creation of statement object in pysqlite_statement_create(), the caller does not need to worry about GC syncronization, and eliminates the possibility of getting a badly created object. All related fault handling is moved to pysqlite_statement_create(). Co-authored-by: Victor Stinner <vstinner@python.org>
Diffstat (limited to 'Modules/_sqlite/statement.c')
-rw-r--r--Modules/_sqlite/statement.c49
1 files changed, 35 insertions, 14 deletions
diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c
index 332bf030a9b..3a18ad8331f 100644
--- a/Modules/_sqlite/statement.c
+++ b/Modules/_sqlite/statement.c
@@ -48,7 +48,8 @@ typedef enum {
TYPE_UNKNOWN
} parameter_type;
-int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* connection, PyObject* sql)
+pysqlite_Statement *
+pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
{
const char* tail;
int rc;
@@ -56,27 +57,36 @@ int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* con
Py_ssize_t sql_cstr_len;
const char* p;
- self->st = NULL;
- self->in_use = 0;
-
assert(PyUnicode_Check(sql));
sql_cstr = PyUnicode_AsUTF8AndSize(sql, &sql_cstr_len);
if (sql_cstr == NULL) {
- rc = PYSQLITE_SQL_WRONG_TYPE;
- return rc;
+ PyErr_Format(pysqlite_Warning,
+ "SQL is of wrong type ('%s'). Must be string.",
+ Py_TYPE(sql)->tp_name);
+ return NULL;
}
if (strlen(sql_cstr) != (size_t)sql_cstr_len) {
- PyErr_SetString(PyExc_ValueError, "the query contains a null character");
- return PYSQLITE_SQL_WRONG_TYPE;
+ PyErr_SetString(PyExc_ValueError,
+ "the query contains a null character");
+ return NULL;
}
- self->in_weakreflist = NULL;
+ pysqlite_Statement *self = PyObject_GC_New(pysqlite_Statement,
+ pysqlite_StatementType);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->db = connection->db;
+ self->st = NULL;
self->sql = Py_NewRef(sql);
+ self->in_use = 0;
+ self->is_dml = 0;
+ self->in_weakreflist = NULL;
/* 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 ' ':
@@ -94,22 +104,33 @@ int pysqlite_statement_create(pysqlite_Statement* self, pysqlite_Connection* con
}
Py_BEGIN_ALLOW_THREADS
- rc = sqlite3_prepare_v2(connection->db,
+ rc = sqlite3_prepare_v2(self->db,
sql_cstr,
-1,
&self->st,
&tail);
Py_END_ALLOW_THREADS
- self->db = connection->db;
+ PyObject_GC_Track(self);
+
+ if (rc != SQLITE_OK) {
+ _pysqlite_seterror(self->db);
+ goto error;
+ }
if (rc == SQLITE_OK && pysqlite_check_remaining_sql(tail)) {
(void)sqlite3_finalize(self->st);
self->st = NULL;
- rc = PYSQLITE_TOO_MUCH_SQL;
+ PyErr_SetString(pysqlite_Warning,
+ "You can only execute one statement at a time.");
+ goto error;
}
- return rc;
+ return self;
+
+error:
+ Py_DECREF(self);
+ return NULL;
}
int pysqlite_statement_bind_parameter(pysqlite_Statement* self, int pos, PyObject* parameter)