/ Check-in [01d4e866]
Login

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

Overview
Comment:Have zipfile support DELETE commands.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256: 01d4e866fb7b01aeada537d41c4a47747c7810e2028f51077ee5b8b78c348954
User & Date: dan 2017-12-30 18:32:27
Context
2018-01-04
19:54
Merge in all recent trunk enhancements. check-in: 406f7918 user: drh tags: sqlar-shell-support
2017-12-30
18:32
Have zipfile support DELETE commands. check-in: 01d4e866 user: dan tags: sqlar-shell-support
14:26
Rearrange things a bit so that writing to a zipfile does not invert the order of objects it contains. check-in: f69e8194 user: dan tags: sqlar-shell-support
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/misc/zipfile.c.

   196    196     u16 mDate;
   197    197     u32 crc32;
   198    198     u32 szCompressed;
   199    199     u32 szUncompressed;
   200    200     u16 nFile;
   201    201     u16 nExtra;
   202    202   };
          203  +
          204  +typedef struct ZipfileEntry ZipfileEntry;
          205  +struct ZipfileEntry {
          206  +  char *zPath;               /* Path of zipfile entry */
          207  +  i64 iRowid;                /* Rowid for this value if queried */
          208  +  u8 *aCdsEntry;             /* Buffer containing entire CDS entry */
          209  +  int nCdsEntry;             /* Size of buffer aCdsEntry[] in bytes */
          210  +  int bDeleted;              /* True if entry has been deleted */
          211  +  ZipfileEntry *pNext;       /* Next element in in-memory CDS */
          212  +};
   203    213   
   204    214   /* 
   205    215   ** Cursor type for recursively iterating through a directory structure.
   206    216   */
   207    217   typedef struct ZipfileCsr ZipfileCsr;
   208         -
   209    218   struct ZipfileCsr {
   210    219     sqlite3_vtab_cursor base;  /* Base class - must be first */
   211         -  i64 iRowid;                /* Rowid for current row */
   212         -  FILE *pFile;               /* Zip file */
   213         -  i64 nByte;                 /* Size of zip file on disk */
   214    220     int bEof;                  /* True when at EOF */
          221  +
          222  +  /* Used outside of write transactions */
          223  +  FILE *pFile;               /* Zip file */
   215    224     i64 iNextOff;              /* Offset of next record in central directory */
   216    225     ZipfileEOCD eocd;          /* Parse of central directory record */
          226  +
          227  +  /* Used inside write transactions */
          228  +  ZipfileEntry *pCurrent;
          229  +
   217    230     ZipfileCDS cds;            /* Central Directory Structure */
   218    231     ZipfileLFH lfh;            /* Local File Header for current entry */
   219    232     i64 iDataOff;              /* Offset in zipfile to data */
   220    233     u32 mTime;                 /* Extended mtime value */
   221    234     int flags;                 /* Flags byte (see below for bits) */
          235  +  ZipfileCsr *pCsrNext;      /* Next cursor on same virtual table */
   222    236   };
   223    237   
   224    238   /*
   225    239   ** Values for ZipfileCsr.flags.
   226    240   */
   227    241   #define ZIPFILE_MTIME_VALID 0x0001
   228    242   
   229         -typedef struct ZipfileEntry ZipfileEntry;
   230         -struct ZipfileEntry {
   231         -  char *zPath;               /* Path of zipfile entry */
   232         -  i64 iRowid;                /* Rowid for this value if queried */
   233         -  u8 *aCdsEntry;             /* Buffer containing entire CDS entry */
   234         -  int nCdsEntry;             /* Size of buffer aCdsEntry[] in bytes */
   235         -  ZipfileEntry *pNext;       /* Next element in in-memory CDS */
   236         -};
   237         -
   238    243   typedef struct ZipfileTab ZipfileTab;
   239    244   struct ZipfileTab {
   240    245     sqlite3_vtab base;         /* Base class - must be first */
   241    246     char *zFile;               /* Zip file this table accesses (may be NULL) */
   242    247     u8 *aBuffer;               /* Temporary buffer used for various tasks */
   243    248   
   244    249     /* The following are used by write transactions only */
          250  +  ZipfileCsr *pCsrList;      /* List of cursors */
   245    251     ZipfileEntry *pFirstEntry; /* Linked list of all files (if pWriteFd!=0) */
   246    252     ZipfileEntry *pLastEntry;  /* Last element in pFirstEntry list */
   247    253     FILE *pWriteFd;            /* File handle open on zip archive */
   248    254     i64 szCurrent;             /* Current size of zip archive */
   249    255     i64 szOrig;                /* Size of archive at start of transaction */
   250    256   };
   251    257   
