1.删除的记录在表的第2页,但是进入balance()函数后,为什么pCur->iPage是0?
sqlite3BtreeFirst—>moveToRoot里会把pCur->iPage设为0,pCur->iPage不是数据库文件的页面,而是pCur->apPage的索引。
2.cursor是什么时候创建的
在allocateCursor里,中有OpenRead或OpenWrite时才创建。
3.allocateCursor和sqlite3BtreeCursor有什么区别
前者只是分配空间,后者是初始化的一些补充
4. insert里打印的变量,pCur->pgnoRoot和pPage->pgno的区别
pPage = pCur->pPage;
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
pCur->pgnoRoot是当前表根节点所在页面,pPage->pgno是当前要插入的页面序号,虚拟机在执行NewRowid命令时会调用
sqlite3BtreeLast()------->moveToRoot()--------->getAndInitPage()
来给pCur->pPage赋值,其中ppPage就是pCur->pPage的地址
rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly);
if( rc ){
goto getAndInitPage_error1;
}
*ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
if( (*ppPage)->isInit==0 ){
btreePageFromDbPage(pDbPage, pgno, pBt);
rc = btreeInitPage(*ppPage);
if( rc!=SQLITE_OK ){
goto getAndInitPage_error2;
}
}
一个表里可能有很多页,pCur根据pCur->apPage记录的路径来跳转到相应的也,类似的代码如
if( pCur ){
pCur->iPage--;
pCur->pPage = pCur->apPage[pCur->iPage];
}
5.什么时候创建sqlite_master表
在newDatabase的zeroPage,写完数据库的头100个自己后,就会跟着建立一张sqlite_master表,这张表的数据更新也和普通表一样,在insertCell函数里
data = pPage->aData;
assert( &data[pPage->cellOffset]==pPage->aCellIdx );
rc = allocateSpace(pPage, sz, &idx);
if( rc ){ *pRC = rc; return; }
/* The allocateSpace() routine guarantees the following properties
** if it returns successfully */
assert( idx >= 0 );
assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nFree -= (u16)(2 + sz);
if( iChild ){
/* In a corrupt database where an entry in the cell index section of
** a btree page has a value of 3 or less, the pCell value might point
** as many as 4 bytes in front of the start of the aData buffer for
** the source page. Make sure this does not cause problems by not
** reading the first 4 bytes */
memcpy(&data[idx+4], pCell+4, sz-4);
put4byte(&data[idx], iChild);
}else{
memcpy(&data[idx], pCell, sz);
}
pIns = pPage->aCellIdx + i*2;//更新cell索引数组
memmove(pIns+2, pIns, 2*(pPage->nCell - i));
put2byte(pIns, idx);
pPage->nCell++;
/* increment the cell count */
//更新这一页的cell数量
if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
allocateSpace里更新第一个cell的偏移地址
top -= nByte;
put2byte(&data[hdr+5], top);
assert( top+nByte <= (int)pPage->pBt->usableSize );
*pIdx = top;
那么pPage->aCellIdx和pPage->cellOffset是哪里来的呢,在zeroPage函数里,会被newDatabase()和btreeCreateTable()函数调用
data[hdr] = (char)flags;
first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8);
memset(&data[hdr+1], 0, 4);
data[hdr+7] = 0;
put2byte(&data[hdr+5], pBt->usableSize);
pPage->nFree = (u16)(pBt->usableSize - first);
decodeFlags(pPage, flags);
pPage->cellOffset = first;
pPage->aDataEnd = &data[pBt->usableSize];
pPage->aCellIdx = &data[first];
5.为什么insert之后却没有写入到数据库文件
相关测试代码如下
do_execsql_test btree01-1.1 {
PRAGMA page_size=1024;
PRAGMA vdbe_trace = 1;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);
WITH RECURSIVE
c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<1)
INSERT INTO t1(a,b) SELECT i, zeroblob(6500) FROM c;
UPDATE t1 SET b=zeroblob(3000);
UPDATE t1 SET b=zeroblob(64000) WHERE a=2;
PRAGMA integrity_check;
} {ok}
看打印日志,c(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM c WHERE i<1)对应的应该是
INSERT: table=1 nkey=1 ndata=2 page=1 new entry
大概会在第1页的1020字节处写入数据,之所以没有写入到数据库的原因是,因为这个是临时的,可以看字节码后面会执行到Delete指令,还没存到文件之前就把这个cell给删了,即把这些数据清0,调用关系
sqlite3BtreeDelete()----->dropCell()------>freeSpace()
但是我们知道第一页在之前已经向文件里写入了sqlite_master的数据了,而现在第一页在内存里的内容(pBt->pPage1)是清零的,为什么最后在pager层写文件时
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
这里的pData是原来文件里的数据,而不是清0后的数据
其实看上面这个图片就可以知道pBt->pPage1 被设为了0,而且p->pBt对象页被释放了
甚至在commit时,btree对象也被换掉了
我们再回过头来看当时insert时的btree对象是哪来的
pCx->pBtx好像是一个临时对象
struct VdbeCursor {
。。。。。。
Btree *pBtx; /* Separate file holding temporary table */
所以这2个根本就不是同一个btree对象
Btree *pBt = db->aDb[i].pBt;//真实数据库文件对应的表
VdbeCursor *pCx;
pCx->pBtx//临时表,只在内存中存在