我正在为我的程序添加删除数据库中任何行的功能,但该数据库与其他 6 个数据库共享密钥。我确保外键已启用:
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 中删除它们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;
}