/ Check-in [dba42f0e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add support for reading simple (no compression, no encryption) zonefile files.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | zonefile
Files: files | file ages | folders
SHA3-256: dba42f0e1efae7bad4579d23ad98e2c06e38053abe55f5cb277d7eecea42c56c
User & Date: dan 2018-02-12 20:04:22
Context
2018-02-13
17:33
Pad the 26 byte Zonefile header to 32 bytes so that the ZonefileIndex object is 8-byte aligned. check-in: fdb6c0c5 user: dan tags: zonefile
2018-02-12
20:04
Add support for reading simple (no compression, no encryption) zonefile files. check-in: dba42f0e user: dan tags: zonefile
2018-02-10
21:04
Add start of "zonefile" virtual table. check-in: 0b7bd169 user: dan tags: zonefile
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/zonefile/zonefile.c.

    33     33   # define ALWAYS(X)      (X)
    34     34   # define NEVER(X)       (X)
    35     35   #endif
    36     36   #endif   /* SQLITE_AMALGAMATION */
    37     37   
    38     38   #define ZONEFILE_MAGIC_NUMBER 0x464B3138
    39     39   
    40         -#define ZONEFILE_SZ_HEADER 26
           40  +#define ZONEFILE_SZ_HEADER           26
           41  +#define ZONEFILE_SZ_KEYOFFSETS_ENTRY 20
    41     42   
    42     43   #define ZONEFILE_DEFAULT_MAXAUTOFRAMESIZE (64*1024)
    43     44   #define ZONEFILE_DEFAULT_ENCRYPTION       0
    44     45   #define ZONEFILE_DEFAULT_COMPRESSION      0
    45     46   
    46     47   
    47     48   #define ZONEFILE_SCHEMA          \
................................................................................
   255    256   
   256    257   static u32 zonefileGet32(u8 *aBuf){
   257    258     return (((u32)aBuf[0]) << 24)
   258    259          + (((u32)aBuf[1]) << 16)
   259    260          + (((u32)aBuf[2]) <<  8)
   260    261          + (((u32)aBuf[3]) <<  0);
   261    262   }
          263  +
          264  +static u64 zonefileGet64(u8 *aBuf){
          265  +  return (((u64)aBuf[0]) << 56)
          266  +       + (((u64)aBuf[1]) << 48)
          267  +       + (((u64)aBuf[2]) << 40)
          268  +       + (((u64)aBuf[3]) << 32) 
          269  +       + (((u64)aBuf[4]) << 24)
          270  +       + (((u64)aBuf[5]) << 16)
          271  +       + (((u64)aBuf[6]) <<  8)
          272  +       + (((u64)aBuf[7]) <<  0);
          273  +}
   262    274   
   263    275   static void zonefileAppend32(ZonefileBuffer *pBuf, u32 v){
   264    276     zonefilePut32(&pBuf->a[pBuf->n], v);
   265    277     pBuf->n += 4;
   266    278   }
   267    279   
   268    280   static void zonefileAppend64(ZonefileBuffer *pBuf, u64 v){
................................................................................
   271    283   }
   272    284   
   273    285   static void zonefileAppendBlob(ZonefileBuffer *pBuf, const u8 *p, int n){
   274    286     memcpy(&pBuf->a[pBuf->n], p, n);
   275    287     pBuf->n += n;
   276    288   }
   277    289   
   278         -static int zonefileWrite(FILE *pFd, const u8 *aBuf, int nBuf){
          290  +static int zonefileFileWrite(FILE *pFd, const u8 *aBuf, int nBuf){
   279    291     size_t res = fwrite(aBuf, 1, nBuf, pFd);
   280    292     return res!=nBuf ? SQLITE_ERROR : SQLITE_OK;
   281    293   }
   282    294   
   283         -static int zonefileRead(FILE *pFd, u8 *aBuf, int nBuf, i64 iOff){
          295  +static int zonefileFileRead(FILE *pFd, u8 *aBuf, int nBuf, i64 iOff){
   284    296     int rc = fseek(pFd, iOff, SEEK_SET);
   285    297     if( rc==0 ){
   286    298       rc = fread(aBuf, 1, nBuf, pFd);
   287    299       rc = (rc==nBuf) ? SQLITE_OK : SQLITE_ERROR;
   288    300     }
   289    301     return rc;
   290    302   }
          303  +
          304  +static FILE *zonefileFileOpen(const char *zFile, int bWrite, char **pzErr){
          305  +  FILE *pFd = fopen(zFile, bWrite ? "w" : "r");
          306  +  if( pFd==0 ){
          307  +    *pzErr = sqlite3_mprintf("failed to open file \"%s\" for %s",
          308  +        zFile, bWrite ? "writing" : "reading"
          309  +    );
          310  +  }
          311  +  return pFd;
          312  +}
          313  +
          314  +static void zonefileFileClose(FILE *pFd){
          315  +  if( pFd ) fclose(pFd);
          316  +}
   291    317   
   292    318   /*
   293    319   ** Function:     zonefile_write(F,T[,J])
   294    320   */
   295    321   static void zonefileWriteFunc(
   296    322     sqlite3_context *pCtx,       /* Context object */
   297    323     int objc,                       /* Number of SQL arguments */
................................................................................
   357    383           sqlite3_result_error_nomem(pCtx);
   358    384           goto zone_write_out;
   359    385         }
   360    386         nFrame++;
   361    387       }
   362    388   
   363    389       /* Add new entry to sKeyIdx */
   364         -    if( zonefileBufferGrow(pCtx, &sKeyIdx, 20) ) goto zone_write_out;
          390  +    if( zonefileBufferGrow(pCtx, &sKeyIdx, ZONEFILE_SZ_KEYOFFSETS_ENTRY) ){
          391  +      goto zone_write_out;
          392  +    }
   365    393       zonefileAppend64(&sKeyIdx, k);
   366    394       zonefileAppend32(&sKeyIdx, nFrame-1);
   367    395       zonefileAppend32(&sKeyIdx, szFrame);
   368    396       zonefileAppend32(&sKeyIdx, nBlob);
   369    397   
   370    398       /* Add data for new entry to sFrames */
   371    399       if( zonefileBufferGrow(pCtx, &sFrames, nBlob) ) goto zone_write_out;
................................................................................
   386    414     zonefilePut32(&aHdr[18], nKey);
   387    415     aHdr[22] = sWrite.encryptionType;
   388    416     aHdr[23] = 0;                   /* Encryption key index */
   389    417     aHdr[24] = 0;                   /* extended header version */
   390    418     aHdr[25] = 0;                   /* extended header size */
   391    419     assert( ZONEFILE_SZ_HEADER==26 );
   392    420   
   393         -  rc = zonefileWrite(pFd, aHdr, ZONEFILE_SZ_HEADER);
   394         -  if( rc==SQLITE_OK ) rc = zonefileWrite(pFd, sFrameIdx.a, sFrameIdx.n);
   395         -  if( rc==SQLITE_OK ) rc = zonefileWrite(pFd, sKeyIdx.a, sKeyIdx.n);
   396         -  if( rc==SQLITE_OK ) rc = zonefileWrite(pFd, sFrames.a, sFrames.n);
          421  +  rc = zonefileFileWrite(pFd, aHdr, ZONEFILE_SZ_HEADER);
          422  +  if( rc==SQLITE_OK ) rc = zonefileFileWrite(pFd, sFrameIdx.a, sFrameIdx.n);
          423  +  if( rc==SQLITE_OK ) rc = zonefileFileWrite(pFd, sKeyIdx.a, sKeyIdx.n);
          424  +  if( rc==SQLITE_OK ) rc = zonefileFileWrite(pFd, sFrames.a, sFrames.n);
   397    425     if( rc ){
   398    426       zonefileCtxError(pCtx, "error writing file \"%s\" (fwrite())", zFile);
   399    427       goto zone_write_out;
   400    428     }
   401    429   
   402    430     if( fclose(pFd) ){
   403    431       zonefileCtxError(pCtx, "error writing file \"%s\" (fclose())", zFile);
................................................................................
   415    443   typedef struct ZonefileFilesTab ZonefileFilesTab;
   416    444   struct ZonefileFilesTab {
   417    445     sqlite3_vtab base;              /* Base class - must be first */
   418    446     sqlite3 *db;
   419    447     char *zBase;                    /* Name of this table */
   420    448     char *zDb;                      /* Database containing this table */
   421    449     sqlite3_stmt *pInsert;          /* Insert into the %_shadow_file table */
          450  +  sqlite3_stmt *pInsertIdx;       /* Insert into the %_shadow_idx table */
          451  +  sqlite3_stmt *pDeleteIdx;       /* Delete by fileid from %_shadow_idx table */
   422    452     sqlite3_stmt *pDelete;          /* Delete by rowid from %_shadow_file table */
   423    453   };
   424    454   
   425    455   typedef struct ZonefileFilesCsr ZonefileFilesCsr;
   426    456   struct ZonefileFilesCsr {
   427    457     sqlite3_vtab_cursor base;       /* Base class - must be first */
   428    458     sqlite3_stmt *pSelect;
................................................................................
   503    533   /* 
   504    534   ** zonefile_files virtual table module xDisconnect method.
   505    535   */
   506    536   static int zffDisconnect(sqlite3_vtab *pVtab){
   507    537     ZonefileFilesTab *pTab = (ZonefileFilesTab*)pVtab;
   508    538     sqlite3_finalize(pTab->pInsert);
   509    539     sqlite3_finalize(pTab->pDelete);
          540  +  sqlite3_finalize(pTab->pInsertIdx);
          541  +  sqlite3_finalize(pTab->pDeleteIdx);
   510    542     sqlite3_free(pTab);
   511    543     return SQLITE_OK;
   512    544   }
   513    545   
   514    546   /* 
   515    547   ** zonefile_files virtual table module xBestIndex method.
   516    548   */
   517         -static int zffBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
          549  +static int zffBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
   518    550     return SQLITE_OK;
   519    551   }
   520    552   
   521    553   /* 
   522    554   ** zonefile_files virtual table module xOpen method.
   523    555   */
   524    556   static int zffOpen(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
................................................................................
   528    560       return SQLITE_NOMEM;
   529    561     }
   530    562     memset(pCsr, 0, sizeof(ZonefileFilesCsr));
   531    563     *ppCursor = (sqlite3_vtab_cursor*)pCsr;
   532    564     return SQLITE_OK;
   533    565   }
   534    566   
          567  +/* 
          568  +** Reset a ZonefileFilesCsr object to the state it is in immediately after
          569  +** it is allocated by zffOpen().
          570  +*/
   535    571   static void zffCursorReset(ZonefileFilesCsr *pCsr){
   536    572     sqlite3_finalize(pCsr->pSelect);
   537    573     pCsr->pSelect = 0;
   538    574   }
   539    575   
   540    576   /* 
   541    577   ** zonefile_files virtual table module xClose method.
................................................................................
   618    654   static void zonefileJsonHeader(sqlite3_context *pCtx, const char *zFile){
   619    655     FILE *pFd = zonefileOpenFile(pCtx, zFile);
   620    656     if( pFd ){
   621    657       int rc;
   622    658       ZonefileHeader hdr;
   623    659       u8 aBuf[ZONEFILE_SZ_HEADER];
   624    660   
   625         -    rc = zonefileRead(pFd, aBuf, ZONEFILE_SZ_HEADER, 0);
          661  +    rc = zonefileFileRead(pFd, aBuf, ZONEFILE_SZ_HEADER, 0);
   626    662       if( rc==SQLITE_OK ){
   627    663         zonefileHeaderDeserialize(aBuf, &hdr);
   628    664       }
   629    665   
   630    666       if( rc!=SQLITE_OK ){
   631    667         zonefileCtxError(pCtx, "failed to read header from file: \"%s\"", zFile);
   632    668       }else{
................................................................................
   691    727   ** zonefile_files virtual table module xRowid method.
   692    728   */
   693    729   static int zffRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
   694    730     ZonefileFilesCsr *pCsr = (ZonefileFilesCsr*)cur;
   695    731     *pRowid = sqlite3_column_int64(pCsr->pSelect, 1);
   696    732     return SQLITE_OK;
   697    733   }
          734  +
          735  +/*
          736  +** Read and decode a Zonefile header from the start of the file opened
          737  +** by file-handle pFd. If successful, populate object (*pHdr) before
          738  +** returning SQLITE_OK. Otherwise, if an error occurs, set output
          739  +** parameter (*pzErr) to point to an English language error message and
          740  +** return an SQLite error code.
          741  +**
          742  +** It is the responsibility of the caller to eventually free any error
          743  +** message returned via (*pzErr) using sqlite3_free().
          744  +*/
          745  +static int zonefileReadHeader(
          746  +  FILE *pFd,                      /* File to read from */
          747  +  const char *zFile,              /* Name of file opened by pFd */
          748  +  ZonefileHeader *pHdr,           /* Populate this object before returning */
          749  +  char **pzErr                    /* OUT: Error message */
          750  +){
          751  +  u8 aBuf[ZONEFILE_SZ_HEADER];
          752  +  int rc = zonefileFileRead(pFd, aBuf, ZONEFILE_SZ_HEADER, 0);
          753  +  if( rc==SQLITE_OK ){
          754  +    zonefileHeaderDeserialize(aBuf, pHdr);
          755  +    if( pHdr->magicNumber!=ZONEFILE_MAGIC_NUMBER ){
          756  +      rc = SQLITE_ERROR;
          757  +    }
          758  +  }
          759  +
          760  +  if( rc!=SQLITE_OK ){
          761  +    *pzErr = sqlite3_mprintf(
          762  +        "failed to read zonefile header from file \"%s\"", zFile
          763  +    );
          764  +  }
          765  +
          766  +  return rc;
          767  +}
          768  +
          769  +static int zonefilePopulateIndex(
          770  +  ZonefileFilesTab *pTab,
          771  +  const char *zFile,
          772  +  i64 iFileid
          773  +){
          774  +  ZonefileHeader hdr;
          775  +  int rc;
          776  +  FILE *pFd = zonefileFileOpen(zFile, 0, &pTab->base.zErrMsg);
          777  +
          778  +  if( pFd==0 ){
          779  +    rc = SQLITE_ERROR;
          780  +  }else{
          781  +    rc = zonefileReadHeader(pFd, zFile, &hdr, &pTab->base.zErrMsg);
          782  +  }
          783  +
          784  +  if( rc==SQLITE_OK && hdr.numKeys>0 ){
          785  +    /* TODO: Deal with encrypted and compressed ZonefileIndex objects */
          786  +    i64 iOff;                     /* Offset of keyOffsets array */
          787  +    u8 *aKey;                     /* Entire KeyOffsets array */
          788  +    int nKey;                     /* Size of buffer aKey[] in bytes */
          789  +    int i;
          790  +    assert( hdr.encryptionType==0 && hdr.compressionTypeIndexData==0 );
          791  +
          792  +    iOff = ZONEFILE_SZ_HEADER + (hdr.numFrames * 4);
          793  +    nKey = ZONEFILE_SZ_KEYOFFSETS_ENTRY * hdr.numKeys;
          794  +    aKey = (u8*)sqlite3_malloc(nKey);
          795  +    if( aKey==0 ){
          796  +      rc = SQLITE_NOMEM;
          797  +    }else{
          798  +      rc = zonefileFileRead(pFd, aKey, nKey, iOff);
          799  +    }
          800  +
          801  +    if( rc==SQLITE_OK && pTab->pInsertIdx==0 ){
          802  +      rc = zonefilePrepare(pTab->db, &pTab->pInsertIdx, &pTab->base.zErrMsg,
          803  +          "INSERT INTO %Q.'%q_shadow_idx'(k, fileid, frame, ofst, sz)"
          804  +          "VALUES(?,?,?,?,?)",
          805  +          pTab->zDb, pTab->zBase
          806  +      );
          807  +    }
          808  +
          809  +    for(i=0; i<hdr.numKeys && rc==SQLITE_OK; i++){
          810  +      u8 *aEntry = &aKey[ZONEFILE_SZ_KEYOFFSETS_ENTRY * i];
          811  +
          812  +      sqlite3_bind_int64(pTab->pInsertIdx, 1, (i64)zonefileGet64(&aEntry[0]));
          813  +      sqlite3_bind_int64(pTab->pInsertIdx, 2, iFileid);
          814  +      sqlite3_bind_int64(pTab->pInsertIdx, 3, (i64)zonefileGet32(&aEntry[8]));
          815  +      sqlite3_bind_int64(pTab->pInsertIdx, 4, (i64)zonefileGet32(&aEntry[12]));
          816  +      sqlite3_bind_int64(pTab->pInsertIdx, 5, (i64)zonefileGet32(&aEntry[16]));
          817  +
          818  +      sqlite3_step(pTab->pInsertIdx);
          819  +      rc = sqlite3_reset(pTab->pInsertIdx);
          820  +      if( rc!=SQLITE_OK ){
          821  +        pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
          822  +      }
          823  +    }
          824  +    sqlite3_free(aKey);
          825  +  }
          826  +
          827  +  zonefileFileClose(pFd);
          828  +  return rc;
          829  +}
   698    830   
   699    831   /*
   700    832   ** zonefile_files virtual table module xUpdate method.
   701    833   **
   702    834   ** A delete specifies a single argument - the rowid of the row to remove.
   703    835   ** 
   704    836   ** Update and insert operations pass:
................................................................................
   714    846     sqlite_int64 *pRowid
   715    847   ){
   716    848     int rc = SQLITE_OK;
   717    849     ZonefileFilesTab *pTab = (ZonefileFilesTab*)pVtab;
   718    850   
   719    851     if( sqlite3_value_type(apVal[0])==SQLITE_INTEGER ){
   720    852       if( pTab->pDelete==0 ){
   721         -      rc = zonefilePrepare(pTab->db, &pTab->pInsert, &pVtab->zErrMsg,
   722         -          "DELETE FROM %Q.'%q_shadow_file WHERE fileid=?",
          853  +      rc = zonefilePrepare(pTab->db, &pTab->pDelete, &pVtab->zErrMsg,
          854  +          "DELETE FROM %Q.'%q_shadow_file' WHERE fileid=?",
          855  +          pTab->zDb, pTab->zBase
          856  +      );
          857  +    }
          858  +    if( rc==SQLITE_OK && pTab->pDeleteIdx==0 ){
          859  +      rc = zonefilePrepare(pTab->db, &pTab->pDeleteIdx, &pVtab->zErrMsg,
          860  +          "DELETE FROM %Q.'%q_shadow_idx' WHERE fileid=?",
   723    861             pTab->zDb, pTab->zBase
   724    862         );
   725    863       }
          864  +
   726    865       if( rc==SQLITE_OK ){
   727    866         sqlite3_bind_value(pTab->pDelete, 1, apVal[0]);
   728    867         sqlite3_step(pTab->pDelete);
   729    868         rc = sqlite3_reset(pTab->pDelete);
   730    869       }
          870  +    if( rc==SQLITE_OK ){
          871  +      sqlite3_bind_value(pTab->pDeleteIdx, 1, apVal[0]);
          872  +      sqlite3_step(pTab->pDeleteIdx);
          873  +      rc = sqlite3_reset(pTab->pDeleteIdx);
          874  +    }
   731    875     }
   732    876     if( nVal>1 ){
          877  +    const char *zFile = (const char*)sqlite3_value_text(apVal[2]);
          878  +
   733    879       if( pTab->pInsert==0 ){
   734    880         rc = zonefilePrepare(pTab->db, &pTab->pInsert, &pVtab->zErrMsg,
   735    881             "INSERT INTO %Q.'%q_shadow_file'(filename) VALUES(?)",
   736    882             pTab->zDb, pTab->zBase
   737    883         );
   738    884       }
   739    885   
          886  +    /* Add the new entry to the %_shadow_file table. */
   740    887       if( rc==SQLITE_OK ){
   741         -      const char *zFile = (const char*)sqlite3_value_text(apVal[2]);
   742    888         sqlite3_bind_text(pTab->pInsert, 1, zFile, -1, SQLITE_TRANSIENT);
   743    889         sqlite3_step(pTab->pInsert);
   744    890         rc = sqlite3_reset(pTab->pInsert);
   745    891       }
          892  +
          893  +    /* Populate the %_shadow_idx table with entries for all keys in
          894  +    ** the zonefile just added to %_shadow_file.  */
          895  +    if( rc==SQLITE_OK ){
          896  +      i64 iFileid = sqlite3_last_insert_rowid(pTab->db);
          897  +      rc = zonefilePopulateIndex(pTab, zFile, iFileid);
          898  +    }
   746    899     }
   747    900   
   748         -  return SQLITE_OK;
          901  +  return rc;
   749    902   }
   750    903   
   751    904   typedef struct ZonefileTab ZonefileTab;
   752    905   struct ZonefileTab {
   753    906     sqlite3_vtab base;         /* Base class - must be first */
   754    907     sqlite3 *db;
          908  +  sqlite3_stmt *pIdToName;   /* Translate fileid to filename */
   755    909     char *zName;               /* Name of this table */
   756    910     char *zDb;                 /* Name of db containing this table */
   757    911   };
          912  +
          913  +typedef struct ZonefileCsr ZonefileCsr;
          914  +struct ZonefileCsr {
          915  +  sqlite3_vtab_cursor base;  /* Base class - must be first */
          916  +  sqlite3_stmt *pSelect;     /* SELECT on %_shadow_idx table */
          917  +};
   758    918   
   759    919   /*
   760    920   ** This function does the work of xCreate (if bCreate!=0) or xConnect
   761    921   ** (if bCreate==0) for the zonefile module.
   762    922   **
   763    923   **   argv[0]   -> module name  ("zonefile")
   764    924   **   argv[1]   -> database name
................................................................................
   855   1015   }
   856   1016   
   857   1017   /* 
   858   1018   ** zonefile virtual table module xDisconnect method.
   859   1019   */
   860   1020   static int zonefileDisconnect(sqlite3_vtab *pVtab){
   861   1021     ZonefileTab *pTab = (ZonefileTab*)pVtab;
         1022  +  sqlite3_finalize(pTab->pIdToName);
   862   1023     sqlite3_free(pTab);
   863   1024     return SQLITE_OK;
   864   1025   }
   865   1026   
   866   1027   /* 
   867         -** zonefile virtual table module xBestIndex method.
         1028  +** Zonefile virtual table module xBestIndex method.
         1029  +**
         1030  +** Equality and range constraints on either the rowid or column "k" (which
         1031  +** are the same thing) are processed. Bits in the idxNum parameter are
         1032  +** set to indicate the constraints present:
         1033  +**
         1034  +**   0x01:   k == ? 
         1035  +**   0x02:   k <  ? 
         1036  +**   0x04:   k <= ? 
         1037  +**   0x08:   k >  ? 
         1038  +**   0x10:   k >= ? 
         1039  +**
         1040  +** Only some combinations are valid:
         1041  +**
         1042  +**   * If an == constraint is found, no other bits are set.
         1043  +**   * If a < constraint is present, any <= is ignored.
         1044  +**   * If a > constraint is present, any >= is ignored.
   868   1045   */
   869         -static int zonefileBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
         1046  +static int zonefileBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pInfo){
         1047  +  int iEq = -1;
         1048  +  int iLt = -1;
         1049  +  int iLe = -1;
         1050  +  int iGt = -1;
         1051  +  int iGe = -1;
         1052  +  int i;
         1053  +  int idxNum = 0;
         1054  +  double cost = 1000000000.0;
         1055  +
         1056  +  for(i=0; i<pInfo->nConstraint; i++){
         1057  +    struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
         1058  +    if( p->usable && p->iColumn<=0 ){
         1059  +      switch( p->op ){
         1060  +        case SQLITE_INDEX_CONSTRAINT_EQ: iEq = i; break;
         1061  +        case SQLITE_INDEX_CONSTRAINT_LT: iLt = i; break;
         1062  +        case SQLITE_INDEX_CONSTRAINT_LE: iLe = i; break;
         1063  +        case SQLITE_INDEX_CONSTRAINT_GT: iGt = i; break;
         1064  +        case SQLITE_INDEX_CONSTRAINT_GE: iGe = i; break;
         1065  +      }
         1066  +    }
         1067  +  }
         1068  +
         1069  +  if( iEq>=0 ){
         1070  +    cost = 10.0;
         1071  +    idxNum = 0x01;
         1072  +    pInfo->aConstraintUsage[iEq].argvIndex = 1;
         1073  +  }else{
         1074  +    int iIdx = 1;
         1075  +    if( iLt>=0 ){
         1076  +      pInfo->aConstraintUsage[iLt].argvIndex = iIdx++;
         1077  +      idxNum |= 0x02;
         1078  +    }else if( iLe>=0 ){
         1079  +      pInfo->aConstraintUsage[iLe].argvIndex = iIdx++;
         1080  +      idxNum |= 0x04;
         1081  +    }
         1082  +    if( iGt>=0 ){
         1083  +      pInfo->aConstraintUsage[iGt].argvIndex = iIdx++;
         1084  +      idxNum |= 0x08;
         1085  +    }else if( iGe>=0 ){
         1086  +      pInfo->aConstraintUsage[iGe].argvIndex = iIdx++;
         1087  +      idxNum |= 0x10;
         1088  +    }
         1089  +
         1090  +    if( iIdx==2 ) cost = 10000.0;
         1091  +    if( iIdx==3 ) cost = 100.0;
         1092  +  }
         1093  +
         1094  +  pInfo->idxNum = idxNum;
         1095  +  pInfo->estimatedCost = cost;
         1096  +
   870   1097     return SQLITE_OK;
   871   1098   }
   872   1099   
   873   1100   /* 
   874   1101   ** zonefile virtual table module xDestroy method.
   875   1102   */
   876   1103   static int zonefileDestroy(sqlite3_vtab *pVtab){
................................................................................
   882   1109         "DROP TABLE IF EXISTS %Q.'%q_files';",
   883   1110         pTab->zDb, pTab->zName, pTab->zDb, pTab->zName, pTab->zDb, pTab->zName
   884   1111     );
   885   1112     if( zSql==0 ){
   886   1113       rc = SQLITE_NOMEM;
   887   1114     }else{
   888   1115       rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
         1116  +    sqlite3_free(zSql);
   889   1117     }
   890   1118   
   891   1119     if( rc==SQLITE_OK ){
   892   1120       zonefileDisconnect(pVtab);
   893   1121     }
   894   1122     return rc;
   895   1123   }
   896   1124   
   897   1125   /* 
   898   1126   ** zonefile virtual table module xOpen method.
   899   1127   */
   900   1128   static int zonefileOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
         1129  +  ZonefileCsr *pCsr;
         1130  +  pCsr = (ZonefileCsr*)sqlite3_malloc(sizeof(ZonefileCsr));
         1131  +  if( pCsr==0 ){
         1132  +    return SQLITE_NOMEM;
         1133  +  }
         1134  +  memset(pCsr, 0, sizeof(ZonefileCsr));
         1135  +  *ppCursor = (sqlite3_vtab_cursor*)pCsr;
   901   1136     return SQLITE_OK;
   902   1137   }
         1138  +
         1139  +/* 
         1140  +** Reset a ZonefileCsr object to the state it is in immediately after
         1141  +** it is allocated by zffOpen().
         1142  +*/
         1143  +static void zonefileCursorReset(ZonefileCsr *pCsr){
         1144  +  sqlite3_finalize(pCsr->pSelect);
         1145  +  pCsr->pSelect = 0;
         1146  +}
   903   1147   
   904   1148   /* 
   905   1149   ** zonefile virtual table module xClose method.
   906   1150   */
   907   1151   static int zonefileClose(sqlite3_vtab_cursor *cur){
   908         -  return SQLITE_OK;
   909         -}
   910         -
   911         -/* 
   912         -** zonefile virtual table module xFilter method.
   913         -*/
   914         -static int zonefileFilter(
   915         -  sqlite3_vtab_cursor *pVtabCursor, 
   916         -  int idxNum, const char *idxStr,
   917         -  int argc, sqlite3_value **argv
   918         -){
         1152  +  ZonefileCsr *pCsr = (ZonefileCsr*)cur;
         1153  +  zonefileCursorReset(pCsr);
         1154  +  sqlite3_free(pCsr);
   919   1155     return SQLITE_OK;
   920   1156   }
   921   1157   
   922   1158   /* 
   923   1159   ** zonefile virtual table module xNext method.
   924   1160   */
   925         -static int zonefileNext(sqlite3_vtab_cursor *pVtabCursor){
   926         -  return SQLITE_OK;
         1161  +static int zonefileNext(sqlite3_vtab_cursor *cur){
         1162  +  ZonefileCsr *pCsr = (ZonefileCsr*)cur;
         1163  +  int rc = SQLITE_OK;
         1164  +  if( SQLITE_ROW!=sqlite3_step(pCsr->pSelect) ){
         1165  +    rc = sqlite3_finalize(pCsr->pSelect);
         1166  +    if( rc!=SQLITE_OK ){
         1167  +      ZonefileTab *pTab = (ZonefileTab*)pCsr->base.pVtab;
         1168  +      pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
         1169  +    }
         1170  +    pCsr->pSelect = 0;
         1171  +  }
         1172  +  return rc;
         1173  +}
         1174  +
         1175  +/* 
         1176  +** zonefile virtual table module xFilter method.
         1177  +*/
         1178  +static int zonefileFilter(
         1179  +  sqlite3_vtab_cursor *cur, 
         1180  +  int idxNum, const char *idxStr,
         1181  +  int argc, sqlite3_value **argv
         1182  +){
         1183  +  ZonefileCsr *pCsr = (ZonefileCsr*)cur;
         1184  +  ZonefileTab *pTab = (ZonefileTab*)pCsr->base.pVtab;
         1185  +  int rc;
         1186  +
         1187  +  const char *z1 = 0;
         1188  +  const char *z2 = 0;
         1189  +
         1190  +  if( idxNum & 0x01 ){
         1191  +    z1 = "k = ?";
         1192  +  }else{
         1193  +    if( idxNum & 0x02 ) { z1 = "k < ?"; }
         1194  +    if( idxNum & 0x04 ) { z1 = "k <= ?"; }
         1195  +    if( idxNum & 0x08 ) { if( z1 ) z2 = "k > ?"; else z1 = "k > ?"; }
         1196  +    if( idxNum & 0x10 ) { if( z1 ) z2 = "k <= ?"; else z1 = "k <= ?"; }
         1197  +  }
         1198  +
         1199  +  rc = zonefilePrepare(pTab->db, &pCsr->pSelect, &pTab->base.zErrMsg, 
         1200  +      "SELECT k, fileid, frame, ofst, sz FROM %Q.'%q_shadow_idx'%s%s%s%s",
         1201  +      pTab->zDb, pTab->zName,
         1202  +      z1 ? " WHERE " : "", z1, 
         1203  +      z2 ? " AND " : "", z2
         1204  +  );
         1205  +
         1206  +  if( z1 ) sqlite3_bind_value(pCsr->pSelect, 1, argv[0]);
         1207  +  if( z2 ) sqlite3_bind_value(pCsr->pSelect, 2, argv[1]);
         1208  +
         1209  +  if( rc==SQLITE_OK ){
         1210  +    rc = zonefileNext(cur);
         1211  +  }
         1212  +
         1213  +  return rc;
   927   1214   }
   928   1215   
   929   1216   /*
   930   1217   ** zonefile virtual table module xEof method.
   931   1218   */
   932   1219   static int zonefileEof(sqlite3_vtab_cursor *cur){
   933         -  return SQLITE_OK;
         1220  +  ZonefileCsr *pCsr = (ZonefileCsr*)cur;
         1221  +  return (pCsr->pSelect==0);
         1222  +}
         1223  +
         1224  +static void zonefileFree(void *p){
         1225  +  sqlite3_free(p);
         1226  +}
         1227  +
         1228  +static int zonefileGetValue(sqlite3_context *pCtx, ZonefileCsr *pCsr){
         1229  +  ZonefileTab *pTab = (ZonefileTab*)pCsr->base.pVtab;
         1230  +  const char *zFile = 0;
         1231  +  char *zErr = 0;
         1232  +  FILE *pFd = 0;
         1233  +  int rc = SQLITE_OK;
         1234  +  u32 iOff = 0;                   /* Offset of frame in file */
         1235  +  ZonefileHeader hdr;
         1236  +
         1237  +  if( pTab->pIdToName==0 ){
         1238  +    rc = zonefilePrepare(pTab->db, &pTab->pIdToName, &pTab->base.zErrMsg, 
         1239  +        "SELECT filename FROM %Q.'%q_shadow_file' WHERE fileid=?",
         1240  +        pTab->zDb, pTab->zName
         1241  +    );
         1242  +    if( rc!=SQLITE_OK ){
         1243  +      zonefileTransferError(pCtx);
         1244  +      return SQLITE_ERROR;
         1245  +    }
         1246  +  }
         1247  +
         1248  +  /* Open the file to read the blob from */
         1249  +  sqlite3_bind_int64(pTab->pIdToName, 1, sqlite3_column_int64(pCsr->pSelect,1));
         1250  +  if( SQLITE_ROW==sqlite3_step(pTab->pIdToName) ){
         1251  +    zFile = (const char*)sqlite3_column_text(pTab->pIdToName, 0);
         1252  +    pFd = zonefileFileOpen(zFile, 0, &zErr);
         1253  +  }
         1254  +  if( zFile==0 ){
         1255  +    rc = sqlite3_reset(pTab->pIdToName);
         1256  +    if( rc!=SQLITE_OK ){
         1257  +      zonefileTransferError(pCtx);
         1258  +    }else{
         1259  +      rc = SQLITE_CORRUPT_VTAB;
         1260  +    }
         1261  +  }else if( pFd==0 ){
         1262  +    rc = SQLITE_ERROR;
         1263  +  }
         1264  +
         1265  +  /* Read the zonefile header */
         1266  +  if( rc==SQLITE_OK ){
         1267  +    rc = zonefileReadHeader(pFd, zFile, &hdr, &zErr);
         1268  +  }
         1269  +
         1270  +  /* Calculate the offset of the frame to read the blob from */
         1271  +  if( rc==SQLITE_OK ){
         1272  +    u8 aOff[4] = {0,0,0,0};
         1273  +    int iFrame = sqlite3_column_int(pCsr->pSelect, 2);
         1274  +    rc = zonefileFileRead(pFd, aOff, 4, ZONEFILE_SZ_HEADER + 4 * iFrame);
         1275  +    iOff = zonefileGet32(aOff);
         1276  +  }
         1277  +
         1278  +  /* Read the blob */
         1279  +  if( rc==SQLITE_OK ){
         1280  +    int sz = sqlite3_column_int(pCsr->pSelect, 4);
         1281  +    int ofst = sqlite3_column_int(pCsr->pSelect, 3);
         1282  +    u8 *aBuf = sqlite3_malloc(sz);
         1283  +    if( aBuf==0 ){
         1284  +      rc = SQLITE_NOMEM;
         1285  +    }else{
         1286  +      rc = zonefileFileRead(pFd, aBuf, sz, hdr.byteOffsetFrames + iOff + ofst);
         1287  +      if( rc==SQLITE_OK ){
         1288  +        sqlite3_result_blob(pCtx, aBuf, sz, zonefileFree);
         1289  +      }else{
         1290  +        zErr = sqlite3_mprintf(
         1291  +            "failed to read %d bytes from file \"%s\"", sz, zFile
         1292  +        );
         1293  +      }
         1294  +    }
         1295  +  }
         1296  +
         1297  +  sqlite3_reset(pTab->pIdToName);
         1298  +  if( zErr ){
         1299  +    assert( rc!=SQLITE_OK );
         1300  +    sqlite3_result_error(pCtx, zErr, -1);
         1301  +    sqlite3_free(zErr);
         1302  +  }
         1303  +  zonefileFileClose(pFd);
         1304  +  return rc;
   934   1305   }
   935   1306   
   936   1307   /* 
   937   1308   ** zonefile virtual table module xColumn method.
   938   1309   */
   939         -static int zonefileColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
   940         -  return SQLITE_OK;
         1310  +static int zonefileColumn(
         1311  +  sqlite3_vtab_cursor *cur, 
         1312  +  sqlite3_context *pCtx, 
         1313  +  int i
         1314  +){
         1315  +  ZonefileCsr *pCsr = (ZonefileCsr*)cur;
         1316  +  int rc = SQLITE_OK;
         1317  +  switch( i ){
         1318  +    case 0: /* k */
         1319  +      sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 0));
         1320  +      break;
         1321  +    case 1: /* v */
         1322  +      rc = zonefileGetValue(pCtx, pCsr);
         1323  +      break;
         1324  +    case 2: /* fileid */
         1325  +      sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 1));
         1326  +      break;
         1327  +    case 3: /* frame */
         1328  +      sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 2));
         1329  +      break;
         1330  +    case 4: /* ofst */
         1331  +      sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 3));
         1332  +      break;
         1333  +    default: /* sz */
         1334  +      sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pSelect, 4));
         1335  +      break;
         1336  +  }
         1337  +  return rc;
   941   1338   }
   942   1339   
   943   1340   /* 
   944   1341   ** zonefile virtual table module xRowid method.
   945   1342   */
   946         -static int zonefileRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
         1343  +static int zonefileRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
         1344  +  ZonefileCsr *pCsr = (ZonefileCsr*)cur;
         1345  +  *pRowid = sqlite3_column_int64(pCsr->pSelect, 0);
   947   1346     return SQLITE_OK;
   948   1347   }
   949         -
   950   1348   
   951   1349   /*
   952   1350   ** Register the "zonefile" extensions.
   953   1351   */
   954   1352   static int zonefileRegister(sqlite3 *db){
   955   1353     static sqlite3_module filesModule = {
   956   1354       0,                            /* iVersion */
................................................................................
   973   1371       0,                            /* xRollback - rollback transaction */
   974   1372       0,                            /* xFindFunction - function overloading */
   975   1373       0,                            /* xRename - rename the table */
   976   1374       0,                            /* xSavepoint */
   977   1375       0,                            /* xRelease */
   978   1376       0                             /* xRollbackTo */
   979   1377     };
   980         -
   981   1378     static sqlite3_module zonefileModule = {
   982   1379       0,                            /* iVersion */
   983   1380       zonefileCreate,               /* xCreate - create a table */
   984   1381       zonefileConnect,              /* xConnect - connect to an existing table */
   985   1382       zonefileBestIndex,            /* xBestIndex - Determine search strategy */
   986   1383       zonefileDisconnect,           /* xDisconnect - Disconnect from a table */
   987   1384       zonefileDestroy,              /* xDestroy - Drop a table */

Changes to ext/zonefile/zonefile1.test.

    22     22   do_execsql_test 1.0 {
    23     23     CREATE TABLE zz(k INTEGER PRIMARY KEY, frame INTEGER, idx INTEGER, v BLOB);
    24     24     INSERT INTO zz VALUES(1, -1, -1, randomblob(100));
    25     25     INSERT INTO zz VALUES(2, -1, -1, randomblob(100));
    26     26     INSERT INTO zz VALUES(3, -1, -1, randomblob(100));
    27     27   }
    28     28   
    29         -
    30     29   do_execsql_test 1.1 {
    31     30     SELECT zonefile_write('test.zonefile', 'zz');
    32     31   } {{}}
    33     32   
    34         -do_execsql_test 2.0 {
           33  +do_execsql_test 1.2 {
    35     34     CREATE VIRTUAL TABLE z1 USING zonefile;
    36     35     SELECT name FROM sqlite_master WHERE name LIKE 'z1%' ORDER BY 1;
    37     36   } {z1 z1_files z1_shadow_file z1_shadow_idx}
    38     37   
    39         -do_execsql_test 2.1 {
           38  +do_execsql_test 1.3 {
    40     39     INSERT INTO z1_files(filename) VALUES('test.zonefile');
    41     40     SELECT filename, 
    42     41            json_extract(header, '$.magicNumber'),
    43     42            json_extract(header, '$.numFrames'),
    44     43            json_extract(header, '$.numKeys')
    45     44            FROM z1_files;
    46     45   } {test.zonefile 1179332920 1 3}
    47     46   
           47  +do_execsql_test 1.4 { SELECT count(*) FROM z1_shadow_idx } 3
           48  +
           49  +do_execsql_test 1.5.1 { SELECT k FROM z1 } {1 2 3}
           50  +do_execsql_test 1.5.2 { SELECT fileid FROM z1 } {1 1 1}
           51  +do_execsql_test 1.5.3 { SELECT frame FROM z1 } {0 0 0}
           52  +do_execsql_test 1.5.4 { SELECT sz FROM z1 } {100 100 100}
           53  +
           54  +do_execsql_test 1.5.5 {
           55  +  SELECT zz.v==z1.v FROM zz, z1 WHERE zz.k=z1.k
           56  +} {1 1 1}
           57  +
           58  +do_execsql_test 1.5 {
           59  +  DELETE FROM z1_files;
           60  +  SELECT * FROM z1_files;
           61  +} {}
           62  +
           63  +do_execsql_test 1.6 { SELECT count(*) FROM z1_shadow_idx } 0
           64  +
           65  +do_execsql_test 1.7 { DROP TABLE z1 }
           66  +
    48     67   finish_test
           68  +