modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/fts5/fts5_config.c (about)

     1  /*
     2  ** 2014 Jun 09
     3  **
     4  ** The author disclaims copyright to this source code.  In place of
     5  ** a legal notice, here is a blessing:
     6  **
     7  **    May you do good and not evil.
     8  **    May you find forgiveness for yourself and forgive others.
     9  **    May you share freely, never taking more than you give.
    10  **
    11  ******************************************************************************
    12  **
    13  ** This is an SQLite module implementing full-text search.
    14  */
    15  
    16  
    17  #include "fts5Int.h"
    18  
    19  #define FTS5_DEFAULT_PAGE_SIZE   4050
    20  #define FTS5_DEFAULT_AUTOMERGE      4
    21  #define FTS5_DEFAULT_USERMERGE      4
    22  #define FTS5_DEFAULT_CRISISMERGE   16
    23  #define FTS5_DEFAULT_HASHSIZE    (1024*1024)
    24  
    25  /* Maximum allowed page size */
    26  #define FTS5_MAX_PAGE_SIZE (128*1024)
    27  
    28  static int fts5_iswhitespace(char x){
    29    return (x==' ');
    30  }
    31  
    32  static int fts5_isopenquote(char x){
    33    return (x=='"' || x=='\'' || x=='[' || x=='`');
    34  }
    35  
    36  /*
    37  ** Argument pIn points to a character that is part of a nul-terminated 
    38  ** string. Return a pointer to the first character following *pIn in 
    39  ** the string that is not a white-space character.
    40  */
    41  static const char *fts5ConfigSkipWhitespace(const char *pIn){
    42    const char *p = pIn;
    43    if( p ){
    44      while( fts5_iswhitespace(*p) ){ p++; }
    45    }
    46    return p;
    47  }
    48  
    49  /*
    50  ** Argument pIn points to a character that is part of a nul-terminated 
    51  ** string. Return a pointer to the first character following *pIn in 
    52  ** the string that is not a "bareword" character.
    53  */
    54  static const char *fts5ConfigSkipBareword(const char *pIn){
    55    const char *p = pIn;
    56    while ( sqlite3Fts5IsBareword(*p) ) p++;
    57    if( p==pIn ) p = 0;
    58    return p;
    59  }
    60  
    61  static int fts5_isdigit(char a){
    62    return (a>='0' && a<='9');
    63  }
    64  
    65  
    66  
    67  static const char *fts5ConfigSkipLiteral(const char *pIn){
    68    const char *p = pIn;
    69    switch( *p ){
    70      case 'n': case 'N':
    71        if( sqlite3_strnicmp("null", p, 4)==0 ){
    72          p = &p[4];
    73        }else{
    74          p = 0;
    75        }
    76        break;
    77  
    78      case 'x': case 'X':
    79        p++;
    80        if( *p=='\'' ){
    81          p++;
    82          while( (*p>='a' && *p<='f') 
    83              || (*p>='A' && *p<='F') 
    84              || (*p>='0' && *p<='9') 
    85              ){
    86            p++;
    87          }
    88          if( *p=='\'' && 0==((p-pIn)%2) ){
    89            p++;
    90          }else{
    91            p = 0;
    92          }
    93        }else{
    94          p = 0;
    95        }
    96        break;
    97  
    98      case '\'':
    99        p++;
   100        while( p ){
   101          if( *p=='\'' ){
   102            p++;
   103            if( *p!='\'' ) break;
   104          }
   105          p++;
   106          if( *p==0 ) p = 0;
   107        }
   108        break;
   109  
   110      default:
   111        /* maybe a number */
   112        if( *p=='+' || *p=='-' ) p++;
   113        while( fts5_isdigit(*p) ) p++;
   114  
   115        /* At this point, if the literal was an integer, the parse is 
   116        ** finished. Or, if it is a floating point value, it may continue
   117        ** with either a decimal point or an 'E' character. */
   118        if( *p=='.' && fts5_isdigit(p[1]) ){
   119          p += 2;
   120          while( fts5_isdigit(*p) ) p++;
   121        }
   122        if( p==pIn ) p = 0;
   123  
   124        break;
   125    }
   126  
   127    return p;
   128  }
   129  
   130  /*
   131  ** The first character of the string pointed to by argument z is guaranteed
   132  ** to be an open-quote character (see function fts5_isopenquote()).
   133  **
   134  ** This function searches for the corresponding close-quote character within
   135  ** the string and, if found, dequotes the string in place and adds a new
   136  ** nul-terminator byte.
   137  **
   138  ** If the close-quote is found, the value returned is the byte offset of
   139  ** the character immediately following it. Or, if the close-quote is not 
   140  ** found, -1 is returned. If -1 is returned, the buffer is left in an 
   141  ** undefined state.
   142  */
   143  static int fts5Dequote(char *z){
   144    char q;
   145    int iIn = 1;
   146    int iOut = 0;
   147    q = z[0];
   148  
   149    /* Set stack variable q to the close-quote character */
   150    assert( q=='[' || q=='\'' || q=='"' || q=='`' );
   151    if( q=='[' ) q = ']';  
   152  
   153    while( ALWAYS(z[iIn]) ){
   154      if( z[iIn]==q ){
   155        if( z[iIn+1]!=q ){
   156          /* Character iIn was the close quote. */
   157          iIn++;
   158          break;
   159        }else{
   160          /* Character iIn and iIn+1 form an escaped quote character. Skip
   161          ** the input cursor past both and copy a single quote character 
   162          ** to the output buffer. */
   163          iIn += 2;
   164          z[iOut++] = q;
   165        }
   166      }else{
   167        z[iOut++] = z[iIn++];
   168      }
   169    }
   170  
   171    z[iOut] = '\0';
   172    return iIn;
   173  }
   174  
   175  /*
   176  ** Convert an SQL-style quoted string into a normal string by removing
   177  ** the quote characters.  The conversion is done in-place.  If the
   178  ** input does not begin with a quote character, then this routine
   179  ** is a no-op.
   180  **
   181  ** Examples:
   182  **
   183  **     "abc"   becomes   abc
   184  **     'xyz'   becomes   xyz
   185  **     [pqr]   becomes   pqr
   186  **     `mno`   becomes   mno
   187  */
   188  void sqlite3Fts5Dequote(char *z){
   189    char quote;                     /* Quote character (if any ) */
   190  
   191    assert( 0==fts5_iswhitespace(z[0]) );
   192    quote = z[0];
   193    if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
   194      fts5Dequote(z);
   195    }
   196  }
   197  
   198  
   199  struct Fts5Enum {
   200    const char *zName;
   201    int eVal;
   202  };
   203  typedef struct Fts5Enum Fts5Enum;
   204  
   205  static int fts5ConfigSetEnum(
   206    const Fts5Enum *aEnum, 
   207    const char *zEnum, 
   208    int *peVal
   209  ){
   210    int nEnum = (int)strlen(zEnum);
   211    int i;
   212    int iVal = -1;
   213  
   214    for(i=0; aEnum[i].zName; i++){
   215      if( sqlite3_strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){
   216        if( iVal>=0 ) return SQLITE_ERROR;
   217        iVal = aEnum[i].eVal;
   218      }
   219    }
   220  
   221    *peVal = iVal;
   222    return iVal<0 ? SQLITE_ERROR : SQLITE_OK;
   223  }
   224  
   225  /*
   226  ** Parse a "special" CREATE VIRTUAL TABLE directive and update
   227  ** configuration object pConfig as appropriate.
   228  **
   229  ** If successful, object pConfig is updated and SQLITE_OK returned. If
   230  ** an error occurs, an SQLite error code is returned and an error message
   231  ** may be left in *pzErr. It is the responsibility of the caller to
   232  ** eventually free any such error message using sqlite3_free().
   233  */
   234  static int fts5ConfigParseSpecial(
   235    Fts5Global *pGlobal,
   236    Fts5Config *pConfig,            /* Configuration object to update */
   237    const char *zCmd,               /* Special command to parse */
   238    const char *zArg,               /* Argument to parse */
   239    char **pzErr                    /* OUT: Error message */
   240  ){
   241    int rc = SQLITE_OK;
   242    int nCmd = (int)strlen(zCmd);
   243    if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){
   244      const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
   245      const char *p;
   246      int bFirst = 1;
   247      if( pConfig->aPrefix==0 ){
   248        pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
   249        if( rc ) return rc;
   250      }
   251  
   252      p = zArg;
   253      while( 1 ){
   254        int nPre = 0;
   255  
   256        while( p[0]==' ' ) p++;
   257        if( bFirst==0 && p[0]==',' ){
   258          p++;
   259          while( p[0]==' ' ) p++;
   260        }else if( p[0]=='\0' ){
   261          break;
   262        }
   263        if( p[0]<'0' || p[0]>'9' ){
   264          *pzErr = sqlite3_mprintf("malformed prefix=... directive");
   265          rc = SQLITE_ERROR;
   266          break;
   267        }
   268  
   269        if( pConfig->nPrefix==FTS5_MAX_PREFIX_INDEXES ){
   270          *pzErr = sqlite3_mprintf(
   271              "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES
   272          );
   273          rc = SQLITE_ERROR;
   274          break;
   275        }
   276  
   277        while( p[0]>='0' && p[0]<='9' && nPre<1000 ){
   278          nPre = nPre*10 + (p[0] - '0');
   279          p++;
   280        }
   281  
   282        if( nPre<=0 || nPre>=1000 ){
   283          *pzErr = sqlite3_mprintf("prefix length out of range (max 999)");
   284          rc = SQLITE_ERROR;
   285          break;
   286        }
   287  
   288        pConfig->aPrefix[pConfig->nPrefix] = nPre;
   289        pConfig->nPrefix++;
   290        bFirst = 0;
   291      }
   292      assert( pConfig->nPrefix<=FTS5_MAX_PREFIX_INDEXES );
   293      return rc;
   294    }
   295  
   296    if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
   297      const char *p = (const char*)zArg;
   298      int nArg = (int)strlen(zArg) + 1;
   299      char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
   300      char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
   301      char *pSpace = pDel;
   302  
   303      if( azArg && pSpace ){
   304        if( pConfig->pTok ){
   305          *pzErr = sqlite3_mprintf("multiple tokenize=... directives");
   306          rc = SQLITE_ERROR;
   307        }else{
   308          for(nArg=0; p && *p; nArg++){
   309            const char *p2 = fts5ConfigSkipWhitespace(p);
   310            if( *p2=='\'' ){
   311              p = fts5ConfigSkipLiteral(p2);
   312            }else{
   313              p = fts5ConfigSkipBareword(p2);
   314            }
   315            if( p ){
   316              memcpy(pSpace, p2, p-p2);
   317              azArg[nArg] = pSpace;
   318              sqlite3Fts5Dequote(pSpace);
   319              pSpace += (p - p2) + 1;
   320              p = fts5ConfigSkipWhitespace(p);
   321            }
   322          }
   323          if( p==0 ){
   324            *pzErr = sqlite3_mprintf("parse error in tokenize directive");
   325            rc = SQLITE_ERROR;
   326          }else{
   327            rc = sqlite3Fts5GetTokenizer(pGlobal, 
   328                (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi,
   329                pzErr
   330            );
   331          }
   332        }
   333      }
   334  
   335      sqlite3_free(azArg);
   336      sqlite3_free(pDel);
   337      return rc;
   338    }
   339  
   340    if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){
   341      if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
   342        *pzErr = sqlite3_mprintf("multiple content=... directives");
   343        rc = SQLITE_ERROR;
   344      }else{
   345        if( zArg[0] ){
   346          pConfig->eContent = FTS5_CONTENT_EXTERNAL;
   347          pConfig->zContent = sqlite3Fts5Mprintf(&rc, "%Q.%Q", pConfig->zDb,zArg);
   348        }else{
   349          pConfig->eContent = FTS5_CONTENT_NONE;
   350        }
   351      }
   352      return rc;
   353    }
   354  
   355    if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
   356      if( pConfig->zContentRowid ){
   357        *pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
   358        rc = SQLITE_ERROR;
   359      }else{
   360        pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1);
   361      }
   362      return rc;
   363    }
   364  
   365    if( sqlite3_strnicmp("columnsize", zCmd, nCmd)==0 ){
   366      if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
   367        *pzErr = sqlite3_mprintf("malformed columnsize=... directive");
   368        rc = SQLITE_ERROR;
   369      }else{
   370        pConfig->bColumnsize = (zArg[0]=='1');
   371      }
   372      return rc;
   373    }
   374  
   375    if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){
   376      const Fts5Enum aDetail[] = {
   377        { "none", FTS5_DETAIL_NONE },
   378        { "full", FTS5_DETAIL_FULL },
   379        { "columns", FTS5_DETAIL_COLUMNS },
   380        { 0, 0 }
   381      };
   382  
   383      if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
   384        *pzErr = sqlite3_mprintf("malformed detail=... directive");
   385      }
   386      return rc;
   387    }
   388  
   389    *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
   390    return SQLITE_ERROR;
   391  }
   392  
   393  /*
   394  ** Allocate an instance of the default tokenizer ("simple") at 
   395  ** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
   396  ** code if an error occurs.
   397  */
   398  static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
   399    assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
   400    return sqlite3Fts5GetTokenizer(
   401        pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
   402    );
   403  }
   404  
   405  /*
   406  ** Gobble up the first bareword or quoted word from the input buffer zIn.
   407  ** Return a pointer to the character immediately following the last in
   408  ** the gobbled word if successful, or a NULL pointer otherwise (failed
   409  ** to find close-quote character).
   410  **
   411  ** Before returning, set pzOut to point to a new buffer containing a
   412  ** nul-terminated, dequoted copy of the gobbled word. If the word was
   413  ** quoted, *pbQuoted is also set to 1 before returning.
   414  **
   415  ** If *pRc is other than SQLITE_OK when this function is called, it is
   416  ** a no-op (NULL is returned). Otherwise, if an OOM occurs within this
   417  ** function, *pRc is set to SQLITE_NOMEM before returning. *pRc is *not*
   418  ** set if a parse error (failed to find close quote) occurs.
   419  */
   420  static const char *fts5ConfigGobbleWord(
   421    int *pRc,                       /* IN/OUT: Error code */
   422    const char *zIn,                /* Buffer to gobble string/bareword from */
   423    char **pzOut,                   /* OUT: malloc'd buffer containing str/bw */
   424    int *pbQuoted                   /* OUT: Set to true if dequoting required */
   425  ){
   426    const char *zRet = 0;
   427  
   428    int nIn = (int)strlen(zIn);
   429    char *zOut = sqlite3_malloc(nIn+1);
   430  
   431    assert( *pRc==SQLITE_OK );
   432    *pbQuoted = 0;
   433    *pzOut = 0;
   434  
   435    if( zOut==0 ){
   436      *pRc = SQLITE_NOMEM;
   437    }else{
   438      memcpy(zOut, zIn, nIn+1);
   439      if( fts5_isopenquote(zOut[0]) ){
   440        int ii = fts5Dequote(zOut);
   441        zRet = &zIn[ii];
   442        *pbQuoted = 1;
   443      }else{
   444        zRet = fts5ConfigSkipBareword(zIn);
   445        if( zRet ){
   446          zOut[zRet-zIn] = '\0';
   447        }
   448      }
   449    }
   450  
   451    if( zRet==0 ){
   452      sqlite3_free(zOut);
   453    }else{
   454      *pzOut = zOut;
   455    }
   456  
   457    return zRet;
   458  }
   459  
   460  static int fts5ConfigParseColumn(
   461    Fts5Config *p, 
   462    char *zCol, 
   463    char *zArg, 
   464    char **pzErr
   465  ){
   466    int rc = SQLITE_OK;
   467    if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME) 
   468     || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME) 
   469    ){
   470      *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol);
   471      rc = SQLITE_ERROR;
   472    }else if( zArg ){
   473      if( 0==sqlite3_stricmp(zArg, "unindexed") ){
   474        p->abUnindexed[p->nCol] = 1;
   475      }else{
   476        *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg);
   477        rc = SQLITE_ERROR;
   478      }
   479    }
   480  
   481    p->azCol[p->nCol++] = zCol;
   482    return rc;
   483  }
   484  
   485  /*
   486  ** Populate the Fts5Config.zContentExprlist string.
   487  */
   488  static int fts5ConfigMakeExprlist(Fts5Config *p){
   489    int i;
   490    int rc = SQLITE_OK;
   491    Fts5Buffer buf = {0, 0, 0};
   492  
   493    sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid);
   494    if( p->eContent!=FTS5_CONTENT_NONE ){
   495      for(i=0; i<p->nCol; i++){
   496        if( p->eContent==FTS5_CONTENT_EXTERNAL ){
   497          sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]);
   498        }else{
   499          sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i);
   500        }
   501      }
   502    }
   503  
   504    assert( p->zContentExprlist==0 );
   505    p->zContentExprlist = (char*)buf.p;
   506    return rc;
   507  }
   508  
   509  /*
   510  ** Arguments nArg/azArg contain the string arguments passed to the xCreate
   511  ** or xConnect method of the virtual table. This function attempts to 
   512  ** allocate an instance of Fts5Config containing the results of parsing
   513  ** those arguments.
   514  **
   515  ** If successful, SQLITE_OK is returned and *ppOut is set to point to the
   516  ** new Fts5Config object. If an error occurs, an SQLite error code is 
   517  ** returned, *ppOut is set to NULL and an error message may be left in
   518  ** *pzErr. It is the responsibility of the caller to eventually free any 
   519  ** such error message using sqlite3_free().
   520  */
   521  int sqlite3Fts5ConfigParse(
   522    Fts5Global *pGlobal,
   523    sqlite3 *db,
   524    int nArg,                       /* Number of arguments */
   525    const char **azArg,             /* Array of nArg CREATE VIRTUAL TABLE args */
   526    Fts5Config **ppOut,             /* OUT: Results of parse */
   527    char **pzErr                    /* OUT: Error message */
   528  ){
   529    int rc = SQLITE_OK;             /* Return code */
   530    Fts5Config *pRet;               /* New object to return */
   531    int i;
   532    int nByte;
   533  
   534    *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
   535    if( pRet==0 ) return SQLITE_NOMEM;
   536    memset(pRet, 0, sizeof(Fts5Config));
   537    pRet->db = db;
   538    pRet->iCookie = -1;
   539  
   540    nByte = nArg * (sizeof(char*) + sizeof(u8));
   541    pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte);
   542    pRet->abUnindexed = (u8*)&pRet->azCol[nArg];
   543    pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
   544    pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
   545    pRet->bColumnsize = 1;
   546    pRet->eDetail = FTS5_DETAIL_FULL;
   547  #ifdef SQLITE_DEBUG
   548    pRet->bPrefixIndex = 1;
   549  #endif
   550    if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
   551      *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
   552      rc = SQLITE_ERROR;
   553    }
   554  
   555    for(i=3; rc==SQLITE_OK && i<nArg; i++){
   556      const char *zOrig = azArg[i];
   557      const char *z;
   558      char *zOne = 0;
   559      char *zTwo = 0;
   560      int bOption = 0;
   561      int bMustBeCol = 0;
   562  
   563      z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol);
   564      z = fts5ConfigSkipWhitespace(z);
   565      if( z && *z=='=' ){
   566        bOption = 1;
   567        z++;
   568        if( bMustBeCol ) z = 0;
   569      }
   570      z = fts5ConfigSkipWhitespace(z);
   571      if( z && z[0] ){
   572        int bDummy;
   573        z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy);
   574        if( z && z[0] ) z = 0;
   575      }
   576  
   577      if( rc==SQLITE_OK ){
   578        if( z==0 ){
   579          *pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
   580          rc = SQLITE_ERROR;
   581        }else{
   582          if( bOption ){
   583            rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr);
   584          }else{
   585            rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
   586            zOne = 0;
   587          }
   588        }
   589      }
   590  
   591      sqlite3_free(zOne);
   592      sqlite3_free(zTwo);
   593    }
   594  
   595    /* If a tokenizer= option was successfully parsed, the tokenizer has
   596    ** already been allocated. Otherwise, allocate an instance of the default
   597    ** tokenizer (unicode61) now.  */
   598    if( rc==SQLITE_OK && pRet->pTok==0 ){
   599      rc = fts5ConfigDefaultTokenizer(pGlobal, pRet);
   600    }
   601  
   602    /* If no zContent option was specified, fill in the default values. */
   603    if( rc==SQLITE_OK && pRet->zContent==0 ){
   604      const char *zTail = 0;
   605      assert( pRet->eContent==FTS5_CONTENT_NORMAL 
   606           || pRet->eContent==FTS5_CONTENT_NONE 
   607      );
   608      if( pRet->eContent==FTS5_CONTENT_NORMAL ){
   609        zTail = "content";
   610      }else if( pRet->bColumnsize ){
   611        zTail = "docsize";
   612      }
   613  
   614      if( zTail ){
   615        pRet->zContent = sqlite3Fts5Mprintf(
   616            &rc, "%Q.'%q_%s'", pRet->zDb, pRet->zName, zTail
   617        );
   618      }
   619    }
   620  
   621    if( rc==SQLITE_OK && pRet->zContentRowid==0 ){
   622      pRet->zContentRowid = sqlite3Fts5Strndup(&rc, "rowid", -1);
   623    }
   624  
   625    /* Formulate the zContentExprlist text */
   626    if( rc==SQLITE_OK ){
   627      rc = fts5ConfigMakeExprlist(pRet);
   628    }
   629  
   630    if( rc!=SQLITE_OK ){
   631      sqlite3Fts5ConfigFree(pRet);
   632      *ppOut = 0;
   633    }
   634    return rc;
   635  }
   636  
   637  /*
   638  ** Free the configuration object passed as the only argument.
   639  */
   640  void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
   641    if( pConfig ){
   642      int i;
   643      if( pConfig->pTok ){
   644        pConfig->pTokApi->xDelete(pConfig->pTok);
   645      }
   646      sqlite3_free(pConfig->zDb);
   647      sqlite3_free(pConfig->zName);
   648      for(i=0; i<pConfig->nCol; i++){
   649        sqlite3_free(pConfig->azCol[i]);
   650      }
   651      sqlite3_free(pConfig->azCol);
   652      sqlite3_free(pConfig->aPrefix);
   653      sqlite3_free(pConfig->zRank);
   654      sqlite3_free(pConfig->zRankArgs);
   655      sqlite3_free(pConfig->zContent);
   656      sqlite3_free(pConfig->zContentRowid);
   657      sqlite3_free(pConfig->zContentExprlist);
   658      sqlite3_free(pConfig);
   659    }
   660  }
   661  
   662  /*
   663  ** Call sqlite3_declare_vtab() based on the contents of the configuration
   664  ** object passed as the only argument. Return SQLITE_OK if successful, or
   665  ** an SQLite error code if an error occurs.
   666  */
   667  int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
   668    int i;
   669    int rc = SQLITE_OK;
   670    char *zSql;
   671  
   672    zSql = sqlite3Fts5Mprintf(&rc, "CREATE TABLE x(");
   673    for(i=0; zSql && i<pConfig->nCol; i++){
   674      const char *zSep = (i==0?"":", ");
   675      zSql = sqlite3Fts5Mprintf(&rc, "%z%s%Q", zSql, zSep, pConfig->azCol[i]);
   676    }
   677    zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)", 
   678        zSql, pConfig->zName, FTS5_RANK_NAME
   679    );
   680  
   681    assert( zSql || rc==SQLITE_NOMEM );
   682    if( zSql ){
   683      rc = sqlite3_declare_vtab(pConfig->db, zSql);
   684      sqlite3_free(zSql);
   685    }
   686    
   687    return rc;
   688  }
   689  
   690  /*
   691  ** Tokenize the text passed via the second and third arguments.
   692  **
   693  ** The callback is invoked once for each token in the input text. The
   694  ** arguments passed to it are, in order:
   695  **
   696  **     void *pCtx          // Copy of 4th argument to sqlite3Fts5Tokenize()
   697  **     const char *pToken  // Pointer to buffer containing token
   698  **     int nToken          // Size of token in bytes
   699  **     int iStart          // Byte offset of start of token within input text
   700  **     int iEnd            // Byte offset of end of token within input text
   701  **     int iPos            // Position of token in input (first token is 0)
   702  **
   703  ** If the callback returns a non-zero value the tokenization is abandoned
   704  ** and no further callbacks are issued. 
   705  **
   706  ** This function returns SQLITE_OK if successful or an SQLite error code
   707  ** if an error occurs. If the tokenization was abandoned early because
   708  ** the callback returned SQLITE_DONE, this is not an error and this function
   709  ** still returns SQLITE_OK. Or, if the tokenization was abandoned early
   710  ** because the callback returned another non-zero value, it is assumed
   711  ** to be an SQLite error code and returned to the caller.
   712  */
   713  int sqlite3Fts5Tokenize(
   714    Fts5Config *pConfig,            /* FTS5 Configuration object */
   715    int flags,                      /* FTS5_TOKENIZE_* flags */
   716    const char *pText, int nText,   /* Text to tokenize */
   717    void *pCtx,                     /* Context passed to xToken() */
   718    int (*xToken)(void*, int, const char*, int, int, int)    /* Callback */
   719  ){
   720    if( pText==0 ) return SQLITE_OK;
   721    return pConfig->pTokApi->xTokenize(
   722        pConfig->pTok, pCtx, flags, pText, nText, xToken
   723    );
   724  }
   725  
   726  /*
   727  ** Argument pIn points to the first character in what is expected to be
   728  ** a comma-separated list of SQL literals followed by a ')' character.
   729  ** If it actually is this, return a pointer to the ')'. Otherwise, return
   730  ** NULL to indicate a parse error.
   731  */
   732  static const char *fts5ConfigSkipArgs(const char *pIn){
   733    const char *p = pIn;
   734    
   735    while( 1 ){
   736      p = fts5ConfigSkipWhitespace(p);
   737      p = fts5ConfigSkipLiteral(p);
   738      p = fts5ConfigSkipWhitespace(p);
   739      if( p==0 || *p==')' ) break;
   740      if( *p!=',' ){
   741        p = 0;
   742        break;
   743      }
   744      p++;
   745    }
   746  
   747    return p;
   748  }
   749  
   750  /*
   751  ** Parameter zIn contains a rank() function specification. The format of 
   752  ** this is:
   753  **
   754  **   + Bareword (function name)
   755  **   + Open parenthesis - "("
   756  **   + Zero or more SQL literals in a comma separated list
   757  **   + Close parenthesis - ")"
   758  */
   759  int sqlite3Fts5ConfigParseRank(
   760    const char *zIn,                /* Input string */
   761    char **pzRank,                  /* OUT: Rank function name */
   762    char **pzRankArgs               /* OUT: Rank function arguments */
   763  ){
   764    const char *p = zIn;
   765    const char *pRank;
   766    char *zRank = 0;
   767    char *zRankArgs = 0;
   768    int rc = SQLITE_OK;
   769  
   770    *pzRank = 0;
   771    *pzRankArgs = 0;
   772  
   773    if( p==0 ){
   774      rc = SQLITE_ERROR;
   775    }else{
   776      p = fts5ConfigSkipWhitespace(p);
   777      pRank = p;
   778      p = fts5ConfigSkipBareword(p);
   779  
   780      if( p ){
   781        zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank);
   782        if( zRank ) memcpy(zRank, pRank, p-pRank);
   783      }else{
   784        rc = SQLITE_ERROR;
   785      }
   786  
   787      if( rc==SQLITE_OK ){
   788        p = fts5ConfigSkipWhitespace(p);
   789        if( *p!='(' ) rc = SQLITE_ERROR;
   790        p++;
   791      }
   792      if( rc==SQLITE_OK ){
   793        const char *pArgs; 
   794        p = fts5ConfigSkipWhitespace(p);
   795        pArgs = p;
   796        if( *p!=')' ){
   797          p = fts5ConfigSkipArgs(p);
   798          if( p==0 ){
   799            rc = SQLITE_ERROR;
   800          }else{
   801            zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs);
   802            if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs);
   803          }
   804        }
   805      }
   806    }
   807  
   808    if( rc!=SQLITE_OK ){
   809      sqlite3_free(zRank);
   810      assert( zRankArgs==0 );
   811    }else{
   812      *pzRank = zRank;
   813      *pzRankArgs = zRankArgs;
   814    }
   815    return rc;
   816  }
   817  
   818  int sqlite3Fts5ConfigSetValue(
   819    Fts5Config *pConfig, 
   820    const char *zKey, 
   821    sqlite3_value *pVal,
   822    int *pbBadkey
   823  ){
   824    int rc = SQLITE_OK;
   825  
   826    if( 0==sqlite3_stricmp(zKey, "pgsz") ){
   827      int pgsz = 0;
   828      if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
   829        pgsz = sqlite3_value_int(pVal);
   830      }
   831      if( pgsz<=0 || pgsz>FTS5_MAX_PAGE_SIZE ){
   832        *pbBadkey = 1;
   833      }else{
   834        pConfig->pgsz = pgsz;
   835      }
   836    }
   837  
   838    else if( 0==sqlite3_stricmp(zKey, "hashsize") ){
   839      int nHashSize = -1;
   840      if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
   841        nHashSize = sqlite3_value_int(pVal);
   842      }
   843      if( nHashSize<=0 ){
   844        *pbBadkey = 1;
   845      }else{
   846        pConfig->nHashSize = nHashSize;
   847      }
   848    }
   849  
   850    else if( 0==sqlite3_stricmp(zKey, "automerge") ){
   851      int nAutomerge = -1;
   852      if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
   853        nAutomerge = sqlite3_value_int(pVal);
   854      }
   855      if( nAutomerge<0 || nAutomerge>64 ){
   856        *pbBadkey = 1;
   857      }else{
   858        if( nAutomerge==1 ) nAutomerge = FTS5_DEFAULT_AUTOMERGE;
   859        pConfig->nAutomerge = nAutomerge;
   860      }
   861    }
   862  
   863    else if( 0==sqlite3_stricmp(zKey, "usermerge") ){
   864      int nUsermerge = -1;
   865      if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
   866        nUsermerge = sqlite3_value_int(pVal);
   867      }
   868      if( nUsermerge<2 || nUsermerge>16 ){
   869        *pbBadkey = 1;
   870      }else{
   871        pConfig->nUsermerge = nUsermerge;
   872      }
   873    }
   874  
   875    else if( 0==sqlite3_stricmp(zKey, "crisismerge") ){
   876      int nCrisisMerge = -1;
   877      if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
   878        nCrisisMerge = sqlite3_value_int(pVal);
   879      }
   880      if( nCrisisMerge<0 ){
   881        *pbBadkey = 1;
   882      }else{
   883        if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
   884        pConfig->nCrisisMerge = nCrisisMerge;
   885      }
   886    }
   887  
   888    else if( 0==sqlite3_stricmp(zKey, "rank") ){
   889      const char *zIn = (const char*)sqlite3_value_text(pVal);
   890      char *zRank;
   891      char *zRankArgs;
   892      rc = sqlite3Fts5ConfigParseRank(zIn, &zRank, &zRankArgs);
   893      if( rc==SQLITE_OK ){
   894        sqlite3_free(pConfig->zRank);
   895        sqlite3_free(pConfig->zRankArgs);
   896        pConfig->zRank = zRank;
   897        pConfig->zRankArgs = zRankArgs;
   898      }else if( rc==SQLITE_ERROR ){
   899        rc = SQLITE_OK;
   900        *pbBadkey = 1;
   901      }
   902    }else{
   903      *pbBadkey = 1;
   904    }
   905    return rc;
   906  }
   907  
   908  /*
   909  ** Load the contents of the %_config table into memory.
   910  */
   911  int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
   912    const char *zSelect = "SELECT k, v FROM %Q.'%q_config'";
   913    char *zSql;
   914    sqlite3_stmt *p = 0;
   915    int rc = SQLITE_OK;
   916    int iVersion = 0;
   917  
   918    /* Set default values */
   919    pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE;
   920    pConfig->nAutomerge = FTS5_DEFAULT_AUTOMERGE;
   921    pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
   922    pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
   923    pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
   924  
   925    zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
   926    if( zSql ){
   927      rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p, 0);
   928      sqlite3_free(zSql);
   929    }
   930  
   931    assert( rc==SQLITE_OK || p==0 );
   932    if( rc==SQLITE_OK ){
   933      while( SQLITE_ROW==sqlite3_step(p) ){
   934        const char *zK = (const char*)sqlite3_column_text(p, 0);
   935        sqlite3_value *pVal = sqlite3_column_value(p, 1);
   936        if( 0==sqlite3_stricmp(zK, "version") ){
   937          iVersion = sqlite3_value_int(pVal);
   938        }else{
   939          int bDummy = 0;
   940          sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy);
   941        }
   942      }
   943      rc = sqlite3_finalize(p);
   944    }
   945    
   946    if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){
   947      rc = SQLITE_ERROR;
   948      if( pConfig->pzErrmsg ){
   949        assert( 0==*pConfig->pzErrmsg );
   950        *pConfig->pzErrmsg = sqlite3_mprintf(
   951            "invalid fts5 file format (found %d, expected %d) - run 'rebuild'",
   952            iVersion, FTS5_CURRENT_VERSION
   953        );
   954      }
   955    }
   956  
   957    if( rc==SQLITE_OK ){
   958      pConfig->iCookie = iCookie;
   959    }
   960    return rc;
   961  }