]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Use xFetch() to access temporary files in vdbesort.c. Use a single large allocation...
authordan <dan@noemail.net>
Thu, 27 Mar 2014 17:23:41 +0000 (17:23 +0000)
committerdan <dan@noemail.net>
Thu, 27 Mar 2014 17:23:41 +0000 (17:23 +0000)
FossilOrigin-Name: f4ac1bf28c4ba395ccab8f1c9df72614a61095a7

manifest
manifest.uuid
src/vdbesort.c

index 50b1f52a8ef4716eea94d9e2d6bcdc58b42d83f7..dc6ca295c7ff93d4781ea333141cc70bb53fdf4c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\sthe\slatest\strunk\schanges\sand\sthe\sfix\sfor\sthe\scrash\son\sa\scorrupt\ndatabase.
-D 2014-03-27T00:09:00.185
+C Use\sxFetch()\sto\saccess\stemporary\sfiles\sin\svdbesort.c.\sUse\sa\ssingle\slarge\sallocation\sinstead\sof\smany\ssmall\sallocations\swhen\saccumulating\srecords\sin\svdbesort.c.\sThis\sis\san\sinterim\scommit\s-\sit\sallocates\sa\sbuffer\sthe\ssize\sof\sthe\spage-cache\severy\stime\sdata\sis\ssorted.
+D 2014-03-27T17:23:41.403
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -285,7 +285,7 @@ F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4
 F src/vdbeaux.c f81ef920dcf76aceaa1ce77081e9fc5d7a0993dd
 F src/vdbeblob.c 15377abfb59251bccedd5a9c7d014a895f0c04aa
 F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447
-F src/vdbesort.c 691f2186ae0943cd746ea7f5498cc9abebb7a7cc
+F src/vdbesort.c d46f384af1997a4441ef9c65759181954efc89cf
 F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
 F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
 F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8
@@ -1159,7 +1159,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P e005f2d6dd9faf38cc8fdb9428b5aa6192a6adae f585f5d7a0f9bf8c590388654a3638231eba8892
-R 9012cf758152dd3775ad665fff626f11
-U drh
-Z ffbdc2f043e4deddac6c4b509aff14ac
+P 0b35346c32dba14963c85ec178f2b46aa2bbf6dc
+R 8f417577dbf4ab5b5ce5a802872c70f0
+U dan
+Z 2c1a0a402e8f93dd4df9cfe6e1bca5e6
index f8b9980e3d3907e9e1a8e087d76e22c683ab6a34..853b60d3d318398810d3142665b8b7d5a7b8ef06 100644 (file)
@@ -1 +1 @@
-0b35346c32dba14963c85ec178f2b46aa2bbf6dc
\ No newline at end of file
+f4ac1bf28c4ba395ccab8f1c9df72614a61095a7
\ No newline at end of file
index 7b435542823a2c839be0ba774dd522ef06f9ec00..2a15168fd2f816ec75aa42d57de42bf98f15a088 100644 (file)
@@ -105,6 +105,8 @@ struct VdbeSorter {
   sqlite3_file *pTemp1;           /* PMA file 1 */
   SorterRecord *pRecord;          /* Head of in-memory record list */
   UnpackedRecord *pUnpacked;      /* Used to unpack keys */
+  u8* aMemory;                    /* Block to allocate records from */
+  int iMemory;                    /* Offset of free space in aMemory */
 };
 
 /*
@@ -121,6 +123,7 @@ struct VdbeSorterIter {
   u8 *aKey;                       /* Pointer to current key */
   u8 *aBuffer;                    /* Current read buffer */
   int nBuffer;                    /* Size of read buffer in bytes */
+  u8 *aMap;                       /* Pointer to mapping of pFile */
 };
 
 /*
@@ -163,6 +166,7 @@ struct SorterRecord {
 static void vdbeSorterIterZero(sqlite3 *db, VdbeSorterIter *pIter){
   sqlite3DbFree(db, pIter->aAlloc);
   sqlite3DbFree(db, pIter->aBuffer);
+  if( pIter->aMap ) sqlite3OsUnfetch(pIter->pFile, 0, pIter->aMap);
   memset(pIter, 0, sizeof(VdbeSorterIter));
 }
 
@@ -183,6 +187,13 @@ static int vdbeSorterIterRead(
 ){
   int iBuf;                       /* Offset within buffer to read from */
   int nAvail;                     /* Bytes of data available in buffer */
