]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
If a zipfile virtual table is created with no argument - "CREATE VIRTUAL TABLE
authordan <dan@noemail.net>
Sat, 27 Jan 2018 16:29:59 +0000 (16:29 +0000)
committerdan <dan@noemail.net>
Sat, 27 Jan 2018 16:29:59 +0000 (16:29 +0000)
zzz USING zipfile()" - accumulate data in memory. Support "SELECT
zipfile_blob(z) FROM zzz LIMIT 1" to retrieve a zip archive image.

FossilOrigin-Name: e63185edfe0c316aa60c1fa085d032425ecc7db54536dfa5a977772eaf3c240e

ext/misc/zipfile.c
manifest
manifest.uuid
test/zipfile.test

index 61e60f63d220bb6eafa0aed9134e9f6d8702e7c9..4e59675c1d9e8e487d30197e3620bce1f416b265 100644 (file)
@@ -103,6 +103,8 @@ static const char ZIPFILE_SCHEMA[] =
 #define ZIPFILE_SIGNATURE_EOCD    0x06054b50
 #define ZIPFILE_LFH_FIXED_SZ      30
 
+#define ZIPFILE_EOCD_FIXED_SZ     22
+
 /*
 ** Set the error message contained in context ctx to the results of
 ** vprintf(zFmt, ...).
@@ -645,7 +647,7 @@ static int zipfileGetEntry(
   int rc = SQLITE_OK;
 
   if( aBlob==0 ){
-    aRead = (u8*)pTab->aBuffer;
+    aRead = pTab->aBuffer;
     rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
   }else{
     aRead = (u8*)&aBlob[iOff];
@@ -1094,10 +1096,7 @@ static int zipfileFilter(
   if( pTab->zFile ){
     zFile = pTab->zFile;
   }else if( idxNum==0 ){
-    /* Error. This is an eponymous virtual table and the user has not 
-    ** supplied a file name. */
-    zipfileSetErrmsg(pCsr, "table function zipfile() requires an argument");
-    return SQLITE_ERROR;
+    bInMemory = 1;
   }else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
     const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
     int nBlob = sqlite3_value_bytes(argv[0]);
@@ -1166,11 +1165,14 @@ static int zipfileBestIndex(
   return SQLITE_OK;
 }
 
