我使用的是SQLITE 3.8.7,并尝试在语句未通过某个约束(如违反外键约束)或sqlite3_step语句返回SQLITE_MISUSE时创建一个自定义异常处理类。
在析构函数中,我执行1)回滚; 2)清理; 3)记录异常。
~SQLException()
{
try
{
_db->Execute("ROLLBACK");
try
{
_db->ResetStatement();
}
catch (sql_exception anotherException)
{
_db->ResetStatement();
}
LOG_ERROR("sql_exception: %d %s\n", _e.code, _e.message.c_str());
}
catch (sql_execption ex)
{
LOG_ERROR("sql_exception: %d %s\n", ex.code, ex.message.c_str());
}
Execute()只是sqlite3_exec的 Package 器,而ResetStatement()是sqlite3_reset()的 Package 器。
void DatabaseConnection::Execute(const char* text)
{
ASSERT(_handle);
auto const result = sqlite3_exec(_handle.get(), text, nullptr, nullptr, nullptr);
if (SQLITE_OK != result)
{
throw sql_exception(result, sqlite3_errmsg(_handle.get()));
}
}
void DatabaseConnection::ResetStatement()
{
auto const result = sqlite3_reset(_stmt.get());
if (SQLITE_OK != result)
{
throw sql_exception(result, sqlite3_errmsg(sqlite3_db_handle(_stmt.get())));
}
}
我写了一个单元测试,触发了一个外键约束冲突(这触发了这个清理代码),然后尝试执行额外的插入,就像什么也没发生一样。回滚工作正常,但是第一次重置抛出了同样的外键约束冲突异常。只有当我第二次调用ResetStatement()时,事情才被清理干净,我才能继续执行额外的插入等等。
- 为什么需要调用两次reset语句?**
谢谢。
UPDATE:下面是一个简单的示例,它会导致两个ResetStatements()都被命中...
Table 1: Teams
TeamID INTEGER NOT NULL,
Name TEXT NOT NULL,
PRIMARY KEY(TeamID)
Table 2: Players
PlayerID INTEGER NOT NULL,
Name TEXT NOT NULL,
TeamID INTEGER NOT NULL,
PRIMARY KEY(PlayerID),
FOREIGN KEY(TeamID) REFERENCES Teams(TeamID)
try
{
INSERT INTO Players VALUES(1, "Joe Montana", 1) -->causes expected Foreign Key constraint violation and you cannot proceed with another insert until ResetStatement() is called twice.
}
catch (...)
{
SQLException();
}
到目前为止,这似乎只发生在外键约束冲突。我抛出的其他异常似乎只在一次调用后就被正确重置。
这是原始数据库代码。注解行保留了第一次传递时的外键约束冲突(rc == 19),但在第二次传递时仍存在(rc == 0)。
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){
int rc;
if( pStmt==0 ){
rc = SQLITE_OK;
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3_mutex_enter(v->db->mutex);
rc = sqlite3VdbeReset(v); //first pass rc = 19, second pass rc = 0
sqlite3VdbeRewind(v);
assert( (rc & (v->db->errMask))==rc );
rc = sqlite3ApiExit(v->db, rc);
sqlite3_mutex_leave(v->db->mutex);
}
return rc;
}
在SQLite的最新版本3.8.11.1中存在相同的行为。
1条答案
按热度按时间r6l8ljro1#
所描述的
sqlite3_reset
的行为现在是documented:如果最近一次为预准备语句S调用sqlite3_step(S)时返回了SQLITE_ROW或SQLITE_DONE,或者以前从未在S上调用过sqlite3_step(S),则sqlite3_reset(S)返回SQLITE_OK。
如果最近一次为预准备语句S调用sqlite3_step(S)时指示错误,则sqlite3_reset(S)返回相应的错误代码。
这可能是
sqlite3_step
的初始goofy interface的遗留问题,它只返回一般的SQLITE_ERROR代码,并且需要调用sqlite3_reset
或sqlite3_finalize
才能找到特定的错误代码,这更好地描述了错误。从那时起,接口已经更新,允许sqlite3_step
直接返回特定的错误代码。但是sqlite3_reset
仍然以旧的方式工作。