sqlite-1.0.0源码执行的基本流程概述

sqlite-1.0.0原理概述

sqlite是一款嵌入式的轻量级的数据库,首个版本诞生于2000年,该数据库遵守ACID的关系数据库管理系统,SQLite不是一个cs架构的数据库引擎,而是被集成在用户程序中,SQLite实现了大部分的SQL标准。本文主要概述sqlite-1.0.0版本的启动的简单流程。

sqlite-1.0.0的源码
sqlite源码目录

在这里插入图片描述

主要包括了源码目录src,vdbe.c中主要包括了sqlite虚拟机的执行过程,where.c解析where语法的过程,shell.c主要是终端交互的过程中来获取输入并解析执行sql语句,insert.c主要就是插入数据的解析;在tool中的lemon的分析工具等。

sqlite的基本解析过程

一条sql的执行的主要过程如下:

在这里插入图片描述

在sqlite-1.0.0的实现过程中,使用了gdbm来做底层的数据存储与管理,sqlite的整个的实现流程,先是输入的语法词法解析,然后通过生成的语法生成相应的sql的虚拟机的执行指令,最后再执行该虚拟机指令,最终虚拟机指令通过gdbm来操作数据。

sqlite-1.0.0的源码的执行过程

通过在终端中输入sqlite执行,执行的入口函数为shell.c中的main函数;

int main(int argc, char **argv){
  sqlite *db;
  char *zErrMsg = 0;
  char *argv0 = argv[0];
  struct callback_data data;

  memset(&data, 0, sizeof(data));
  data.mode = MODE_List;
  strcpy(data.separator,"|");
  data.showHeader = 0;
  while( argc>=2 && argv[1][0]=='-' ){          // 处理传入参数的处理 是否是html 是否为header
    if( strcmp(argv[1],"-html")==0 ){
      data.mode = MODE_Html;
      argc--;
      argv++;
    }else if( strcmp(argv[1],"-list")==0 ){
      data.mode = MODE_List;
      argc--;
      argv++;
    }else if( strcmp(argv[1],"-line")==0 ){
      data.mode = MODE_Line;
      argc--;
      argv++;
    }else if( argc>=3 && strcmp(argv[0],"-separator")==0 ){
      sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[2]);
      argc -= 2;
      argv += 2;
    }else if( strcmp(argv[1],"-header")==0 ){
      data.showHeader = 1;
      argc--;
      argv++;
    }else if( strcmp(argv[1],"-noheader")==0 ){
      data.showHeader = 0;
      argc--;
      argv++;
    }else{
      fprintf(stderr,"%s: unknown option: %s\n", argv0, argv[1]);
      return 1;
    }
  }
  if( argc!=2 && argc!=3 ){
    fprintf(stderr,"Usage: %s ?OPTIONS? FILENAME ?SQL?\n", argv0);
    exit(1);
  }
  data.db = db = sqlite_open(argv[1], 0666, &zErrMsg);            // 打开文件
  if( db==0 ){
    data.db = db = sqlite_open(argv[1], 0444, &zErrMsg);          // 如果打不开就换个模式打开
    if( db==0 ){
      if( zErrMsg ){                                              // 出错信息
        fprintf(stderr,"Unable to open database \"%s\": %s\n", argv[1],zErrMsg);
      }else{
        fprintf(stderr,"Unable to open database %s\n", argv[1]);
      }
      exit(1);
    }else{
      printf("Database \"%s\" opened READ ONLY!\n", argv[1]);
    }
  }
  data.out = stdout;                                            // 输出信息
  if( argc==3 ){                                                // 三个输入参数 第三个参数为sql语句
    if( sqlite_exec(db, argv[2], callback, &data, &zErrMsg)!=0 && zErrMsg!=0 ){
      fprintf(stderr,"SQL error: %s\n", zErrMsg);
      exit(1);
    }
  }else{                                                        // 打开sql的交互界面
    char *zLine;
    char *zSql = 0;
    int nSql = 0;
    int istty = isatty(0);
    if( istty ){
      printf(
        "Enter \".help\" for instructions\n"
      );
    }
    while( (zLine = one_input_line(zSql, istty))!=0 ){          // 读取终端命令行的输入数据
      if( zLine && zLine[0]=='.' ){
        do_meta_command(zLine, db, &data);                      // 展示元数据信息
        free(zLine);
        continue;
      }
      if( zSql==0 ){
        nSql = strlen(zLine);
        zSql = malloc( nSql+1 );
        strcpy(zSql, zLine);
      }else{
        int len = strlen(zLine);                                // 获取输入的长度
        zSql = realloc( zSql, nSql + len + 2 );                 // 重新增加内存
        if( zSql==0 ){
          fprintf(stderr,"%s: out of memory!\n", argv0);        // 如果申请内存失败则报错
          exit(1);
        }
        strcpy(&zSql[nSql++], "\n");                            // 给字符串数组加\n
        strcpy(&zSql[nSql], zLine);                             // 拷贝获取的字符串数据
        nSql += len;
      }
      free(zLine);
      if( sqlite_complete(zSql) ){                              // 检查是否是完成的字符串数据
        data.cnt = 0;
        if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0     // 执行该sql语句并设置回调显示函数callback
             && zErrMsg!=0 ){
          printf("SQL error: %s\n", zErrMsg);                       // 如果出错则报错
          free(zErrMsg);
          zErrMsg = 0;
        }
        free(zSql);                                           // 释放内存
        zSql = 0;
        nSql = 0;
      }
    }                                                         // 等待下一条数据输入
  }
  sqlite_close(db);                                           // 关闭该数据库
  return 0;
}

