SQLite and BOOST_SCOPE_EXIT_ALL



例如,为什么SQLite C接口在使用BOOST_SCOPE_EXIT_ALL时需要通过引用传递指针?

假设我有以下代码:

#include <boost/scope_exit.hpp>
#include <sqlite3.h>
#include <cstdlib>
#include <iostream>
int main()
{
  sqlite3* db;
  BOOST_SCOPE_EXIT_ALL(db)
  {
    if (sqlite3_close(db) != SQLITE_OK)
    {
      std::cerr << sqlite3_errmsg(db) << 'n';
    }
  };
  if (sqlite3_open(":memory:", &db) != SQLITE_OK)
  {
    std::cerr << sqlite3_errmsg(db) << 'n';
    return EXIT_FAILURE;
  }
  sqlite3_stmt* prepared_stmt = NULL;
  BOOST_SCOPE_EXIT_ALL(db, prepared_stmt)
  {
    if (sqlite3_finalize(prepared_stmt) != SQLITE_OK)
    {
      std::cerr << sqlite3_errmsg(db) << 'n';
    }
  };
  if (sqlite3_prepare_v2(db, "CREATE TABLE IF NOT EXISTS foo(bar TEXT, baz TEXT)", -1, &prepared_stmt, 0) != SQLITE_OK)
  {
    std::cerr << sqlite3_errmsg(db) << 'n';
    return EXIT_FAILURE;
  }
  if (sqlite3_step(prepared_stmt) != SQLITE_DONE)
  {
    std::cerr << sqlite3_errmsg(db) << 'n';
    return EXIT_FAILURE;
  }
}

它在尝试调用函数时崩溃sqlite3_close并且不会向 stderr 打印任何内容。但是,如果我改变

BOOST_SCOPE_EXIT_ALL(db)

BOOST_SCOPE_EXIT_ALL(&db)

它指出

由于未完成的语句或未完成的备份而无法关闭

尝试调用sqlite3_close函数时。

如果我也通过引用传递prepared_stmt,它会按预期工作:

BOOST_SCOPE_EXIT_ALL(db, &prepared_stmt)

为什么?我认为SQLite库只抱怨dbprepared_stmt指向的地址。

提前谢谢。

main(( 中的第一行

sqlite3* db;

可能是 sqlite3_close(( 崩溃的原因。 编译器可能正在重用该声明指针的内存位置,并且不一定使用空位置或将其设为空。 因此,对 sqlite3_close(( 的调用正在接收虚假指针。

https://www.sqlite.org/c3ref/close.html 的文档指出:

sqlite3_close(C( 和 sqlite3_close_v2(C( 的 C 参数必须是 从以下位置获取的 NULL 指针或 sqlite3 对象指针 sqlite3_open((、sqlite3_open16(( 或 sqlite3_open_v2((,而不是 以前关闭。调用 sqlite3_close(( 或 sqlite3_close_v2(( NULL 指针参数是无害的无操作。

因此,根据上述语句,以下代码应删除崩溃:

 sqlite3* db = nullptr;

当然,如果你只是完全删除了这个不必要的代码块,所有这些都是没有意义的:

BOOST_SCOPE_EXIT_ALL(db)
{
  if (sqlite3_close(db) != SQLITE_OK)
  {
    std::cerr << sqlite3_errmsg(db) << 'n';
  }
};

没有理由关闭从未打开的数据库。


编辑:这是我尝试的函数版本。 我现在真的没时间了,所以我无法测试它,但我认为它会起作用。 我删除了提升宏,因为我不熟悉它们,但您可以重新添加它们。 希望这有帮助!

int main()
{
sqlite3* db = nullptr;
if (sqlite3_open(":memory:", &db) != SQLITE_OK)
    goto ERROR_OCCURRED;
sqlite3_stmt* prepared_stmt = nullptr;
if (sqlite3_finalize(prepared_stmt) != SQLITE_OK)
    std::cerr << sqlite3_errmsg(db) << 'n';
if (sqlite3_prepare_v2(db, "CREATE TABLE IF NOT EXISTS foo(bar TEXT, baz TEXT)", -1, &prepared_stmt, 0) != SQLITE_OK)
    goto ERROR_OCCURRED;
if (sqlite3_step(prepared_stmt) != SQLITE_DONE)
    goto ERROR_OCCURRED;
if (sqlite3_close(db) != SQLITE_OK)
{   
     std::cerr << sqlite3_errmsg(db) << 'n';
     sqlite3_close(db);
     return EXIT_FAILURE;
}
return 0;
ERROR_OCCURRED:
    std::cerr << sqlite3_errmsg(db) << 'n';
    sqlite3_close(db);
    return EXIT_FAILURE;
}

不幸的是,我没有时间检查您对 sqlite3_finalize(( 的调用,但我也认为该函数可能还为时过早。 我认为没有理由最终确定空指针。

最新更新