/ Check-in [9bf1cfb4]
Login

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

Overview
Comment:Merge sorter optimizations with this branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | insert-select-opt
Files: files | file ages | folders
SHA1: 9bf1cfb4d99328bb95567330fdae76518f6386e2
User & Date: dan 2015-03-30 15:45:45
Context
2015-04-10
08:28
Update this branch with the latest changes from sorter-opt. Leaf check-in: 08c0b19b user: dan tags: insert-select-opt
2015-03-30
15:45
Merge sorter optimizations with this branch. check-in: 9bf1cfb4 user: dan tags: insert-select-opt
12:06
Improve performance of multi-field sorts where the first field has a low cardinality. check-in: 601e7b6b user: dan tags: sorter-opt
2015-03-26
12:38
Merge sorter optimization into this branch. check-in: aeb8e9a9 user: dan tags: insert-select-opt
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/vdbe.h.

   209    209   #ifndef SQLITE_OMIT_TRACE
   210    210     char *sqlite3VdbeExpandSql(Vdbe*, const char*);
   211    211   #endif
   212    212   int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
   213    213   
   214    214   void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
   215    215   int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
          216  +int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
   216    217   UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
   217    218   
   218    219   typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
   219    220   RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
   220    221   
   221    222   #ifndef SQLITE_OMIT_TRIGGER
   222    223   void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);

Changes to src/vdbeaux.c.

  3581   3581   ** returned.
  3582   3582   **
  3583   3583   ** If database corruption is discovered, set pPKey2->errCode to 
  3584   3584   ** SQLITE_CORRUPT and return 0. If an OOM error is encountered, 
  3585   3585   ** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the
  3586   3586   ** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
  3587   3587   */
  3588         -static int vdbeRecordCompareWithSkip(
         3588  +int sqlite3VdbeRecordCompareWithSkip(
  3589   3589     int nKey1, const void *pKey1,   /* Left key */
  3590   3590     UnpackedRecord *pPKey2,         /* Right key */
  3591   3591     int bSkip                       /* If true, skip the first field */
  3592   3592   ){
  3593   3593     u32 d1;                         /* Offset into aKey[] of next data element */
  3594   3594     int i;                          /* Index of next field to compare */
  3595   3595     u32 szHdr1;                     /* Size of record header in bytes */
................................................................................
  3767   3767     );
  3768   3768     return pPKey2->default_rc;
  3769   3769   }
  3770   3770   int sqlite3VdbeRecordCompare(
  3771   3771     int nKey1, const void *pKey1,   /* Left key */
  3772   3772     UnpackedRecord *pPKey2          /* Right key */
  3773   3773   ){
  3774         -  return vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0);
         3774  +  return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0);
  3775   3775   }
  3776   3776   
  3777   3777   
  3778   3778   /*
  3779   3779   ** This function is an optimized version of sqlite3VdbeRecordCompare() 
  3780   3780   ** that (a) the first field of pPKey2 is an integer, and (b) the 
  3781   3781   ** size-of-header varint at the start of (pKey1/nKey1) fits in a single
................................................................................
  3855   3855     if( v>lhs ){
  3856   3856       res = pPKey2->r1;
  3857   3857     }else if( v<lhs ){
  3858   3858       res = pPKey2->r2;
  3859   3859     }else if( pPKey2->nField>1 ){
  3860   3860       /* The first fields of the two keys are equal. Compare the trailing 
  3861   3861       ** fields.  */
  3862         -    res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
         3862  +    res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
  3863   3863     }else{
  3864   3864       /* The first fields of the two keys are equal and there are no trailing
  3865   3865       ** fields. Return pPKey2->default_rc in this case. */
  3866   3866       res = pPKey2->default_rc;
  3867   3867     }
  3868   3868   
  3869   3869     assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
