我们了解sqlite3数据库是一个文件格式的数据库,那显然它必然至少会持有一个打开的文件描述符,本文简单介绍 sqlite3结构体本身与这个文件描述符之间的关系,以及调用sqlite3_open最终打开这个文件描述符的流程。
先上结论,在sqlite3的数据结构中,与文件描述符的定义关系大致如下流程所示:
struct sqlite3——>(struct Db)aDb——>(struct Btree *)pBt——>
(struct BtShared *)pBt——>(struct Pager *)pPager ——>
(struct sqlite3_file)fd = struct unixFile ——>(int)h;
for reference
struct sqlite3 line 16840
aDb代表这个数据库句柄上打开的数据库文件,aDb[0]为主数据库,可以包含多个,比如内存数据库,以及attach上来的数据库。
struct Btree .definition in line 66184
sqlite3_file的定义根据VFS的实现不同而不同。在默认的unix中,是如下定义
/*
** The unixFile structure is subclass of sqlite3_file specific to the unix
** VFS implementations.
*/
typedef struct unixFile unixFile;
打开数据库文件流程
下面分析一下打开数据库文件的流程
在最基本的使用方式中,我们通过如下的方式打开一个sqlite3数据库的句柄。
sqlite3* db;
int iRet = sqlite3_open("test.db",&db);
整体的调用链如下,中间还包括了其他一些填充的流程。
sqlite3_open——》openDatabase——》sqlite3BtreeOpen——》sqlite3PagerOpen——》
sqlite3OsOpen——》 pVfs->xOpen ——》unixOpen ——》robust_open
在我这里使用的 3.39.4 版本中,其实际调用了openDatabase函数。
/*
** Open a new database handle.
*/
SQLITE_API int sqlite3_open(
const char *zFilename,
sqlite3 **ppDb
){
return openDatabase(zFilename, ppDb,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
}
openDatabase函数中执行了数据库handle的分配和初始化,这里注意下openDatabase携带了4个参数,除了需要打开的数据库路径和需要返回出来的sqlite3的Handle,另外两个参数分别是打开的flag和应使用的操作系统接口对应的操作系统名称。这两个参数,在一个不同于sqlite3_open的接口 sqlite3_open_v2接口中是允许配置的,该接口的语义可以参考如下网址。https://www.sqlite.net.cn/c3ref/open.html
按照默认的不配置的老接口,会使用默认的sqlite3_vfs对象。该对象实际是sqlite3结构体的第一个参数,也是我们后面将会用到的很重要的一个成员。
static int openDatabase(
const char *zFilename, /* Database filename UTF-8 encoded */
sqlite3 **ppDb, /* OUT: Returned database handle */
unsigned int flags, /* Operational flags */
const char *zVfs /* Name of the VFS to use */
){
sqlite3 *db; /* Store allocated handle here */
...
rc = sqlite3_initialize();
if( rc ) return rc;
...
db = sqlite3MallocZero( sizeof(sqlite3) );
...
/*关键的路径参数被传递给了 sqlite3ParseUri,同时将给db的pVfs参数,也就是 sqlite3_vfs 对象进行了赋值,下文提供了sqlite3ParseUri的定义*/
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
...
/* Open the backend database driver */
rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, 0,
flags | SQLITE_OPEN_MAIN_DB);
...
}
sqlite3ParseUri完成了比较关键的解析,他接受了完整的路径入参,默认该路径为uri的形式,然后提取出路径赋值到 zOpen中,并根据zVfs的参数,给VFS赋值。因为这里没有赋值,所以默认返回vfsList中的第一个vfs。这个vfsList在sqlite3_os_init中被初始化和定义。sqlite3_os_init为内部初始化调用,在sqlite3_initialize中被初始化。是openDatabase函数的第一步。
SQLITE_PRIVATE int sqlite3ParseUri(
const char *zDefaultVfs, /* VFS to use if no "vfs=xxx" query option */
const char *zUri, /* Nul-terminated URI to parse */
unsigned int *pFlags, /* IN/OUT: SQLITE_OPEN_XXX flags */
sqlite3_vfs **ppVfs, /* OUT: VFS to use */
char **pzFile, /* OUT: Filename component of URI */
char **pzErrMsg /* OUT: Error message (if rc!=SQLITE_OK) */
){
}
然后根据上述流程在 openDatabase中,接力棒给到了sqlite3BtreeOpen,该函数执行了实际数据库流程的打开工作,这里比较复杂的流程是需要把打开的数据库实例对应的 BtShared 关联到BtTree,再关联到sqlite3的aDb数组。
真正执行打开的关键操作是在sqlite3PagerOpen。其会返回BtShared持有的pPager,BtShared代表一个数据库文件的实例。
line 69301
/*
** Open a database file.
**...
*/
SQLITE_PRIVATE int sqlite3BtreeOpen(
sqlite3_vfs *pVfs, /* VFS to use for this b-tree */
const char *zFilename, /* Name of the file containing the BTree database */
sqlite3 *db, /* Associated database handle */
Btree **ppBtree, /* Pointer to new Btree object written here */
int flags, /* Options */
int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */
){
...
rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
sizeof(MemPage), flags, vfsFlags, pageReinit);
...
sqlite3PagerOpen函数打开了一个Pager对象,并将其赋值给BtShared,根据Pager的定义,每一个打开的文件,都被一个独立的Pager实例结构体管理 。其打开调用了 sqlite3OsOpen,而sqlite3OsOpen 又调用了 pVfs->xOpen ,即我们之前提到的 sqlite3_vfs 中的xOpen的handle。
/*
** Each open file is managed by a separate instance of the "Pager" structure.
*/
typedef struct Pager Pager;
...
line 58590
SQLITE_PRIVATE int sqlite3PagerOpen(
sqlite3_vfs *pVfs, /* The virtual file system to use */
Pager **ppPager, /* OUT: Return th

本文详细介绍了SQLite3数据库从调用sqlite3_open开始到最终打开文件描述符的过程。包括数据库句柄分配、初始化、文件描述符关联等步骤。

1315

被折叠的 条评论
为什么被折叠?



