/ Check-in [e63185ed]
Login

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

Overview
Comment:If a zipfile virtual table is created with no argument - "CREATE VIRTUAL TABLE zzz USING zipfile()" - accumulate data in memory. Support "SELECT zipfile_blob(z) FROM zzz LIMIT 1" to retrieve a zip archive image.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e63185edfe0c316aa60c1fa085d032425ecc7db54536dfa5a977772eaf3c240e
User & Date: dan 2018-01-27 16:29:59
Context
2018-01-27
18:55
Fix missing header comments and other code issues in zipfile.c. check-in: 6ea8ba31 user: dan tags: trunk
16:29
If a zipfile virtual table is created with no argument - "CREATE VIRTUAL TABLE zzz USING zipfile()" - accumulate data in memory. Support "SELECT zipfile_blob(z) FROM zzz LIMIT 1" to retrieve a zip archive image. check-in: e63185ed user: dan tags: trunk
14:25
Changes to avoid a harmless UB warning from clang. check-in: 19f5c140 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/misc/zipfile.c.

    99     99   #define ZIPFILE_NEWENTRY_REQUIRED 20
   100    100   #define ZIPFILE_NEWENTRY_FLAGS    0x800
   101    101   #define ZIPFILE_SIGNATURE_CDS     0x02014b50
   102    102   #define ZIPFILE_SIGNATURE_LFH     0x04034b50
   103    103   #define ZIPFILE_SIGNATURE_EOCD    0x06054b50
   104    104   #define ZIPFILE_LFH_FIXED_SZ      30
   105    105   
          106  +#define ZIPFILE_EOCD_FIXED_SZ     22
          107  +
   106    108   /*
   107    109   ** Set the error message contained in context ctx to the results of
   108    110   ** vprintf(zFmt, ...).
   109    111   */
   110    112   static void zipfileCtxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){
   111    113     char *zMsg = 0;
   112    114     va_list ap;
................................................................................
   641    643     ZipfileEntry **ppEntry
   642    644   ){
   643    645     u8 *aRead;
   644    646     char **pzErr = &pTab->base.zErrMsg;
   645    647     int rc = SQLITE_OK;
   646    648   
   647    649     if( aBlob==0 ){
   648         -    aRead = (u8*)pTab->aBuffer;
          650  +    aRead = pTab->aBuffer;
   649    651       rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
   650    652     }else{
   651    653       aRead = (u8*)&aBlob[iOff];
   652    654     }
   653    655   
   654    656     if( rc==SQLITE_OK ){
   655    657       int nAlloc;
................................................................................
  1090   1092     int bInMemory = 0;              /* True for an in-memory zipfile */
  1091   1093   
  1092   1094     zipfileResetCursor(pCsr);
  1093   1095   
  1094   1096     if( pTab->zFile ){
  1095   1097       zFile = pTab->zFile;
  1096   1098     }else if( idxNum==0 ){
  1097         -    /* Error. This is an eponymous virtual table and the user has not 
  1098         -    ** supplied a file name. */
  1099         -    zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
  1100         -    return SQLITE_ERROR;
         1099  +    bInMemory = 1;
  1101   1100     }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
  1102   1101       const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
  1103   1102       int nBlob = sqlite3_value_bytes(argv[0]);
  1104   1103       assert( pTab->pFirstEntry==0 );
  1105   1104       rc = zipfileLoadDirectory(pTab, aBlob, nBlob);
  1106   1105       pCsr->pFreeEntry = pTab->pFirstEntry;
  1107   1106       pTab->pFirstEntry = pTab->pLastEntry = 0;
................................................................................
  1162   1161       pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50);
  1163   1162       pIdxInfo->idxNum = 0;
  1164   1163     }
  1165   1164   
  1166   1165     return SQLITE_OK;
  1167   1166   }
  1168   1167   
  1169         -static ZipfileEntry *zipfileNewEntry(const char *zPath){
         1168  +static ZipfileEntry *zipfileNewEntry(const char *zPath, int nData){
  1170   1169     ZipfileEntry *pNew;
  1171         -  pNew = sqlite3_malloc(sizeof(ZipfileEntry));
         1170  +  pNew = sqlite3_malloc(sizeof(ZipfileEntry) + nData);
  1172   1171     if( pNew ){
  1173   1172       memset(pNew, 0, sizeof(ZipfileEntry));
         1173  +    if( nData ){
         1174  +      pNew->aData = (u8*)&pNew[1];
         1175  +    }
  1174   1176       pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
  1175   1177       if( pNew->cds.zFile==0 ){
  1176   1178         sqlite3_free(pNew);
  1177   1179         pNew = 0;
  1178   1180       }
  1179   1181     }
  1180   1182     return pNew;
  1181   1183   }
  1182   1184   
  1183         -static int zipfileAppendEntry(
  1184         -  ZipfileTab *pTab,
  1185         -  ZipfileCDS *pCds,
  1186         -  const char *zPath,              /* Path for new entry */
  1187         -  int nPath,                      /* strlen(zPath) */
  1188         -  const u8 *pData,
  1189         -  int nData,
  1190         -  u32 mTime
  1191         -){
  1192         -  u8 *aBuf = pTab->aBuffer;
  1193         -  int rc;
         1185  +static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){
         1186  +  ZipfileCDS *pCds = &pEntry->cds;
         1187  +  u8 *a = aBuf;
  1194   1188   
  1195   1189     pCds->nExtra = 9;
  1196   1190   
  1197         -  zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_LFH);
  1198         -  zipfileWrite16(aBuf, pCds->iVersionExtract);
  1199         -  zipfileWrite16(aBuf, pCds->flags);
  1200         -  zipfileWrite16(aBuf, pCds->iCompression);
  1201         -  zipfileWrite16(aBuf, pCds->mTime);
  1202         -  zipfileWrite16(aBuf, pCds->mDate);
  1203         -  zipfileWrite32(aBuf, pCds->crc32);
  1204         -  zipfileWrite32(aBuf, pCds->szCompressed);
  1205         -  zipfileWrite32(aBuf, pCds->szUncompressed);
  1206         -  zipfileWrite16(aBuf, (u16)nPath);
  1207         -  zipfileWrite16(aBuf, pCds->nExtra);
  1208         -  assert( aBuf==&pTab->aBuffer[ZIPFILE_LFH_FIXED_SZ] );
  1209         -  rc = zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer));
  1210         -  if( rc==SQLITE_OK ){
  1211         -    rc = zipfileAppendData(pTab, (const u8*)zPath, nPath);
  1212         -  }
  1213         -
  1214         -  if( rc==SQLITE_OK && pCds->nExtra ){
  1215         -    aBuf = pTab->aBuffer;
  1216         -    zipfileWrite16(aBuf, ZIPFILE_EXTRA_TIMESTAMP);
  1217         -    zipfileWrite16(aBuf, 5);
  1218         -    *aBuf++ = 0x01;
  1219         -    zipfileWrite32(aBuf, mTime);
  1220         -    rc = zipfileAppendData(pTab, pTab->aBuffer, 9);
  1221         -  }
  1222         -
         1191  +  /* Write the LFH itself */
         1192  +  zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH);
         1193  +  zipfileWrite16(a, pCds->iVersionExtract);
         1194  +  zipfileWrite16(a, pCds->flags);
         1195  +  zipfileWrite16(a, pCds->iCompression);
         1196  +  zipfileWrite16(a, pCds->mTime);
         1197  +  zipfileWrite16(a, pCds->mDate);
         1198  +  zipfileWrite32(a, pCds->crc32);
         1199  +  zipfileWrite32(a, pCds->szCompressed);
         1200  +  zipfileWrite32(a, pCds->szUncompressed);
         1201  +  zipfileWrite16(a, (u16)pCds->nFile);
         1202  +  zipfileWrite16(a, pCds->nExtra);
         1203  +  assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] );
         1204  +
         1205  +  /* Add the file name */
         1206  +  memcpy(a, pCds->zFile, (int)pCds->nFile);
         1207  +  a += (int)pCds->nFile;
         1208  +
         1209  +  /* The "extra" data */
         1210  +  zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
         1211  +  zipfileWrite16(a, 5);
         1212  +  *a++ = 0x01;
         1213  +  zipfileWrite32(a, pEntry->mUnixTime);
         1214  +
         1215  +  return a-aBuf;
         1216  +}
         1217  +
         1218  +static int zipfileAppendEntry(
         1219  +  ZipfileTab *pTab,
         1220  +  ZipfileEntry *pEntry,
         1221  +  const u8 *pData,
         1222  +  int nData
         1223  +){
         1224  +  u8 *aBuf = pTab->aBuffer;
         1225  +  int nBuf;
         1226  +  int rc;
         1227  +
         1228  +  nBuf = zipfileSerializeLFH(pEntry, aBuf);
         1229  +  rc = zipfileAppendData(pTab, aBuf, nBuf);
  1223   1230     if( rc==SQLITE_OK ){
  1224   1231       rc = zipfileAppendData(pTab, pData, nData);
  1225   1232     }
  1226   1233   
  1227   1234     return rc;
  1228   1235   }
  1229   1236   