................................................................................
  3903   3903       nCmp = MIN( pPKey2->aMem[0].n, nStr );
  3904   3904       res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
  3905   3905   
  3906   3906       if( res==0 ){
  3907   3907         res = nStr - pPKey2->aMem[0].n;
  3908   3908         if( res==0 ){
  3909   3909           if( pPKey2->nField>1 ){
  3910         -          res = vdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
         3910  +          res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
  3911   3911           }else{
  3912   3912             res = pPKey2->default_rc;
  3913   3913           }
  3914   3914         }else if( res>0 ){
  3915   3915           res = pPKey2->r2;
  3916   3916         }else{
  3917   3917           res = pPKey2->r1;

Changes to src/vdbesort.c.

   287    287   **      to sqlite3ThreadJoin() is likely to block. Cases that are likely to
   288    288   **      block provoke debugging output.
   289    289   **
   290    290   ** In both cases, the effects of the main thread seeing (bDone==0) even
   291    291   ** after the thread has finished are not dire. So we don't worry about
   292    292   ** memory barriers and such here.
   293    293   */
          294  +typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int);
   294    295   struct SortSubtask {
   295    296     SQLiteThread *pThread;          /* Background thread, if any */
   296    297     int bDone;                      /* Set if thread is finished but not joined */
   297    298     VdbeSorter *pSorter;            /* Sorter that owns this sub-task */
   298    299     UnpackedRecord *pUnpacked;      /* Space to unpack a record */
   299    300     SorterList list;                /* List for thread to write to a PMA */
   300    301     int nPMA;                       /* Number of PMAs currently in file */
   301         -  RecordCompare xCompare;         /* Compare function to use */
          302  +  SorterCompare xCompare;         /* Compare function to use */
   302    303     SorterFile file;                /* Temp file for level-0 PMAs */
   303    304     SorterFile file2;               /* Space for other PMAs */
   304    305   };
          306  +
   305    307   
   306    308   /*
   307    309   ** Main sorter structure. A single instance of this is allocated for each 
   308    310   ** sorter cursor created by the VDBE.
   309    311   **
   310    312   ** mxKeysize:
   311    313   **   As records are added to the sorter by calls to sqlite3VdbeSorterWrite(),
................................................................................
   743    745   
   744    746     if( rc==SQLITE_OK ){
   745    747       rc = vdbePmaReaderNext(pReadr);
   746    748     }
   747    749     return rc;
   748    750   }
   749    751   
          752  +/*
          753  +** A version of vdbeSorterCompare() that assumes that it has already been
          754  +** determined that the first field of key1 is equal to the first field of 
          755  +** key2.
          756  +*/
          757  +static int vdbeSorterCompareTail(
          758  +  SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
          759  +  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
          760  +  const void *pKey1, int nKey1,   /* Left side of comparison */
          761  +  const void *pKey2, int nKey2    /* Right side of comparison */
          762  +){
          763  +  UnpackedRecord *r2 = pTask->pUnpacked;
          764  +  if( *pbKey2Cached==0 ){
          765  +    sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
          766  +    *pbKey2Cached = 1;
          767  +  }
          768  +  return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1);
          769  +}
   750    770   
   751    771   /*
   752    772   ** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2, 
   753    773   ** size nKey2 bytes). Use (pTask->pKeyInfo) for the collation sequences
   754    774   ** used by the comparison. Return the result of the comparison.
   755    775   **
   756         -** Before returning, object (pTask->pUnpacked) is populated with the
   757         -** unpacked version of key2. Or, if pKey2 is passed a NULL pointer, then it 
   758         -** is assumed that the (pTask->pUnpacked) structure already contains the 
   759         -** unpacked key to use as key2.
          776  +** If IN/OUT parameter *pbKey2Cached is true when this function is called,
          777  +** it is assumed that (pTask->pUnpacked) contains the unpacked version
          778  +** of key2. If it is false, (pTask->pUnpacked) is populated with the unpacked
          779  +** version of key2 and *pbKey2Cached set to true before returning.
   760    780   **
   761    781   ** If an OOM error is encountered, (pTask->pUnpacked->error_rc) is set
   762    782   ** to SQLITE_NOMEM.
   763    783   */
   764    784   static int vdbeSorterCompare(
   765    785     SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
          786  +  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
   766    787     const void *pKey1, int nKey1,   /* Left side of comparison */
   767    788     const void *pKey2, int nKey2    /* Right side of comparison */
   768    789   ){
   769    790     UnpackedRecord *r2 = pTask->pUnpacked;
   770         -  if( pKey2 ){
          791  +  if( !*pbKey2Cached ){
   771    792       sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
          793  +    *pbKey2Cached = 1;
   772    794     }
   773         -  return pTask->xCompare(nKey1, pKey1, r2);
          795  +  return sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
          796  +}
          797  +
          798  +/*
          799  +** A specially optimized version of vdbeSorterCompare() that assumes that
          800  +** the first field of each key is a TEXT value and that the collation
          801  +** sequence to compare them with is BINARY.
          802  +*/
          803  +static int vdbeSorterCompareText(
          804  +  SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
          805  +  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
          806  +  const void *pKey1, int nKey1,   /* Left side of comparison */
          807  +  const void *pKey2, int nKey2    /* Right side of comparison */
          808  +){
          809  +  const u8 * const p1 = (const u8 * const)pKey1;
          810  +  const u8 * const p2 = (const u8 * const)pKey2;
          811  +  const u8 * const v1 = &p1[ p1[0] ];   /* Pointer to value 1 */
          812  +  const u8 * const v2 = &p2[ p2[0] ];   /* Pointer to value 2 */
          813  +
          814  +  int n1;
          815  +  int n2;
          816  +  int res;
          817  +
          818  +  getVarint32(&p1[1], n1); n1 = (n1 - 13) / 2;
          819  +  getVarint32(&p2[1], n2); n2 = (n2 - 13) / 2;
          820  +  res = memcmp(v1, v2, MIN(n1, n2));
          821  +  if( res==0 ){
          822  +    res = n1 - n2;
          823  +  }
          824  +
          825  +  if( res==0 ){
          826  +    if( pTask->pSorter->pKeyInfo->nField>1 ){
          827  +      res = vdbeSorterCompareTail(
          828  +          pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
          829  +      );
          830  +    }
          831  +  }else{
          832  +    if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
          833  +      res = res * -1;
          834  +    }
          835  +  }
          836  +
          837  +  return res;
          838  +}
          839  +
          840  +/*
          841  +** A specially optimized version of vdbeSorterCompare() that assumes that
          842  +** the first field of each key is an INTEGER value.
          843  +*/
          844  +static int vdbeSorterCompareInt(
          845  +  SortSubtask *pTask,             /* Subtask context (for pKeyInfo) */
          846  +  int *pbKey2Cached,              /* True if pTask->pUnpacked is pKey2 */
          847  +  const void *pKey1, int nKey1,   /* Left side of comparison */
          848  +  const void *pKey2, int nKey2    /* Right side of comparison */
          849  +){
          850  +  const u8 * const p1 = (const u8 * const)pKey1;
          851  +  const u8 * const p2 = (const u8 * const)pKey2;
          852  +  const int s1 = p1[1];                 /* Left hand serial type */
          853  +  const int s2 = p2[1];                 /* Right hand serial type */
          854  +  const u8 * const v1 = &p1[ p1[0] ];   /* Pointer to value 1 */
          855  +  const u8 * const v2 = &p2[ p2[0] ];   /* Pointer to value 2 */
          856  +  int res;                              /* Return value */
          857  +
          858  +  assert( (s1>0 && s1<7) || s1==8 || s1==9 );
          859  +  assert( (s2>0 && s2<7) || s2==8 || s2==9 );
          860  +
          861  +  if( s1>7 && s2>7 ){
          862  +    res = s1 - s2;
          863  +  }else{
          864  +    if( s1==s2 ){
          865  +      if( (*v1 ^ *v2) & 0x80 ){
          866  +        /* The two values have different signs */
          867  +        res = (*v1 & 0x80) ? -1 : +1;
          868  +      }else{
          869  +        /* The two values have the same sign. Compare using memcmp(). */
          870  +        static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8 };
          871  +        int i;
          872  +        res = 0;
          873  +        for(i=0; i<aLen[s1]; i++){
          874  +          if( (res = v1[i] - v2[i]) ) break;
          875  +        }
          876  +      }
          877  +    }else{
          878  +      if( s2>7 ){
          879  +        res = +1;
          880  +      }else if( s1>7 ){
          881  +        res = -1;
          882  +      }else{
          883  +        res = s1 - s2;
          884  +      }
          885  +
          886  +      if( res>0 ){
          887  +        if( *v1 & 0x80 ) res = -1;
          888  +      }else if( res<0 ){
          889  +        if( *v2 & 0x80 ) res = +1;
          890  +      }
          891  +    }
          892  +  }
          893  +
          894  +  if( res==0 ){
          895  +    if( pTask->pSorter->pKeyInfo->nField>1 ){
          896  +      res = vdbeSorterCompareTail(
          897  +          pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
          898  +      );
          899  +    }
          900  +  }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
          901  +    res = res * -1;
          902  +  }
          903  +
          904  +  return res;
   774    905   }
   775    906   
   776    907   /*
   777    908   ** Initialize the temporary index cursor just opened as a sorter cursor.
   778    909   **
   779    910   ** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nField)
   780    911   ** to determine the number of fields that should be compared from the
................................................................................
   869   1000           assert( pSorter->iMemory==0 );
   870   1001           pSorter->nMemory = pgsz;
   871   1002           pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz);
   872   1003           if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM;
   873   1004         }
   874   1005       }
   875   1006   
   876         -    if( (pKeyInfo->nField+pKeyInfo->nXField)<13 && pKeyInfo->aColl[0]==0 ){
         1007  +    if( (pKeyInfo->nField+pKeyInfo->nXField)<13 
         1008  +     && (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
         1009  +    ){
   877   1010         pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
   878   1011       }
   879   1012     }
   880   1013   
   881   1014     return rc;
   882   1015   }
   883   1016   #undef nWorker   /* Defined at the top of this function */
