列出 SQLite 外键违规 C++

问题描述 投票:0回答:1

我正在为我的程序添加删除数据库中任何行的功能,但该数据库与其他 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本质上不输出到回调?

c++ sqlite foreign-keys
1个回答
0
投票

所以事实证明,不是数据库 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;

}

© www.soinside.com 2019 - 2024. All rights reserved.