5、创建恢复日志(Creating A Rollback Journal File) 在对数据库进行写操作之前,SQLite先要创建一个单独的日志文件,然后把要修改的页面的原始数据写入日志。回滚日志包含一个日志头(图中的绿色)——记录数据库文件的原始大小。所以即使数据库文件大小改变了,我们仍知道数据库的原始大小。 从OS的角度来看,当一个文件创建时,大多数OS(Windows,Linux,Mac OS X)不会向磁盘写入数据,新创建的文件此时位于磁盘缓存中,之后才会真正写入磁盘。如图,日志文件位于OS磁盘缓存中,而不是位于磁盘。
上面 5步的代码的实现:
Code //事务指令的实现 //p1为数据库文件的索引号---0为main database;1为temporary tables使用的文件 //p2 不为0,一个写事务开始 case OP_Transaction: { //数据库的索引号 int i = pOp->p1; //指向数据库对应的btree Btree *pBt;
//开始一个事务,如果第二个参数不为0,则一个写事务开始,否则是一个读事务 //如果wrflag>=2,一个exclusive事务开始,此时别的连接不能访问数据库 int sqlite3BtreeBeginTrans(Btree *p, int wrflag){ BtShared *pBt = p->pBt; int rc = SQLITE_OK;
btreeIntegrity(p);
/* If the btree is already in a write-transaction, or it ** is already in a read-transaction and a read-transaction ** is requested, this is a no-op. */ //如果b-tree处于一个写事务;或者处于一个读事务,一个读事务又请求,则返回SQLITE_OK if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ &&!wrflag) ){ return SQLITE_OK; }
/* Write transactions are not possible on a read-only database */ //写事务不能访问只读数据库 if( pBt->readOnly && wrflag ){ return SQLITE_READONLY; }
/* If another database handle has already opened a write transaction ** on this shared-btree structure and a second write transaction is ** requested, return SQLITE_BUSY. */ //如果数据库已存在一个写事务,则该写事务请求时返回SQLITE_BUSY if( pBt->inTransaction==TRANS_WRITE && wrflag ){ return SQLITE_BUSY; }
failed_to_open_journal: sqliteFree(pPager->aInJournal); pPager->aInJournal =0; if( rc==SQLITE_NOMEM ){ /* If this was a malloc() failure, then we will not be closing the pager ** file. So delete any journal file we may have just created. Otherwise, ** the system will get confused, we have a read-lock on the file and a ** mysterious journal has appeared in the filesystem. */ sqlite3OsDelete(pPager->zJournal); }else{ sqlite3OsUnlock(pPager->fd, NO_LOCK); pPager->state = PAGER_UNLOCK; } return rc; }
/*写入日志文件头 **journal header的格式如下: ** - 8 bytes: 标志日志文件的魔数 ** - 4 bytes: 日志文件中记录数 ** - 4 bytes: Random number used for page hash. ** - 4 bytes: 原来数据库的大小(kb) ** - 4 bytes: 扇区大小512byte */ staticint writeJournalHdr(Pager *pPager){ //日志文件头 char zHeader[sizeof(aJournalMagic)+16];
int rc = seekJournalHdr(pPager); if( rc ) return rc;
/* FIX ME: ** ** Possibly for a pager not in no-sync mode, the journal magic should not ** be written until nRec is filled in as part of next syncJournal(). ** ** Actually maybe the whole journal header should be delayed until that ** point. Think about this. */ memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); /* The nRec Field. 0xFFFFFFFF for no-sync journals. */ put32bits(&zHeader[sizeof(aJournalMagic)], pPager->noSync ?0xffffffff : 0); /* The random check-hash initialiser */ sqlite3Randomness(sizeof(pPager->cksumInit), &pPager->cksumInit); put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit); /* The initial database size */ put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbSize); /* The assumed sector size for this process */ put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize); //写入文件头 rc = sqlite3OsWrite(pPager->jfd, zHeader, sizeof(zHeader));
/* The journal header has been written successfully. Seek the journal ** file descriptor to the end of the journal header sector. */ if( rc==SQLITE_OK ){ rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff-1); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->jfd, "/000", 1); } } return rc; }