该main函数主要就是处理输入的命令行参数,并循环处理传入的终端的命令行的sql语句。接着我们就继续分析sqlit_exec函数的执行过程;

/*
** Execute SQL code.  Return one of the SQLITE_ success/failure
** codes.  Also write an error message into memory obtained from
** malloc() and make *pzErrMsg point to that message.
**
** If the SQL is a query, then for each row in the query result
** the xCallback() function is called.  pArg becomes the first
** argument to xCallback().  If xCallback=NULL then no callback
** is invoked, even for queries.
*/
int sqlite_exec(
  sqlite *db,                 /* The database on which the SQL executes */
  char *zSql,                 /* The SQL to be executed */
  sqlite_callback xCallback,  /* Invoke this callback routine */
  void *pArg,                 /* First argument to xCallback() */
  char **pzErrMsg             /* Write error messages here */
){
  Parse sParse;                                     // 解析的保存的数据结构
  int rc;

  if( pzErrMsg ) *pzErrMsg = 0;
  if( (db->flags & SQLITE_Initialized)==0 ){
    int rc = sqliteInit(db, pzErrMsg);            // 检查是否数据库初始化完成如果未初始化完成则报错返回
    if( rc!=SQLITE_OK ) return rc;
  }
  memset(&sParse, 0, sizeof(sParse));             // 变量的内容指向的地址设置为空
  sParse.db = db;                                 // 设置数据库
  sParse.xCallback = xCallback;                   // 设置回调函数
  sParse.pArg = pArg;                             // 设置传入参数
  rc = sqliteRunParser(&sParse, zSql, pzErrMsg);    // 开始解析并执行
  sqliteStrRealloc(pzErrMsg);
  return rc;
}

主要就是进行了检查并设置了相关的参数进行配置,接着就执行了sqliteRunParser函数来执行;

