/ Check-in [5c46820d]
Login

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

Overview
Comment:Avoid redundant string comparisons while merging fts5 segment b-trees.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fts5
Files: files | file ages | folders
SHA1: 5c46820d9b4aae791a8704b69145bd81f1e6780d
User & Date: dan 2015-03-10 19:24:30
Context
2015-03-11
14:51
Add an optimization to the fts5 unicode tokenizer code. check-in: f5db4892 user: dan tags: fts5
2015-03-10
19:24
Avoid redundant string comparisons while merging fts5 segment b-trees. check-in: 5c46820d user: dan tags: fts5
2015-03-07
15:46
Fix some compiler warnings caused by signed/unsigned pointer conversions. check-in: cccee7b5 user: dan tags: fts5
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/fts5/fts5_index.c.

   406    406   ** comparison in this context is the index of the iterator that currently
   407    407   ** points to the smaller term/rowid combination. Iterators at EOF are
   408    408   ** considered to be greater than all other iterators.
   409    409   **
   410    410   ** aFirst[1] contains the index in aSeg[] of the iterator that points to
   411    411   ** the smallest key overall. aFirst[0] is unused. 
   412    412   */
          413  +
          414  +typedef struct Fts5CResult Fts5CResult;
          415  +struct Fts5CResult {
          416  +  u16 iFirst;                     /* aSeg[] index of firstest iterator */
          417  +  u8 bTermEq;                     /* True if the terms are equal */
          418  +};
          419  +
   413    420   struct Fts5MultiSegIter {
   414    421     int nSeg;                       /* Size of aSeg[] array */
   415    422     int bRev;                       /* True to iterate in reverse order */
   416    423     int bSkipEmpty;                 /* True to skip deleted entries */
   417    424     Fts5SegIter *aSeg;              /* Array of segment iterators */
   418         -  u16 *aFirst;                    /* Current merge state (see above) */
          425  +  Fts5CResult *aFirst;            /* Current merge state (see above) */
   419    426   };
   420    427   
   421    428   /*
   422    429   ** Object for iterating through a single segment, visiting each term/docid
   423    430   ** pair in the segment.
   424    431   **
   425    432   ** pSeg:
................................................................................
  1740   1747   **
  1741   1748   ** If an error occurs, Fts5Index.rc is set to an appropriate error code. It 
  1742   1749   ** is not considered an error if the iterator reaches EOF. If an error has 
  1743   1750   ** already occurred when this function is called, it is a no-op.
  1744   1751   */
  1745   1752   static void fts5SegIterNext(
  1746   1753     Fts5Index *p,                   /* FTS5 backend object */
  1747         -  Fts5SegIter *pIter              /* Iterator to advance */
         1754  +  Fts5SegIter *pIter,             /* Iterator to advance */
         1755  +  int *pbNewTerm                  /* OUT: Set for new term */
  1748   1756   ){
         1757  +  assert( pbNewTerm==0 || *pbNewTerm==0 );
  1749   1758     if( p->rc==SQLITE_OK ){
  1750   1759       if( pIter->flags & FTS5_SEGITER_REVERSE ){
  1751   1760         if( pIter->iRowidOffset>0 ){
  1752   1761           u8 *a = pIter->pLeaf->p;
  1753   1762           int iOff;
  1754   1763           int nPos;
  1755   1764           i64 iDelta;
................................................................................
  1837   1846         /* Check if the iterator is now at EOF. If so, return early. */
  1838   1847         if( pIter->pLeaf && bNewTerm ){
  1839   1848           if( pIter->flags & FTS5_SEGITER_ONETERM ){
  1840   1849             fts5DataRelease(pIter->pLeaf);
  1841   1850             pIter->pLeaf = 0;
  1842   1851           }else{
  1843   1852             fts5SegIterLoadTerm(p, pIter, nKeep);
         1853  +          if( pbNewTerm ) *pbNewTerm = 1;
  1844   1854           }
  1845   1855         }
  1846   1856       }
  1847   1857     }
  1848   1858   }
  1849   1859   
  1850   1860   /*
................................................................................
  2029   2039     if( pIter->pLeaf ){
  2030   2040       int res;
  2031   2041       pIter->iLeafOffset = fts5GetU16(&pIter->pLeaf->p[2]);
  2032   2042       fts5SegIterLoadTerm(p, pIter, 0);
  2033   2043       do {
  2034   2044         res = fts5BufferCompareBlob(&pIter->term, pTerm, nTerm);
  2035   2045         if( res>=0 ) break;
  2036         -      fts5SegIterNext(p, pIter);
         2046  +      fts5SegIterNext(p, pIter, 0);
  2037   2047       }while( pIter->pLeaf && p->rc==SQLITE_OK );
  2038   2048   
  2039   2049       if( bGe==0 && res ){
  2040   2050         /* Set iterator to point to EOF */
  2041   2051         fts5DataRelease(pIter->pLeaf);
  2042   2052         pIter->pLeaf = 0;
  2043   2053       }