................................................................................
  1299   1306     int iMethod = 0;                /* Compression method for new entry */
  1300   1307     u8 *pFree = 0;                  /* Free this */
  1301   1308     char *zFree = 0;                /* Also free this */
  1302   1309     ZipfileEntry *pOld = 0;
  1303   1310     int bIsDir = 0;
  1304   1311     u32 iCrc32 = 0;
  1305   1312   
  1306         -  assert( pTab->zFile );
  1307         -  assert( pTab->pWriteFd );
         1313  +  assert( (pTab->zFile==0)==(pTab->pWriteFd==0) );
  1308   1314   
  1309   1315     /* If this is a DELETE or UPDATE, find the archive entry to delete. */
  1310   1316     if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
  1311   1317       const char *zDelete = (const char*)sqlite3_value_text(apVal[0]);
  1312   1318       int nDelete = (int)strlen(zDelete);
  1313   1319       for(pOld=pTab->pFirstEntry; 1; pOld=pOld->pNext){
  1314   1320         if( zipfileComparePath(pOld->cds.zFile, zDelete, nDelete)==0 ){
................................................................................
  1403   1409             break;
  1404   1410           }
  1405   1411         }
  1406   1412       }
  1407   1413   
  1408   1414       if( rc==SQLITE_OK ){
  1409   1415         /* Create the new CDS record. */
  1410         -      pNew = zipfileNewEntry(zPath);
         1416  +      pNew = zipfileNewEntry(zPath, pTab->zFile ? 0 : (nData+1));
  1411   1417         if( pNew==0 ){
  1412   1418           rc = SQLITE_NOMEM;
  1413   1419         }else{
  1414   1420           pNew->cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY;
  1415   1421           pNew->cds.iVersionExtract = ZIPFILE_NEWENTRY_REQUIRED;
  1416   1422           pNew->cds.flags = ZIPFILE_NEWENTRY_FLAGS;
  1417   1423           pNew->cds.iCompression = (u16)iMethod;
................................................................................
  1419   1425           pNew->cds.crc32 = iCrc32;
  1420   1426           pNew->cds.szCompressed = nData;
  1421   1427           pNew->cds.szUncompressed = (u32)sz;
  1422   1428           pNew->cds.iExternalAttr = (mode<<16);
  1423   1429           pNew->cds.iOffset = (u32)pTab->szCurrent;
  1424   1430           pNew->cds.nFile = nPath;
  1425   1431           pNew->mUnixTime = (u32)mTime;
  1426         -        rc = zipfileAppendEntry(
  1427         -            pTab, &pNew->cds, zPath, nPath, pData, nData, pNew->mUnixTime
  1428         -        );
         1432  +        if( pTab->zFile ){
         1433  +          rc = zipfileAppendEntry(pTab, pNew, pData, nData);
         1434  +        }else{
         1435  +          memcpy(pNew->aData, pData, nData);
         1436  +        }
  1429   1437           zipfileAddEntry(pTab, pOld, pNew);
  1430   1438         }
  1431   1439       }
  1432   1440     }
  1433   1441   
  1434   1442     if( rc==SQLITE_OK && pOld ){
  1435   1443       ZipfileEntry **pp;
................................................................................
  1445   1453       zipfileEntryFree(pOld);
  1446   1454     }
  1447   1455   
  1448   1456     sqlite3_free(pFree);
  1449   1457     sqlite3_free(zFree);
  1450   1458     return rc;
  1451   1459   }
         1460  +
         1461  +static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){
         1462  +  u8 *a = aBuf;
         1463  +  zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD);
         1464  +  zipfileWrite16(a, p->iDisk);
         1465  +  zipfileWrite16(a, p->iFirstDisk);
         1466  +  zipfileWrite16(a, p->nEntry);
         1467  +  zipfileWrite16(a, p->nEntryTotal);
         1468  +  zipfileWrite32(a, p->nSize);
         1469  +  zipfileWrite32(a, p->iOffset);
         1470  +  zipfileWrite16(a, 0);        /* Size of trailing comment in bytes*/
         1471  +
         1472  +  return a-aBuf;
         1473  +}
  1452   1474   
  1453   1475   static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
  1454         -  u8 *aBuf = pTab->aBuffer;
  1455         -
  1456         -  zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_EOCD);
  1457         -  zipfileWrite16(aBuf, p->iDisk);
  1458         -  zipfileWrite16(aBuf, p->iFirstDisk);
  1459         -  zipfileWrite16(aBuf, p->nEntry);
  1460         -  zipfileWrite16(aBuf, p->nEntryTotal);
  1461         -  zipfileWrite32(aBuf, p->nSize);
  1462         -  zipfileWrite32(aBuf, p->iOffset);
  1463         -  zipfileWrite16(aBuf, 0);        /* Size of trailing comment in bytes*/
  1464         -
  1465         -  assert( (aBuf-pTab->aBuffer)==22 );
  1466         -  return zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer));
         1476  +  int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer);
         1477  +  assert( nBuf==ZIPFILE_EOCD_FIXED_SZ );
         1478  +  return zipfileAppendData(pTab, pTab->aBuffer, nBuf);
  1467   1479   }
  1468   1480   
  1469   1481   static int zipfileBegin(sqlite3_vtab *pVtab){
  1470   1482     ZipfileTab *pTab = (ZipfileTab*)pVtab;
  1471   1483     int rc = SQLITE_OK;
  1472   1484   
  1473   1485     assert( pTab->pWriteFd==0 );
  1474         -
  1475         -  /* This table is only writable if a default archive path was specified 
  1476         -  ** as part of the CREATE VIRTUAL TABLE statement. */
  1477         -  if( pTab->zFile==0 ){
  1478         -    pTab->base.zErrMsg = sqlite3_mprintf(
  1479         -        "zipfile: writing requires a default archive"
  1480         -    );
  1481         -    return SQLITE_ERROR;
  1482         -  }
  1483         -
  1484         -  /* Open a write fd on the file. Also load the entire central directory
  1485         -  ** structure into memory. During the transaction any new file data is 
  1486         -  ** appended to the archive file, but the central directory is accumulated
  1487         -  ** in main-memory until the transaction is committed.  */
  1488         -  pTab->pWriteFd = fopen(pTab->zFile, "ab+");
  1489         -  if( pTab->pWriteFd==0 ){
  1490         -    pTab->base.zErrMsg = sqlite3_mprintf(
  1491         -        "zipfile: failed to open file %s for writing", pTab->zFile
  1492         -    );
  1493         -    rc = SQLITE_ERROR;
  1494         -  }else{
  1495         -    fseek(pTab->pWriteFd, 0, SEEK_END);
  1496         -    pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
  1497         -    rc = zipfileLoadDirectory(pTab, 0, 0);
  1498         -  }
  1499         -
  1500         -  if( rc!=SQLITE_OK ){
  1501         -    zipfileCleanupTransaction(pTab);
         1486  +  if( pTab->zFile ){
         1487  +    /* Open a write fd on the file. Also load the entire central directory
         1488  +    ** structure into memory. During the transaction any new file data is 
         1489  +    ** appended to the archive file, but the central directory is accumulated
         1490  +    ** in main-memory until the transaction is committed.  */
         1491  +    pTab->pWriteFd = fopen(pTab->zFile, "ab+");
         1492  +    if( pTab->pWriteFd==0 ){
         1493  +      pTab->base.zErrMsg = sqlite3_mprintf(
         1494  +          "zipfile: failed to open file %s for writing", pTab->zFile
         1495  +      );
         1496  +      rc = SQLITE_ERROR;
         1497  +    }else{
         1498  +      fseek(pTab->pWriteFd, 0, SEEK_END);
         1499  +      pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
         1500  +      rc = zipfileLoadDirectory(pTab, 0, 0);
         1501  +    }
         1502  +
         1503  +    if( rc!=SQLITE_OK ){
         1504  +      zipfileCleanupTransaction(pTab);
         1505  +    }
  1502   1506     }
  1503   1507   
  1504   1508     return rc;
  1505   1509   }
  1506   1510   
  1507   1511   /*
  1508   1512   ** Serialize the CDS structure into buffer aBuf[]. Return the number
................................................................................
  1639   1643       }else{
  1640   1644         sqlite3_result_text(context, zRes, -1, SQLITE_TRANSIENT);
  1641   1645         sqlite3_free(zRes);
  1642   1646       }
  1643   1647     }
  1644   1648   }
  1645   1649   
         1650  +static void zipfileFree(void *p) { sqlite3_free(p); }
         1651  +
         1652  +static void zipfileFunctionBlob(
         1653  +  sqlite3_context *context,
         1654  +  int argc,
         1655  +  sqlite3_value **argv
         1656  +){
         1657  +  ZipfileCsr *pCsr;
         1658  +  ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context);
         1659  +  ZipfileEntry *p;
         1660  +  int nBody = 0;
         1661  +  int nCds = 0;
         1662  +  int nEocd = ZIPFILE_EOCD_FIXED_SZ;
         1663  +  ZipfileEOCD eocd;
         1664  +
         1665  +  u8 *aZip;
         1666  +  int nZip;
         1667  +
         1668  +  u8 *aBody;
         1669  +  u8 *aCds;
         1670  +
         1671  +  pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0]));
         1672  +  if( pCsr->pFile || pTab->zFile ){
         1673  +    sqlite3_result_error(context, "illegal use of zipfile_blob()", -1);
         1674  +    return;
         1675  +  }
         1676  +
         1677  +  /* Figure out how large the final file will be */
         1678  +  for(p=pTab->pFirstEntry; p; p=p->pNext){
         1679  +    nBody += ZIPFILE_LFH_FIXED_SZ + p->cds.nFile + 9 + p->cds.szCompressed;
         1680  +    nCds += ZIPFILE_CDS_FIXED_SZ + p->cds.nFile + 9;
         1681  +  }
         1682  +
         1683  +  /* Allocate space to create the serialized file */
         1684  +  nZip = nBody + nCds + nEocd;
         1685  +  aZip = (u8*)sqlite3_malloc(nZip);
         1686  +  if( aZip==0 ){
         1687  +    sqlite3_result_error_nomem(context);
         1688  +    return;
         1689  +  }
         1690  +  aBody = aZip;
         1691  +  aCds = &aZip[nBody];
         1692  +
         1693  +  /* Populate the body and CDS */
         1694  +  memset(&eocd, 0, sizeof(eocd));
         1695  +  for(p=pTab->pFirstEntry; p; p=p->pNext){
         1696  +    p->cds.iOffset = (aBody - aZip);
         1697  +    aBody += zipfileSerializeLFH(p, aBody);
         1698  +    if( p->cds.szCompressed ){
         1699  +      memcpy(aBody, p->aData, p->cds.szCompressed);
         1700  +      aBody += p->cds.szCompressed;
         1701  +    }
         1702  +    aCds += zipfileSerializeCDS(p, aCds);
         1703  +    eocd.nEntry++;
         1704  +  }
         1705  +
         1706  +  /* Append the EOCD record */
         1707  +  assert( aBody==&aZip[nBody] );
         1708  +  assert( aCds==&aZip[nBody+nCds] );
         1709  +  eocd.nEntryTotal = eocd.nEntry;
         1710  +  eocd.nSize = nCds;
         1711  +  eocd.iOffset = nBody;
         1712  +  zipfileSerializeEOCD(&eocd, aCds);
         1713  +
         1714  +  sqlite3_result_blob(context, aZip, nZip, zipfileFree);
         1715  +}
         1716  +
  1646   1717   
  1647   1718   /*
  1648   1719   ** xFindFunction method.
  1649   1720   */
  1650   1721   static int zipfileFindFunction(
  1651   1722     sqlite3_vtab *pVtab,            /* Virtual table handle */
  1652   1723     int nArg,                       /* Number of SQL function arguments */
................................................................................
  1655   1726     void **ppArg                    /* OUT: User data for *pxFunc */
  1656   1727   ){
  1657   1728     if( nArg>0 ){
  1658   1729       if( sqlite3_stricmp("zipfile_cds", zName)==0 ){
  1659   1730         *pxFunc = zipfileFunctionCds;
  1660   1731         *ppArg = (void*)pVtab;
  1661   1732         return 1;
         1733  +    }
         1734  +    if( sqlite3_stricmp("zipfile_blob", zName)==0 ){
         1735  +      *pxFunc = zipfileFunctionBlob;
         1736  +      *ppArg = (void*)pVtab;
         1737  +      return 1;
  1662   1738       }
  1663   1739     }
  1664   1740   
  1665   1741     return 0;
  1666   1742   }
  1667   1743   
  1668   1744   /*
................................................................................
  1689   1765       zipfileCommit,             /* xCommit */
  1690   1766       zipfileRollback,           /* xRollback */
  1691   1767       zipfileFindFunction,       /* xFindMethod */
  1692   1768       0,                         /* xRename */
  1693   1769     };
  1694   1770   
  1695   1771     int rc = sqlite3_create_module(db, "zipfile"  , &zipfileModule, 0);
  1696         -  if( rc==SQLITE_OK ){
  1697         -    rc = sqlite3_overload_function(db, "zipfile_cds", -1);
  1698         -  }
         1772  +  if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1);
         1773  +  if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_blob", -1);
  1699   1774     return rc;
  1700   1775   }
  1701   1776   #else         /* SQLITE_OMIT_VIRTUALTABLE */
  1702   1777   # define zipfileRegister(x) SQLITE_OK
  1703   1778   #endif
  1704   1779   
  1705   1780   #ifdef _WIN32

