我正在编写一个使用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_id
和user_version
字段。如果它们为零,则只创建一个数据库。我研究了这种方法,发现许多应用程序实际上并不打算在新创建的数据库中设置这些字段,所以这种方法充其量是模糊的,我不认为它解决了我的问题。
有没有一种方法可以确定数据库在打开之前是否存在,而不涉及这样的竞争条件?如果我只能通过sqlite3查明数据库文件是否已初始化(例如,截断文件中填充了sqlite3标题),也是可以接受的。
拥有这样一个例程的目的是能够找出我是否需要在数据库中创建我需要的所有表。我不想意外地覆盖另一个应用程序放置在那里的另一个文件。
可以在以下bash脚本中找到该问题的简化说明。它模拟两个应用程序。他们都以类似的方式工作:
test.db
test
并在其中写入一行。第一个应用程序的值为1,第二个应用程序的值为2。以下是代码:
#!/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
的数据库。
答案 0 :(得分:2)
无法区分新创建的数据库与之前创建的空数据库。
但是,空数据库是唯一存在此问题的数据库。
通过检查sqlite_master
表是否为空,可以检测到任何其他数据库。在事务中执行此操作并创建表,并且没有竞争条件。
如果您第一次写入数据库(实际创建文件时)是设置application_id,那么您知道任何具有其他ID的文件不是您的。 (已注册的申请ID是唯一的。)