int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
  int nErr = 0;
  int i;
  void *pEngine;
  int once = 1;
  static FILE *trace = 0;
  extern void *sqliteParserAlloc(void*(*)(int));                    // 该函数就是通过lemon来生成的函数
  extern void sqliteParserFree(void*, void(*)(void*));
  extern int sqliteParser(void*, int, ...);                         // 调用lemon定义的语法解析的函数,传入token来解析
  extern void sqliteParserTrace(FILE*, char *);

  i = 0;
  sqliteParseInfoReset(pParse);
  pEngine = sqliteParserAlloc((void*(*)(int))malloc);             
  if( pEngine==0 ){
    sqliteSetString(pzErrMsg, "out of memory", 0);
    return 1;
  }
  sqliteParserTrace(trace, "parser: ");                           // 设置追踪
  while( nErr==0 && i>=0 && zSql[i]!=0 ){                         // 循环遍历数据
    int tokenType;
    
    pParse->sLastToken.z = &zSql[i];                              
    pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType);  // 解析传入的数据获取token
    i += pParse->sLastToken.n;
    if( once ){
      pParse->sFirstToken = pParse->sLastToken;
      once = 0;
    }
    switch( tokenType ){                                          // 判断解析获得的数据
      case TK_SPACE:
        break;
      case TK_COMMENT: {
        /* Various debugging modes can be turned on and off using
        ** special SQL comments.  Check for the special comments
        ** here and take approriate action if found.
        */
#ifndef NDEBUG                                                        // 是否打日志
        char *z = pParse->sLastToken.z;
        if( sqliteStrNICmp(z,"--parser-trace-on--",19)==0 ){
          trace = stderr;
          sqliteParserTrace(trace, "parser: ");
        }else if( sqliteStrNICmp(z,"--parser-trace-off--", 20)==0 ){
          trace = 0;
          sqliteParserTrace(trace, "parser: ");
        }else if( sqliteStrNICmp(z,"--vdbe-trace-on--",17)==0 ){
          pParse->db->flags |= SQLITE_VdbeTrace;
        }else if( sqliteStrNICmp(z,"--vdbe-trace-off--", 18)==0 ){
          pParse->db->flags &= ~SQLITE_VdbeTrace;
#ifdef MEMORY_DEBUG
        }else if( sqliteStrNICmp(z,"--malloc-fail=",14)==0 ){
          sqlite_iMallocFail = atoi(&z[14]);
        }else if( sqliteStrNICmp(z,"--malloc-stats--", 16)==0 ){
          if( pParse->xCallback ){
            static char *azName[4] = {"malloc", "free", "to_fail", 0 };
            char *azArg[4];
            char zVal[3][30];
            sprintf(zVal[0],"%d", sqlite_nMalloc);
            sprintf(zVal[1],"%d", sqlite_nFree);
            sprintf(zVal[2],"%d", sqlite_iMallocFail);
            azArg[0] = zVal[0];
            azArg[1] = zVal[1];
            azArg[2] = zVal[2];
            azArg[3] = 0;
            pParse->xCallback(pParse->pArg, 3, azArg, azName);            // 通过回调函数打印对应的日志
          }
#endif
        }
#endif
        break;
      }
      case TK_ILLEGAL:                                                    // 解析的token不合法
        sqliteSetNString(pzErrMsg, "unrecognized token: \"", -1, 
           pParse->sLastToken.z, pParse->sLastToken.n, "\"", 1, 0);
        nErr++;
        break;
      default:
        sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse);     // 传入sqliteParser函数保存该词法
        if( pParse->zErrMsg && pParse->sErrToken.z ){                     // 检查是否出现错误
          sqliteSetNString(pzErrMsg, "near \"", -1, 
             pParse->sErrToken.z, pParse->sErrToken.n,
             "\": ", -1,
             pParse->zErrMsg, -1,
             0);
          nErr++;
          sqliteFree(pParse->zErrMsg);
          pParse->zErrMsg = 0;
        }
        break;
    }
  }
  if( nErr==0 ){                                                        // 如果没有错误
    sqliteParser(pEngine, 0, pParse->sLastToken, pParse);               // 开始执行词法与语法解析
    if( pParse->zErrMsg && pParse->sErrToken.z ){
       sqliteSetNString(pzErrMsg, "near \"", -1, 
          pParse->sErrToken.z, pParse->sErrToken.n,
          "\": ", -1,
          pParse->zErrMsg, -1,
          0);
       nErr++;
       sqliteFree(pParse->zErrMsg);
       pParse->zErrMsg = 0;
    }
  }
  sqliteParserFree(pEngine, free);                                    // 执行完成后释放内存
  if( pParse->zErrMsg ){                                              
    if( pzErrMsg ){
      sqliteFree(*pzErrMsg);
      *pzErrMsg = pParse->zErrMsg;
    }else{
      sqliteFree(pParse->zErrMsg);
    }
    if( !nErr ) nErr++;
  }
  if( pParse->pVdbe ){                                              // 释放虚拟机
    sqliteVdbeDelete(pParse->pVdbe);
    pParse->pVdbe = 0;
  }
  if( pParse->pNewTable ){                                          // 删除对应的table
    sqliteDeleteTable(pParse->db, pParse->pNewTable);
    pParse->pNewTable = 0;
  }
  sqliteParseInfoReset(pParse);                                     // 重置pParse并继续解析执行
  return nErr;
}