Changes to test/zipfile.test.

    28     28     ]
    29     29   
    30     30     set fd [open $file]
    31     31     fconfigure $fd -translation binary -encoding binary
    32     32     set data [read $fd]
    33     33     close $fd
    34     34   
    35         -  set res2 [db eval { SELECT name,mode,mtime,method,quote(data) FROM zipfile($data) }]
    36         -  uplevel [list do_test $tn [list set {} $res2] $res1]
           35  +  set res2 [db eval { 
           36  +    SELECT name,mode,mtime,method,quote(data) FROM zipfile($data) 
           37  +  }]
           38  +
           39  +  uplevel [list do_test $tn.1 [list set {} $res2] $res1]
           40  +
           41  +  set T "$file.test_zip"
           42  +  set fd [open $T w]
           43  +  fconfigure $fd -translation binary -encoding binary
           44  +  puts -nonewline $fd $data
           45  +  close $fd
           46  +
           47  +  set res3 [
           48  +    db eval { SELECT name,mode,mtime,method,quote(data) FROM zipfile($T) }
           49  +  ]
           50  +
           51  +  uplevel [list do_test $tn.2 [list set {} $res3] $res1]
    37     52   }
    38     53   
    39     54   forcedelete test.zip
    40     55   do_execsql_test 1.0 {
    41     56     CREATE VIRTUAL TABLE temp.zz USING zipfile('test.zip');
    42     57     PRAGMA table_info(zz);
    43     58   } {
................................................................................
   266    281       INSERT INTO x1(name, data) VALUES($fname, 'abcd');
   267    282     } {1 {constraint failed}}
   268    283   }
   269    284   
   270    285   do_catchsql_test 3.2 {
   271    286     SELECT rowid FROM x1
   272    287   } {1 {no such column: rowid}}
          288  +
          289  +#-------------------------------------------------------------------------
          290  +reset_db
          291  +forcedelete test.zip
          292  +load_static_extension db zipfile
          293  +
          294  +do_execsql_test 4.0 {
          295  +  CREATE VIRTUAL TABLE x2 USING zipfile();
          296  +  INSERT INTO x2(name, data) VALUES('dir1/', NULL);
          297  +  INSERT INTO x2(name, data) VALUES('file1', '1234');
          298  +  INSERT INTO x2(name, data) VALUES('dir1/file2', '5678');
          299  +  SELECT name, data FROM x2
          300  +} {
          301  +  dir1/ {} file1 1234 dir1/file2 5678
          302  +}
          303  +
          304  +do_test 4.1 {
          305  +  set data [db one {SELECT zipfile_blob(z) FROM x2 LIMIT 1}]
          306  +  db eval { SELECT name, data FROM zipfile($data) }
          307  +} {dir1/ {} file1 1234 dir1/file2 5678}
          308  +
   273    309   
   274    310   
   275    311   finish_test
   276    312