sqlite源码之memjournal.c

/*
2008 October 7

The author disclaims copyright to this source code. In place of
a legal notice, here is a blessing:

May you do good and not evil.
May you find forgiveness for yourself and forgive others.
May you share freely, never taking more than you give.


This file contains code use to implement an in-memory rollback journal.
The in-memory rollback journal is used to journal transactions for
“:memory:” databases and when the journal_mode=MEMORY pragma is used.

Update: The in-memory journal is also used to temporarily cache
smaller journals that are not critical for power-loss recovery.
For example, statement journals that are not too big will be held
entirely in memory, thus reducing the number of file I/O calls, and
more importantly, reducing temporary file creation events. If these
journals become too large for memory, they are spilled to disk. But
in the common case, they are usually small and no file I/O needs to
occur.
*/
MemJournal示例图

#include "sqliteInt.h"

/* Forward references to internal structures */
typedef struct MemJournal MemJournal;
typedef struct FilePoint FilePoint;
typedef struct FileChunk FileChunk;

/*
The rollback journal is composed of a linked list of these structures.

The zChunk array is always at least 8 bytes in size - usually much more.
Its actual size is stored in the MemJournal.nChunkSize variable.
*/
struct FileChunk {
  FileChunk *pNext;               /* Next chunk in the journal */
  u8 zChunk[8];                   /* Content of this chunk */
};

/*
By default, allocate this many bytes of memory for each FileChunk object.
默认情况下,为每个FileChunk对象分配这么多字节的内存
*/

#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024

/*
For chunk size nChunkSize, return the number of bytes that should
be allocated for each FileChunk structure.
*/

#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8))

/*
An instance of this object serves as a cursor into the rollback journal.
The cursor can be either for reading or writing.
*/

struct FilePoint {
  sqlite3_int64 iOffset;          /* Offset from the beginning of the file */
  FileChunk *pChunk;              /* Specific chunk into which cursor points */
};

/*
This structure is a subclass of sqlite3_file. Each open memory-journal
is an instance of this class.
*/

struct MemJournal {
  const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
  int nChunkSize;                 /* In-memory chunk-size */

  int nSpill;                     /* Bytes of data before flushing */
  int nSize;                      /* Bytes of data currently in memory */
  FileChunk *pFirst;              /* Head of in-memory chunk-list */
  FilePoint endpoint;             /* Pointer to the end of the file */
  FilePoint readpoint;            /* Pointer to the end of the last xRead() */

  int flags;                      /* xOpen flags */
  sqlite3_vfs *pVfs;              /* The "real" underlying VFS */
  const char *zJournal;           /* Name of the journal file */
};

/*
Read data from the in-memory journal file. This is the implementation
of the sqlite3_vfs.xRead method.
*/

static int memjrnlRead(
  sqlite3_file *pJfd,    /* The journal file from which to read */
  void *zBuf,            /* Put the results here */
  int iAmt,              /* Number of bytes to read */
  sqlite_int64 iOfst     /* Begin reading at this offset */
){
  MemJournal *p = (MemJournal *)pJfd;
  u8 *zOut = zBuf;
  int nRead = iAmt;
  int iChunkOffset;
  FileChunk *pChunk;

#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
 || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
  if( (iAmt+iOfst)>p->endpoint.iOffset ){
    return SQLITE_IOERR_SHORT_READ;
  }
#endif

  assert( (iAmt+iOfst)<=p->endpoint.iOffset );//小于结束点的偏移
  assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );//要么改FP的偏移为0或者改FP的FileChunk不存在
  if( p->readpoint.iOffset!=iOfst || iOfst==0 ){//当前正在读取的那个FP的偏移 不等于iOfst,或者iOfst为0  那么就从头读取
    sqlite3_int64 iOff = 0;//重新定义一个iOff
    for(pChunk=p->pFirst;
        ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
        pChunk=pChunk->pNext//寻找下一个pChunk
    ){
      iOff += p->nChunkSize;//更新iOff(根据一个个的Chunk来计算)
    }
  }else{//不需要修正,直接赋值
    pChunk = p->readpoint.pChunk;//直接给前面定义的FileChunk赋值
    assert( pChunk!=0 );//确保FileChunk不为空
  }

  iChunkOffset = (int)(iOfst%p->nChunkSize);//计算出这个FileChunk的偏移量
  do {
    int iSpace = p->nChunkSize - iChunkOffset;//这个Chunk剩余的空间量(也是本Chunk中需要进行赋值的那部分内容的大小)
    int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));//在本Chunk剩余的空间和要读的大小之间去最小的那个
    memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy);//D,S,N,D是目的地址,S是原地址,N是赋值的字节数
    zOut += nCopy;//更新zOut指针,赋值了多少内容,就把zOut往后移动多少个字节的内容
    nRead -= iSpace;//更新nRead
  } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );//满足条件进行下一次循环
  iChunkOffset = 0;//更新iChunkOffset
  p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0;//更新readpoint(FilePoint)的iOffset
  p->readpoint.pChunk = pChunk;//更新readpoint的FileChunk

  return SQLITE_OK;
}