此时,由于使用的lemon的解析工具,对应的词法分析过程需要自行解析,所以,查看sqliteGetToken函数;

int sqliteGetToken(const char *z, int *tokenType){
  int i;
  switch( *z ){
    case ' ': case '\t': case '\n': case '\f': case '\r': {
      for(i=1; z[i] && isspace(z[i]); i++){}
      *tokenType = TK_SPACE;
      return i;
    }
    case '-': {
      if( z[1]==0 ) return -1;
      if( z[1]=='-' ){
        for(i=2; z[i] && z[i]!='\n'; i++){}
        *tokenType = TK_COMMENT;
        return i;
      }
      *tokenType = TK_MINUS;
      return 1;
    }
    case '(': {
      *tokenType = TK_LP;
      return 1;
    }
    case ')': {
      *tokenType = TK_RP;
      return 1;
    }
    case ';': {
      *tokenType = TK_SEMI;
      return 1;
    }
    case '+': {
      *tokenType = TK_PLUS;
      return 1;
    }
    case '*': {
      *tokenType = TK_STAR;
      return 1;
    }
    case '/': {
      *tokenType = TK_SLASH;
      return 1;
    }
    case '=': {
      *tokenType = TK_EQ;
      return 1 + (z[1]=='=');
    }
    case '<': {
      if( z[1]=='=' ){
        *tokenType = TK_LE;
        return 2;
      }else if( z[1]=='>' ){
        *tokenType = TK_NE;
        return 2;
      }else{
        *tokenType = TK_LT;
        return 1;
      }
    }
    case '>': {
      if( z[1]=='=' ){
        *tokenType = TK_GE;
        return 2;
      }else{
        *tokenType = TK_GT;
        return 1;
      }
    }
    case '!': {
      if( z[1]!='=' ){
        *tokenType = TK_ILLEGAL;
        return 2;
      }else{
        *tokenType = TK_NE;
        return 2;
      }
    }
    case '|': {
      if( z[1]!='|' ){
        *tokenType = TK_ILLEGAL;
        return 1;
      }else{
        *tokenType = TK_CONCAT;
        return 2;
      }
    }
    case ',': {
      *tokenType = TK_COMMA;
      return 1;
    }
    case '\'': case '"': {
      int delim = z[0];
      for(i=1; z[i]; i++){
        if( z[i]==delim ){
          if( z[i+1]==delim ){
            i++;
          }else{
            break;
          }
        }
      }
      if( z[i] ) i++;
      *tokenType = TK_STRING;
      return i;
    }
    case '.': {
      if( !isdigit(z[1]) ){
        *tokenType = TK_DOT;
        return 1;
      }
      /* Fall thru into the next case */
    }
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9': {
      *tokenType = TK_INTEGER;
      for(i=1; z[i] && isdigit(z[i]); i++){}
      if( z[i]=='.' ){
        i++;
        while( z[i] && isdigit(z[i]) ){ i++; }
        *tokenType = TK_FLOAT;
      }
      if( (z[i]=='e' || z[i]=='E') &&
           ( isdigit(z[i+1]) 
            || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
           )
      ){
        i += 2;
        while( z[i] && isdigit(z[i]) ){ i++; }
        *tokenType = TK_FLOAT;
      }else if( z[0]=='.' ){
        *tokenType = TK_FLOAT;
      }
      return i;
    }
    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
    case 'y': case 'z': case '_':
    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
    case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
    case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
    case 'Y': case 'Z': {
      for(i=1; z[i] && (isalnum(z[i]) || z[i]=='_'); i++){}
      *tokenType = sqliteKeywordCode(z, i);
      return i;
    }
    default: {
      break;
    }
  }
  *tokenType = TK_ILLEGAL;
  return 1;
}

该函数就是解析传入的内容,讲其转为对应的Token值返回,再传入词法之后,通过sqliteParser(pEngine, 0, pParse->sLastToken, pParse)开始执行对应的词法与语法过程,此时我们查看对应的parse.y文件;

