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

     1  /*
     2  ** 2011 Jan 27
     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  */
    14  #include "fts3Int.h"
    15  #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
    16  
    17  #include <string.h>
    18  #include <assert.h>
    19  
    20  typedef struct Fts3auxTable Fts3auxTable;
    21  typedef struct Fts3auxCursor Fts3auxCursor;
    22  
    23  struct Fts3auxTable {
    24    sqlite3_vtab base;              /* Base class used by SQLite core */
    25    Fts3Table *pFts3Tab;
    26  };
    27  
    28  struct Fts3auxCursor {
    29    sqlite3_vtab_cursor base;       /* Base class used by SQLite core */
    30    Fts3MultiSegReader csr;        /* Must be right after "base" */
    31    Fts3SegFilter filter;
    32    char *zStop;
    33    int nStop;                      /* Byte-length of string zStop */
    34    int iLangid;                    /* Language id to query */
    35    int isEof;                      /* True if cursor is at EOF */
    36    sqlite3_int64 iRowid;           /* Current rowid */
    37  
    38    int iCol;                       /* Current value of 'col' column */
    39    int nStat;                      /* Size of aStat[] array */
    40    struct Fts3auxColstats {
    41      sqlite3_int64 nDoc;           /* 'documents' values for current csr row */
    42      sqlite3_int64 nOcc;           /* 'occurrences' values for current csr row */
    43    } *aStat;
    44  };
    45  
    46  /*
    47  ** Schema of the terms table.
    48  */
    49  #define FTS3_AUX_SCHEMA \
    50    "CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)"
    51  
    52  /*
    53  ** This function does all the work for both the xConnect and xCreate methods.
    54  ** These tables have no persistent representation of their own, so xConnect
    55  ** and xCreate are identical operations.
    56  */
    57  static int fts3auxConnectMethod(
    58    sqlite3 *db,                    /* Database connection */
    59    void *pUnused,                  /* Unused */
    60    int argc,                       /* Number of elements in argv array */
    61    const char * const *argv,       /* xCreate/xConnect argument array */
    62    sqlite3_vtab **ppVtab,          /* OUT: New sqlite3_vtab object */
    63    char **pzErr                    /* OUT: sqlite3_malloc'd error message */
    64  ){
    65    char const *zDb;                /* Name of database (e.g. "main") */
    66    char const *zFts3;              /* Name of fts3 table */
    67    int nDb;                        /* Result of strlen(zDb) */
    68    int nFts3;                      /* Result of strlen(zFts3) */
    69    int nByte;                      /* Bytes of space to allocate here */
    70    int rc;                         /* value returned by declare_vtab() */
    71    Fts3auxTable *p;                /* Virtual table object to return */
    72  
    73    UNUSED_PARAMETER(pUnused);
    74  
    75    /* The user should invoke this in one of two forms:
    76    **
    77    **     CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table);
    78    **     CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table-db, fts4-table);
    79    */
    80    if( argc!=4 && argc!=5 ) goto bad_args;
    81  
    82    zDb = argv[1]; 
    83    nDb = (int)strlen(zDb);
    84    if( argc==5 ){
    85      if( nDb==4 && 0==sqlite3_strnicmp("temp", zDb, 4) ){
    86        zDb = argv[3]; 
    87        nDb = (int)strlen(zDb);
    88        zFts3 = argv[4];
    89      }else{
    90        goto bad_args;
    91      }
    92    }else{
    93      zFts3 = argv[3];
    94    }
    95    nFts3 = (int)strlen(zFts3);
    96  
    97    rc = sqlite3_declare_vtab(db, FTS3_AUX_SCHEMA);
    98    if( rc!=SQLITE_OK ) return rc;
    99  
   100    nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
   101    p = (Fts3auxTable *)sqlite3_malloc(nByte);
   102    if( !p ) return SQLITE_NOMEM;
   103    memset(p, 0, nByte);
   104  
   105    p->pFts3Tab = (Fts3Table *)&p[1];
   106    p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1];
   107    p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1];
   108    p->pFts3Tab->db = db;
   109    p->pFts3Tab->nIndex = 1;
   110  
   111    memcpy((char *)p->pFts3Tab->zDb, zDb, nDb);
   112    memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3);
   113    sqlite3Fts3Dequote((char *)p->pFts3Tab->zName);
   114  
   115    *ppVtab = (sqlite3_vtab *)p;
   116    return SQLITE_OK;
   117  
   118   bad_args:
   119    sqlite3Fts3ErrMsg(pzErr, "invalid arguments to fts4aux constructor");
   120    return SQLITE_ERROR;
   121  }
   122  
   123  /*
   124  ** This function does the work for both the xDisconnect and xDestroy methods.
   125  ** These tables have no persistent representation of their own, so xDisconnect
   126  ** and xDestroy are identical operations.
   127  */
   128  static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){
   129    Fts3auxTable *p = (Fts3auxTable *)pVtab;
   130    Fts3Table *pFts3 = p->pFts3Tab;
   131    int i;
   132  
   133    /* Free any prepared statements held */
   134    for(i=0; i<SizeofArray(pFts3->aStmt); i++){
   135      sqlite3_finalize(pFts3->aStmt[i]);
   136    }
   137    sqlite3_free(pFts3->zSegmentsTbl);
   138    sqlite3_free(p);
   139    return SQLITE_OK;
   140  }
   141  
   142  #define FTS4AUX_EQ_CONSTRAINT 1
   143  #define FTS4AUX_GE_CONSTRAINT 2
   144  #define FTS4AUX_LE_CONSTRAINT 4
   145  
   146  /*
   147  ** xBestIndex - Analyze a WHERE and ORDER BY clause.
   148  */
   149  static int fts3auxBestIndexMethod(
   150    sqlite3_vtab *pVTab, 
   151    sqlite3_index_info *pInfo
   152  ){
   153    int i;
   154    int iEq = -1;
   155    int iGe = -1;
   156    int iLe = -1;
   157    int iLangid = -1;
   158    int iNext = 1;                  /* Next free argvIndex value */
   159  
   160    UNUSED_PARAMETER(pVTab);
   161  
   162    /* This vtab delivers always results in "ORDER BY term ASC" order. */
   163    if( pInfo->nOrderBy==1 
   164     && pInfo->aOrderBy[0].iColumn==0 
   165     && pInfo->aOrderBy[0].desc==0
   166    ){
   167      pInfo->orderByConsumed = 1;
   168    }
   169  
   170    /* Search for equality and range constraints on the "term" column. 
   171    ** And equality constraints on the hidden "languageid" column. */
   172    for(i=0; i<pInfo->nConstraint; i++){
   173      if( pInfo->aConstraint[i].usable ){
   174        int op = pInfo->aConstraint[i].op;
   175        int iCol = pInfo->aConstraint[i].iColumn;
   176  
   177        if( iCol==0 ){
   178          if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
   179          if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
   180          if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
   181          if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
   182          if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
   183        }
   184        if( iCol==4 ){
   185          if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iLangid = i;
   186        }
   187      }
   188    }
   189  
   190    if( iEq>=0 ){
   191      pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
   192      pInfo->aConstraintUsage[iEq].argvIndex = iNext++;
   193      pInfo->estimatedCost = 5;
   194    }else{
   195      pInfo->idxNum = 0;
   196      pInfo->estimatedCost = 20000;
   197      if( iGe>=0 ){
   198        pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
   199        pInfo->aConstraintUsage[iGe].argvIndex = iNext++;
   200        pInfo->estimatedCost /= 2;
   201      }
   202      if( iLe>=0 ){
   203        pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
   204        pInfo->aConstraintUsage[iLe].argvIndex = iNext++;
   205        pInfo->estimatedCost /= 2;
   206      }
   207    }
   208    if( iLangid>=0 ){
   209      pInfo->aConstraintUsage[iLangid].argvIndex = iNext++;
   210      pInfo->estimatedCost--;
   211    }
   212  
   213    return SQLITE_OK;
   214  }
   215  
   216  /*
   217  ** xOpen - Open a cursor.
   218  */
   219  static int fts3auxOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
   220    Fts3auxCursor *pCsr;            /* Pointer to cursor object to return */
   221  
   222    UNUSED_PARAMETER(pVTab);
   223  
   224    pCsr = (Fts3auxCursor *)sqlite3_malloc(sizeof(Fts3auxCursor));
   225    if( !pCsr ) return SQLITE_NOMEM;
   226    memset(pCsr, 0, sizeof(Fts3auxCursor));
   227  
   228    *ppCsr = (sqlite3_vtab_cursor *)pCsr;
   229    return SQLITE_OK;
   230  }
   231  
   232  /*
   233  ** xClose - Close a cursor.
   234  */
   235  static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){
   236    Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
   237    Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
   238  
   239    sqlite3Fts3SegmentsClose(pFts3);
   240    sqlite3Fts3SegReaderFinish(&pCsr->csr);
   241    sqlite3_free((void *)pCsr->filter.zTerm);
   242    sqlite3_free(pCsr->zStop);
   243    sqlite3_free(pCsr->aStat);
   244    sqlite3_free(pCsr);
   245    return SQLITE_OK;
   246  }
   247  
   248  static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){
   249    if( nSize>pCsr->nStat ){
   250      struct Fts3auxColstats *aNew;
   251      aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, 
   252          sizeof(struct Fts3auxColstats) * nSize
   253      );
   254      if( aNew==0 ) return SQLITE_NOMEM;
   255      memset(&aNew[pCsr->nStat], 0, 
   256          sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat)
   257      );
   258      pCsr->aStat = aNew;
   259      pCsr->nStat = nSize;
   260    }
   261    return SQLITE_OK;
   262  }
   263  
   264  /*
   265  ** xNext - Advance the cursor to the next row, if any.
   266  */
   267  static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
   268    Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
   269    Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
   270    int rc;
   271  
   272    /* Increment our pretend rowid value. */
   273    pCsr->iRowid++;
   274  
   275    for(pCsr->iCol++; pCsr->iCol<pCsr->nStat; pCsr->iCol++){
   276      if( pCsr->aStat[pCsr->iCol].nDoc>0 ) return SQLITE_OK;
   277    }
   278  
   279    rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr);
   280    if( rc==SQLITE_ROW ){
   281      int i = 0;
   282      int nDoclist = pCsr->csr.nDoclist;
   283      char *aDoclist = pCsr->csr.aDoclist;
   284      int iCol;
   285  
   286      int eState = 0;
   287  
   288      if( pCsr->zStop ){
   289        int n = (pCsr->nStop<pCsr->csr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm;
   290        int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n);
   291        if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){
   292          pCsr->isEof = 1;
   293          return SQLITE_OK;
   294        }
   295      }
   296  
   297      if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM;
   298      memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat);
   299      iCol = 0;
   300  
   301      while( i<nDoclist ){
   302        sqlite3_int64 v = 0;
   303  
   304        i += sqlite3Fts3GetVarint(&aDoclist[i], &v);
   305        switch( eState ){
   306          /* State 0. In this state the integer just read was a docid. */
   307          case 0:
   308            pCsr->aStat[0].nDoc++;
   309            eState = 1;
   310            iCol = 0;
   311            break;
   312  
   313          /* State 1. In this state we are expecting either a 1, indicating
   314          ** that the following integer will be a column number, or the
   315          ** start of a position list for column 0.  
   316          ** 
   317          ** The only difference between state 1 and state 2 is that if the
   318          ** integer encountered in state 1 is not 0 or 1, then we need to
   319          ** increment the column 0 "nDoc" count for this term.
   320          */
   321          case 1:
   322            assert( iCol==0 );
   323            if( v>1 ){
   324              pCsr->aStat[1].nDoc++;
   325            }
   326            eState = 2;
   327            /* fall through */
   328  
   329          case 2:
   330            if( v==0 ){       /* 0x00. Next integer will be a docid. */
   331              eState = 0;
   332            }else if( v==1 ){ /* 0x01. Next integer will be a column number. */
   333              eState = 3;
   334            }else{            /* 2 or greater. A position. */
   335              pCsr->aStat[iCol+1].nOcc++;
   336              pCsr->aStat[0].nOcc++;
   337            }
   338            break;
   339  
   340          /* State 3. The integer just read is a column number. */
   341          default: assert( eState==3 );
   342            iCol = (int)v;
   343            if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM;
   344            pCsr->aStat[iCol+1].nDoc++;
   345            eState = 2;
   346            break;
   347        }
   348      }
   349  
   350      pCsr->iCol = 0;
   351      rc = SQLITE_OK;
   352    }else{
   353      pCsr->isEof = 1;
   354    }
   355    return rc;
   356  }
   357  
   358  /*
   359  ** xFilter - Initialize a cursor to point at the start of its data.
   360  */
   361  static int fts3auxFilterMethod(
   362    sqlite3_vtab_cursor *pCursor,   /* The cursor used for this query */
   363    int idxNum,                     /* Strategy index */
   364    const char *idxStr,             /* Unused */
   365    int nVal,                       /* Number of elements in apVal */
   366    sqlite3_value **apVal           /* Arguments for the indexing scheme */
   367  ){
   368    Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
   369    Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
   370    int rc;
   371    int isScan = 0;
   372    int iLangVal = 0;               /* Language id to query */
   373  
   374    int iEq = -1;                   /* Index of term=? value in apVal */
   375    int iGe = -1;                   /* Index of term>=? value in apVal */
   376    int iLe = -1;                   /* Index of term<=? value in apVal */
   377    int iLangid = -1;               /* Index of languageid=? value in apVal */
   378    int iNext = 0;
   379  
   380    UNUSED_PARAMETER(nVal);
   381    UNUSED_PARAMETER(idxStr);
   382  
   383    assert( idxStr==0 );
   384    assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0
   385         || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
   386         || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
   387    );
   388  
   389    if( idxNum==FTS4AUX_EQ_CONSTRAINT ){
   390      iEq = iNext++;
   391    }else{
   392      isScan = 1;
   393      if( idxNum & FTS4AUX_GE_CONSTRAINT ){
   394        iGe = iNext++;
   395      }
   396      if( idxNum & FTS4AUX_LE_CONSTRAINT ){
   397        iLe = iNext++;
   398      }
   399    }
   400    if( iNext<nVal ){
   401      iLangid = iNext++;
   402    }
   403  
   404    /* In case this cursor is being reused, close and zero it. */
   405    testcase(pCsr->filter.zTerm);
   406    sqlite3Fts3SegReaderFinish(&pCsr->csr);
   407    sqlite3_free((void *)pCsr->filter.zTerm);
   408    sqlite3_free(pCsr->aStat);
   409    memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr);
   410  
   411    pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
   412    if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
   413  
   414    if( iEq>=0 || iGe>=0 ){
   415      const unsigned char *zStr = sqlite3_value_text(apVal[0]);
   416      assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) );
   417      if( zStr ){
   418        pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
   419        pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
   420        if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
   421      }
   422    }
   423  
   424    if( iLe>=0 ){
   425      pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe]));
   426      pCsr->nStop = sqlite3_value_bytes(apVal[iLe]);
   427      if( pCsr->zStop==0 ) return SQLITE_NOMEM;
   428    }
   429    
   430    if( iLangid>=0 ){
   431      iLangVal = sqlite3_value_int(apVal[iLangid]);
   432  
   433      /* If the user specified a negative value for the languageid, use zero
   434      ** instead. This works, as the "languageid=?" constraint will also
   435      ** be tested by the VDBE layer. The test will always be false (since
   436      ** this module will not return a row with a negative languageid), and
   437      ** so the overall query will return zero rows.  */
   438      if( iLangVal<0 ) iLangVal = 0;
   439    }
   440    pCsr->iLangid = iLangVal;
   441  
   442    rc = sqlite3Fts3SegReaderCursor(pFts3, iLangVal, 0, FTS3_SEGCURSOR_ALL,
   443        pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
   444    );
   445    if( rc==SQLITE_OK ){
   446      rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter);
   447    }
   448  
   449    if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor);
   450    return rc;
   451  }
   452  
   453  /*
   454  ** xEof - Return true if the cursor is at EOF, or false otherwise.
   455  */
   456  static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){
   457    Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
   458    return pCsr->isEof;
   459  }
   460  
   461  /*
   462  ** xColumn - Return a column value.
   463  */
   464  static int fts3auxColumnMethod(
   465    sqlite3_vtab_cursor *pCursor,   /* Cursor to retrieve value from */
   466    sqlite3_context *pCtx,          /* Context for sqlite3_result_xxx() calls */
   467    int iCol                        /* Index of column to read value from */
   468  ){
   469    Fts3auxCursor *p = (Fts3auxCursor *)pCursor;
   470  
   471    assert( p->isEof==0 );
   472    switch( iCol ){
   473      case 0: /* term */
   474        sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
   475        break;
   476  
   477      case 1: /* col */
   478        if( p->iCol ){
   479          sqlite3_result_int(pCtx, p->iCol-1);
   480        }else{
   481          sqlite3_result_text(pCtx, "*", -1, SQLITE_STATIC);
   482        }
   483        break;
   484  
   485      case 2: /* documents */
   486        sqlite3_result_int64(pCtx, p->aStat[p->iCol].nDoc);
   487        break;
   488  
   489      case 3: /* occurrences */
   490        sqlite3_result_int64(pCtx, p->aStat[p->iCol].nOcc);
   491        break;
   492  
   493      default: /* languageid */
   494        assert( iCol==4 );
   495        sqlite3_result_int(pCtx, p->iLangid);
   496        break;
   497    }
   498  
   499    return SQLITE_OK;
   500  }
   501  
   502  /*
   503  ** xRowid - Return the current rowid for the cursor.
   504  */
   505  static int fts3auxRowidMethod(
   506    sqlite3_vtab_cursor *pCursor,   /* Cursor to retrieve value from */
   507    sqlite_int64 *pRowid            /* OUT: Rowid value */
   508  ){
   509    Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
   510    *pRowid = pCsr->iRowid;
   511    return SQLITE_OK;
   512  }
   513  
   514  /*
   515  ** Register the fts3aux module with database connection db. Return SQLITE_OK
   516  ** if successful or an error code if sqlite3_create_module() fails.
   517  */
   518  int sqlite3Fts3InitAux(sqlite3 *db){
   519    static const sqlite3_module fts3aux_module = {
   520       0,                           /* iVersion      */
   521       fts3auxConnectMethod,        /* xCreate       */
   522       fts3auxConnectMethod,        /* xConnect      */
   523       fts3auxBestIndexMethod,      /* xBestIndex    */
   524       fts3auxDisconnectMethod,     /* xDisconnect   */
   525       fts3auxDisconnectMethod,     /* xDestroy      */
   526       fts3auxOpenMethod,           /* xOpen         */
   527       fts3auxCloseMethod,          /* xClose        */
   528       fts3auxFilterMethod,         /* xFilter       */
   529       fts3auxNextMethod,           /* xNext         */
   530       fts3auxEofMethod,            /* xEof          */
   531       fts3auxColumnMethod,         /* xColumn       */
   532       fts3auxRowidMethod,          /* xRowid        */
   533       0,                           /* xUpdate       */
   534       0,                           /* xBegin        */
   535       0,                           /* xSync         */
   536       0,                           /* xCommit       */
   537       0,                           /* xRollback     */
   538       0,                           /* xFindFunction */
   539       0,                           /* xRename       */
   540       0,                           /* xSavepoint    */
   541       0,                           /* xRelease      */
   542       0                            /* xRollbackTo   */
   543    };
   544    int rc;                         /* Return code */
   545  
   546    rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0);
   547    return rc;
   548  }
   549  
   550  #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */