SQLite 学习之路 第二节 模块介绍和Pager对象
Pager介绍
根据上图我们能够大致了解Pager模块在SQLite占据的地位,它是底层模块了,能够对数据库文件进行直接的读与写。在SQLite里面,BTree模块不会直接和磁盘上的数据文件打交道,而是通过Pager对象里面的各种资源来操作数据文件。上图表示,Pager模块包含四个模块,事务管理模块,锁管理模块,缓存模块和日志管理模块,其中事务管理模块的实现依赖于另外三个模块。
对于Lock manager:为了实验数据文件的并发控制,锁管理模块必须确保事务在访问数据页之前,一定先对数据文件上锁。
对于Page cache:当应用程序访问数据库文件时,pager模块会以块为单位进行缓存,每一个连接都有自己独有的pager模块,因此每个连接都有自己独有的缓存。
对于Log manager:是保证了事务的原子性,要么完整写入数据,要么不写入数据。日志记录了事务在更新数据库文件之前的数据,如果数据写入过程中发生异常中断,在下一次连接时会把日志里记录的原始数据还原回数据库文件。
SQLite对数据文件进行控制时,需要创建一个pager对象,通过pager对象内的资源来操作文件。对于同一个文件,一个进程可能有多个pager对象;这些对象之间都是相互独立的。对于共享缓存模式,每个数据文件只有一个pager对象,所有连接共享这个pager对象。
Pager对象
Pager对象是SQLite访问数据文件必不可少的元素,以下是pager对象具体实现代码
英文版:
struct Pager {
sqlite3_vfs *pVfs;
u8 exclusiveMode;
u8 journalMode;
u8 useJournal;
u8 noSync;
u8 fullSync;
u8 extraSync;
u8 syncFlags;
u8 walSyncFlags;
u8 tempFile;
u8 noLock;
u8 readOnly;
u8 memDb;
u8 eState;
u8 eLock;
u8 changeCountDone;
u8 setSuper;
u8 doNotSpill;
u8 subjInMemory;
u8 bUseFetch;
u8 hasHeldSharedLock;
Pgno dbSize;
Pgno dbOrigSize;
Pgno dbFileSize;
Pgno dbHintSize;
int errCode;
int nRec;
u32 cksumInit;
u32 nSubRec;
Bitvec *pInJournal;
sqlite3_file *fd;
sqlite3_file *jfd;
sqlite3_file *sjfd;
i64 journalOff;
i64 journalHdr;
sqlite3_backup *pBackup;
PagerSavepoint *aSavepoint;
int nSavepoint;
u32 iDataVersion;
char dbFileVers[16];
int nMmapOut;
sqlite3_int64 szMmap;
PgHdr *pMmapFreelist;
u16 nExtra;
i16 nReserve;
u32 vfsFlags;
u32 sectorSize;
int pageSize;
Pgno mxPgno;
i64 journalSizeLimit;
char *zFilename;
char *zJournal;
int (*xBusyHandler)(void*);
void *pBusyHandlerArg;
int aStat[4];
#ifdef SQLITE_TEST
int nRead;
#endif
void (*xReiniter)(DbPage*);
int (*xGet)(Pager*,Pgno,DbPage**,int);
char *pTmpSpace;
PCache *pPCache;
#ifndef SQLITE_OMIT_WAL
Wal *pWal;
char *zWal;
#endif
};
部分重要信息翻译后:
struct Pager {
sqlite3_vfs *pVfs;
u8 exclusiveMode;
u8 journalMode;
u8 useJournal;
u8 noSync;
u8 fullSync;
u8 extraSync;
u8 ckptSyncFlags;
u8 walSyncFlags;
u8 syncFlags;
u8 tempFile;
u8 noLock;
u8 readOnly;
u8 memDb;
u8 eState;
u8 eLock;
u8 changeCountDone;
u8 setMaster;
u8 doNotSpill;
u8 subjInMemory;
u8 bUseFetch;
u8 hasHeldSharedLock;
Pgno dbSize;
Pgno dbOrigSize;
Pgno dbFileSize;
Pgno dbHintSize;
int errCode;
int nRec;
u32 cksumInit;
u32 nSubRec;
Bitvec *pInJournal;
sqlite3_file *fd;
sqlite3_file *jfd;
sqlite3_file *sjfd;
i64 journalOff;
i64 journalHdr;
sqlite3_backup *pBackup;
PagerSavepoint *aSavepoint;
int nSavepoint;
u32 iDataVersion;
char dbFileVers[16];
int nMmapOut;
sqlite3_int64 szMmap;
PgHdr *pMmapFreelist;
u16 nExtra;
i16 nReserve;
u32 vfsFlags;
u32 sectorSize;
int pageSize;
Pgno mxPgno;
i64 journalSizeLimit;
char *zFilename;
char *zJournal;
int (*xBusyHandler)(void*);
void *pBusyHandlerArg;
int aStat[3];
#ifdef SQLITE_TEST
int nRead;
#endif
void (*xReiniter)(DbPage*);
int (*xGet)(Pager*,Pgno,DbPage**,int);
#ifdef SQLITE_HAS_CODEC
void *(*xCodec)(void*,void*,Pgno,int);
void (*xCodecSizeChng)(void*,int,int);
void (*xCodecFree)(void*);
void *pCodec;
#endif
char *pTmpSpace;
PCache *pPCache;
#ifndef SQLITE_OMIT_WAL
Wal *pWal;
char *zWal;
#endif
};
我们能够看出pager对象十分复杂,里面包含了许多变量,这些都和上面介绍的各个子模块相对应,例如锁、日志、文件读写、cache这些操作。而pager对象是如何生成的呢?通过查询得知,是sqlite3PagerOpe()创建一个pager对象。于是下面附上sqlite3PagerOpen()函数的代码
SQLITE_PRIVATE int sqlite3PagerOpen(
sqlite3_vfs *pVfs,
Pager **ppPager,
const char *zFilename,
int nExtra,
int flags,
int vfsFlags,
void (*xReinit)(DbPage*)
){
u8 *pPtr;
Pager *pPager = 0;
int rc = SQLITE_OK;
int tempFile = 0;
int memDb = 0;
#ifndef SQLITE_OMIT_DESERIALIZE
int memJM = 0;
#else
# define memJM 0
#endif
int readOnly = 0;
int journalFileSize;
char *zPathname = 0;
int nPathname = 0;
int useJournal = (flags & PAGER_OMIT_JOURNAL)==0;
int pcacheSize = sqlite3PcacheSize();
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE;
const char *zUri = 0;
int nUriByte = 1;
int nUri = 0;
journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
*ppPager = 0;
#ifndef SQLITE_OMIT_MEMORYDB
if( flags & PAGER_MEMORY ){
memDb = 1;
if( zFilename && zFilename[0] ){
zPathname = sqlite3DbStrDup(0, zFilename);
if( zPathname==0 ) return SQLITE_NOMEM_BKPT;
nPathname = sqlite3Strlen30(zPathname);
zFilename = 0;
}
}
#endif
if( zFilename && zFilename[0] ){
const char *z;
nPathname = pVfs->mxPathname+1;
zPathname = sqlite3DbMallocRaw(0, nPathname*2);
if( zPathname==0 ){
return SQLITE_NOMEM_BKPT;
}
zPathname[0] = 0;
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_OK_SYMLINK ){
if( vfsFlags & SQLITE_OPEN_NOFOLLOW ){
rc = SQLITE_CANTOPEN_SYMLINK;
}else{
rc = SQLITE_OK;
}
}
}
nPathname = sqlite3Strlen30(zPathname);
z = zUri = &zFilename[sqlite3Strlen30(zFilename)+1];
while( *z ){
z += strlen(z)+1;
z += strlen(z)+1;
nUri++;
}
nUriByte = (int)(&z[1] - zUri);
assert( nUriByte>=1 );
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
rc = SQLITE_CANTOPEN_BKPT;
}
if( rc!=SQLITE_OK ){
sqlite3DbFree(0, zPathname);
return rc;
}
}
pPtr = (u8 *)sqlite3MallocZero(
ROUND8(sizeof(*pPager)) +
ROUND8(pcacheSize) +
ROUND8(pVfs->szOsFile) +
journalFileSize * 2 +
sizeof(pPager) +
4 +
nPathname + 1 +
nUriByte +
nPathname + 8 + 1 +
#ifndef SQLITE_OMIT_WAL
nPathname + 4 + 1 +
#endif
3
);
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
if( !pPtr ){
sqlite3DbFree(0, zPathname);
return SQLITE_NOMEM_BKPT;
}
pPager = (Pager*)pPtr; pPtr += ROUND8(sizeof(*pPager));
pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize);
pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile);
pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager);
pPtr += 4;
pPager->zFilename = (char*)pPtr;
if( nPathname>0 ){
memcpy(pPtr, zPathname, nPathname); pPtr += nPathname + 1;
if( zUri ){
memcpy(pPtr, zUri, nUriByte); pPtr += nUriByte;
}else{
pPtr++;
}
}
if( nPathname>0 ){
pPager->zJournal = (char*)pPtr;
memcpy(pPtr, zPathname, nPathname); pPtr += nPathname;
memcpy(pPtr, "-journal",8); pPtr += 8 + 1;
#ifdef SQLITE_ENABLE_8_3_NAMES
sqlite3FileSuffix3(zFilename,pPager->zJournal);
pPtr = (u8*)(pPager->zJournal + sqlite3Strlen30(pPager->zJournal)+1);
#endif
}else{
pPager->zJournal = 0;
}
#ifndef SQLITE_OMIT_WAL
if( nPathname>0 ){
pPager->zWal = (char*)pPtr;
memcpy(pPtr, zPathname, nPathname); pPtr += nPathname;
memcpy(pPtr, "-wal", 4); pPtr += 4 + 1;
#ifdef SQLITE_ENABLE_8_3_NAMES
sqlite3FileSuffix3(zFilename, pPager->zWal);
pPtr = (u8*)(pPager->zWal + sqlite3Strlen30(pPager->zWal)+1);
#endif
}else{
pPager->zWal = 0;
}
#endif
if( nPathname ) sqlite3DbFree(0, zPathname);
pPager->pVfs = pVfs;
pPager->vfsFlags = vfsFlags;
if( zFilename && zFilename[0] ){
int fout = 0;
rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout);
assert( !memDb );
#ifndef SQLITE_OMIT_DESERIALIZE
memJM = (fout&SQLITE_OPEN_MEMORY)!=0;
#endif
readOnly = (fout&SQLITE_OPEN_READONLY)!=0;
if( rc==SQLITE_OK ){
int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
if( !readOnly ){
setSectorSize(pPager);
assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
if( szPageDflt<pPager->sectorSize ){
if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
}else{
szPageDflt = (u32)pPager->sectorSize;
}
}
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
{
int ii;
assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){
szPageDflt = ii;
}
}
}
#endif
}
pPager->noLock = sqlite3_uri_boolean(pPager->zFilename, "nolock", 0);
if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0
|| sqlite3_uri_boolean(pPager->zFilename, "immutable", 0) ){
vfsFlags |= SQLITE_OPEN_READONLY;
goto act_like_temp_file;
}
}
}else{
act_like_temp_file:
tempFile = 1;
pPager->eState = PAGER_READER;
pPager->eLock = EXCLUSIVE_LOCK;
pPager->noLock = 1;
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
}
if( rc==SQLITE_OK ){
assert( pPager->memDb==0 );
rc = sqlite3PagerSetPagesize(pPager, &szPageDflt, -1);
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK ){
nExtra = ROUND8(nExtra);
assert( nExtra>=8 && nExtra<1000 );
rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
!memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
}
if( rc!=SQLITE_OK ){
sqlite3OsClose(pPager->fd);
sqlite3PageFree(pPager->pTmpSpace);
sqlite3_free(pPager);
return rc;
}
PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename));
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
pPager->useJournal = (u8)useJournal;
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
pPager->tempFile = (u8)tempFile;
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
|| tempFile==PAGER_LOCKINGMODE_EXCLUSIVE );
assert( PAGER_LOCKINGMODE_EXCLUSIVE==1 );
pPager->exclusiveMode = (u8)tempFile;
pPager->changeCountDone = pPager->tempFile;
pPager->memDb = (u8)memDb;
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
if( pPager->noSync ){
assert( pPager->fullSync==0 );
assert( pPager->extraSync==0 );
assert( pPager->syncFlags==0 );
assert( pPager->walSyncFlags==0 );
}else{
pPager->fullSync = 1;
pPager->extraSync = 0;
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->walSyncFlags = SQLITE_SYNC_NORMAL | (SQLITE_SYNC_NORMAL<<2);
}
pPager->nExtra = (u16)nExtra;
pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT;
assert( isOpen(pPager->fd) || tempFile );
setSectorSize(pPager);
if( !useJournal ){
pPager->journalMode = PAGER_JOURNALMODE_OFF;
}else if( memDb || memJM ){
pPager->journalMode = PAGER_JOURNALMODE_MEMORY;
}
pPager->xReiniter = xReinit;
setGetterMethod(pPager);
*ppPager = pPager;
return SQLITE_OK;
}
SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
Pager *pPager;
while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
zName--;
}
pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
return pPager->fd;
}
代码比较长,主要就是分配内存并初始化该对象,并用一个指针指向它。zFilename参数是要打开的数据库文件的路径。如果zFilename为空,则创建一个随机命名的临时文件并用作要缓存的文件。如果zFilename不为空,说明数据库存在磁盘上,调用sqlite3OsFullPathname()获取文件的路径全名zPathname,再取得路径长度nPathname。通过sqlite3agegetextra(),指定分配的空间字节数nExtra。
|