-static ZipfileEntry *zipfileNewEntry(const char *zPath){
+static ZipfileEntry *zipfileNewEntry(const char *zPath, int nData){
   ZipfileEntry *pNew;
-  pNew = sqlite3_malloc(sizeof(ZipfileEntry));
+  pNew = sqlite3_malloc(sizeof(ZipfileEntry) + nData);
   if( pNew ){
     memset(pNew, 0, sizeof(ZipfileEntry));
+    if( nData ){
+      pNew->aData = (u8*)&pNew[1];
+    }
     pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
     if( pNew->cds.zFile==0 ){
       sqlite3_free(pNew);
@@ -1180,46 +1182,51 @@ static ZipfileEntry *zipfileNewEntry(const char *zPath){
   return pNew;
 }
 
+static int zipfileSerializeLFH(ZipfileEntry *pEntry, u8 *aBuf){
+  ZipfileCDS *pCds = &pEntry->cds;
+  u8 *a = aBuf;
+
+  pCds->nExtra = 9;
+
+  /* Write the LFH itself */
+  zipfileWrite32(a, ZIPFILE_SIGNATURE_LFH);
+  zipfileWrite16(a, pCds->iVersionExtract);
+  zipfileWrite16(a, pCds->flags);
+  zipfileWrite16(a, pCds->iCompression);
+  zipfileWrite16(a, pCds->mTime);
+  zipfileWrite16(a, pCds->mDate);
+  zipfileWrite32(a, pCds->crc32);
+  zipfileWrite32(a, pCds->szCompressed);
+  zipfileWrite32(a, pCds->szUncompressed);
+  zipfileWrite16(a, (u16)pCds->nFile);
+  zipfileWrite16(a, pCds->nExtra);
+  assert( a==&aBuf[ZIPFILE_LFH_FIXED_SZ] );
+
+  /* Add the file name */
+  memcpy(a, pCds->zFile, (int)pCds->nFile);
+  a += (int)pCds->nFile;
+
+  /* The "extra" data */
+  zipfileWrite16(a, ZIPFILE_EXTRA_TIMESTAMP);
+  zipfileWrite16(a, 5);
+  *a++ = 0x01;
+  zipfileWrite32(a, pEntry->mUnixTime);
+
+  return a-aBuf;
+}
+
 static int zipfileAppendEntry(
   ZipfileTab *pTab,
-  ZipfileCDS *pCds,
-  const char *zPath,              /* Path for new entry */
-  int nPath,                      /* strlen(zPath) */
+  ZipfileEntry *pEntry,
   const u8 *pData,
-  int nData,
-  u32 mTime
+  int nData
 ){
   u8 *aBuf = pTab->aBuffer;
+  int nBuf;
   int rc;
 
-  pCds->nExtra = 9;
-
-  zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_LFH);
-  zipfileWrite16(aBuf, pCds->iVersionExtract);
-  zipfileWrite16(aBuf, pCds->flags);
-  zipfileWrite16(aBuf, pCds->iCompression);
-  zipfileWrite16(aBuf, pCds->mTime);
-  zipfileWrite16(aBuf, pCds->mDate);
-  zipfileWrite32(aBuf, pCds->crc32);
-  zipfileWrite32(aBuf, pCds->szCompressed);
-  zipfileWrite32(aBuf, pCds->szUncompressed);
-  zipfileWrite16(aBuf, (u16)nPath);
-  zipfileWrite16(aBuf, pCds->nExtra);
-  assert( aBuf==&pTab->aBuffer[ZIPFILE_LFH_FIXED_SZ] );
-  rc = zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer));
-  if( rc==SQLITE_OK ){
-    rc = zipfileAppendData(pTab, (const u8*)zPath, nPath);
-  }
-
-  if( rc==SQLITE_OK && pCds->nExtra ){
-    aBuf = pTab->aBuffer;
-    zipfileWrite16(aBuf, ZIPFILE_EXTRA_TIMESTAMP);
-    zipfileWrite16(aBuf, 5);
-    *aBuf++ = 0x01;
-    zipfileWrite32(aBuf, mTime);
-    rc = zipfileAppendData(pTab, pTab->aBuffer, 9);
-  }
-
+  nBuf = zipfileSerializeLFH(pEntry, aBuf);
+  rc = zipfileAppendData(pTab, aBuf, nBuf);
   if( rc==SQLITE_OK ){
     rc = zipfileAppendData(pTab, pData, nData);
   }
@@ -1303,8 +1310,7 @@ static int zipfileUpdate(
   int bIsDir = 0;
   u32 iCrc32 = 0;
 
-  assert( pTab->zFile );
-  assert( pTab->pWriteFd );
+  assert( (pTab->zFile==0)==(pTab->pWriteFd==0) );
 
   /* If this is a DELETE or UPDATE, find the archive entry to delete. */
   if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
@@ -1407,7 +1413,7 @@ static int zipfileUpdate(
 
     if( rc==SQLITE_OK ){
       /* Create the new CDS record. */
-      pNew = zipfileNewEntry(zPath);
+      pNew = zipfileNewEntry(zPath, pTab->zFile ? 0 : (nData+1));
       if( pNew==0 ){
         rc = SQLITE_NOMEM;
       }else{
@@ -1423,9 +1429,11 @@ static int zipfileUpdate(
         pNew->cds.iOffset = (u32)pTab->szCurrent;
         pNew->cds.nFile = nPath;
         pNew->mUnixTime = (u32)mTime;
-        rc = zipfileAppendEntry(
-            pTab, &pNew->cds, zPath, nPath, pData, nData, pNew->mUnixTime
-        );
+        if( pTab->zFile ){
+          rc = zipfileAppendEntry(pTab, pNew, pData, nData);
+        }else{
+          memcpy(pNew->aData, pData, nData);
+        }
         zipfileAddEntry(pTab, pOld, pNew);
       }
     }
@@ -1450,20 +1458,24 @@ static int zipfileUpdate(
   return rc;
 }
 
-static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
-  u8 *aBuf = pTab->aBuffer;
+static int zipfileSerializeEOCD(ZipfileEOCD *p, u8 *aBuf){
+  u8 *a = aBuf;
+  zipfileWrite32(a, ZIPFILE_SIGNATURE_EOCD);
+  zipfileWrite16(a, p->iDisk);
+  zipfileWrite16(a, p->iFirstDisk);
+  zipfileWrite16(a, p->nEntry);
+  zipfileWrite16(a, p->nEntryTotal);
+  zipfileWrite32(a, p->nSize);
+  zipfileWrite32(a, p->iOffset);
+  zipfileWrite16(a, 0);        /* Size of trailing comment in bytes*/
 
-  zipfileWrite32(aBuf, ZIPFILE_SIGNATURE_EOCD);
-  zipfileWrite16(aBuf, p->iDisk);
-  zipfileWrite16(aBuf, p->iFirstDisk);
-  zipfileWrite16(aBuf, p->nEntry);
-  zipfileWrite16(aBuf, p->nEntryTotal);
-  zipfileWrite32(aBuf, p->nSize);
-  zipfileWrite32(aBuf, p->iOffset);
-  zipfileWrite16(aBuf, 0);        /* Size of trailing comment in bytes*/
-
-  assert( (aBuf-pTab->aBuffer)==22 );
-  return zipfileAppendData(pTab, pTab->aBuffer, (int)(aBuf - pTab->aBuffer));
+  return a-aBuf;
+}
+
+static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){
+  int nBuf = zipfileSerializeEOCD(p, pTab->aBuffer);
+  assert( nBuf==ZIPFILE_EOCD_FIXED_SZ );
+  return zipfileAppendData(pTab, pTab->aBuffer, nBuf);
 }
 
 static int zipfileBegin(sqlite3_vtab *pVtab){
@@ -1471,34 +1483,26 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
   int rc = SQLITE_OK;
 
   assert( pTab->pWriteFd==0 );
+  if( pTab->zFile ){
+    /* Open a write fd on the file. Also load the entire central directory
+    ** structure into memory. During the transaction any new file data is 
+    ** appended to the archive file, but the central directory is accumulated
+    ** in main-memory until the transaction is committed.  */
+    pTab->pWriteFd = fopen(pTab->zFile, "ab+");
+    if( pTab->pWriteFd==0 ){
+      pTab->base.zErrMsg = sqlite3_mprintf(
+          "zipfile: failed to open file %s for writing", pTab->zFile
+      );
+      rc = SQLITE_ERROR;
+    }else{
+      fseek(pTab->pWriteFd, 0, SEEK_END);
+      pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
+      rc = zipfileLoadDirectory(pTab, 0, 0);
+    }
 
-  /* This table is only writable if a default archive path was specified 
-  ** as part of the CREATE VIRTUAL TABLE statement. */
-  if( pTab->zFile==0 ){
-    pTab->base.zErrMsg = sqlite3_mprintf(
-        "zipfile: writing requires a default archive"
-    );
-    return SQLITE_ERROR;
-  }
-
-  /* Open a write fd on the file. Also load the entire central directory
-  ** structure into memory. During the transaction any new file data is 
-  ** appended to the archive file, but the central directory is accumulated
-  ** in main-memory until the transaction is committed.  */
-  pTab->pWriteFd = fopen(pTab->zFile, "ab+");
-  if( pTab->pWriteFd==0 ){
-    pTab->base.zErrMsg = sqlite3_mprintf(
-        "zipfile: failed to open file %s for writing", pTab->zFile
-    );
-    rc = SQLITE_ERROR;
-  }else{
-    fseek(pTab->pWriteFd, 0, SEEK_END);
-    pTab->szCurrent = pTab->szOrig = (i64)ftell(pTab->pWriteFd);
-    rc = zipfileLoadDirectory(pTab, 0, 0);
-  }
-
-  if( rc!=SQLITE_OK ){
-    zipfileCleanupTransaction(pTab);
+    if( rc!=SQLITE_OK ){
+      zipfileCleanupTransaction(pTab);
+    }
   }
 
   return rc;
@@ -1643,6 +1647,73 @@ static void zipfileFunctionCds(
   }
 }
 
+static void zipfileFree(void *p) { sqlite3_free(p); }
+
+static void zipfileFunctionBlob(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  ZipfileCsr *pCsr;
+  ZipfileTab *pTab = (ZipfileTab*)sqlite3_user_data(context);
+  ZipfileEntry *p;
+  int nBody = 0;
+  int nCds = 0;
+  int nEocd = ZIPFILE_EOCD_FIXED_SZ;
+  ZipfileEOCD eocd;
+
+  u8 *aZip;
+  int nZip;
+
+  u8 *aBody;
+  u8 *aCds;
+
+  pCsr = zipfileFindCursor(pTab, sqlite3_value_int64(argv[0]));
+  if( pCsr->pFile || pTab->zFile ){
+    sqlite3_result_error(context, "illegal use of zipfile_blob()", -1);
+    return;
+  }
+
+  /* Figure out how large the final file will be */
+  for(p=pTab->pFirstEntry; p; p=p->pNext){
+    nBody += ZIPFILE_LFH_FIXED_SZ + p->cds.nFile + 9 + p->cds.szCompressed;
+    nCds += ZIPFILE_CDS_FIXED_SZ + p->cds.nFile + 9;
+  }
+
+  /* Allocate space to create the serialized file */
+  nZip = nBody + nCds + nEocd;
+  aZip = (u8*)sqlite3_malloc(nZip);
+  if( aZip==0 ){
+    sqlite3_result_error_nomem(context);
+    return;
+  }
+  aBody = aZip;
+  aCds = &aZip[nBody];
+
+  /* Populate the body and CDS */
+  memset(&eocd, 0, sizeof(eocd));
+  for(p=pTab->pFirstEntry; p; p=p->pNext){
+    p->cds.iOffset = (aBody - aZip);
+    aBody += zipfileSerializeLFH(p, aBody);
+    if( p->cds.szCompressed ){
+      memcpy(aBody, p->aData, p->cds.szCompressed);
+      aBody += p->cds.szCompressed;
+    }
+    aCds += zipfileSerializeCDS(p, aCds);
+    eocd.nEntry++;
+  }
+
+  /* Append the EOCD record */
+  assert( aBody==&aZip[nBody] );
+  assert( aCds==&aZip[nBody+nCds] );
+  eocd.nEntryTotal = eocd.nEntry;
+  eocd.nSize = nCds;
+  eocd.iOffset = nBody;
+  zipfileSerializeEOCD(&eocd, aCds);
+
+  sqlite3_result_blob(context, aZip, nZip, zipfileFree);
+}
+
 
 /*
 ** xFindFunction method.
@@ -1660,6 +1731,11 @@ static int zipfileFindFunction(
       *ppArg = (void*)pVtab;
       return 1;
     }
+    if( sqlite3_stricmp("zipfile_blob", zName)==0 ){
+      *pxFunc = zipfileFunctionBlob;
+      *ppArg = (void*)pVtab;
+      return 1;
+    }
   }
 
   return 0;
@@ -1693,9 +1769,8 @@ static int zipfileRegister(sqlite3 *db){
   };
 
   int rc = sqlite3_create_module(db, "zipfile"  , &zipfileModule, 0);
-  if( rc==SQLITE_OK ){
-    rc = sqlite3_overload_function(db, "zipfile_cds", -1);
-  }
+  if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_cds", -1);
+  if( rc==SQLITE_OK ) rc = sqlite3_overload_function(db, "zipfile_blob", -1);
   return rc;
 }
 #else         /* SQLITE_OMIT_VIRTUALTABLE */
index faa8c1e35697723534efc7784096601294fe725b..96a7ea5fe4253014af73c84a48c4f5a7ebe58fcc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Changes\sto\savoid\sa\sharmless\sUB\swarning\sfrom\sclang.
-D 2018-01-27T14:25:27.955
+C If\sa\szipfile\svirtual\stable\sis\screated\swith\sno\sargument\s-\s"CREATE\sVIRTUAL\sTABLE\nzzz\sUSING\szipfile()"\s-\saccumulate\sdata\sin\smemory.\sSupport\s"SELECT\nzipfile_blob(z)\sFROM\szzz\sLIMIT\s1"\sto\sretrieve\sa\szip\sarchive\simage.
+D 2018-01-27T16:29:59.042
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 7a3f714b4fcf793108042b7b0a5c720b0b310ec84314d61ba7f3f49f27e550ea
@@ -304,7 +304,7 @@ F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
 F ext/misc/vtablog.c 31d0d8f4406795679dcd3a67917c213d3a2a5fb3ea5de35f6e773491ed7e13c9
 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
 F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
-F ext/misc/zipfile.c 40195c1cfb43e0ebdf176d37346fea1cb18246e35fbfce477eadc2ff5f7490df
+F ext/misc/zipfile.c df57128d9ad2a1e60097d7971e787b582fb66ce0926577cb6f5978f7af210b8c
 F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
 F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
 F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee
@@ -1603,7 +1603,7 @@ F test/wordcount.c cb589cec469a1d90add05b1f8cee75c7210338d87a5afd65260ed5c0f4bbf
 F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
 F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
 F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc
-F test/zipfile.test 0834b33e000991a80d94167e84af346162d212526d8efee0b9d4ba84589fe292
+F test/zipfile.test 1b213bdc31eddd4a41042875dbdb29b6ea12b7da7a372a8eb07f61d0d76d800f
 F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
 F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d
 F tool/Replace.cs 02c67258801c2fb5f63231e0ac0f220b4b36ba91
@@ -1702,7 +1702,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 5259d4847f2b73f26b2385f9d8cff8fe0cabc54b4deab8477c87c8d1bb5535b1
-R dc25ffafa3daf6e245ccddfac4936cfd
-U drh
-Z 83f6cfb83d43c9463ef36c14a8c84938
+P 19f5c1400054df10688ab448e7e23afef97cab4a7c7a3e411f7527509b515dd8
+R 8b2b96d9b8d1e07c4df7e0d2971e5e57
+U dan
+Z e468bfc5bd8b08bb5b9404b26ad000bc
index 3f53762e600ba1968b0062aa7ce356a16e8ee488..a8aca2c7e58dd66d058ad31f422aaf53f54a32b5 100644 (file)
@@ -1 +1 @@
-19f5c1400054df10688ab448e7e23afef97cab4a7c7a3e411f7527509b515dd8
\ No newline at end of file
+e63185edfe0c316aa60c1fa085d032425ecc7db54536dfa5a977772eaf3c240e
\ No newline at end of file
index 60fde2ee1568274543d8dfa7465c99332e0fd3c4..e74f81ed1dc6ecfa42737000a361b8a0d2cff927 100644 (file)
@@ -32,8 +32,23 @@ proc do_zipfile_blob_test {tn file} {
   set data [read $fd]
   close $fd
 
-  set res2 [db eval { SELECT name,mode,mtime,method,quote(data) FROM zipfile($data) }]
-  uplevel [list do_test $tn [list set {} $res2] $res1]
+  set res2 [db eval { 
+    SELECT name,mode,mtime,method,quote(data) FROM zipfile($data) 
+  }]
+
+  uplevel [list do_test $tn.1 [list set {} $res2] $res1]
+
+  set T "$file.test_zip"
+  set fd [open $T w]
+  fconfigure $fd -translation binary -encoding binary
+  puts -nonewline $fd $data
+  close $fd
+
+  set res3 [
+    db eval { SELECT name,mode,mtime,method,quote(data) FROM zipfile($T) }
+  ]
+
+  uplevel [list do_test $tn.2 [list set {} $res3] $res1]
 }
 
 forcedelete test.zip
@@ -271,6 +286,27 @@ do_catchsql_test 3.2 {
   SELECT rowid FROM x1
 } {1 {no such column: rowid}}
 
+#-------------------------------------------------------------------------
+reset_db
+forcedelete test.zip
+load_static_extension db zipfile
+
+do_execsql_test 4.0 {
+  CREATE VIRTUAL TABLE x2 USING zipfile();
+  INSERT INTO x2(name, data) VALUES('dir1/', NULL);
+  INSERT INTO x2(name, data) VALUES('file1', '1234');
+  INSERT INTO x2(name, data) VALUES('dir1/file2', '5678');
+  SELECT name, data FROM x2
+} {
+  dir1/ {} file1 1234 dir1/file2 5678
+}
+
+do_test 4.1 {
+  set data [db one {SELECT zipfile_blob(z) FROM x2 LIMIT 1}]
+  db eval { SELECT name, data FROM zipfile($data) }
+} {dir1/ {} file1 1234 dir1/file2 5678}
+
+
 
 finish_test