+
+  if( p->aMap ){
+    *ppOut = &p->aMap[p->iReadOff];
+    p->iReadOff += nByte;
+    return SQLITE_OK;
+  }
+
   assert( p->aBuffer );
 
   /* If there is no more data to be read from the buffer, read the next 
@@ -264,18 +275,22 @@ static int vdbeSorterIterRead(
 static int vdbeSorterIterVarint(sqlite3 *db, VdbeSorterIter *p, u64 *pnOut){
   int iBuf;
 
-  iBuf = p->iReadOff % p->nBuffer;
-  if( iBuf && (p->nBuffer-iBuf)>=9 ){
-    p->iReadOff += sqlite3GetVarint(&p->aBuffer[iBuf], pnOut);
+  if( p->aMap ){
+    p->iReadOff += sqlite3GetVarint(&p->aMap[p->iReadOff], pnOut);
   }else{
-    u8 aVarint[16], *a;
-    int i = 0, rc;
-    do{
-      rc = vdbeSorterIterRead(db, p, 1, &a);
-      if( rc ) return rc;
-      aVarint[(i++)&0xf] = a[0];
-    }while( (a[0]&0x80)!=0 );
-    sqlite3GetVarint(aVarint, pnOut);
+    iBuf = p->iReadOff % p->nBuffer;
+    if( iBuf && (p->nBuffer-iBuf)>=9 ){
+      p->iReadOff += sqlite3GetVarint(&p->aBuffer[iBuf], pnOut);
+    }else{
+      u8 aVarint[16], *a;
+      int i = 0, rc;
+      do{
+        rc = vdbeSorterIterRead(db, p, 1, &a);
+        if( rc ) return rc;
+        aVarint[(i++)&0xf] = a[0];
+      }while( (a[0]&0x80)!=0 );
+      sqlite3GetVarint(aVarint, pnOut);
+    }
   }
 
   return SQLITE_OK;
@@ -323,6 +338,7 @@ static int vdbeSorterIterInit(
 ){
   int rc = SQLITE_OK;
   int nBuf;
+  void *pMap;
 
   nBuf = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
 
@@ -333,33 +349,41 @@ static int vdbeSorterIterInit(
   pIter->iReadOff = iStart;
   pIter->nAlloc = 128;
   pIter->aAlloc = (u8 *)sqlite3DbMallocRaw(db, pIter->nAlloc);
-  pIter->nBuffer = nBuf;
-  pIter->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf);
 
-  if( !pIter->aBuffer ){
-    rc = SQLITE_NOMEM;
+  /* See if this PMA can be read using xFetch. */
+  rc = sqlite3OsFetch(pIter->pFile, 0, pSorter->iWriteOff, &pMap);
+  if( rc!=SQLITE_OK ) return rc;
+  if( pMap ){
+    pIter->aMap = (u8*)pMap;
   }else{
-    int iBuf;
+    pIter->nBuffer = nBuf;
+    pIter->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf);
+
+    if( !pIter->aBuffer ){
+      rc = SQLITE_NOMEM;
+    }else{
+      int iBuf;
 
-    iBuf = iStart % nBuf;
-    if( iBuf ){
-      int nRead = nBuf - iBuf;
-      if( (iStart + nRead) > pSorter->iWriteOff ){
-        nRead = (int)(pSorter->iWriteOff - iStart);
+      iBuf = iStart % nBuf;
+      if( iBuf ){
+        int nRead = nBuf - iBuf;
+        if( (iStart + nRead) > pSorter->iWriteOff ){
+          nRead = (int)(pSorter->iWriteOff - iStart);
+        }
+        rc = sqlite3OsRead(
+            pSorter->pTemp1, &pIter->aBuffer[iBuf], nRead, iStart
+        );
+        assert( rc!=SQLITE_IOERR_SHORT_READ );
       }
-      rc = sqlite3OsRead(
-          pSorter->pTemp1, &pIter->aBuffer[iBuf], nRead, iStart
-      );
-      assert( rc!=SQLITE_IOERR_SHORT_READ );
     }
+  }
 
-    if( rc==SQLITE_OK ){
-      u64 nByte;                       /* Size of PMA in bytes */
-      pIter->iEof = pSorter->iWriteOff;
-      rc = vdbeSorterIterVarint(db, pIter, &nByte);
-      pIter->iEof = pIter->iReadOff + nByte;
-      *pnByte += nByte;
-    }
+  if( rc==SQLITE_OK ){
+    u64 nByte;                       /* Size of PMA in bytes */
+    pIter->iEof = pSorter->iWriteOff;
+    rc = vdbeSorterIterVarint(db, pIter, &nByte);
+    pIter->iEof = pIter->iReadOff + nByte;
+    *pnByte += nByte;
   }
 
   if( rc==SQLITE_OK ){
@@ -488,6 +512,10 @@ int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){
     mxCache = db->aDb[0].pSchema->cache_size;
     if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
     pSorter->mxPmaSize = mxCache * pgsz;
+
+    pSorter->aMemory = (u8*)sqlite3DbMallocRaw(db, pSorter->mxPmaSize);
+    assert( pSorter->iMemory==0 );
+    if( !pSorter->aMemory ) return SQLITE_NOMEM;
   }
 
   return SQLITE_OK;
@@ -521,7 +549,9 @@ void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
     sqlite3OsCloseFree(pSorter->pTemp1);
     pSorter->pTemp1 = 0;
   }