................................................................................
  1191   1324       pTask->pUnpacked = sqlite3VdbeAllocUnpackedRecord(
  1192   1325           pTask->pSorter->pKeyInfo, 0, 0, &pFree
  1193   1326       );
  1194   1327       assert( pTask->pUnpacked==(UnpackedRecord*)pFree );
  1195   1328       if( pFree==0 ) return SQLITE_NOMEM;
  1196   1329       pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField;
  1197   1330       pTask->pUnpacked->errCode = 0;
  1198         -    if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
  1199         -      pTask->pUnpacked->r1 = 1;
  1200         -      pTask->pUnpacked->r2 = -1;
  1201         -    }else{
  1202         -      pTask->pUnpacked->r1 = -1;
  1203         -      pTask->pUnpacked->r2 = 1;
  1204         -    }
  1205   1331     }
  1206   1332     return SQLITE_OK;
  1207   1333   }
  1208   1334   
  1209   1335   
  1210   1336   /*
  1211   1337   ** Merge the two sorted lists p1 and p2 into a single list.
................................................................................
  1215   1341     SortSubtask *pTask,             /* Calling thread context */
  1216   1342     SorterRecord *p1,               /* First list to merge */
  1217   1343     SorterRecord *p2,               /* Second list to merge */
  1218   1344     SorterRecord **ppOut            /* OUT: Head of merged list */
  1219   1345   ){
  1220   1346     SorterRecord *pFinal = 0;
  1221   1347     SorterRecord **pp = &pFinal;
  1222         -  void *pVal2 = p2 ? SRVAL(p2) : 0;
         1348  +  int bCached = 0;
  1223   1349   
  1224   1350     while( p1 && p2 ){
  1225   1351       int res;
  1226         -    res = vdbeSorterCompare(pTask, SRVAL(p1), p1->nVal, pVal2, p2->nVal);
         1352  +    res = pTask->xCompare(
         1353  +        pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal
         1354  +    );
         1355  +
  1227   1356       if( res<=0 ){
  1228   1357         *pp = p1;
  1229   1358         pp = &p1->u.pNext;
  1230   1359         p1 = p1->u.pNext;
  1231         -      pVal2 = 0;
  1232   1360       }else{
  1233   1361         *pp = p2;
  1234         -       pp = &p2->u.pNext;
         1362  +      pp = &p2->u.pNext;
  1235   1363         p2 = p2->u.pNext;
  1236         -      if( p2==0 ) break;
  1237         -      pVal2 = SRVAL(p2);
         1364  +      bCached = 0;
  1238   1365       }
  1239   1366     }
  1240   1367     *pp = p1 ? p1 : p2;
  1241   1368     *ppOut = pFinal;
  1242   1369   }
         1370  +
         1371  +/*
         1372  +** Return the SorterCompare function to compare values collected by the
         1373  +** sorter object passed as the only argument.
         1374  +*/
         1375  +static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){
         1376  +  if( p->typeMask==SORTER_TYPE_INTEGER ){
         1377  +    return vdbeSorterCompareInt;
         1378  +  }else if( p->typeMask==SORTER_TYPE_TEXT ){
         1379  +    return vdbeSorterCompareText; 
         1380  +  }
         1381  +  return vdbeSorterCompare;
         1382  +}
  1243   1383   
  1244   1384   /*
  1245   1385   ** Sort the linked list of records headed at pTask->pList. Return 
  1246   1386   ** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if 
  1247   1387   ** an error occurs.
  1248   1388   */
  1249   1389   static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