%token_prefix TK_
%token_type {Token}                                         // token类型定义
%extra_argument {Parse *pParse}                             // 添加额外的传入值
%syntax_error {
  sqliteSetString(&pParse->zErrMsg,"syntax error",0);
  pParse->sErrToken = TOKEN;
}
%name sqliteParser                                          // 重命名解析函数
%include {
#include "sqliteInt.h"
#include "parse.h"
}


// Input is zero or more commands.
input ::= cmdlist.                                          // 输入多条cmd或者一条cmd

// These are extra tokens used by the lexer but never seen by the
// parser.  We put them in a rule so that the parser generator will
// add them to the parse.h output file.
//
input ::= END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
          UMINUS COLUMN AGG_FUNCTION.

// A list of commands is zero or more commands
//
cmdlist ::= ecmd.
cmdlist ::= cmdlist SEMI ecmd.
ecmd ::= explain cmd.  {sqliteExec(pParse);}              // 最终会执行sqliteExec(pParse)函数
ecmd ::= cmd.          {sqliteExec(pParse);}              
ecmd ::= .
explain ::= EXPLAIN.    {pParse->explain = 1;}            // 是否是分析该命令行

// The first form of a command is a CREATE TABLE statement.
//
cmd ::= create_table create_table_args.                   // 创建表
create_table ::= CREATE(X) TABLE id(Y).    {sqliteStartTable(pParse,&X,&Y);}    // 调用开始table
create_table_args ::= LP columnlist conslist_opt RP(X).
                                           {sqliteEndTable(pParse,&X);}
columnlist ::= columnlist COMMA column.
columnlist ::= column.