................................................................................
  2119   2129     fts5BufferFree(&pIter->term);
  2120   2130     fts5DataRelease(pIter->pLeaf);
  2121   2131     fts5DlidxIterFree(pIter->pDlidx);
  2122   2132     sqlite3_free(pIter->aRowidOffset);
  2123   2133     memset(pIter, 0, sizeof(Fts5SegIter));
  2124   2134   }
  2125   2135   
         2136  +#ifdef SQLITE_DEBUG
         2137  +
         2138  +/*
         2139  +** This function is used as part of the big assert() procedure implemented by
         2140  +** fts5AssertMultiIterSetup(). It ensures that the result currently stored
         2141  +** in *pRes is the correct result of comparing the current positions of the
         2142  +** two iterators.
         2143  +*/
         2144  +static void fts5AssertComparisonResult(
         2145  +  Fts5MultiSegIter *pIter, 
         2146  +  Fts5SegIter *p1,
         2147  +  Fts5SegIter *p2,
         2148  +  Fts5CResult *pRes
         2149  +){
         2150  +  int i1 = p1 - pIter->aSeg;
         2151  +  int i2 = p2 - pIter->aSeg;
         2152  +
         2153  +  if( p1->pLeaf || p2->pLeaf ){
         2154  +    if( p1->pLeaf==0 ){
         2155  +      assert( pRes->iFirst==i2 );
         2156  +    }else if( p2->pLeaf==0 ){
         2157  +      assert( pRes->iFirst==i1 );
         2158  +    }else{
         2159  +      int nMin = MIN(p1->term.n, p2->term.n);
         2160  +      int res = memcmp(p1->term.p, p2->term.p, nMin);
         2161  +      if( res==0 ) res = p1->term.n - p2->term.n;
         2162  +
         2163  +      if( res==0 ){
         2164  +        assert( pRes->bTermEq==1 );
         2165  +        assert( p1->iRowid!=p2->iRowid );
         2166  +        res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : 1;
         2167  +      }else{
         2168  +        assert( pRes->bTermEq==0 );
         2169  +      }
         2170  +
         2171  +      if( res<0 ){
         2172  +        assert( pRes->iFirst==i1 );
         2173  +      }else{
         2174  +        assert( pRes->iFirst==i2 );
         2175  +      }
         2176  +    }
         2177  +  }
         2178  +}
         2179  +
         2180  +/*
         2181  +** This function is a no-op unless SQLITE_DEBUG is defined when this module
         2182  +** is compiled. In that case, this function is essentially an assert() 
         2183  +** statement used to verify that the contents of the pIter->aFirst[] array
         2184  +** are correct.
         2185  +*/
         2186  +static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5MultiSegIter *pIter){
         2187  +  if( p->rc==SQLITE_OK ){
         2188  +    int i;
         2189  +    for(i=0; i<pIter->nSeg; i+=2){
         2190  +      Fts5SegIter *p1 = &pIter->aSeg[i];
         2191  +      Fts5SegIter *p2 = &pIter->aSeg[i+1];
         2192  +      Fts5CResult *pRes = &pIter->aFirst[(pIter->nSeg + i) / 2];
         2193  +      fts5AssertComparisonResult(pIter, p1, p2, pRes);
         2194  +    }
         2195  +
         2196  +    for(i=1; i<(pIter->nSeg / 2); i+=2){
         2197  +      Fts5CResult *pRes = &pIter->aFirst[i];
         2198  +      Fts5SegIter *p1 = &pIter->aSeg[ pIter->aFirst[i*2].iFirst ];
         2199  +      Fts5SegIter *p2 = &pIter->aSeg[ pIter->aFirst[i*2+1].iFirst ];
         2200  +
         2201  +      fts5AssertComparisonResult(pIter, p1, p2, pRes);
         2202  +    }
         2203  +  }
         2204  +}
         2205  +#else
         2206  +# define fts5AssertMultiIterSetup(x,y)
         2207  +#endif
         2208  +
  2126   2209   /*
  2127   2210   ** Do the comparison necessary to populate pIter->aFirst[iOut].
  2128   2211   **
  2129   2212   ** If the returned value is non-zero, then it is the index of an entry
  2130   2213   ** in the pIter->aSeg[] array that is (a) not at EOF, and (b) pointing
  2131   2214   ** to a key that is a duplicate of another, higher priority, 
  2132   2215   ** segment-iterator in the pSeg->aSeg[] array.
................................................................................
  2133   2216   */
  2134   2217   static int fts5MultiIterDoCompare(Fts5MultiSegIter *pIter, int iOut){
  2135   2218     int i1;                         /* Index of left-hand Fts5SegIter */
  2136   2219     int i2;                         /* Index of right-hand Fts5SegIter */
  2137   2220     int iRes;
  2138   2221     Fts5SegIter *p1;                /* Left-hand Fts5SegIter */
  2139   2222     Fts5SegIter *p2;                /* Right-hand Fts5SegIter */
         2223  +  Fts5CResult *pRes = &pIter->aFirst[iOut];
  2140   2224   
  2141   2225     assert( iOut<pIter->nSeg && iOut>0 );
  2142   2226     assert( pIter->bRev==0 || pIter->bRev==1 );
  2143   2227   
  2144   2228     if( iOut>=(pIter->nSeg/2) ){
  2145   2229       i1 = (iOut - pIter->nSeg/2) * 2;
  2146   2230       i2 = i1 + 1;
  2147   2231     }else{
  2148         -    i1 = pIter->aFirst[iOut*2];
  2149         -    i2 = pIter->aFirst[iOut*2+1];
         2232  +    i1 = pIter->aFirst[iOut*2].iFirst;
         2233  +    i2 = pIter->aFirst[iOut*2+1].iFirst;
  2150   2234     }
  2151   2235     p1 = &pIter->aSeg[i1];
  2152   2236     p2 = &pIter->aSeg[i2];
  2153   2237   
         2238  +  pRes->bTermEq = 0;
  2154   2239     if( p1->pLeaf==0 ){           /* If p1 is at EOF */
  2155   2240       iRes = i2;
  2156   2241     }else if( p2->pLeaf==0 ){     /* If p2 is at EOF */
  2157   2242       iRes = i1;
  2158   2243     }else{
  2159   2244       int res = fts5BufferCompare(&p1->term, &p2->term);
  2160   2245       if( res==0 ){
  2161   2246         assert( i2>i1 );
  2162   2247         assert( i2!=0 );
         2248  +      pRes->bTermEq = 1;
  2163   2249         if( p1->iRowid==p2->iRowid ) return i2;
  2164   2250         res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
  2165   2251       }
  2166   2252       assert( res!=0 );
  2167   2253       if( res<0 ){
  2168   2254         iRes = i1;
  2169   2255       }else{
  2170   2256         iRes = i2;
  2171   2257       }
  2172   2258     }
  2173   2259   
  2174         -  pIter->aFirst[iOut] = iRes;
         2260  +  pRes->iFirst = iRes;
  2175   2261     return 0;
  2176   2262   }
  2177   2263   
  2178   2264   /*
  2179   2265   ** Move the seg-iter so that it points to the first rowid on page iLeafPgno.
  2180   2266   ** It is an error if leaf iLeafPgno contains no rowid.
  2181   2267   */
