API
API由两部分组成: 核心API(core API) 和扩展API(extension API)
核心API的函数实现基本的数据库操作:连接数据库,处理sql,遍历结果集。它也包括一些实用函数,比如字符串转换,操作控制,调试和错误处理。
扩展API通过创建你自定义的sql函数去扩展sqlite。
2.2.1 主要的数据结构
sqlite由很多部分组成-parser,tokenize,virtual machine等等。但是从程序员的角度,最需要知道的是: connection,statements,B-tree和pager。图2.2展示了它们之间的关系,它告诉我们在编程需要知道的三个主要方面:API,事务(Transaction)和锁(Locks)。从技术上来说,B-tree和pager不是API的一部分。但是它们却在事务和锁上起着关键作用。
Connection和statement是执行sql命令涉及的两个主要数据结构,几乎所有通过API进行的操作都要用到它们。一个连接(Connection)代表在一个独立的事务环境下的一个单一连接。在C API中,他们分别由sqlite3和sqlite3_stmt直接进行处理。每一个statement都和一个connection关联,它通常表示一个编译过的sql语句,在内部,它以VDBE字节码表示。Statement包括执行一个命令所需要一切,包括保存VDBE程序执行状态所需的资源,指向硬盘记录的B-树游标,以及参数等等。
图2.2 sqlite C API对象模型
一个connection可以有多个database对象—一个主要的数据库以及附加的数据库,每一个数据库对象有一个B-tree对象,一个B-tree有一个pager对象。
一个connection的结构一般如下所示:
struct ShellState {
sqlite3 *db; /* The database */
int echoOn; /* True to echo input commands */
int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach sql stmt */
int statsOn; /* True to display memory stats before each finalize */
int scanstatsOn; /* True to display scan stats before each finalize */
int backslashOn; /* Resolve C-style \x escapes in sql input text */
int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */
int nErr; /* Number of errors seen */
int mode; /* An output mode setting */
int writableSchema; /* True if PRAGMA writable_schema=ON */
int showHeader; /* True to show column names in List or Column mode */
unsigned shellFlgs; /* VarIoUs flags */
char *zDestTable; /* Name of destination table when MODE_Insert */
char colSeparator[20]; /* Column separator character for several modes */
char rowSeparator[20]; /* Row separator character for MODE_Ascii */
int colWidth[100]; /* Requested width of each column when in column mode*/
int actualWidth[100]; /* Actual width of each column */
char nullValue[20]; /* The text to print when a NULL comes back from ** the database */
SavedModeInfo normalMode;/* Holds the mode just before .explain ON */
char outfile[FILENAME_MAX]; /* Filename for *out */
const char *zDbFilename; /* name of the database file */
char *zFreeOnClose; /* Filename to free when closing */
const char *zVfs; /* Name of VFS to use */
sqlite3_stmt *pStmt; /* Current statement if any. */
FILE *pLog; /* Write log output here */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
};
Statement最终都是通过connection的B-tree和pager从数据库读或者写数据,通过B-tree的游标(cursor)遍历存储在页面(page)中的记录。游标在访问页面之前要把它从disk加载到内存,而这就是pager的任务。任何时候,当B-tree需要页面时,它都会请求pager从disk读取数据,然后把页面(page)加载到页面缓冲区(page cache),之后,B-tree和与之关联的游标就可以访问位于page中的记录了。 如果cursor改变了page,为了防止事务回滚,pager必须采取特殊的方式保存原始page。总的来说,pager负责读写数据库,管理内存缓存和页面(page),以及管理事务,锁和崩溃恢复。 总之,关于connection和transaction,你必须知道两件事: (1)对数据库的任何操作,一个连接存在于一个事务下。 (2)一个连接决不会同时存在多个事务下。 2.2.2 核心API 核心API 主要与执行sql命令有关,本质上有两种方法执行sql语句:prepared query 和wrapped query。Prepared query由三个阶段构成:preparation,execution和finalization。其实wrapped query只是对prepared query的三个过程封装而已,最终也会转化为prepared query的执行。