sqlite3结构体与实际打开的文件描述符之间的关系

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

我们了解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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值