................................................................................
  2248   2334         pIter->iLeafPgno = iLeafPgno+1;
  2249   2335         fts5SegIterReverseNewPage(p, pIter);
  2250   2336         bMove = 0;
  2251   2337       }
  2252   2338     }
  2253   2339   
  2254   2340     while( 1 ){
  2255         -    if( bMove ) fts5SegIterNext(p, pIter);
         2341  +    if( bMove ) fts5SegIterNext(p, pIter, 0);
  2256   2342       if( pIter->pLeaf==0 ) break;
  2257   2343       if( bRev==0 && pIter->iRowid>=iMatch ) break;
  2258   2344       if( bRev!=0 && pIter->iRowid<=iMatch ) break;
  2259   2345       bMove = 1;
  2260   2346     }
  2261   2347   }
  2262   2348   
................................................................................
  2280   2366     int iChanged,                   /* Index of sub-iterator just advanced */
  2281   2367     int iMinset                     /* Minimum entry in aFirst[] to set */
  2282   2368   ){
  2283   2369     int i;
  2284   2370     for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){
  2285   2371       int iEq;
  2286   2372       if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){
  2287         -      fts5SegIterNext(p, &pIter->aSeg[iEq]);
         2373  +      fts5SegIterNext(p, &pIter->aSeg[iEq], 0);
  2288   2374         i = pIter->nSeg + iEq;
  2289   2375       }
  2290   2376     }
  2291   2377   }
         2378  +
         2379  +static int fts5MultiIterAdvanceRowid(
         2380  +  Fts5Index *p,                   /* FTS5 backend to iterate within */
         2381  +  Fts5MultiSegIter *pIter,        /* Iterator to update aFirst[] array for */
         2382  +  int iChanged                    /* Index of sub-iterator just advanced */
         2383  +){
         2384  +  int i;
         2385  +  Fts5SegIter *pNew = &pIter->aSeg[iChanged];
         2386  +  Fts5SegIter *pOther = &pIter->aSeg[iChanged ^ 0x0001];
         2387  +
         2388  +  for(i=(pIter->nSeg+iChanged)/2; p->rc==SQLITE_OK; i=i/2){
         2389  +    Fts5CResult *pRes = &pIter->aFirst[i];
         2390  +
         2391  +    assert( pNew->pLeaf );
         2392  +    assert( pRes->bTermEq==0 || pOther->pLeaf );
         2393  +    
         2394  +    if( pRes->bTermEq ){
         2395  +      if( pNew->iRowid==pOther->iRowid ){
         2396  +        return 1;
         2397  +      }else if( (pOther->iRowid>pNew->iRowid)==pIter->bRev ){
         2398  +        pNew = pOther;
         2399  +      }
         2400  +    }
         2401  +    pRes->iFirst = (pNew - pIter->aSeg);
         2402  +    if( i==1 ) break;
         2403  +
         2404  +    pOther = &pIter->aSeg[ pIter->aFirst[i ^ 0x0001].iFirst ];
         2405  +  }
         2406  +
         2407  +  return 0;
         2408  +}
  2292   2409   
  2293   2410   /*
  2294   2411   ** Move the iterator to the next entry. 
  2295   2412   **
  2296   2413   ** If an error occurs, an error code is left in Fts5Index.rc. It is not 
  2297   2414   ** considered an error if the iterator reaches EOF, or if it is already at 
  2298   2415   ** EOF when this function is called.
................................................................................
  2302   2419     Fts5MultiSegIter *pIter,
  2303   2420     int bFrom,                      /* True if argument iFrom is valid */
  2304   2421     i64 iFrom                       /* Advance at least as far as this */
  2305   2422   ){
  2306   2423     if( p->rc==SQLITE_OK ){
  2307   2424       int bUseFrom = bFrom;
  2308   2425       do {
  2309         -      int iFirst = pIter->aFirst[1];
         2426  +      int iFirst = pIter->aFirst[1].iFirst;
         2427  +      int bNewTerm = 0;
  2310   2428         Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
  2311   2429         if( bUseFrom && pSeg->pDlidx ){
  2312   2430           fts5SegIterNextFrom(p, pSeg, iFrom);
  2313   2431         }else{
  2314         -        fts5SegIterNext(p, pSeg);
         2432  +        fts5SegIterNext(p, pSeg, &bNewTerm);
  2315   2433         }
  2316         -      fts5MultiIterAdvanced(p, pIter, iFirst, 1);
         2434  +
         2435  +      if( pSeg->pLeaf==0 || bNewTerm 
         2436  +       || fts5MultiIterAdvanceRowid(p, pIter, iFirst)
         2437  +      ){
         2438  +        fts5MultiIterAdvanced(p, pIter, iFirst, 1);
         2439  +      }
         2440  +      fts5AssertMultiIterSetup(p, pIter);
         2441  +
  2317   2442         bUseFrom = 0;
  2318   2443       }while( pIter->bSkipEmpty 
  2319         -         && fts5SegIterIsDelete(p, &pIter->aSeg[pIter->aFirst[1]])
         2444  +         && fts5SegIterIsDelete(p, &pIter->aSeg[pIter->aFirst[1].iFirst])
  2320   2445       );
  2321   2446     }
  2322   2447   }
  2323   2448   
  2324   2449   /*
  2325   2450   ** Allocate a new Fts5MultiSegIter object.
  2326   2451   **
................................................................................
  2332   2457   ** The iterator initially points to the first term/rowid entry in the 
  2333   2458   ** iterated data.
  2334   2459   */
  2335   2460   static void fts5MultiIterNew(
  2336   2461     Fts5Index *p,                   /* FTS5 backend to iterate within */
  2337   2462     Fts5Structure *pStruct,         /* Structure of specific index */
  2338   2463     int iIdx,                       /* Config.aHash[] index of FTS index */
  2339         -  int bSkipEmpty,
         2464  +  int bSkipEmpty,                 /* True to ignore delete-keys */
  2340   2465     int flags,                      /* True for >= */
  2341   2466     const u8 *pTerm, int nTerm,     /* Term to seek to (or NULL/0) */
  2342   2467     int iLevel,                     /* Level to iterate (-1 for all) */
  2343   2468     int nSegment,                   /* Number of segments to merge (iLevel>=0) */
  2344   2469     Fts5MultiSegIter **ppOut        /* New object */
  2345   2470   ){
  2346   2471     int nSeg;                       /* Number of segments merged */
................................................................................
  2359   2484     }else{
  2360   2485       nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
  2361   2486     }
  2362   2487     for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
  2363   2488     *ppOut = pNew = fts5IdxMalloc(p, 
  2364   2489         sizeof(Fts5MultiSegIter) +          /* pNew */
  2365   2490         sizeof(Fts5SegIter) * nSlot +       /* pNew->aSeg[] */
  2366         -      sizeof(u16) * nSlot                 /* pNew->aFirst[] */
         2491  +      sizeof(Fts5CResult) * nSlot         /* pNew->aFirst[] */
  2367   2492     );
  2368   2493     if( pNew==0 ) return;
  2369   2494     pNew->nSeg = nSlot;
  2370   2495     pNew->aSeg = (Fts5SegIter*)&pNew[1];
  2371         -  pNew->aFirst = (u16*)&pNew->aSeg[nSlot];
         2496  +  pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
  2372   2497     pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
  2373   2498     pNew->bSkipEmpty = bSkipEmpty;
  2374   2499   
  2375   2500     /* Initialize each of the component segment iterators. */
  2376   2501     if( iLevel<0 ){
  2377   2502       Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
  2378   2503       if( p->apHash ){
................................................................................
  2403   2528     ** to the first entry in its segment. In this case initialize the 
  2404   2529     ** aFirst[] array. Or, if an error has occurred, free the iterator
  2405   2530     ** object and set the output variable to NULL.  */
  2406   2531     if( p->rc==SQLITE_OK ){
  2407   2532       for(iIter=nSlot-1; iIter>0; iIter--){
  2408   2533         int iEq;
  2409   2534         if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
  2410         -        fts5SegIterNext(p, &pNew->aSeg[iEq]);
         2535  +        fts5SegIterNext(p, &pNew->aSeg[iEq], 0);
  2411   2536           fts5MultiIterAdvanced(p, pNew, iEq, iIter);
  2412   2537         }
  2413   2538       }
         2539  +    fts5AssertMultiIterSetup(p, pNew);
  2414   2540   
  2415   2541       if( pNew->bSkipEmpty 
  2416         -     && fts5SegIterIsDelete(p, &pNew->aSeg[pNew->aFirst[1]]) 
         2542  +     && fts5SegIterIsDelete(p, &pNew->aSeg[pNew->aFirst[1].iFirst]) 
  2417   2543       ){
  2418   2544         fts5MultiIterNext(p, pNew, 0, 0);
  2419   2545       }
  2420   2546     }else{
  2421   2547       fts5MultiIterFree(p, pNew);
  2422   2548       *ppOut = 0;
  2423   2549     }
................................................................................
  2424   2550   }
  2425   2551   
  2426   2552   /*
  2427   2553   ** Return true if the iterator is at EOF or if an error has occurred. 
  2428   2554   ** False otherwise.
  2429   2555   */
  2430   2556   static int fts5MultiIterEof(Fts5Index *p, Fts5MultiSegIter *pIter){
  2431         -  return (p->rc || pIter->aSeg[ pIter->aFirst[1] ].pLeaf==0);
         2557  +  return (p->rc || pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0);
  2432   2558   }
  2433   2559   
  2434   2560   /*
  2435   2561   ** Return the rowid of the entry that the iterator currently points
  2436   2562   ** to. If the iterator points to EOF when this function is called the
  2437   2563   ** results are undefined.
  2438   2564   */
  2439   2565   static i64 fts5MultiIterRowid(Fts5MultiSegIter *pIter){
  2440         -  assert( pIter->aSeg[ pIter->aFirst[1] ].pLeaf );
  2441         -  return pIter->aSeg[ pIter->aFirst[1] ].iRowid;
         2566  +  assert( pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf );
         2567  +  return pIter->aSeg[ pIter->aFirst[1].iFirst ].iRowid;
  2442   2568   }
  2443   2569   
  2444   2570   /*
  2445   2571   ** Move the iterator to the next entry at or following iMatch.
  2446   2572   */
  2447   2573   static void fts5MultiIterNextFrom(
  2448   2574     Fts5Index *p, 
................................................................................
  2460   2586   }
  2461   2587   
  2462   2588   /*
  2463   2589   ** Return a pointer to a buffer containing the term associated with the 
  2464   2590   ** entry that the iterator currently points to.
  2465   2591   */
  2466   2592   static const u8 *fts5MultiIterTerm(Fts5MultiSegIter *pIter, int *pn){
  2467         -  Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1] ];
         2593  +  Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
  2468   2594     *pn = p->term.n;
  2469   2595     return p->term.p;
  2470   2596   }
  2471   2597   
  2472   2598   /*
  2473   2599   ** Return true if the chunk iterator passed as the second argument is
  2474   2600   ** at EOF. Or if an error has already occurred. Otherwise, return false.
................................................................................
  2589   2715   */
  2590   2716   static void fts5PosIterInit(
  2591   2717     Fts5Index *p,                   /* FTS5 backend object */
  2592   2718     Fts5MultiSegIter *pMulti,       /* Multi-seg iterator to read pos-list from */
  2593   2719     Fts5PosIter *pIter              /* Initialize this object */
  2594   2720   ){
  2595   2721     if( p->rc==SQLITE_OK ){
  2596         -    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ];
         2722  +    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ];
  2597   2723       memset(pIter, 0, sizeof(*pIter));
  2598   2724       fts5ChunkIterInit(p, pSeg, &pIter->chunk);
  2599   2725       if( fts5ChunkIterEof(p, &pIter->chunk)==0 ){
  2600   2726         fts5PosIterNext(p, pIter);
  2601   2727       }
  2602   2728     }
  2603   2729   }
