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

我正在编写一个程序,它使用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字段。 如果它们为零,则只创建一个数据库。 我研究了这种方法,发现许多应用程序实际上并不打算在新创建的数据库中设置这些字段,因此这种方法充其量是模糊的,我不认为它解决了我的问题。

有没有一种方法可以确定数据库在打开之前是否存在,而不涉及这样的竞争条件? 如果我只能通过sqlite3查明数据库文件是否已初始化(如在sqlite3标题中填充了截断文件),也是可以接受的。

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

示例代码

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

  1. 创建或打开数据库test.db
  2. 如果之前不存在数据库,则创建一个表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 ($1);" 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表是否为空来检测任何其他数据库。 在事务中执行此操作并创建表,并且没有竞争条件。

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