................................................................................
  1252   1392     SorterRecord *p;
  1253   1393     int rc;
  1254   1394   
  1255   1395     rc = vdbeSortAllocUnpacked(pTask);
  1256   1396     if( rc!=SQLITE_OK ) return rc;
  1257   1397   
  1258   1398     p = pList->pList;
  1259         -  if( pTask->pSorter->typeMask==0 ){
  1260         -    pTask->xCompare = sqlite3VdbeRecordCompare;
  1261         -  }else{
  1262         -    UnpackedRecord *pRec = pTask->pUnpacked;
  1263         -    sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, p->nVal, SRVAL(p), pRec);
  1264         -    pTask->xCompare = sqlite3VdbeFindCompare(pRec);
  1265         -  }
         1399  +  pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter);
  1266   1400   
  1267   1401     aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
  1268   1402     if( !aSlot ){
  1269   1403       return SQLITE_NOMEM;
  1270   1404     }
  1271   1405   
  1272   1406     while( p ){
................................................................................
  1478   1612     rc = vdbePmaReaderNext(&pMerger->aReadr[iPrev]);
  1479   1613   
  1480   1614     /* Update contents of aTree[] */
  1481   1615     if( rc==SQLITE_OK ){
  1482   1616       int i;                      /* Index of aTree[] to recalculate */
  1483   1617       PmaReader *pReadr1;         /* First PmaReader to compare */
  1484   1618       PmaReader *pReadr2;         /* Second PmaReader to compare */
  1485         -    u8 *pKey2;                  /* To pReadr2->aKey, or 0 if record cached */
         1619  +    int bCached = 0;
  1486   1620   
  1487   1621       /* Find the first two PmaReaders to compare. The one that was just
  1488   1622       ** advanced (iPrev) and the one next to it in the array.  */
  1489   1623       pReadr1 = &pMerger->aReadr[(iPrev & 0xFFFE)];
  1490   1624       pReadr2 = &pMerger->aReadr[(iPrev | 0x0001)];
  1491         -    pKey2 = pReadr2->aKey;
  1492   1625   
  1493   1626       for(i=(pMerger->nTree+iPrev)/2; i>0; i=i/2){
  1494   1627         /* Compare pReadr1 and pReadr2. Store the result in variable iRes. */
  1495   1628         int iRes;
  1496   1629         if( pReadr1->pFd==0 ){
  1497   1630           iRes = +1;
  1498   1631         }else if( pReadr2->pFd==0 ){
  1499   1632           iRes = -1;
  1500   1633         }else{
  1501         -        iRes = vdbeSorterCompare(pTask, 
  1502         -            pReadr1->aKey, pReadr1->nKey, pKey2, pReadr2->nKey
         1634  +        iRes = pTask->xCompare(pTask, &bCached,
         1635  +            pReadr1->aKey, pReadr1->nKey, pReadr2->aKey, pReadr2->nKey
  1503   1636           );
  1504   1637         }
  1505   1638   
  1506   1639         /* If pReadr1 contained the smaller value, set aTree[i] to its index.
  1507   1640         ** Then set pReadr2 to the next PmaReader to compare to pReadr1. In this
  1508   1641         ** case there is no cache of pReadr2 in pTask->pUnpacked, so set
  1509   1642         ** pKey2 to point to the record belonging to pReadr2.
................................................................................
  1517   1650         ** If the two values were equal, then the value from the oldest
  1518   1651         ** PMA should be considered smaller. The VdbeSorter.aReadr[] array
  1519   1652         ** is sorted from oldest to newest, so pReadr1 contains older values
  1520   1653         ** than pReadr2 iff (pReadr1<pReadr2).  */
  1521   1654         if( iRes<0 || (iRes==0 && pReadr1<pReadr2) ){
  1522   1655           pMerger->aTree[i] = (int)(pReadr1 - pMerger->aReadr);
  1523   1656           pReadr2 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ];
  1524         -        pKey2 = pReadr2->aKey;
         1657  +        bCached = 0;
  1525   1658         }else{
  1526         -        if( pReadr1->pFd ) pKey2 = 0;
         1659  +        bCached = (pReadr1->pFd!=0);
  1527   1660           pMerger->aTree[i] = (int)(pReadr2 - pMerger->aReadr);
  1528   1661           pReadr1 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ];
  1529   1662         }
  1530   1663       }
  1531   1664       *pbEof = (pMerger->aReadr[pMerger->aTree[1]].pFd==0);
  1532   1665     }
  1533   1666   
