带RETURNING的INSERT语句调用sqlite3_reset后数据回滚问题咨询
问题根因
你的错误核心是未将带RETURNING子句的预编译语句执行到完全结束的状态,就提前调用了sqlite3_reset。
- SQLite中
sqlite3_step()返回SQLITE_ROW仅代表当前有结果行可读,不代表整条语句执行完成。只有当sqlite3_step()返回SQLITE_DONE时,才算整条INSERT语句的所有逻辑(包括数据持久化写入)全部完成。 - 你在读取完RETURNING返回的ID后,没有继续调用
sqlite3_step()获取最终的SQLITE_DONE状态,此时直接调用sqlite3_reset()会被SQLite判定为中断当前语句执行,自动回滚这条INSERT操作产生的所有变更,这就是你reset后数据消失的原因,和Windows构建的OMIT参数没有关系。
修复方案
在你读取完返回的自增ID之后,再加一次sqlite3_step()调用确认语句执行完成即可,修改后的循环逻辑如下:
for (int i = 0; i < dataset_length; i++) { sqlite3_bind_int(pStmt,1,dataset[i].value1); sqlite3_bind_int(pStmt,2,dataset[i].value2); int step_ret = sqlite3_step(pStmt); switch (step_ret) { case SQLITE_ROW: { // 插入成功,获取返回的自增ID dataset[i].id = sqlite3_column_int(pStmt,0); // 新增:继续调用step直到返回SQLITE_DONE,确认语句执行完成 step_ret = sqlite3_step(pStmt); if (step_ret != SQLITE_DONE) { // 非预期结果,返回错误 return false; } } break; case SQLITE_DONE: { // 无返回结果,返回错误 return false; } default: { // 其他错误,返回错误 return false; } } // 此时再调用reset就不会触发回滚了 sqlite3_reset(pStmt); // 可选:如果要复用语句可新增这行清理之前的绑定参数 // sqlite3_clear_bindings(pStmt); }
额外说明
- 去掉
sqlite3_reset()能正常工作属于巧合:因为循环下次调用sqlite3_step()的时候,SQLite会自动处理上一次未完成的语句状态,刚好把上次的执行推进到SQLITE_DONE,所以数据不会被回滚,但这不符合SQLite预编译语句的标准使用规范,不建议依赖这种隐式行为。 - 该行为是SQLite的标准设计,所有平台版本逻辑一致,不需要怀疑Windows构建的参数配置问题。
内容的提问来源于stack exchange,提问作者Devilish Spirits
相关产品推荐
相关产品推荐