-  vdbeSorterRecordFree(db, pSorter->pRecord);
+  if( pSorter->aMemory==0 ){
+    vdbeSorterRecordFree(db, pSorter->pRecord);
+  }
   pSorter->pRecord = 0;
   pSorter->iWriteOff = 0;
   pSorter->iReadOff = 0;
@@ -529,6 +559,7 @@ void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
   pSorter->nTree = 0;
   pSorter->nPMA = 0;
   pSorter->aTree = 0;
+  pSorter->iMemory = 0;
 }
 
 
@@ -540,6 +571,7 @@ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
   if( pSorter ){
     sqlite3VdbeSorterReset(db, pSorter);
     sqlite3DbFree(db, pSorter->pUnpacked);
+    sqlite3DbFree(db, pSorter->aMemory);
     sqlite3DbFree(db, pSorter);
     pCsr->pSorter = 0;
   }
@@ -551,12 +583,17 @@ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
 ** Otherwise, set *ppFile to 0 and return an SQLite error code.
 */
 static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
-  int dummy;
-  return sqlite3OsOpenMalloc(db->pVfs, 0, ppFile,
+  int rc;
+  rc = sqlite3OsOpenMalloc(db->pVfs, 0, ppFile,
       SQLITE_OPEN_TEMP_JOURNAL |
       SQLITE_OPEN_READWRITE    | SQLITE_OPEN_CREATE |
-      SQLITE_OPEN_EXCLUSIVE    | SQLITE_OPEN_DELETEONCLOSE, &dummy
+      SQLITE_OPEN_EXCLUSIVE    | SQLITE_OPEN_DELETEONCLOSE, &rc
   );
+  if( rc==SQLITE_OK ){
+    i64 max = SQLITE_MAX_MMAP_SIZE;
+    sqlite3OsFileControlHint( *ppFile, SQLITE_FCNTL_MMAP_SIZE, (void*)&max);
+  }
+  return rc;
 }
 
 /*
@@ -725,6 +762,29 @@ static void fileWriterWriteVarint(FileWriter *p, u64 iVal){
   fileWriterWrite(p, aByte, nByte);
 }
 
+#if SQLITE_MAX_MMAP_SIZE>0
+/*
+** The first argument is a file-handle open on a temporary file. The file
+** is guaranteed to be nByte bytes or smaller in size. This function 
+** attempts to extend the file to nByte bytes in size and to ensure that
+** the VFS has memory mapped it.
+**
+** Whether or not the file does end up memory mapped of course depends on 
+** the specific VFS implementation.
+*/
+static int vdbeSorterExtendFile(sqlite3_file *pFile, i64 nByte){
+  int rc = sqlite3OsTruncate(pFile, nByte);
+  if( rc==SQLITE_OK ){
+    void *p = 0;
+    sqlite3OsFetch(pFile, 0, nByte, &p);
+    sqlite3OsUnfetch(pFile, 0, p);
+  }
+  return rc;
+}
+#else
+# define vdbeSorterExtendFile(x,y) SQLITE_OK
+#endif
+
 /*
 ** Write the current contents of the in-memory linked-list to a PMA. Return
 ** SQLITE_OK if successful, or an SQLite error code otherwise.
@@ -760,6 +820,13 @@ static int vdbeSorterListToPMA(sqlite3 *db, const VdbeCursor *pCsr){
     assert( pSorter->nPMA==0 );
   }
 
+  /* Try to get the file to memory map */
+  if( rc==SQLITE_OK ){
+    rc = vdbeSorterExtendFile(
+        pSorter->pTemp1, pSorter->iWriteOff + pSorter->nInMemory + 9
+    );
+  }
+
   if( rc==SQLITE_OK ){
     SorterRecord *p;
     SorterRecord *pNext = 0;
@@ -771,12 +838,14 @@ static int vdbeSorterListToPMA(sqlite3 *db, const VdbeCursor *pCsr){
       pNext = p->pNext;
       fileWriterWriteVarint(&writer, p->nVal);
       fileWriterWrite(&writer, p->pVal, p->nVal);
-      sqlite3DbFree(db, p);
+      if( pSorter->aMemory==0 ) sqlite3DbFree(db, p);
     }
     pSorter->pRecord = p;
     rc = fileWriterFinish(db, &writer, &pSorter->iWriteOff);
   }
 
