bpo-43265: Improve sqlite3.Connection.backup error handling (GH-24586)

This commit is contained in:
Erlend Egeberg Aasland 2021-04-14 13:45:49 +02:00 committed by GitHub
parent b8509ffa82
commit c1ae741997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 57 deletions

View file

@ -149,10 +149,7 @@ class BackupTests(unittest.TestCase):
with self.assertRaises(sqlite.OperationalError) as cm: with self.assertRaises(sqlite.OperationalError) as cm:
with sqlite.connect(':memory:') as bck: with sqlite.connect(':memory:') as bck:
self.cx.backup(bck, name='non-existing') self.cx.backup(bck, name='non-existing')
self.assertIn( self.assertIn("unknown database", str(cm.exception))
str(cm.exception),
['SQL logic error', 'SQL logic error or missing database']
)
self.cx.execute("ATTACH DATABASE ':memory:' AS attached_db") self.cx.execute("ATTACH DATABASE ':memory:' AS attached_db")
self.cx.execute('CREATE TABLE attached_db.foo (key INTEGER)') self.cx.execute('CREATE TABLE attached_db.foo (key INTEGER)')

View file

@ -0,0 +1,3 @@
Improve :meth:`sqlite3.Connection.backup` error handling. The error message
for non-existant target database names is now ``unknown database <database
name>`` instead of ``SQL logic error``. Patch by Erlend E. Aasland.

View file

@ -1601,7 +1601,6 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
/*[clinic end generated code: output=306a3e6a38c36334 input=30ae45fc420bfd3b]*/ /*[clinic end generated code: output=306a3e6a38c36334 input=30ae45fc420bfd3b]*/
{ {
int rc; int rc;
int callback_error = 0;
int sleep_ms = (int)(sleep * 1000.0); int sleep_ms = (int)(sleep * 1000.0);
sqlite3 *bck_conn; sqlite3 *bck_conn;
sqlite3_backup *bck_handle; sqlite3_backup *bck_handle;
@ -1643,26 +1642,29 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
bck_handle = sqlite3_backup_init(bck_conn, "main", self->db, name); bck_handle = sqlite3_backup_init(bck_conn, "main", self->db, name);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (bck_handle) { if (bck_handle == NULL) {
_pysqlite_seterror(bck_conn, NULL);
return NULL;
}
do { do {
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
rc = sqlite3_backup_step(bck_handle, pages); rc = sqlite3_backup_step(bck_handle, pages);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if (progress != Py_None) { if (progress != Py_None) {
PyObject *res; int remaining = sqlite3_backup_remaining(bck_handle);
int pagecount = sqlite3_backup_pagecount(bck_handle);
res = PyObject_CallFunction(progress, "iii", rc, PyObject *res = PyObject_CallFunction(progress, "iii", rc,
sqlite3_backup_remaining(bck_handle), remaining, pagecount);
sqlite3_backup_pagecount(bck_handle));
if (res == NULL) { if (res == NULL) {
/* User's callback raised an error: interrupt the loop and /* Callback failed: abort backup and bail. */
propagate it. */ Py_BEGIN_ALLOW_THREADS
callback_error = 1; sqlite3_backup_finish(bck_handle);
rc = -1; Py_END_ALLOW_THREADS
} else { return NULL;
Py_DECREF(res);
} }
Py_DECREF(res);
} }
/* Sleep for a while if there are still further pages to copy and /* Sleep for a while if there are still further pages to copy and
@ -1677,26 +1679,13 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
rc = sqlite3_backup_finish(bck_handle); rc = sqlite3_backup_finish(bck_handle);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
} else {
rc = _pysqlite_seterror(bck_conn, NULL);
}
if (!callback_error && rc != SQLITE_OK) { if (rc != SQLITE_OK) {
/* We cannot use _pysqlite_seterror() here because the backup APIs do _pysqlite_seterror(bck_conn, NULL);
not set the error status on the connection object, but rather on
the backup handle. */
if (rc == SQLITE_NOMEM) {
(void)PyErr_NoMemory();
} else {
PyErr_SetString(pysqlite_OperationalError, sqlite3_errstr(rc));
}
}
if (!callback_error && rc == SQLITE_OK) {
Py_RETURN_NONE;
} else {
return NULL; return NULL;
} }
Py_RETURN_NONE;
} }
/*[clinic input] /*[clinic input]