如何检测 sqlite3 是否创建了数据库文件?

How do I detect if sqlite3 created a database file?

我正在编写一个使用 sqlite3 数据库文件来存储其数据的程序。如果我用

打开一个数据库文件
sqlite3_open_v2(filename, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)

数据库文件不存在则创建。如何在打开数据库文件之前确定它是否存在? sqlite3 shell 使用这样的代码:

  /* Go ahead and open the database file if it already exists.  If the
  ** file does not exist, delay opening it.  This prevents empty database
  ** files from being created if a user mistypes the database name argument
  ** to the sqlite command-line tool.
  */
  if( access(data.zDbFilename, 0)==0 ){
    open_db(&data, 0);
  }

然而,当数据库文件在 access 调用之后和 open_db 调用之前由另一个进程创建时,此代码存在竞争条件(检查时间与检查时间)使用).

另一个答案(我现在找不到)建议检查 application_iduser_version 字段。如果它们为零,则刚刚创建了一个数据库。我研究了这种方法,发现许多应用程序实际上并不费心在新创建的数据库中设置这些字段,所以这种方法充其量是模糊的,我认为它不能解决我的问题。

有没有一种方法可以在打开数据库之前查明数据库是否存在而不涉及像这样的竞争条件?它也是 acceptable 如果我只能找出数据库文件是否被 sqlite3.

初始化(例如,一个 t运行cated 文件填充了一个 sqlite3 头文件)

有这样一个例程的目的是为了能够找出我是否需要在数据库中创建我需要的所有table。我不想不小心覆盖其他应用程序放置在那里的另一个文件。

示例代码

问题的简化说明可以在以下 bash 脚本中找到。它模拟两个应用程序。它们的工作方式相似:

  1. 创建或打开数据库test.db
  2. 如果数据库之前不存在,创建一个table test 并向其中写入一行。第一个应用程序的值为 1,第二个应用程序的值为 2。
  3. 打印数据库的内容。

代码如下:

#!/bin/sh

# sleeps are inserted to make the race conditions easier to trigger

application() {
    echo Checking if database exists...
    [ ! -f test.db ]
    status=$?
    sleep 2

    if (exit $status)
    then
        echo Database not found, making tables
        sqlite3 test.db "CREATE TABLE IF NOT EXISTS test (a);"
        sleep 2
        echo Writing initial record into database
        sqlite3 test.db "INSERT INTO test VALUES ();"
        sleep 2
    else
        echo Database found, checking if it belongs to me
    fi

    echo Printing content of database
    sqlite3 test.db "SELECT * FROM test;"
}

rm -f test.db
echo First test: app1 and app1 race
application 1 & (sleep 1 ; application 1)

rm -f test.db
echo Second test: app2 and app1 race
application 2 & (sleep 1 ; application 1)

我的目标是确保以下情况永远不会发生:

如果 application 编程正确,每个 运行 只会初始化数据库,如果它之前没有初始化的话。因此,您将看到的唯一输出是仅包含行 1 的数据库或仅包含行 2.

的数据库

无法区分新创建的数据库和之前创建的空数据库。

但是,空数据库是唯一存在此问题的数据库。 可以通过检查 sqlite_master table 是否为空来检测任何其他数据库。在事务中执行此操作和您的 table 创建,并且没有竞争条件。

如果您第一次写入数据库(即实际创建文件时)设置了 application_id,那么您就知道任何具有另一个 ID 的文件都不是您的。 (注册的应用程序 ID 是唯一的。)