/*
Free the list of FileChunk structures headed at MemJournal.pFirst.//释放以pFirst开始的FileChunk组成的列表
*/

static void memjrnlFreeChunks(MemJournal *p){//
  FileChunk *pIter;
  FileChunk *pNext;
  for(pIter=p->pFirst; pIter; pIter=pNext){
    pNext = pIter->pNext;
    sqlite3_free(pIter);//调用函数释放这个pIter指针指向的内存区域
  } 
  p->pFirst = 0;
}

/*
Flush the contents of memory to a real file on disk.
*/
//将内存的内容刷新到磁盘的真实文件中

static int memjrnlCreateFile(MemJournal *p){
  int rc;
  sqlite3_file *pReal = (sqlite3_file*)p;
  MemJournal copy = *p;

  memset(p, 0, sizeof(MemJournal));//清零指针p所占用的内存
  rc = sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0);//copy.flags : xOpen flags  使用虚拟文件系统打开文件
  if( rc==SQLITE_OK ){
    int nChunk = copy.nChunkSize;//In-memory chunk-size 在内存中的chunk的size
    i64 iOff = 0;
    FileChunk *pIter;
    for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){
      if( iOff + nChunk > copy.endpoint.iOffset ){
        nChunk = copy.endpoint.iOffset - iOff;//对nChunk调整
      }
      rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff);
      if( rc ) break;
      iOff += nChunk;//把iOff进行累加
    }
    if( rc==SQLITE_OK ){
      /* No error has occurred. Free the in-memory buffers. */
      memjrnlFreeChunks(&copy);//释放以pFirst开始的FileChunk组成的列表
    }
  }
  if( rc!=SQLITE_OK ){
    /* If an error occurred while creating or writing to the file, restore
    the original before returning. This way, SQLite uses the in-memory
    journal data to roll back changes made to the internal page-cache
    before this function was called.  */
    sqlite3OsClose(pReal);
    *p = copy;
  }
  return rc;
}

/*
Write data to the file.
*/
//向文件中写入数据

static int memjrnlWrite(
  sqlite3_file *pJfd,    /* The journal file into which to write */
  const void *zBuf,      /* Take data to be written from here */
  int iAmt,              /* Number of bytes to write */
  sqlite_int64 iOfst     /* Begin writing at this offset into the file */
){
  MemJournal *p = (MemJournal *)pJfd;
  int nWrite = iAmt;
  u8 *zWrite = (u8 *)zBuf;

  /* If the file should be created now, create it and write the new data
  into the file on disk. */
  //创建文件,写入数据
  //nSpill 在刷入文件之前,数据占用的字节
  if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){//因为该文件还没有被创建的话,那么nSpill为0
    int rc = memjrnlCreateFile(p);
    if( rc==SQLITE_OK ){
      rc = sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst);//写入iAmt字节的数据
    }
    return rc;
  }
  /* If the contents of this write should be stored in memory *///写入的内容需要存入到数据库当中
  else{
    /* An in-memory journal file should only ever be appended to. Random
    access writes are not required. The only exception to this is when
    the in-memory journal is being used by a connection using the
    atomic-write optimization. In this case the first 28 bytes of the
    journal file may be written as part of committing the transaction. */ 
    assert( iOfst==p->endpoint.iOffset || iOfst==0 );
#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
 || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
    if( iOfst==0 && p->pFirst ){
      assert( p->nChunkSize>iAmt );
      memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
    }else
#else
    assert( iOfst>0 || p->pFirst==0 );
#endif
    {
      while( nWrite>0 ){
        FileChunk *pChunk = p->endpoint.pChunk;
        int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);//通过endpoint来计算iChunkOffset的大小
        int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);//计算这个Chunk还可以写入的空间的大小

        if( iChunkOffset==0 ){
          /* New chunk is required to extend the file. 为了拓展文件,需要分配新的chunk*/
          FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
          if( !pNew ){//如果分配失败了
            return SQLITE_IOERR_NOMEM_BKPT;
          }
          pNew->pNext = 0;
          if( pChunk ){
            assert( p->pFirst );
            pChunk->pNext = pNew;//pFirst存在的情况下,指向下一个
          }else{
            assert( !p->pFirst );
            p->pFirst = pNew;//pFirst不存在时,pNew就是pFirst
          }
          p->endpoint.pChunk = pNew;//更新endpoint.pChunk
        }

        memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
        zWrite += iSpace;//更新zWrite
        nWrite -= iSpace;//更新nWrite
        p->endpoint.iOffset += iSpace;//每写入一点数据,endpoint.iOffset就更新一点偏移
      }
      p->nSize = iAmt + iOfst;//更新在内存中数据的字节数
    }
  }

  return SQLITE_OK;
}