................................................................................
  3210   3336   #endif
  3211   3337   
  3212   3338     assert( iLvl>=0 );
  3213   3339     for(fts5MultiIterNew(p, pStruct, iIdx, 0, 0, 0, 0, iLvl, nInput, &pIter);
  3214   3340         fts5MultiIterEof(p, pIter)==0;
  3215   3341         fts5MultiIterNext(p, pIter, 0, 0)
  3216   3342     ){
  3217         -    Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1] ];
         3343  +    Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
  3218   3344       Fts5ChunkIter sPos;           /* Used to iterate through position list */
  3219   3345   
  3220   3346       /* If the segment being written is the oldest in the entire index and
  3221   3347       ** the position list is empty (i.e. the entry is a delete marker), no
  3222   3348       ** entry need be written to the output.  */
  3223   3349       fts5ChunkIterInit(p, pSeg, &sPos);
  3224   3350       if( bOldest==0 || sPos.nRem>0 ){
................................................................................
  3936   4062     Fts5Index *p,
  3937   4063     Fts5MultiSegIter *pMulti,
  3938   4064     int bSz,
  3939   4065     Fts5Buffer *pBuf
  3940   4066   ){
  3941   4067     if( p->rc==SQLITE_OK ){
  3942   4068       Fts5ChunkIter iter;
  3943         -    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1] ];
         4069  +    Fts5SegIter *pSeg = &pMulti->aSeg[ pMulti->aFirst[1].iFirst ];
  3944   4070       assert( fts5MultiIterEof(p, pMulti)==0 );
  3945   4071       fts5ChunkIterInit(p, pSeg, &iter);
  3946   4072       if( fts5ChunkIterEof(p, &iter)==0 ){
  3947   4073         if( bSz ){
  3948   4074           fts5BufferAppendVarint(&p->rc, pBuf, iter.nRem);
  3949   4075         }
  3950   4076         while( fts5ChunkIterEof(p, &iter)==0 ){