// About the only information used for a column is the name of the
// column.  The type is always just "text".  But the code will accept
// an elaborate typename.  Perhaps someday we'll do something with it.
//
column ::= columnid type carglist. 
columnid ::= id(X).                {sqliteAddColumn(pParse,&X);}                // 添加列
%type id {Token}
id(A) ::= ID(X).     {A = X;}
id(A) ::= STRING(X). {A = X;}
type ::= typename.
type ::= typename LP signed RP.
type ::= typename LP signed COMMA signed RP.
typename ::= id.
typename ::= typename id.
signed ::= INTEGER.
signed ::= PLUS INTEGER.
signed ::= MINUS INTEGER.
carglist ::= carglist carg.
carglist ::= .
carg ::= CONSTRAINT id ccons.
carg ::= ccons.
carg ::= DEFAULT STRING(X).          {sqliteAddDefaultValue(pParse,&X,0);}          // 设置默认值
carg ::= DEFAULT ID(X).              {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT INTEGER(X).         {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT PLUS INTEGER(X).    {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT MINUS INTEGER(X).   {sqliteAddDefaultValue(pParse,&X,1);}
carg ::= DEFAULT FLOAT(X).           {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT PLUS FLOAT(X).      {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT MINUS FLOAT(X).     {sqliteAddDefaultValue(pParse,&X,1);}
carg ::= DEFAULT NULL. 

// In addition to the type name, we also care about the primary key.
//
ccons ::= NOT NULL.
ccons ::= PRIMARY KEY sortorder.     {sqliteCreateIndex(pParse,0,0,0,0,0);}       // 创建索引
ccons ::= UNIQUE.
ccons ::= CHECK LP expr RP.

以上代码只是部分的parser.y文件,大家可以了解一下lemon的使用方法,此时先最终都会执行到sqliteExec函数;此时我们可以先分析一下sqliteInsert插入函数的执行的过程可知,

void sqliteInsert(
  Parse *pParse,        /* Parser context */
  Token *pTableName,    /* Name of table into which we are inserting */
  ExprList *pList,      /* List of values to be inserted */
  Select *pSelect,      /* A SELECT statement to use as the data source */
  IdList *pColumn       /* Column names corresponding to IDLIST. */
){
  Table *pTab;          /* The table to insert into */
  char *zTab;           /* Name of the table into which we are inserting */
  int i, j, idx;        /* Loop counters */
  Vdbe *v;              /* Generate code into this virtual machine */
  Index *pIdx;          /* For looping over indices of the table */
  int srcTab;           /* Date comes from this temporary cursor if >=0 */
  int nColumn;          /* Number of columns in the data */
  int base;             /* First available cursor */
  int iCont, iBreak;    /* Beginning and end of the loop over srcTab */

  /* Locate the table into which we will be inserting new information.
  */
  zTab = sqliteTableNameFromToken(pTableName);
  pTab = sqliteFindTable(pParse->db, zTab);
  sqliteFree(zTab);
  if( pTab==0 ){
    sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0, 
        pTableName->z, pTableName->n, 0);
    pParse->nErr++;
    goto insert_cleanup;
  }
  if( pTab->readOnly ){
    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
        " may not be modified", 0);
    pParse->nErr++;
    goto insert_cleanup;
  }

  /* Allocate a VDBE
  */
  v = sqliteGetVdbe(pParse);
  if( v==0 ) goto insert_cleanup;

  /* Figure out how many columns of data are supplied.  If the data
  ** is comming from a SELECT statement, then this step has to generate
  ** all the code to implement the SELECT statement and leave the data
  ** in a temporary table.  If data is coming from an expression list,
  ** then we just have to count the number of expressions.
  */
  if( pSelect ){
    int rc;
    srcTab = pParse->nTab++;
    sqliteVdbeAddOp(v, OP_Open, srcTab, 1, 0, 0);
    rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab);
    if( rc ) goto insert_cleanup;
    assert( pSelect->pEList );
    nColumn = pSelect->pEList->nExpr;
  }else{
    srcTab = -1;
    assert( pList );
    nColumn = pList->nExpr;
  }

  /* Make sure the number of columns in the source data matches the number
  ** of columns to be inserted into the table.
  */
  if( pColumn==0 && nColumn!=pTab->nCol ){
    char zNum1[30];
    char zNum2[30];
    sprintf(zNum1,"%d", nColumn);
    sprintf(zNum2,"%d", pTab->nCol);
    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
       " has ", zNum2, " columns but ",
       zNum1, " values were supplied", 0);
    pParse->nErr++;
    goto insert_cleanup;
  }
  if( pColumn!=0 && nColumn!=pColumn->nId ){
    char zNum1[30];
    char zNum2[30];
    sprintf(zNum1,"%d", nColumn);
    sprintf(zNum2,"%d", pColumn->nId);
    sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
       zNum2, " columns", 0);
    pParse->nErr++;
    goto insert_cleanup;
  }

  /* If the INSERT statement included an IDLIST term, then make sure
  ** all elements of the IDLIST really are columns of the table and 
  ** remember the column indices.
  */
  if( pColumn ){
    for(i=0; i<pColumn->nId; i++){
      pColumn->a[i].idx = -1;
    }
    for(i=0; i<pColumn->nId; i++){
      for(j=0; j<pTab->nCol; j++){
        if( sqliteStrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
          pColumn->a[i].idx = j;
          break;
        }
      }
      if( j>=pTab->nCol ){
        sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
           " has no column named ", pColumn->a[i].zName, 0);
        pParse->nErr++;
        goto insert_cleanup;
      }
    }
  }

  /* Open cursors into the table that is received the new data and
  ** all indices of that table.
  */
  base = pParse->nTab;
  sqliteVdbeAddOp(v, OP_Open, base, 1, pTab->zName, 0);
  for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
    sqliteVdbeAddOp(v, OP_Open, idx+base, 1, pIdx->zName, 0);
  }

  /* If the data source is a SELECT statement, then we have to create
  ** a loop because there might be multiple rows of data.  If the data
  ** source is an expression list, then exactly one row will be inserted
  ** and the loop is not used.
  */
  if( srcTab>=0 ){
    sqliteVdbeAddOp(v, OP_Rewind, srcTab, 0, 0, 0);
    iBreak = sqliteVdbeMakeLabel(v);
    iCont = sqliteVdbeAddOp(v, OP_Next, srcTab, iBreak, 0, 0);
  }

  /* Create a new entry in the table and fill it with data.
  */
  sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0);
  if( pTab->pIndex ){
    sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
  }
  for(i=0; i<pTab->nCol; i++){
    if( pColumn==0 ){
      j = i;
    }else{
      for(j=0; j<pColumn->nId; j++){
        if( pColumn->a[j].idx==i ) break;
      }
    }
    if( pColumn && j>=pColumn->nId ){
      char *zDflt = pTab->aCol[i].zDflt;
      if( zDflt==0 ){
        sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
      }else{
        sqliteVdbeAddOp(v, OP_String, 0, 0, zDflt, 0);
      }
    }else if( srcTab>=0 ){
      sqliteVdbeAddOp(v, OP_Field, srcTab, i, 0, 0); 
    }else{
      sqliteExprCode(pParse, pList->a[j].pExpr);
    }
  }
  sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
  sqliteVdbeAddOp(v, OP_Put, base, 0, 0, 0);

  /* Create appropriate entries for the new data row in all indices
  ** of the table.
  */
  for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
    if( pIdx->pNext ){
      sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
    }
    for(i=0; i<pIdx->nColumn; i++){
      int idx = pIdx->aiColumn[i];
      if( pColumn==0 ){
        j = idx;
      }else{
        for(j=0; j<pColumn->nId; j++){
          if( pColumn->a[j].idx==idx ) break;
        }
      }
      if( pColumn && j>=pColumn->nId ){
        char *zDflt = pTab->aCol[idx].zDflt;
        if( zDflt==0 ){
          sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
        }else{
          sqliteVdbeAddOp(v, OP_String, 0, 0, zDflt, 0);
        }
      }else if( srcTab>=0 ){
        sqliteVdbeAddOp(v, OP_Field, srcTab, idx, 0, 0); 
      }else{
        sqliteExprCode(pParse, pList->a[j].pExpr);
      }
    }
    sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
    sqliteVdbeAddOp(v, OP_PutIdx, idx+base, 0, 0, 0);
  }

  /* The bottom of the loop, if the data source is a SELECT statement
  */
  if( srcTab>=0 ){
    sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
    sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, iBreak);
  }