+  if( pSorter->aMemory ) pSorter->pRecord = 0;
+  assert( pSorter->pRecord==0 || rc!=SQLITE_OK );
   return rc;
 }
 
@@ -789,22 +858,36 @@ int sqlite3VdbeSorterWrite(
   Mem *pVal                       /* Memory cell containing record */
 ){
   VdbeSorter *pSorter = pCsr->pSorter;
+  SorterRecord sRecord;           /* Used for aMemory overflow record */
   int rc = SQLITE_OK;             /* Return Code */
   SorterRecord *pNew;             /* New list element */
 
   assert( pSorter );
   pSorter->nInMemory += sqlite3VarintLen(pVal->n) + pVal->n;
 
-  pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n + sizeof(SorterRecord));
-  if( pNew==0 ){
-    rc = SQLITE_NOMEM;
+  if( pSorter->aMemory ){
+    int nReq = sizeof(SorterRecord) + pVal->n;
+    if( (pSorter->iMemory+nReq) > pSorter->mxPmaSize ){
+      pNew = &sRecord;
+      pNew->pVal = pVal->z;
+    }else{
+      pNew = &pSorter->aMemory[pSorter->iMemory];
+      pSorter->iMemory += ROUND8(nReq);
+    }
   }else{
-    pNew->pVal = (void *)&pNew[1];
+    pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n+sizeof(SorterRecord));
+    if( pNew==0 ){
+      return SQLITE_NOMEM;
+    }
+  }
+
+  if( pNew!=&sRecord ){
+    pNew->pVal = (void*)&pNew[1];
     memcpy(pNew->pVal, pVal->z, pVal->n);
-    pNew->nVal = pVal->n;
-    pNew->pNext = pSorter->pRecord;
-    pSorter->pRecord = pNew;
   }
+  pNew->nVal = pVal->n;
+  pNew->pNext = pSorter->pRecord;
+  pSorter->pRecord = pNew;
 
   /* See if the contents of the sorter should now be written out. They
   ** are written out when either of the following are true:
@@ -815,18 +898,22 @@ int sqlite3VdbeSorterWrite(
   **   * The total memory allocated for the in-memory list is greater 
   **     than (page-size * 10) and sqlite3HeapNearlyFull() returns true.
   */
-  if( rc==SQLITE_OK && pSorter->mxPmaSize>0 && (
-        (pSorter->nInMemory>pSorter->mxPmaSize)
-     || (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull())
-  )){
+  if( pSorter->mxPmaSize>0 ){
+    if( (pNew==&sRecord) || (pSorter->aMemory==0 && (
+        (pSorter->nInMemory > pSorter->mxPmaSize)
+     || (pSorter->nInMemory > pSorter->mnPmaSize && sqlite3HeapNearlyFull())
+    ))){
 #ifdef SQLITE_DEBUG
-    i64 nExpect = pSorter->iWriteOff
-                + sqlite3VarintLen(pSorter->nInMemory)
-                + pSorter->nInMemory;
+      i64 nExpect = pSorter->iWriteOff
+        + sqlite3VarintLen(pSorter->nInMemory)
+        + pSorter->nInMemory;
 #endif
-    rc = vdbeSorterListToPMA(db, pCsr);
-    pSorter->nInMemory = 0;
-    assert( rc!=SQLITE_OK || (nExpect==pSorter->iWriteOff) );
+      rc = vdbeSorterListToPMA(db, pCsr);
+      pSorter->nInMemory = 0;
+      pSorter->iMemory = 0;
+      assert( rc!=SQLITE_OK || (nExpect==pSorter->iWriteOff) );
+      assert( rc!=SQLITE_OK || pSorter->pRecord==0 );
+    }
   }
 
   return rc;
@@ -934,6 +1021,9 @@ int sqlite3VdbeSorterRewind(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
       if( pTemp2==0 ){
         assert( iWrite2==0 );
         rc = vdbeSorterOpenTempFile(db, &pTemp2);
+        if( rc==SQLITE_OK ){
+          rc = vdbeSorterExtendFile(pTemp2, pSorter->iWriteOff);
+        }
       }
 
       if( rc==SQLITE_OK ){
@@ -1039,7 +1129,9 @@ int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
     SorterRecord *pFree = pSorter->pRecord;
     pSorter->pRecord = pFree->pNext;
     pFree->pNext = 0;
-    vdbeSorterRecordFree(db, pFree);
+    if( pSorter->aMemory==0 ){
+      vdbeSorterRecordFree(db, pFree);
+    }
     *pbEof = !pSorter->pRecord;
     rc = SQLITE_OK;
   }