Я добавляю для своей программы возможность удалять любую строку в базе данных, но эта база данных использует общие ключи с шестью другими базами данных. Для начала я убедился, что внешние ключи включены:
void SQLitePatternSet::enableForeignKeys()
{
char *msg;
int result = sqlite3_exec(db1, "PRAGMA foreign_keys=ON;", nullptr,
nullptr, &msg);
if (result != SQLITE_OK)
{
std::string errMsg = "Failed to enable foreign key support: ";
errMsg += msg;
sqlite3_free(msg);
throw std::runtime_error(errMsg);
}
}
И что к соответствующим строкам в базе данных прикреплен ON DELETE CASCADE. Несмотря на это, я все еще получаю сообщение об ошибке SQLITE_CONSTRAINT_FOREIGNKEY. Теперь хотелось бы перечислить, какие ключи нарушаются при удалении:
bool SQLitePatternSet::removeRow(const std::int64_t db1RowID)
{
bool removed = true;
std::string sql = "DELETE FROM Database1 WHERE RowID = :id;";
sqlite3_stmt *removeStmt;
prepStatement(sql, removeStmt);
sqlite3_bind_int64(removeStmt, 1, db1RowID);
int sqlResult = sqlite3_step(removeStmt);
if (sqlResult != SQLITE_DONE)
{
if (sqlite3_extended_errcode(db1) == SQLITE_CONSTRAINT_FOREIGNKEY)
{
removed = false;
result = sqlite3_exec(db1, "PRAGMA foreign_key_check(Database1);", foreignKeyCheckCallback, nullptr, nullptr);
}
else
{
auto errMsg = formatErrMsg("removeRow failed");
throw pset_errors::storage_error(errMsg);
}
}
data1.erase(db1RowID);
sqlite3_finalize(removeStmt);
return removed;
}
Но здесь я застрял. Кажется, проверка внешнего ключа должна работать, где:
int foreignKeyCheckCallback(void *a_param, int argc, char **argv, char **column){
std::string msg = "Foreign key check:\n";
for (int i = 0; i < argc; i++) {
msg += argv[i];
if (i < argc - 1)
msg += "\n";
}
throw std::runtime_error(msg);
return 1;
}
Результат возвращает 0, что означает отсутствие ошибок в синтаксисе sqlite3_exec(). Но после нескольких тестов обратный вызов никогда не запускается, что (на самом деле) означает, что нет ключей, которые вызывают рассматриваемую ошибку внешнего ключа. Как такое может быть? Или Foreign_key_check по своей природе просто не выводится в обратный вызов?
Итак, оказывается, что ошибку вызывали не внешние ключи в базе данных 1, а ключи в другой базе данных, которые ссылались на первичный ключ в базе данных 1. Выбор всех строк в базе данных 2, которые ссылаются на 1, а затем удаление их из 2. , решил проблему:
bool SQLitePatternSet::removeRow(const std::int64_t db1RowID)
{
std::string sql1 = "SELECT DB2RowID FROM Database2 WHERE DB1RowID = :id;";
sqlite3_stmt *selStmt;
prepStatement(sql1, selStmt);
sqlite3_bind_int64(selStmt, 1, db1RowID);
int sqlresult1;
std::vector<std::int64_t> db2RowIDs;
do
{
sqlresult1 = sqlite3_step(selStmt);
if (sqlresult1 == SQLITE_ROW)
{
std::int64_t db2RowID = sqlite3_column_int64(selStmt, 0);
db2RowIDs.push_back(db2RowID);
}
else if (sqlresult1 != SQLITE_DONE)
{
throw pset_errors::storage_error(
formatSQLErr("removeRow failed"));
}
} while(sqlresult1 != SQLITE_DONE);
sqlite3_finalize(selStmt);
bool removed = true;
for (std::int64_t db2RowID: db2RowIDs) {
std::string sql2 = "DELETE FROM Database2 WHERE DB2RowID = :id;";
sqlite3_stmt *removeStmt;
prepStatement(sql2, removeStmt);
sqlite3_bind_int64(removeStmt, 1, db2RowID );
int sqlResult2 = sqlite3_step(removeStmt);
if (sqlResult2 != SQLITE_DONE)
{
if (sqlite3_extended_errcode(db1) == SQLITE_CONSTRAINT_FOREIGNKEY)
{
removed = false;
}
else
{
auto errMsg = formatErrMsg("removeRow failed");
throw pset_errors::storage_error(errMsg);
}
}
data1.erase(db1RowID);
sqlite3_finalize(removeStmt);
}
return removed;
}