sqlite3 and R-tree

本文介绍如何在Windows环境下通过编译选项启用SQLite3数据库的R-tree特性。具体操作是在编译SQLite3时添加宏定义/DSQLITE_ENABLE_RTREE=1。

enable R-tree feature by adding following clause when compiling sqlite3 in win32:

 

/D "SQLITE_ENABLE_RTREE=1"

 

 

static int openDatabase( const char *zFilename, /* Database filename UTF-8 encoded */ sqlite3 **ppDb, /* OUT: Returned database handle */ sqlite3_area_info *area,/* db area info */ int wal_cnt, /* db max wal cnt */ unsigned int flags, /* Operational flags */ const char *zVfs /* Name of the VFS to use */ ){ sqlite3 *db; /* Store allocated handle here */ int rc; /* Return code */ int isThreadsafe; /* True for threadsafe connections */ char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ #ifdef SQLITE_ENABLE_API_ARMOR if( ppDb==0 ) return SQLITE_MISUSE_BKPT; #endif *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif /* Only allow sensible combinations of bits in the flags argument. ** Throw an error if any non-sense combination is used. If we ** do not block illegal combinations here, it could trigger ** assert() statements in deeper layers. Sensible combinations ** are: ** ** 1: SQLITE_OPEN_READONLY ** 2: SQLITE_OPEN_READWRITE ** 6: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE */ assert( SQLITE_OPEN_READONLY == 0x01 ); assert( SQLITE_OPEN_READWRITE == 0x02 ); assert( SQLITE_OPEN_CREATE == 0x04 ); testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ if( ((1<<(flags&7)) & 0x46)==0 ){ return SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */ } if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; }else{ isThreadsafe = sqlite3GlobalConfig.bFullMutex; } if( flags & SQLITE_OPEN_PRIVATECACHE ){ flags &= ~SQLITE_OPEN_SHAREDCACHE; }else if( sqlite3GlobalConfig.sharedCacheEnabled ){ flags |= SQLITE_OPEN_SHAREDCACHE; } /* Remove harmful bits from the flags parameter ** ** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were ** dealt with in the previous code block. Besides these, the only ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask ** off all other flags. */ flags &= ~( SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB | SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_WAL ); /* Allocate the sqlite data structure */ db = sqlite3MallocZero( sizeof(sqlite3) ); if( db==0 ) goto opendb_out; if( isThreadsafe ){ db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); if( db->mutex==0 ){ sqlite3_free(db); db = 0; goto opendb_out; } } sqlite3_mutex_enter(db->mutex); db->errMask = 0xff; db->nDb = 2; db->magic = SQLITE_MAGIC_BUSY; db->aDb = db->aDbStatic; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS; db->autoCommit = 1; db->nextAutovac = -1; db->szMmap = sqlite3GlobalConfig.szMmap; db->nextPagesize = 0; db->nMaxSorterMmap = 0x7FFFFFFF; db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill #if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX | SQLITE_AutoIndex #endif #if SQLITE_DEFAULT_CKPTFULLFSYNC | SQLITE_CkptFullFSync #endif #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif #ifdef SQLITE_ENABLE_LOAD_EXTENSION | SQLITE_LoadExtension #endif #if SQLITE_DEFAULT_RECURSIVE_TRIGGERS | SQLITE_RecTriggers #endif #if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS | SQLITE_ForeignKeys #endif #if defined(SQLITE_REVERSE_UNORDERED_SELECTS) | SQLITE_ReverseOrder #endif #if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) | SQLITE_CellSizeCk #endif #if defined(SQLITE_ENABLE_FTS3_TOKENIZER) | SQLITE_Fts3Tokenizer #endif ; sqlite3HashInit(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3HashInit(&db->aModule); #endif /* Add the default collation sequence BINARY. BINARY works for both UTF-8 ** and UTF-16, so add a version for each to avoid any unnecessary ** conversions. The only error that can occur here is a malloc() failure. ** ** EVIDENCE-OF: R-52786-44878 SQLite defines three built-in collating ** functions: */ createCollation(db, sqlite3StrBINARY, SQLITE_UTF8, 0, binCollFunc, 0); createCollation(db, sqlite3StrBINARY, SQLITE_UTF16BE, 0, binCollFunc, 0); createCollation(db, sqlite3StrBINARY, SQLITE_UTF16LE, 0, binCollFunc, 0); createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0); createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0); if( db->mallocFailed ){ goto opendb_out; } /* EVIDENCE-OF: R-08308-17224 The default collating function for all ** strings is BINARY. */ db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0); assert( db->pDfltColl!=0 ); /* Parse the filename/URI argument. */ db->openFlags = flags; rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg); sqlite3_free(zErrMsg); goto opendb_out; } /* Open the backend database driver */ rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, area, wal_cnt, 0, flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM_BKPT; } sqlite3Error(db, rc); goto opendb_out; } sqlite3BtreeEnter(db->aDb[0].pBt); db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db); sqlite3BtreeLeave(db->aDb[0].pBt); db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); /* The default safety_level for the main database is FULL; for the temp ** database it is OFF. This matches the pager layer defaults. */ db->aDb[0].zDbSName = "main"; db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; db->aDb[1].zDbSName = "temp"; db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; db->magic = SQLITE_MAGIC_OPEN; if( db->mallocFailed ){ goto opendb_out; } /* Register all built-in functions, but do not attempt to read the ** database schema yet. This is delayed until the first time the database ** is accessed. */ sqlite3Error(db, SQLITE_OK); sqlite3RegisterPerConnectionBuiltinFunctions(db); rc = sqlite3_errcode(db);代码太长,先给你一部分,给我详细解释一下
最新发布
09-19
非常好的问题!你提供的这段代码是 **SQLite 源码中 `openDatabase()` 函数的完整实现的一部分**,它是 `sqlite3_open` 和 `sqlite3_open_v2` 的底层核心函数。 我们将从头开始、**逐段详细解析这段 C 语言代码**,包括: - 每一行的作用 - 关键变量和结构体的意义 - 宏与断言的设计意图 - 内存分配、线程安全、VFS 初始化等机制 --- ## 📌 函数签名:入口参数详解 ```c static int openDatabase( const char *zFilename, /* Database filename UTF-8 encoded */ sqlite3 **ppDb, /* OUT: Returned database handle */ sqlite3_area_info *area, /* db area info (custom extension?) */ int wal_cnt, /* db max wal cnt (WAL 配置) */ unsigned int flags, /* Operational flags */ const char *zVfs /* Name of the VFS to use */ ) ``` ### ✅ 参数说明: | 参数 | 含义 | |------|------| | `zFilename` | 数据库文件路径(UTF-8 编码字符串) | | `ppDb` | 输出参数:用于返回新创建的 `sqlite3*` 句柄 | | `area` | 自定义内存区域信息(非标准 SQLite,可能是嵌入式定制版本) | | `wal_cnt` | WAL 文件最大页数限制(用于控制日志大小) | | `flags` | 打开标志位,如 `SQLITE_OPEN_READWRITE \| CREATE` | | `zVfs` | 虚拟文件系统名称(可为 NULL 使用默认) | > ⚠️ 注意:这个版本看起来像是一个 **修改版或嵌入式增强版 SQLite**,因为原生 SQLite 并没有 `area` 和 `wal_cnt` 这两个参数。但我们仍按逻辑理解其行为。 --- ## 🔹 第一部分:变量声明与 API 防护 ```c { sqlite3 *db; /* Store allocated handle here */ int rc; /* Return code */ int isThreadsafe; /* True for threadsafe connections */ char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */ char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ #ifdef SQLITE_ENABLE_API_ARMOR if( ppDb==0 ) return SQLITE_MISUSE_BKPT; #endif *ppDb = 0; ``` ### 🧩 解释: #### 1. 局部变量定义: - `db`:指向即将创建的数据库连接对象 - `rc`:保存操作结果码(SQLITE_OK / 错误码) - `isThreadsafe`:是否启用线程互斥锁 - `zOpen`:解析后的文件名(可能经过 URI 处理) - `zErrMsg`:存储错误消息指针 #### 2. `SQLITE_ENABLE_API_ARMOR` 安全检查 ```c #ifdef SQLITE_ENABLE_API_ARMOR if( ppDb==0 ) return SQLITE_MISUSE_BKPT; #endif ``` - 这是一个“API 护甲”机制,防止调用者传入空指针。 - 如果启用了此选项(调试/安全模式),则检查 `ppDb` 是否为空。 - 若为空,直接返回 `SQLITE_MISUSE` 错误,并触发断点(`BKPT` 表示可中断)。 📌 类似于防御性编程中的 null-check。 #### 3. 初始化输出参数 ```c *ppDb = 0; ``` 👉 确保即使失败,`*ppDb` 也是 `NULL`,避免野指针。 --- ## 🔹 第二部分:全局初始化(sqlite3_initialize) ```c #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif ``` ### 🧩 解释: - `sqlite3_initialize()` 是 SQLite 的一次性全局初始化函数。 - 它会: - 初始化内存分配器(malloc/free 替代方案) - 注册默认 VFS(POSIX/Win32) - 初始化随机数生成器 - 设置全局配置项 - `SQLITE_OMIT_AUTOINIT` 是编译选项:如果定义了它,则必须手动调用 `sqlite3_initialize()`。 ✅ 所以这行确保:**SQLite 核心系统已准备就绪** --- ## 🔹 第三部分:校验 flags 合法性 ```c /* Only allow sensible combinations of bits in the flags argument... */ assert( SQLITE_OPEN_READONLY == 0x01 ); assert( SQLITE_OPEN_READWRITE == 0x02 ); assert( SQLITE_OPEN_CREATE == 0x04 ); testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ if( ((1<<(flags&7)) & 0x46)==0 ){ return SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */ } ``` ### 🧩 目的:防止非法 flag 组合 SQLite 只接受以下合法组合: | 组合 | 对应值 | |------|--------| | `SQLITE_OPEN_READONLY` | `0x01` → `(1<<1) = 0x02` | | `SQLITE_OPEN_READWRITE` | `0x02` → `(1<<2) = 0x04` | | `SQLITE_OPEN_READWRITE | CREATE` | `0x06` → `(1<<6)=0x40` | > 为什么是 `(1 << (flags & 7))`? 因为只关心低三位(bit 0~2),对应三个基本标志。 然后用掩码 `0x46`(即二进制 `1000110`)来判断是否匹配任一合法模式: ```text 合法值: 0x02 → READONLY 0x04 → READWRITE 0x40 → READWRITE+CREATE 掩码 0x46 包含这些位 → 只有合法组合才能通过 & ``` ❌ 如果都不符合 → 返回 `SQLITE_MISUSE`(使用错误) 📌 `testcase(...)` 是测试覆盖率标记,告诉测试工具这些分支应该被覆盖。 --- ## 🔹 第四部分:确定线程安全性(isThreadsafe) ```c if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; }else{ isThreadsafe = sqlite3GlobalConfig.bFullMutex; } ``` ### 🧩 解释: 决定是否为这个数据库连接启用 **递归互斥锁(mutex)** | 条件 | 结果 | |------|------| | 全局禁用 mutex(`bCoreMutex==0`) | ❌ 不加锁 | | 用户指定 `NOMUTEX` 标志 | ❌ 不加锁 | | 用户指定 `FULLMUTEX` | ✅ 加锁 | | 否则 | 使用全局默认设置 `bFullMutex` | 📌 这是为了支持: - 单线程模式(性能更高) - 多线程并发访问(更安全) --- ## 🔹 第五部分:共享缓存设置 ```c if( flags & SQLITE_OPEN_PRIVATECACHE ){ flags &= ~SQLITE_OPEN_SHAREDCACHE; }else if( sqlite3GlobalConfig.sharedCacheEnabled ){ flags |= SQLITE_OPEN_SHAREDCACHE; } ``` ### 🧩 功能:控制表缓存(schema cache)是否跨连接共享 | 模式 | 说明 | |------|------| | `PRIVATECACHE` | 此连接独占缓存(不与其他连接共享) | | `SHAREDCACHE` | 多个连接共享同一份 schema 缓存(常用于 WAL 模式) | 👉 如果用户没明确指定 PRIVATECACHE,且全局开启了共享缓存,则自动加上 `SHAREDCACHE` 标志。 --- ## 🔹 第六部分:清理非法标志位 ```c flags &= ~( SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB | SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_WAL ); ``` ### 🧩 解释: 虽然前面已经处理了 `NOMUTEX/FULLMUTEX`,但它们仍然存在于 `flags` 中。 现在要清除所有 **仅用于内部用途或已被处理过的标志位**,只留下: - `READONLY` - `READWRITE` - `CREATE` - `SHAREDCACHE` - `PRIVATECACHE` 这样传递给后续模块时不会引起误解。 > 💡 提示:真正的 `MAIN_DB`、`TEMP_DB` 等会在调用 `sqlite3BtreeOpen` 时重新添加。 --- ## 🔹 第七部分:分配 sqlite3 结构体 ```c db = sqlite3MallocZero( sizeof(sqlite3) ); if( db==0 ) goto opendb_out; ``` - `sqlite3MallocZero()`:带零初始化的内存分配函数 - 分配的是整个 `sqlite3` 结构体(包含连接状态、SQL 编译器、事务管理等) - 失败则跳转到 `opendb_out` 清理并退出 📌 `goto opendb_out;` 是 SQLite 源码中常见的错误处理模式(类似异常捕获) --- ## 🔹 第八部分:初始化互斥锁 ```c if( isThreadsafe ){ db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); if( db->mutex==0 ){ sqlite3_free(db); db = 0; goto opendb_out; } } ``` - 如果需要线程安全,分配一个递归互斥锁(允许同一线程多次进入) - 失败则释放刚分配的 `db`,跳转退出 --- ## 🔹 第九部分:初始化 db 基本字段 ```c sqlite3_mutex_enter(db->mutex); db->errMask = 0xff; db->nDb = 2; db->magic = SQLITE_MAGIC_BUSY; db->aDb = db->aDbStatic; ``` ### 🧩 字段解释: | 字段 | 含义 | |------|------| | `errMask` | 错误码屏蔽位(调试用) | | `nDb = 2` | 默认有两个数据库:`main` 和 `temp` | | `magic = SQLITE_MAGIC_BUSY` | 初始魔数,表示正在初始化中(防重入) | | `aDb = aDbStatic` | 指向内嵌数组(避免额外 malloc)<br>`aDb[0] = main`, `aDb[1] = temp` | > `SQLITE_MAGIC_BUSY` 是中间状态,最后会变成 `SQLITE_MAGIC_OPEN` --- ## 🔹 第十部分:设置资源限制(limits) ```c assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS; ``` - `aLimit` 数组保存各种运行时限制,例如: - `SQLITE_LIMIT_SQL_LENGTH` - `SQLITE_LIMIT_COLUMN` - `SQLITE_LIMIT_EXPR_DEPTH` - 从全局硬编码值 `aHardLimit` 拷贝过来作为初始值 - 特殊设置 worker threads 数量 --- ## 🔹 第十一部分:初始化默认行为标志 ```c db->autoCommit = 1; // 默认自动提交(即非事务模式) db->nextAutovac = -1; // autovacuum 下次触发时机 db->szMmap = sqlite3GlobalConfig.szMmap; // 内存映射大小 db->nextPagesize = 0; db->nMaxSorterMmap = 0x7FFFFFFF; ``` 这些是数据库连接级别的运行时配置。 --- ## 🔹 第十二部分:设置默认 flags(编译期特性开关) ```c db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill #if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX | SQLITE_AutoIndex #endif #if SQLITE_DEFAULT_CKPTFULLFSYNC | SQLITE_CkptFullFSync #endif ... ; ``` 👉 这些都是根据 **编译宏** 决定是否开启的功能: | Flag | 含义 | |------|------| | `ShortColNames` | 使用短列名(SELECT a+b → 名为 `a+b`) | | `EnableTrigger` | 允许触发器 | | `AutoIndex` | 允许自动创建索引优化查询 | | `ForeignKeys` | 是否默认启用外键约束 | | `LoadExtension` | 是否允许加载动态库扩展(如 JSON1) | 📌 所有这些都可以在运行时通过 PRAGMA 修改。 --- ## 🔹 第十三部分:初始化哈希表(collations & modules) ```c sqlite3HashInit(&db->aCollSeq); // 排序规则哈希表 #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3HashInit(&db->aModule); // 虚拟表模块哈希表 #endif ``` - `aCollSeq`:存储 COLLATE 规则(如 BINARY、NOCASE、RTRIM) - `aModule`:虚拟表(Virtual Table)注册表(FTS5、RTREE 等) --- ## 🔹 第十四部分:注册内置排序函数(Collating Sequences) ```c createCollation(db, sqlite3StrBINARY, SQLITE_UTF8, 0, binCollFunc, 0); createCollation(db, sqlite3StrBINARY, SQLITE_UTF16BE, 0, binCollFunc, 0); createCollation(db, sqlite3StrBINARY, SQLITE_UTF16LE, 0, binCollFunc, 0); createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0); createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0); if( db->mallocFailed ){ goto opendb_out; } db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0); assert( db->pDfltColl!=0 ); ``` ### 🧩 解释: SQLite 内置三种排序方式: | 名称 | 行为 | |------|------| | `BINARY` | 按字节比较(默认) | | `NOCASE` | 忽略大小写 ASCII 比较 | | `RTRIM` | 忽略末尾空格后比较 | 👉 `createCollation()` 将它们注册到 `aCollSeq` 哈希表中。 最后设置默认排序为 `BINARY`。 --- ## 🔹 第十五部分:解析 URI 文件名(重要!支持特殊语法) ```c db->openFlags = flags; rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg); if( rc!=SQLITE_OK ){ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db); sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg); sqlite3_free(zErrMsg); goto opendb_out; } ``` ### 🧩 `sqlite3ParseUri()` 做什么? 它解析形如: ``` file:mydb.db?mode=rw&cache=shared&psow=1 ``` 这样的 URI 格式文件名! 功能包括: - 提取 VFS 名称 - 解析附加参数(如 `nolock=1`, `immutable=1`) - 设置最终打开模式 - 输出纯净文件路径到 `zOpen` 📌 支持的关键参数示例: | 参数 | 作用 | |------|------| | `mode=ro/rw/rwc` | 只读 / 读写 / 读写+创建 | | `cache=shared/private` | 是否共享缓存 | | `immutable=1` | 声明数据库不可变(跳过完整性检查) | | `nolock=1` | 禁用文件锁(适用于单进程) | --- ## 🔹 第十六部分:打开底层 B-tree 存储引擎 ```c rc = sqlite3BtreeOpen(db->pVfs, zOpen, db, &db->aDb[0].pBt, area, wal_cnt, 0, flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM_BKPT; } sqlite3Error(db, rc); goto opendb_out; } ``` ### 🧩 关键调用:`sqlite3BtreeOpen` 这是真正打开数据库文件的地方! - 使用指定的 VFS 打开文件 - 创建 B-tree 引擎实例(每个 `.db` 文件对应一棵 B+ 树) - 将句柄存入 `db->aDb[0].pBt`(即 `main` 数据库) - 如果是新文件 → 初始化页结构(512 字节 header) 📌 此时才真正进行磁盘 I/O。 --- ## 🔹 第十七部分:获取 Schema(元数据) ```c sqlite3BtreeEnter(db->aDb[0].pBt); db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db); sqlite3BtreeLeave(db->aDb[0].pBt); db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); ``` - `sqlite3SchemaGet()`:从 `sqlite_master` 表加载所有表、索引、视图定义 - `ENC(db)`:设置文本编码(UTF-8/16) - `aDb[1]`(temp)的 schema 是独立的,用于临时表 --- ## 🔹 第十八部分:设置默认同步级别(safety_level) ```c db->aDb[0].zDbSName = "main"; db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; db->aDb[1].zDbSName = "temp"; db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF; ``` - `main` 数据库默认使用 `FULL` 同步(最安全) - `temp` 数据库存储在内存或临时文件,可以关闭同步以提高速度 --- ## 🔹 第十九部分:完成初始化,设置 magic number ```c db->magic = SQLITE_MAGIC_OPEN; if( db->mallocFailed ){ goto opendb_out; } ``` - 最后一步:将 `magic` 从 `BUSY` 改为 `OPEN` - 表示数据库连接现已完全可用 - 若中途有内存分配失败,则跳转清理 --- ## 🔹 第二十部分:注册内置函数(尚未读 schema) ```c sqlite3Error(db, SQLITE_OK); sqlite3RegisterPerConnectionBuiltinFunctions(db); rc = sqlite3_errcode(db); ``` - 注册所有内置 SQL 函数: - `abs()`, `length()`, `upper()`, `lower()`, `coalesce()` 等 - 但此时还没有读取用户定义的函数或触发器 > schema 的真正加载通常延迟到第一次执行 SQL 时。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值