insert_cleanup:
  if( pList ) sqliteExprListDelete(pList);
  if( pSelect ) sqliteSelectDelete(pSelect);
  sqliteIdListDelete(pColumn);
}

该函数首先获取一个虚拟机sqliteGetVdbe,然后讲获取的虚拟机通过sqliteVdbeAddOp函数讲需要执行的操作全部压入虚拟机栈中,

int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2, const char *p3, int lbl){
  int i, j;

  i = p->nOp;
  p->nOp++;
  if( i>=p->nOpAlloc ){
    int oldSize = p->nOpAlloc;
    p->nOpAlloc = p->nOpAlloc*2 + 10;
    p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op));    // 重新扩展内存
    if( p->aOp==0 ){
      p->nOp = 0;
      p->nOpAlloc = 0;
      return 0;                                               // 如果扩展失败就返回
    }
    memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op));    // 讲申请的内存置空
  }
  p->aOp[i].opcode = op;                                              // 保存对应的opcode
  p->aOp[i].p1 = p1;
  if( p2<0 && (-1-p2)<p->nLabel && p->aLabel[-1-p2]>=0 ){
    p2 = p->aLabel[-1-p2];
  }
  p->aOp[i].p2 = p2;
  if( p3 && p3[0] ){
    p->aOp[i].p3 = sqliteStrDup(p3);
  }else{
    p->aOp[i].p3 = 0;
  }
  if( lbl<0 && (-lbl)<=p->nLabel ){
    p->aLabel[-1-lbl] = i;
    for(j=0; j<i; j++){
      if( p->aOp[j].p2==lbl ) p->aOp[j].p2 = i;
    }
  }
  return i;                                                   // 返回
}

当该插入过程执行完成之后,此时就会执行sqliteExec函数;

void sqliteExec(Parse *pParse){
  if( pParse->pVdbe ){
    if( pParse->explain ){                                                    // 判断是否是需要explain 
      sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg, 
                     &pParse->zErrMsg);                                       // 列表执行
    }else{
      FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0;
      sqliteVdbeTrace(pParse->pVdbe, trace);                                  // 添加trace
      sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg, 
                     &pParse->zErrMsg, pParse->db->pBusyArg,
                     pParse->db->xBusyCallback);                              // 执行添加的虚拟机字节码
    }
    sqliteVdbeDelete(pParse->pVdbe);                                          // 删除该虚拟机内容
    pParse->pVdbe = 0;
    pParse->colNamesSet = 0;
  }
}

此时正常情况下调用sqliteVdbeExec函数;