................................................................................
   331    337   }
   332    338   
   333    339   /*
   334    340   ** Reset a cursor back to the state it was in when first returned
   335    341   ** by zipfileOpen().
   336    342   */
   337    343   static void zipfileResetCursor(ZipfileCsr *pCsr){
          344  +  ZipfileTab *pTab = (ZipfileTab*)(pCsr->base.pVtab);
          345  +  ZipfileCsr **pp;
          346  +
          347  +  /* Remove this cursor from the ZipfileTab.pCsrList list. */
          348  +  for(pp=&pTab->pCsrList; *pp; pp=&((*pp)->pCsrNext)){
          349  +    if( *pp==pCsr ) *pp = pCsr->pCsrNext;
          350  +  }
          351  +
   338    352     sqlite3_free(pCsr->cds.zFile);
   339    353     pCsr->cds.zFile = 0;
   340         -  pCsr->iRowid = 0;
   341    354     pCsr->bEof = 0;
   342    355     if( pCsr->pFile ){
   343    356       fclose(pCsr->pFile);
   344    357       pCsr->pFile = 0;
   345    358     }
   346    359   }
   347    360   
................................................................................
   434    447   ** Magic numbers used to read CDS records.
   435    448   */
   436    449   #define ZIPFILE_CDS_FIXED_SZ  46
   437    450   #define ZIPFILE_CDS_NFILE_OFF 28
   438    451   
   439    452   static int zipfileReadCDS(ZipfileCsr *pCsr){
   440    453     char **pzErr = &pCsr->base.pVtab->zErrMsg;
   441         -  u8 *aRead = zipfileCsrBuffer(pCsr);
   442         -  int rc;
          454  +  u8 *aRead;
          455  +  int rc = SQLITE_OK;
   443    456   
   444    457     sqlite3_free(pCsr->cds.zFile);
   445    458     pCsr->cds.zFile = 0;
   446    459   
   447         -  rc = zipfileReadData(
   448         -      pCsr->pFile, aRead, ZIPFILE_CDS_FIXED_SZ, pCsr->iNextOff, pzErr
   449         -  );
          460  +  if( pCsr->pCurrent==0 ){
          461  +    aRead = zipfileCsrBuffer(pCsr);
          462  +    rc = zipfileReadData(
          463  +        pCsr->pFile, aRead, ZIPFILE_CDS_FIXED_SZ, pCsr->iNextOff, pzErr
          464  +    );
          465  +  }else{
          466  +    aRead = pCsr->pCurrent->aCdsEntry;
          467  +  }
          468  +
   450    469     if( rc==SQLITE_OK ){
   451    470       u32 sig = zipfileRead32(aRead);
   452    471       if( sig!=ZIPFILE_SIGNATURE_CDS ){
          472  +      assert( pCsr->pCurrent==0 );
   453    473         zipfileSetErrmsg(pCsr,"failed to read CDS at offset %lld",pCsr->iNextOff);
   454    474         rc = SQLITE_ERROR;
   455    475       }else{
   456    476         int nRead;
   457    477         pCsr->cds.iVersionMadeBy = zipfileRead16(aRead);
   458    478         pCsr->cds.iVersionExtract = zipfileRead16(aRead);
   459    479         pCsr->cds.flags = zipfileRead16(aRead);
   460    480         pCsr->cds.iCompression = zipfileRead16(aRead);
   461    481         pCsr->cds.mTime = zipfileRead16(aRead);
   462    482         pCsr->cds.mDate = zipfileRead16(aRead);
   463    483         pCsr->cds.crc32 = zipfileRead32(aRead);
   464    484         pCsr->cds.szCompressed = zipfileRead32(aRead);
   465    485         pCsr->cds.szUncompressed = zipfileRead32(aRead);
   466         -      assert( aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_NFILE_OFF );
          486  +      assert( pCsr->pCurrent 
          487  +           || aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_NFILE_OFF 
          488  +      );
   467    489         pCsr->cds.nFile = zipfileRead16(aRead);
   468    490         pCsr->cds.nExtra = zipfileRead16(aRead);
   469    491         pCsr->cds.nComment = zipfileRead16(aRead);
   470    492         pCsr->cds.iDiskStart = zipfileRead16(aRead);
   471    493         pCsr->cds.iInternalAttr = zipfileRead16(aRead);
   472    494         pCsr->cds.iExternalAttr = zipfileRead32(aRead);
   473    495         pCsr->cds.iOffset = zipfileRead32(aRead);
          496  +      assert( pCsr->pCurrent 
          497  +           || aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_FIXED_SZ
          498  +      );
   474    499   
   475         -      assert( aRead==zipfileCsrBuffer(pCsr)+ZIPFILE_CDS_FIXED_SZ );
   476         -
   477         -      nRead = pCsr->cds.nFile + pCsr->cds.nExtra;
   478         -      aRead = zipfileCsrBuffer(pCsr);
   479         -      pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
   480         -      rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr);
          500  +      if( pCsr->pCurrent==0 ){
          501  +        nRead = pCsr->cds.nFile + pCsr->cds.nExtra;
          502  +        aRead = zipfileCsrBuffer(pCsr);
          503  +        pCsr->iNextOff += ZIPFILE_CDS_FIXED_SZ;
          504  +        rc = zipfileReadData(pCsr->pFile, aRead, nRead, pCsr->iNextOff, pzErr);
          505  +      }
   481    506   
   482    507         if( rc==SQLITE_OK ){
   483    508           pCsr->cds.zFile = sqlite3_mprintf("%.*s", (int)pCsr->cds.nFile, aRead);
   484    509           pCsr->iNextOff += pCsr->cds.nFile;
   485    510           pCsr->iNextOff += pCsr->cds.nExtra;
   486    511           pCsr->iNextOff += pCsr->cds.nComment;
   487    512         }
................................................................................
   517    542           }
   518    543         }
   519    544       }
   520    545     }
   521    546   
   522    547     return rc;
   523    548   }
          549  +
          550  +static FILE *zipfileGetFd(ZipfileCsr *pCsr){
          551  +  if( pCsr->pFile ) return pCsr->pFile;
          552  +  return ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
          553  +}
   524    554   
   525    555   static int zipfileReadLFH(ZipfileCsr *pCsr){
          556  +  FILE *pFile = zipfileGetFd(pCsr);
   526    557     char **pzErr = &pCsr->base.pVtab->zErrMsg;
   527    558     static const int szFix = ZIPFILE_LFH_FIXED_SZ;
   528    559     u8 *aRead = zipfileCsrBuffer(pCsr);
   529    560     int rc;
   530    561   
   531         -  rc = zipfileReadData(pCsr->pFile, aRead, szFix, pCsr->cds.iOffset, pzErr);
          562  +  rc = zipfileReadData(pFile, aRead, szFix, pCsr->cds.iOffset, pzErr);
   532    563     if( rc==SQLITE_OK ){
   533    564       u32 sig = zipfileRead32(aRead);
   534    565       if( sig!=ZIPFILE_SIGNATURE_LFH ){
   535    566         zipfileSetErrmsg(pCsr, "failed to read LFH at offset %d", 
   536    567             (int)pCsr->cds.iOffset
   537    568         );
   538    569         rc = SQLITE_ERROR;
................................................................................
   557    588   
   558    589   
   559    590   /*
   560    591   ** Advance an ZipfileCsr to its next row of output.
   561    592   */
   562    593   static int zipfileNext(sqlite3_vtab_cursor *cur){
   563    594     ZipfileCsr *pCsr = (ZipfileCsr*)cur;
   564         -  i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
   565    595     int rc = SQLITE_OK;
          596  +  pCsr->flags = 0;
   566    597   
   567         -  if( pCsr->iNextOff>=iEof ){
   568         -    pCsr->bEof = 1;
          598  +  if( pCsr->pCurrent==0 ){
          599  +    i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
          600  +    if( pCsr->iNextOff>=iEof ){
          601  +      pCsr->bEof = 1;
          602  +    }
   569    603     }else{
   570         -    pCsr->iRowid++;
   571         -    pCsr->flags = 0;
          604  +    assert( pCsr->pFile==0 );
          605  +    do {
          606  +      pCsr->pCurrent = pCsr->pCurrent->pNext;
          607  +    }while( pCsr->pCurrent && pCsr->pCurrent->bDeleted );
          608  +    if( pCsr->pCurrent==0 ){
          609  +      pCsr->bEof = 1;
          610  +    }
          611  +  }
          612  +
          613  +  if( pCsr->bEof==0 ){
   572    614       rc = zipfileReadCDS(pCsr);
   573    615       if( rc==SQLITE_OK ){
   574    616         rc = zipfileReadLFH(pCsr);
   575    617       }
   576    618     }
          619  +
   577    620     return rc;
   578    621   }
   579    622   
   580    623   /*
   581    624   ** "Standard" MS-DOS time format:
   582    625   **
   583    626   **   File modification time:
................................................................................
   654    697       case 4: { /* data */
   655    698         int sz = pCsr->cds.szCompressed;
   656    699         if( sz>0 ){
   657    700           u8 *aBuf = sqlite3_malloc(sz);
   658    701           if( aBuf==0 ){
   659    702             rc = SQLITE_NOMEM;
   660    703           }else{
   661         -          rc = zipfileReadData(pCsr->pFile, aBuf, sz, pCsr->iDataOff,
          704  +          FILE *pFile = zipfileGetFd(pCsr);
          705  +          rc = zipfileReadData(pFile, aBuf, sz, pCsr->iDataOff,
   662    706                 &pCsr->base.pVtab->zErrMsg
   663    707             );
   664    708           }
   665    709           if( rc==SQLITE_OK ){
   666    710             sqlite3_result_blob(ctx, aBuf, sz, SQLITE_TRANSIENT);
   667    711             sqlite3_free(aBuf);
   668    712           }
................................................................................
   674    718         break;
   675    719     }
   676    720   
   677    721     return SQLITE_OK;
   678    722   }
   679    723   
   680    724   /*
   681         -** Return the rowid for the current row. In this implementation, the
   682         -** first row returned is assigned rowid value 1, and each subsequent
   683         -** row a value 1 more than that of the previous.
          725  +** Return the rowid for the current row.
   684    726   */
   685    727   static int zipfileRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
   686    728     ZipfileCsr *pCsr = (ZipfileCsr*)cur;
   687         -  *pRowid = pCsr->iRowid;
          729  +  if( pCsr->pCurrent ){
          730  +    *pRowid = pCsr->pCurrent->iRowid;
          731  +  }else{
          732  +    *pRowid = pCsr->cds.iOffset;
          733  +  }
   688    734     return SQLITE_OK;
   689    735   }
   690    736   
   691    737   /*
   692    738   ** Return TRUE if the cursor has been moved off of the last
   693    739   ** row of output.
   694    740   */
................................................................................
   767    813     ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
   768    814     ZipfileCsr *pCsr = (ZipfileCsr*)cur;
   769    815     const char *zFile;              /* Zip file to scan */
   770    816     int rc = SQLITE_OK;             /* Return Code */
   771    817   
   772    818     zipfileResetCursor(pCsr);
   773    819   
   774         -  assert( idxNum==argc && (idxNum==0 || idxNum==1) );
   775         -  if( idxNum==0 ){
   776         -    ZipfileTab *pTab = (ZipfileTab*)cur->pVtab;
          820  +  if( pTab->zFile ){
   777    821       zFile = pTab->zFile;
   778         -    if( zFile==0 ){
   779         -      /* Error. This is an eponymous virtual table and the user has not 
   780         -      ** supplied a file name. */
   781         -      zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
   782         -      return SQLITE_ERROR;
   783         -    }
          822  +  }else if( idxNum==0 ){
          823  +    /* Error. This is an eponymous virtual table and the user has not 
          824  +    ** supplied a file name. */
          825  +    zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
          826  +    return SQLITE_ERROR;
   784    827     }else{
   785    828       zFile = (const char*)sqlite3_value_text(argv[0]);
   786    829     }
   787         -  pCsr->pFile = fopen(zFile, "rb");
   788         -  if( pCsr->pFile==0 ){
   789         -    zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile);
   790         -    rc = SQLITE_ERROR;
   791         -  }else{
   792         -    rc = zipfileReadEOCD(pTab, pCsr->pFile, &pCsr->eocd);
   793         -    if( rc==SQLITE_OK ){
   794         -      pCsr->iNextOff = pCsr->eocd.iOffset;
   795         -      rc = zipfileNext(cur);
   796         -    }else if( rc==SQLITE_EMPTY ){
   797         -      rc = SQLITE_OK;
   798         -      pCsr->bEof = 1;
          830  +
          831  +  if( pTab->pWriteFd==0 ){
          832  +    pCsr->pFile = fopen(zFile, "rb");
          833  +    if( pCsr->pFile==0 ){
          834  +      zipfileSetErrmsg(pCsr, "cannot open file: %s", zFile);
          835  +      rc = SQLITE_ERROR;
          836  +    }else{
          837  +      rc = zipfileReadEOCD(pTab, pCsr->pFile, &pCsr->eocd);
          838  +      if( rc==SQLITE_OK ){
          839  +        pCsr->iNextOff = pCsr->eocd.iOffset;
          840  +        rc = zipfileNext(cur);
          841  +      }else if( rc==SQLITE_EMPTY ){
          842  +        rc = SQLITE_OK;
          843  +        pCsr->bEof = 1;
          844  +      }
   799    845       }
          846  +  }else{
          847  +    ZipfileEntry e;
          848  +    memset(&e, 0, sizeof(e));
          849  +    e.pNext = pTab->pFirstEntry;
          850  +    pCsr->pCurrent = &e;
          851  +    rc = zipfileNext(cur);
          852  +    assert( pCsr->pCurrent!=&e );
   800    853     }
   801    854   
   802    855     return rc;
   803    856   }
   804    857   
   805    858   /*
   806    859   ** xBestIndex callback.
................................................................................
   836    889   ** Add object pNew to the end of the linked list that begins at
   837    890   ** ZipfileTab.pFirstEntry and ends with pLastEntry.
   838    891   */
   839    892   static void zipfileAddEntry(ZipfileTab *pTab, ZipfileEntry *pNew){
   840    893     assert( (pTab->pFirstEntry==0)==(pTab->pLastEntry==0) );
   841    894     assert( pNew->pNext==0 );
   842    895     if( pTab->pFirstEntry==0 ){
          896  +    pNew->iRowid = 1;
   843    897       pTab->pFirstEntry = pTab->pLastEntry = pNew;
   844    898     }else{
   845    899       assert( pTab->pLastEntry->pNext==0 );
          900  +    pNew->iRowid = pTab->pLastEntry->iRowid+1;
   846    901       pTab->pLastEntry->pNext = pNew;
   847    902       pTab->pLastEntry = pNew;
   848    903     }
   849    904   }
   850    905   
   851    906   static int zipfileLoadDirectory(ZipfileTab *pTab){
   852    907     ZipfileEOCD eocd;
................................................................................
   880    935             sizeof(ZipfileEntry) 
   881    936           + nFile+1 
   882    937           + ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment
   883    938         );
   884    939         if( pNew==0 ){
   885    940           rc = SQLITE_NOMEM;
   886    941         }else{
          942  +        memset(pNew, 0, sizeof(ZipfileEntry));
   887    943           pNew->zPath = (char*)&pNew[1];
   888    944           memcpy(pNew->zPath, &aBuf[ZIPFILE_CDS_FIXED_SZ], nFile);
   889    945           pNew->zPath[nFile] = '\0';
   890    946           pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1];
   891    947           pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
   892         -        pNew->pNext = 0;
   893    948           memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry);
   894    949           zipfileAddEntry(pTab, pNew);
   895    950         }
   896    951   
   897    952         iOff += ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment;
   898    953       }
   899    954   
................................................................................
   918    973     pNew = (ZipfileEntry*)sqlite3_malloc(
   919    974       sizeof(ZipfileEntry) + 
   920    975       nPath+1 + 
   921    976       ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra
   922    977     );
   923    978   
   924    979     if( pNew ){
          980  +    memset(pNew, 0, sizeof(ZipfileEntry));
   925    981       pNew->zPath = (char*)&pNew[1];
   926    982       pNew->aCdsEntry = (u8*)&pNew->zPath[nPath+1];
   927    983       pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ + nPath + pCds->nExtra;
   928         -    pNew->pNext = 0;
   929    984       memcpy(pNew->zPath, zPath, nPath+1);
   930    985   
   931    986       aWrite = pNew->aCdsEntry;
   932    987       zipfileWrite32(aWrite, ZIPFILE_SIGNATURE_CDS);
   933    988       zipfileWrite16(aWrite, pCds->iVersionMadeBy);
   934    989       zipfileWrite16(aWrite, pCds->iVersionExtract);
   935    990       zipfileWrite16(aWrite, pCds->flags);
................................................................................
  1053   1108     int nPath;                      /* strlen(zPath) */
  1054   1109     const u8 *pData;                /* Pointer to buffer containing content */
  1055   1110     int nData;                      /* Size of pData buffer in bytes */
  1056   1111     int iMethod = 0;                /* Compression method for new entry */
  1057   1112     u8 *pFree = 0;                  /* Free this */
  1058   1113     ZipfileCDS cds;                 /* New Central Directory Structure entry */
  1059   1114   
         1115  +  assert( pTab->zFile );
         1116  +  assert( pTab->pWriteFd );
         1117  +
  1060   1118     if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
  1061         -    pTab->base.zErrMsg = sqlite3_mprintf(
  1062         -        "zipfile: UPDATE/DELETE not yet supported"
  1063         -    );
  1064         -    return SQLITE_ERROR;
  1065         -  }
  1066         -
  1067         -  /* This table is only writable if a default archive path was specified 
  1068         -  ** as part of the CREATE VIRTUAL TABLE statement. */
  1069         -  if( pTab->zFile==0 ){
  1070         -    pTab->base.zErrMsg = sqlite3_mprintf(
  1071         -        "zipfile: writing requires a default archive"
  1072         -    );
  1073         -    return SQLITE_ERROR;
  1074         -  }
  1075         -
  1076         -  /* Open a write fd on the file. Also load the entire central directory
  1077         -  ** structure into memory. During the transaction any new file data is 
  1078         -  ** appended to the archive file, but the central directory is accumulated
  1079         -  ** in main-memory until the transaction is committed.  */
  1080         -  if( pTab->pWriteFd==0 ){
  1081         -    pTab->pWriteFd = fopen(pTab->zFile, "ab+");
  1082         -    if( pTab->pWriteFd==0 ){
  1083         -      pTab->base.zErrMsg = sqlite3_mprintf(
  1084         -          "zipfile: failed to open file %s for writing", pTab->zFile
  1085         -      );
  1086         -      return SQLITE_ERROR;
         1119  +    i64 iDelete = sqlite3_value_int64(apVal[0]);
         1120  +    ZipfileEntry *p;
         1121  +    for(p=pTab->pFirstEntry; p; p=p->pNext){
         1122  +      if( p->iRowid==iDelete ){
         1123  +        p->bDeleted = 1;
         1124  +        break;
         1125  +      }
  1087   1126       }
  1088         -    fseek(pTab->pWriteFd, 0, SEEK_END);
  1089         -    pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
  1090         -    rc = zipfileLoadDirectory(pTab);
  1091         -    if( rc!=SQLITE_OK ) return rc;
         1127  +    if( nVal==1 ) return SQLITE_OK;
  1092   1128     }
  1093   1129   
  1094   1130     zPath = (const char*)sqlite3_value_text(apVal[2]);
  1095   1131     nPath = strlen(zPath);
  1096   1132     rc = zipfileGetMode(pTab, apVal[3], &mode);
  1097   1133     if( rc!=SQLITE_OK ) return rc;
  1098   1134     mTime = sqlite3_value_int64(apVal[4]);
................................................................................
  1195   1231     fclose(pTab->pWriteFd);
  1196   1232     pTab->pWriteFd = 0;
  1197   1233     pTab->szCurrent = 0;
  1198   1234     pTab->szOrig = 0;
  1199   1235   }
  1200   1236   
  1201   1237   static int zipfileBegin(sqlite3_vtab *pVtab){
  1202         -  return SQLITE_OK;
         1238  +  ZipfileTab *pTab = (ZipfileTab*)pVtab;
         1239  +  int rc = SQLITE_OK;
         1240  +
         1241  +  assert( pTab->pWriteFd==0 );
         1242  +
         1243  +  /* This table is only writable if a default archive path was specified 
         1244  +  ** as part of the CREATE VIRTUAL TABLE statement. */
         1245  +  if( pTab->zFile==0 ){
         1246  +    pTab->base.zErrMsg = sqlite3_mprintf(
         1247  +        "zipfile: writing requires a default archive"
         1248  +    );
         1249  +    return SQLITE_ERROR;
         1250  +  }
         1251  +
         1252  +  /* Open a write fd on the file. Also load the entire central directory
         1253  +  ** structure into memory. During the transaction any new file data is 
         1254  +  ** appended to the archive file, but the central directory is accumulated
         1255  +  ** in main-memory until the transaction is committed.  */
         1256  +  pTab->pWriteFd = fopen(pTab->zFile, "ab+");
         1257  +  if( pTab->pWriteFd==0 ){
         1258  +    pTab->base.zErrMsg = sqlite3_mprintf(
         1259  +        "zipfile: failed to open file %s for writing", pTab->zFile
         1260  +    );
         1261  +    rc = SQLITE_ERROR;
         1262  +  }else{
         1263  +    fseek(pTab->pWriteFd, 0, SEEK_END);
         1264  +    pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
         1265  +    rc = zipfileLoadDirectory(pTab);
         1266  +  }
         1267  +
         1268  +  if( rc!=SQLITE_OK ){
         1269  +    zipfileCleanupTransaction(pTab);
         1270  +  }
         1271  +
         1272  +  return rc;
  1203   1273   }
  1204   1274   
  1205   1275   static int zipfileCommit(sqlite3_vtab *pVtab){
  1206   1276     ZipfileTab *pTab = (ZipfileTab*)pVtab;
  1207   1277     int rc = SQLITE_OK;
  1208   1278     if( pTab->pWriteFd ){
  1209   1279       i64 iOffset = pTab->szCurrent;
  1210   1280       ZipfileEntry *p;
  1211   1281       ZipfileEOCD eocd;
  1212   1282       int nEntry = 0;
  1213   1283   
  1214         -    /* Write out all entries */
         1284  +    /* Write out all undeleted entries */
  1215   1285       for(p=pTab->pFirstEntry; rc==SQLITE_OK && p; p=p->pNext){
         1286  +      if( p->bDeleted ) continue;
  1216   1287         rc = zipfileAppendData(pTab, p->aCdsEntry, p->nCdsEntry);
  1217   1288         nEntry++;
  1218   1289       }
  1219   1290   
  1220   1291       /* Write out the EOCD record */
  1221   1292       eocd.iDisk = 0;
  1222   1293       eocd.iFirstDisk = 0;

Changes to test/zipfile.test.

    25     25     1 mode {} 0 {} 0 
    26     26     2 mtime {} 0 {} 0 
    27     27     3 sz {} 0 {} 0 
    28     28     4 data {} 0 {} 0
    29     29     5 method {} 0 {} 0
    30     30   }
    31     31   
    32         -do_execsql_test 1.1 {
           32  +do_execsql_test 1.1.1 {
    33     33     INSERT INTO zz VALUES('f.txt', '-rw-r--r--', 1000000000, 5, 'abcde', 0);
           34  +}
           35  +do_execsql_test 1.1.2 {
    34     36     INSERT INTO zz VALUES('g.txt', '-rw-r--r--', 1000000002, 5, '12345', 0);
    35     37   }
    36     38   
    37     39   do_execsql_test 1.2 {
    38         -  SELECT name, mtime, data FROM zipfile('test.zip');
           40  +  SELECT name, mtime, data FROM zipfile('test.zip')
    39     41   } {
    40     42     f.txt 1000000000 abcde 
    41     43     g.txt 1000000002 12345
    42     44   }
    43     45   
    44     46   do_execsql_test 1.3 {
    45     47     INSERT INTO zz VALUES('h.txt', 
................................................................................
    51     53     SELECT name, mtime, zipfile_uncompress(data, sz, method), method
    52     54     FROM zipfile('test.zip');
    53     55   } {
    54     56     f.txt 1000000000 abcde 0
    55     57     g.txt 1000000002 12345 0
    56     58     h.txt 1000000004 aaaaaaaaaabbbbbbbbbb 8
    57     59   }
           60  +
           61  +do_execsql_test 1.5.1 {
           62  +  BEGIN;
           63  +    INSERT INTO zz VALUES('i.txt', '-rw-r--r--', 1000000006, 5, 'zxcvb', 0);
           64  +    SELECT name FROM zz;
           65  +  COMMIT;
           66  +} {f.txt g.txt h.txt i.txt}
           67  +do_execsql_test 1.5.2 {
           68  +  SELECT name FROM zz;
           69  +} {f.txt g.txt h.txt i.txt}
           70  +
           71  +do_execsql_test 1.6.0 {
           72  +  DELETE FROM zz WHERE name='g.txt';
           73  +  SELECT name FROM zz;
           74  +} {f.txt h.txt i.txt}
    58     75   
    59     76   finish_test
    60     77