/*
Truncate the file.

If the journal file is already on disk, truncate it there. Or, if it
is still in main memory but is being truncated to zero bytes in size,
ignore
*/
/如果日志文件已经在内存当中了,那么把它截断。或者,如果它仍然在内存中,但是已经被截断了/

static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
  MemJournal *p = (MemJournal *)pJfd;//强制类型转换
  if( ALWAYS(size==0) ){
    memjrnlFreeChunks(p);
    p->nSize = 0;
    p->endpoint.pChunk = 0;
    p->endpoint.iOffset = 0;
    p->readpoint.pChunk = 0;
    p->readpoint.iOffset = 0;
  }
  return SQLITE_OK;
}

/*
Close the file. 关闭文件
*/

static int memjrnlClose(sqlite3_file *pJfd){
  MemJournal *p = (MemJournal *)pJfd;//强制类型转换
  memjrnlFreeChunks(p);//释放pFirst开始的FileChunk的list
  return SQLITE_OK;
}

/*
Sync the file.

If the real file has been created, call its xSync method. Otherwise,
syncing an in-memory journal is a no-op.
如果真实的文件已经被创建了,调用xSync方法。否则,同步一个在内存当中的日志文件时一个空操作
*/

static int memjrnlSync(sqlite3_file *pJfd, int flags){
  UNUSED_PARAMETER2(pJfd, flags);
  return SQLITE_OK;
}

/*
Query the size of the file in bytes.
查询在文件当中的字节数
*/

static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
  MemJournal *p = (MemJournal *)pJfd;//强制类型转换
  *pSize = (sqlite_int64) p->endpoint.iOffset;//让指针pSize指向的内容为iOffset
  return SQLITE_OK;
}

/*
Table of methods for MemJournal sqlite3_file object. sqlite3_file对象的方法表
*/

static const struct sqlite3_io_methods MemJournalMethods = {//sqliet3_io_methods结构体是在sqliet.h.in文件中定义的
  1,                /* iVersion */
  memjrnlClose,     /* xClose */
  memjrnlRead,      /* xRead */
  memjrnlWrite,     /* xWrite */
  memjrnlTruncate,  /* xTruncate */
  memjrnlSync,      /* xSync */
  memjrnlFileSize,  /* xFileSize */
  0,                /* xLock */
  0,                /* xUnlock */
  0,                /* xCheckReservedLock */
  0,                /* xFileControl */
  0,                /* xSectorSize */
  0,                /* xDeviceCharacteristics */
  0,                /* xShmMap */
  0,                /* xShmLock */
  0,                /* xShmBarrier */
  0,                /* xShmUnmap */
  0,                /* xFetch */
  0                 /* xUnfetch */
};