/*
** Execute the program in the VDBE.
**
** If an error occurs, an error message is written to memory obtained
** from sqliteMalloc() and *pzErrMsg is made to point to that memory.
** The return parameter is the number of errors.
**
** If the callback every returns non-zero, then the program exits
** immediately.  No error message but the function does return SQLITE_ABORT.
**
** A memory allocation error causes this routine to return SQLITE_NOMEM
** and abandon furture processing.
**
** Other fatal errors return SQLITE_ERROR.
**
** If a database file could not be opened because it is locked by
** another database instance, then the xBusy() callback is invoked
** with pBusyArg as its first argument, the name of the table as the
** second argument, and the number of times the open has been attempted
** as the third argument.  The xBusy() callback will typically wait
** for the database file to be openable, then return.  If xBusy()
** returns non-zero, another attempt is made to open the file.  If
** xBusy() returns zero, or if xBusy is NULL, then execution halts
** and this routine returns SQLITE_BUSY.
*/
int sqliteVdbeExec(
  Vdbe *p,                   /* The VDBE */
  sqlite_callback xCallback, /* The callback */
  void *pArg,                /* 1st argument to callback */
  char **pzErrMsg,           /* Error msg written here */
  void *pBusyArg,            /* 1st argument to the busy callback */
  int (*xBusy)(void*,const char*,int)  /* Called when a file is busy */
){
  int pc;                    /* The program counter */
  Op *pOp;                   /* Current operation */
  int rc;                    /* Value to return */
  char zBuf[100];            /* Space to sprintf() and integer */

  p->tos = -1;
  rc = SQLITE_OK;
#ifdef MEMORY_DEBUG
  if( access("vdbe_trace",0)==0 ){
    p->trace = stderr;
  }
#endif
  /* if( pzErrMsg ){ *pzErrMsg = 0; } */
  for(pc=0; rc==SQLITE_OK && pc<p->nOp && pc>=0; pc++){
    pOp = &p->aOp[pc];

    /* Only allow tracing if NDEBUG is not defined.
    */
#ifndef NDEBUG
    if( p->trace ){
      fprintf(p->trace,"%4d %-12s %4d %4d %s\n",
        pc, zOpName[pOp->opcode], pOp->p1, pOp->p2,
           pOp->p3 ? pOp->p3 : "");
    }
#endif

    switch( pOp->opcode ){
      /* Opcode:  Goto P2 * *
      **
      ** An unconditional jump to address P2.
      ** The next instruction executed will be 
      ** the one at index P2 from the beginning of
      ** the program.
      */
      case OP_Goto: {
        pc = pOp->p2 - 1;
        break;
      }

      /* Opcode:  Halt * * *
      **
      ** Exit immediately.  All open DBs, Lists, Sorts, etc are closed
      ** automatically.
      */
      case OP_Halt: {
        pc = p->nOp-1;
        break;
      }

      /* Opcode: Integer P1 * *
      **
      ** The integer value P1 is pushed onto the stack.
      */
      case OP_Integer: {
        int i = ++p->tos;
        if( NeedStack(p, p->tos) ) goto no_mem;
        p->aStack[i].i = pOp->p1;
        p->aStack[i].flags = STK_Int;
        break;
      }

      /* Opcode: String * * P3
      **
      ** The string value P3 is pushed onto the stack.
      */
      case OP_String: {
        int i = ++p->tos;
        char *z;
        if( NeedStack(p, p->tos) ) goto no_mem;
        z = pOp->p3;
        if( z==0 ) z = "";
        p->zStack[i] = z;
        p->aStack[i].n = strlen(z) + 1;
        p->aStack[i].flags = STK_Str;
        break;
      }
      ...
}

从该函数的执行流程可知,该函数就是遍历对应的vdb的栈的列表,根据pOp的操作码来进行相关的操作,例如在OP_PutIdx字节码的操作中通过sqliteDbbePut来调用gdbm_store来操作数据的存储。此时基本的执行流程就概述完成。

总结

本文只是很粗略的描述了sqlite-1.0.0的基本的一个执行流程,其中忽视了大量的细节内容,大家有兴趣可自行查看,该sqlite中由于用到了lemon工具,大家需要自行查阅该文档,再结合代码来分析,概述的执行流程图如下:在这里插入图片描述

由于本人才疏学浅,如有错误请批评指正。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值