................................................................................
  1631   1764     int nReq;                       /* Bytes of memory required */
  1632   1765     int nPMA;                       /* Bytes of PMA space required */
  1633   1766     int t;                          /* serial type of first record field */
  1634   1767   
  1635   1768     getVarint32((const u8*)&pVal->z[1], t);
  1636   1769     if( t>0 && t<10 && t!=7 ){
  1637   1770       pSorter->typeMask &= SORTER_TYPE_INTEGER;
  1638         -  }else if( t & 0x01 ){
         1771  +  }else if( t>10 && (t & 0x01) ){
  1639   1772       pSorter->typeMask &= SORTER_TYPE_TEXT;
  1640   1773     }else{
  1641   1774       pSorter->typeMask = 0;
  1642   1775     }
  1643   1776   
  1644   1777     assert( pSorter );
  1645   1778   
................................................................................
  1901   2034     p2 = &pMerger->aReadr[i2];
  1902   2035   
  1903   2036     if( p1->pFd==0 ){
  1904   2037       iRes = i2;
  1905   2038     }else if( p2->pFd==0 ){
  1906   2039       iRes = i1;
  1907   2040     }else{
         2041  +    SortSubtask *pTask = pMerger->pTask;
         2042  +    int bCached = 0;
  1908   2043       int res;
  1909         -    assert( pMerger->pTask->pUnpacked!=0 );  /* from vdbeSortSubtaskMain() */
  1910         -    res = vdbeSorterCompare(
  1911         -        pMerger->pTask, p1->aKey, p1->nKey, p2->aKey, p2->nKey
         2044  +    assert( pTask->pUnpacked!=0 );  /* from vdbeSortSubtaskMain() */
         2045  +    res = pTask->xCompare(
         2046  +        pTask, &bCached, p1->aKey, p1->nKey, p2->aKey, p2->nKey
  1912   2047       );
  1913   2048       if( res<=0 ){
  1914   2049         iRes = i1;
  1915   2050       }else{
  1916   2051         iRes = i2;
  1917   2052       }
  1918   2053     }
................................................................................
  2322   2457   */
  2323   2458   static int vdbeSorterSetupMerge(VdbeSorter *pSorter){
  2324   2459     int rc;                         /* Return code */
  2325   2460     SortSubtask *pTask0 = &pSorter->aTask[0];
  2326   2461     MergeEngine *pMain = 0;
  2327   2462   #if SQLITE_MAX_WORKER_THREADS
  2328   2463     sqlite3 *db = pTask0->pSorter->db;
  2329         -  RecordCompare xCompare = (pSorter->typeMask==0 ? 
  2330         -      sqlite3VdbeRecordCompare : pSorter->aTask[0].xCompare
  2331         -  );
  2332   2464     int i;
         2465  +  SorterCompare xCompare = vdbeSorterGetCompare(pSorter);
  2333   2466     for(i=0; i<pSorter->nTask; i++){
  2334   2467       pSorter->aTask[i].xCompare = xCompare;
  2335   2468     }
  2336   2469   #endif
  2337   2470   
  2338   2471     rc = vdbeSorterMergeTreeBuild(pSorter, &pMain);
  2339   2472     if( rc==SQLITE_OK ){