/*
Open a journal file.

The behaviour of the journal file depends on the value of parameter
nSpill. If nSpill is 0, then the journal file is always create and
accessed using the underlying VFS. If nSpill is less than zero, then
all content is always stored in main-memory. Finally, if nSpill is a
positive value, then the journal file is initially created in-memory
but may be flushed to disk later on. In this case the journal file is
flushed to disk either when it grows larger than nSpill bytes in size,
or when sqlite3JournalCreate() is called.
打开一个日志文件。日志文件的特征取决于参数nSpill的值。如果nSpill是0,那么日志文件将会被创建并且在VFS支持的情况下可以访问。
如果nSpill<0,那么所有的内容将被存储在主存中。最后,如果nSpill>0,那么日志文件刚开始在内存中被创建,但是后来会被刷入到磁盘中。
在这种情况下,当日志文件比nSpill大或者当sqlite3JournalCreate被调用的时候,日志文件将被刷入到磁盘中。
*/

int sqlite3JournalOpen(
  sqlite3_vfs *pVfs,         /* The VFS to use for actual file I/O */
  const char *zName,         /* Name of the journal file */
  sqlite3_file *pJfd,        /* Preallocated, blank file handle */
  int flags,                 /* Opening flags */
  int nSpill                 /* Bytes buffered before opening the file */
){
  MemJournal *p = (MemJournal*)pJfd;

  /* Zero the file-handle object. If nSpill was passed zero, initialize
  it using the sqlite3OsOpen() function of the underlying VFS. In this
  case none of the code in this module is executed as a result of calls
  made on the journal file-handle.  
将文件句柄对象初始化为0。如果nSpill为0,那么使用VFS提供的sqlite3OsOpen函数(最终调用的是VFS中的xOpen)初始化。
这样的话,不会执行
  */
  memset(p, 0, sizeof(MemJournal));//清零日志文件句柄所在的内存
  if( nSpill==0 ){
    return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);//如果nSpill为0时,就直接返回不往后执行了
  }

  if( nSpill>0 ){
    p->nChunkSize = nSpill;
  }else{//当nSpill<0时
    p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);//初始化chunk size
    assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
  }

  p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
  p->nSpill = nSpill;
  p->flags = flags;
  p->zJournal = zName;
  p->pVfs = pVfs;
  return SQLITE_OK;
}

/*
Open an in-memory journal file.
打开一个在内存中的日志文件
*/

void sqlite3MemJournalOpen(sqlite3_file *pJfd){
  sqlite3JournalOpen(0, 0, pJfd, 0, -1);
}

#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
 || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
/*
If the argument p points to a MemJournal structure that is not an 
in-memory-only journal file (i.e. is one that was opened with a +ve
nSpill parameter or as SQLITE_OPEN_MAIN_JOURNAL), and the underlying 
file has not yet been created, create it now.
如果参数p指向一个不仅仅在内存中的日志文件的MemJournal
(如以a +ve nSpill作为参数或者SQLITE_OPEN_MAIN_JOURNAL)并且尚未创建基础文件,现在创建它。
*/
int sqlite3JournalCreate(sqlite3_file *pJfd){
  int rc = SQLITE_OK;
  MemJournal *p = (MemJournal*)pJfd;
  if( p->pMethod==&MemJournalMethods && (
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
     p->nSpill>0  //当开始在内存中分配,后来可以刷入到磁盘中
#else
     /* While this appears to not be possible without ATOMIC_WRITE, the
     paths are complex, so it seems prudent to leave the test in as
     a NEVER(), in case our analysis is subtly flawed. 
	 尽管不使用ATOMIC_WRITE,以免我们的分析有点瑕疵
     */
     NEVER(p->nSpill>0)
#endif
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
     || (p->flags & SQLITE_OPEN_MAIN_JOURNAL)
#endif
  )){
    rc = memjrnlCreateFile(p);//将内存的内容刷入到真实的文件中
  }
  return rc;
}
#endif

/*
The file-handle passed as the only argument is open on a journal file.
Return true if this “journal file” is currently stored in heap memory,
or false otherwise.
参数中的文件句柄打开一个日志文件。如果journal file被存储在堆内存中,那么返回true,否则返回false
*/

int sqlite3JournalIsInMemory(sqlite3_file *p){
  return p->pMethods==&MemJournalMethods;
}

/*
Return the number of bytes required to store a JournalFile that uses vfs
pVfs to create the underlying on-disk files.
返回存储日志文件需要用到的字节数,该日志文件使用pVFs来创建底层的磁盘文件
*/

int sqlite3JournalSize(sqlite3_vfs *pVfs){
  return MAX(pVfs->szOsFile, (int)sizeof(MemJournal));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值