/ Check-in [055f173a]
Login

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

Overview
Comment:A partial fix for ticket #3292. This fixes the original problem but there are other similar problems lurking in the code still. (CVS 5561)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 055f173ab1b6fb657bf817faa3a37335d8fa60d5
User & Date: drh 2008-08-13 14:07:40
Context
2008-08-13
19:11
Additional changes toward fixing ticket #3292. (CVS 5562) check-in: 0b92cbf5 user: drh tags: trunk
14:07
A partial fix for ticket #3292. This fixes the original problem but there are other similar problems lurking in the code still. (CVS 5561) check-in: 055f173a user: drh tags: trunk
2008-08-12
15:48
Make sure the lookaside test script saturates the lookaside buffer even when SQLITE_DEBUG is off. Ticket #3289 (CVS 5560) check-in: d6aacc5d user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.495 2008/08/02 17:36:46 danielk1977 Exp $
           12  +** $Id: btree.c,v 1.496 2008/08/13 14:07:40 drh Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** See the header comment on "btreeInt.h" for additional information.
    16     16   ** Including a description of file format and an overview of operation.
    17     17   */
    18     18   #include "btreeInt.h"
    19     19   
................................................................................
  3770   3770             c = +1;
  3771   3771           }
  3772   3772         }else{
  3773   3773           int available;
  3774   3774           pCellKey = (void *)fetchPayload(pCur, &available, 0);
  3775   3775           nCellKey = pCur->info.nKey;
  3776   3776           if( available>=nCellKey ){
  3777         -          c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pUnKey);
         3777  +          c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, 0, pUnKey);
  3778   3778           }else{
  3779   3779             pCellKey = sqlite3Malloc( nCellKey );
  3780   3780             if( pCellKey==0 ){
  3781   3781               rc = SQLITE_NOMEM;
  3782   3782               goto moveto_finish;
  3783   3783             }
  3784   3784             rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey);
  3785         -          c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, pUnKey);
         3785  +          c = sqlite3VdbeRecordCompare(nCellKey, pCellKey, 0, pUnKey);
  3786   3786             sqlite3_free(pCellKey);
  3787   3787             if( rc ) goto moveto_finish;
  3788   3788           }
  3789   3789         }
  3790   3790         if( c==0 ){
  3791   3791           pCur->info.nKey = nCellKey;
  3792   3792           if( pPage->intKey && !pPage->leaf ){

Changes to src/sqliteInt.h.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Internal interface definitions for SQLite.
    13     13   **
    14         -** @(#) $Id: sqliteInt.h,v 1.753 2008/08/12 15:21:12 drh Exp $
           14  +** @(#) $Id: sqliteInt.h,v 1.754 2008/08/13 14:07:40 drh Exp $
    15     15   */
    16     16   #ifndef _SQLITEINT_H_
    17     17   #define _SQLITEINT_H_
    18     18   
    19     19   /*
    20     20   ** Include the configuration header output by 'configure' if we're using the
    21     21   ** autoconf-based build
................................................................................
  1042   1042   ** otherwise be equal, then return a result as if the second key
  1043   1043   ** were larger.
  1044   1044   */
  1045   1045   struct KeyInfo {
  1046   1046     sqlite3 *db;        /* The database connection */
  1047   1047     u8 enc;             /* Text encoding - one of the TEXT_Utf* values */
  1048   1048     u8 incrKey;         /* Increase 2nd key by epsilon before comparison */
  1049         -  u8 prefixIsEqual;   /* Treat a prefix as equal */
         1049  +  u8 ckPrefixOnly;    /* Records are equal if shorter is a prefix of longer */
  1050   1050     int nField;         /* Number of entries in aColl[] */
  1051   1051     u8 *aSortOrder;     /* If defined an aSortOrder[i] is true, sort DESC */
  1052   1052     CollSeq *aColl[1];  /* Collating sequence for each term of the key */
  1053   1053   };
  1054   1054   
  1055   1055   /*
  1056   1056   ** Each SQL index is represented in memory by an

Changes to src/vdbe.c.

    39     39   **
    40     40   ** Various scripts scan this source file in order to generate HTML
    41     41   ** documentation, headers files, or other derived files.  The formatting
    42     42   ** of the code in this file is, therefore, important.  See other comments
    43     43   ** in this file for details.  If in doubt, do not deviate from existing
    44     44   ** commenting and indentation practices when changing or adding code.
    45     45   **
    46         -** $Id: vdbe.c,v 1.773 2008/08/11 18:44:58 drh Exp $
           46  +** $Id: vdbe.c,v 1.774 2008/08/13 14:07:40 drh Exp $
    47     47   */
    48     48   #include "sqliteInt.h"
    49     49   #include <ctype.h>
    50     50   #include "vdbeInt.h"
    51     51   
    52     52   /*
    53     53   ** The following global variable is incremented every time a cursor
................................................................................
  3054   3054     assert( i>=0 && i<p->nCursor );
  3055   3055     assert( p->apCsr[i]!=0 );
  3056   3056     if( (pC = p->apCsr[i])->pCursor!=0 ){
  3057   3057       int res;
  3058   3058       assert( pC->isTable==0 );
  3059   3059       assert( pIn3->flags & MEM_Blob );
  3060   3060       if( pOp->opcode==OP_Found ){
  3061         -      pC->pKeyInfo->prefixIsEqual = 1;
         3061  +      pC->pKeyInfo->ckPrefixOnly = 1;
  3062   3062       }
  3063   3063       rc = sqlite3BtreeMoveto(pC->pCursor, pIn3->z, 0, pIn3->n, 0, &res);
  3064         -    pC->pKeyInfo->prefixIsEqual = 0;
         3064  +    pC->pKeyInfo->ckPrefixOnly = 0;
  3065   3065       if( rc!=SQLITE_OK ){
  3066   3066         break;
  3067   3067       }
  3068   3068       alreadyExists = (res==0);
  3069   3069       pC->deferredMoveto = 0;
  3070   3070       pC->cacheStatus = CACHE_STALE;
  3071   3071     }

Changes to src/vdbe.h.

    11     11   *************************************************************************
    12     12   ** Header file for the Virtual DataBase Engine (VDBE)
    13     13   **
    14     14   ** This header defines the interface to the virtual database engine
    15     15   ** or VDBE.  The VDBE implements an abstract machine that runs a
    16     16   ** simple program to access and modify the underlying database.
    17     17   **
    18         -** $Id: vdbe.h,v 1.135 2008/08/01 20:10:08 drh Exp $
           18  +** $Id: vdbe.h,v 1.136 2008/08/13 14:07:41 drh Exp $
    19     19   */
    20     20   #ifndef _SQLITE_VDBE_H_
    21     21   #define _SQLITE_VDBE_H_
    22     22   #include <stdio.h>
    23     23   
    24     24   /*
    25     25   ** A single VDBE is an opaque structure named "Vdbe".  Only routines
................................................................................
   186    186   void sqlite3VdbeSwap(Vdbe*,Vdbe*);
   187    187   
   188    188   #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
   189    189   int sqlite3VdbeReleaseMemory(int);
   190    190   #endif
   191    191   UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,void*,int);
   192    192   void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
   193         -int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
          193  +int sqlite3VdbeRecordCompare(int,const void*,int,UnpackedRecord*);
   194    194   
   195    195   
   196    196   #ifndef NDEBUG
   197    197     void sqlite3VdbeComment(Vdbe*, const char*, ...);
   198    198   # define VdbeComment(X)  sqlite3VdbeComment X
   199    199     void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
   200    200   # define VdbeNoopComment(X)  sqlite3VdbeNoopComment X
   201    201   #else
   202    202   # define VdbeComment(X)
   203    203   # define VdbeNoopComment(X)
   204    204   #endif
   205    205   
   206    206   #endif

Changes to src/vdbeaux.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains code used for creating, destroying, and populating
    13     13   ** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)  Prior
    14     14   ** to version 2.8.7, all this code was combined into the vdbe.c source file.
    15     15   ** But that file was getting too big so this subroutines were split out.
    16     16   **
    17         -** $Id: vdbeaux.c,v 1.405 2008/08/02 03:50:39 drh Exp $
           17  +** $Id: vdbeaux.c,v 1.406 2008/08/13 14:07:41 drh Exp $
    18     18   */
    19     19   #include "sqliteInt.h"
    20     20   #include <ctype.h>
    21     21   #include "vdbeInt.h"
    22     22   
    23     23   
    24     24   
................................................................................
  2270   2270   ** or positive integer if {nKey1, pKey1} is less than, equal to or 
  2271   2271   ** greater than pPKey2.  The {nKey1, pKey1} key must be a blob
  2272   2272   ** created by th OP_MakeRecord opcode of the VDBE.  The pPKey2
  2273   2273   ** key must be a parsed key such as obtained from
  2274   2274   ** sqlite3VdbeParseRecord.
  2275   2275   **
  2276   2276   ** Key1 and Key2 do not have to contain the same number of fields.
  2277         -** But if the lengths differ, Key2 must be the shorter of the two.
         2277  +** The key with fewer fields is usually considered lessor than the 
         2278  +** longer.  However if pPKey2->pKeyInfo->incrKey is set and
         2279  +** the common prefixes are equal, then key1 is less than key2.
         2280  +** Or if pPKey2->pKeyInfo->ckPrefixOnly flag is set and the 
         2281  +** prefixes are equal, then the keys are considered to be equal and
         2282  +** the parts beyond the common prefix are ignored.
         2283  +**
         2284  +** The last nHdrIgnore1 bytes of the header of pKey1 are ignored,
         2285  +** as if they do not exist.  Usually nHdrIgnore1 is 0 which means
         2286  +** that we look at the entire key.  But sometimes nHdrIgnore1 is 1.
         2287  +** When nHdrIgnore1 is 1, the keys are index records and so the last
         2288  +** column is a rowid.  The type code is always one byte in length.
         2289  +** Hence, setting nHdrIgnore1 to 1 means that the final rowid at the
         2290  +** end of the record should be treated as if it does not exist.
  2278   2291   **
  2279   2292   ** Historical note: In earlier versions of this routine both Key1
  2280   2293   ** and Key2 were blobs obtained from OP_MakeRecord.  But we found
  2281   2294   ** that in typical use the same Key2 would be submitted multiple times
  2282   2295   ** in a row.  So an optimization was added to parse the Key2 key
  2283   2296   ** separately and submit the parsed version.  In this way, we avoid
  2284         -** parsing the same Key2 multiple times in a row.
         2297  +** parsing the same Key2 multiple times.
  2285   2298   */
  2286   2299   int sqlite3VdbeRecordCompare(
  2287         -  int nKey1, const void *pKey1, 
  2288         -  UnpackedRecord *pPKey2
         2300  +  int nKey1, const void *pKey1, /* Left key */
         2301  +  int nHdrIgnore1,              /* Omit this much from end of key1 header */
         2302  +  UnpackedRecord *pPKey2        /* Right key */
  2289   2303   ){
  2290   2304     u32 d1;            /* Offset into aKey[] of next data element */
  2291   2305     u32 idx1;          /* Offset into aKey[] of next header element */
  2292   2306     u32 szHdr1;        /* Number of bytes in header */
  2293   2307     int i = 0;
  2294   2308     int nField;
  2295   2309     int rc = 0;
................................................................................
  2301   2315     mem1.enc = pKeyInfo->enc;
  2302   2316     mem1.db = pKeyInfo->db;
  2303   2317     mem1.flags = 0;
  2304   2318     mem1.zMalloc = 0;
  2305   2319     
  2306   2320     idx1 = getVarint32(aKey1, szHdr1);
  2307   2321     d1 = szHdr1;
         2322  +  szHdr1 -= nHdrIgnore1;
  2308   2323     nField = pKeyInfo->nField;
  2309   2324     while( idx1<szHdr1 && i<pPKey2->nField ){
  2310   2325       u32 serial_type1;
  2311   2326   
  2312   2327       /* Read the serial types for the next element in each key. */
  2313   2328       idx1 += getVarint32( aKey1+idx1, serial_type1 );
  2314   2329       if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break;
................................................................................
  2324   2339       if( rc!=0 ){
  2325   2340         break;
  2326   2341       }
  2327   2342       i++;
  2328   2343     }
  2329   2344     if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1);
  2330   2345   
  2331         -  /* One of the keys ran out of fields, but all the fields up to that point
  2332         -  ** were equal. If the incrKey flag is true, then the second key is
  2333         -  ** treated as larger.
  2334         -  */
  2335   2346     if( rc==0 ){
         2347  +    /* rc==0 here means that one of the keys ran out of fields and
         2348  +    ** all the fields up to that point were equal. If the incrKey 
         2349  +    ** flag is true, then break the tie by treating the second key 
         2350  +    ** as larger.  If ckPrefixOnly is true, then keys with common prefixes
         2351  +    ** are considered to be equal.  Otherwise, the longer key is the 
         2352  +    ** larger.  As it happens, the pPKey2 will always be the longer
         2353  +    ** if there is a difference.
         2354  +    */
  2336   2355       if( pKeyInfo->incrKey ){
  2337   2356         rc = -1;
  2338         -    }else if( !pKeyInfo->prefixIsEqual ){
  2339         -      if( d1<nKey1 ){
         2357  +    }else if( pKeyInfo->ckPrefixOnly ){
         2358  +      /* Leave rc==0 */
         2359  +    }else if( idx1<szHdr1 ){
  2340   2360           rc = 1;
  2341   2361         }
  2342         -    }
  2343   2362     }else if( pKeyInfo->aSortOrder && i<pKeyInfo->nField
  2344   2363                  && pKeyInfo->aSortOrder[i] ){
  2345   2364       rc = -rc;
  2346   2365     }
  2347   2366   
  2348   2367     return rc;
  2349   2368   }
................................................................................
  2405   2424   ** Compare the key of the index entry that cursor pC is point to against
  2406   2425   ** the key string in pKey (of length nKey).  Write into *pRes a number
  2407   2426   ** that is negative, zero, or positive if pC is less than, equal to,
  2408   2427   ** or greater than pKey.  Return SQLITE_OK on success.
  2409   2428   **
  2410   2429   ** pKey is either created without a rowid or is truncated so that it
  2411   2430   ** omits the rowid at the end.  The rowid at the end of the index entry
  2412         -** is ignored as well.
         2431  +** is ignored as well.  Hence, this routine only compares the prefixes 
         2432  +** of the keys prior to the final rowid, not the entire key.
         2433  +**
         2434  +** pUnpacked may be an unpacked version of pKey,nKey.  If pUnpacked is
         2435  +** supplied it is used in place of pKey,nKey.
  2413   2436   */
  2414   2437   int sqlite3VdbeIdxKeyCompare(
  2415   2438     Cursor *pC,                 /* The cursor to compare against */
  2416         -  UnpackedRecord *pUnpacked,
         2439  +  UnpackedRecord *pUnpacked,  /* Unpacked version of pKey and nKey */
  2417   2440     int nKey, const u8 *pKey,   /* The key to compare */
  2418   2441     int *res                    /* Write the comparison result here */
  2419   2442   ){
  2420   2443     i64 nCellKey = 0;
  2421   2444     int rc;
  2422   2445     BtCursor *pCur = pC->pCursor;
  2423         -  int lenRowid;
  2424   2446     Mem m;
  2425   2447     UnpackedRecord *pRec;
  2426   2448     char zSpace[200];
  2427   2449   
  2428   2450     sqlite3BtreeKeySize(pCur, &nCellKey);
  2429   2451     if( nCellKey<=0 ){
  2430   2452       *res = 0;
  2431   2453       return SQLITE_OK;
  2432   2454     }
  2433   2455     m.db = 0;
  2434   2456     m.flags = 0;
  2435   2457     m.zMalloc = 0;
  2436         -  if( (rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m))
  2437         -   || (rc = sqlite3VdbeIdxRowidLen((u8*)m.z, m.n, &lenRowid))
  2438         -  ){
         2458  +  rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m);
         2459  +  if( rc ){
  2439   2460       return rc;
  2440   2461     }
  2441   2462     if( !pUnpacked ){
  2442   2463       pRec = sqlite3VdbeRecordUnpack(pC->pKeyInfo, nKey, pKey,
  2443   2464                                   zSpace, sizeof(zSpace));
  2444   2465     }else{
  2445   2466       pRec = pUnpacked;
  2446   2467     }
  2447   2468     if( pRec==0 ){
  2448   2469       return SQLITE_NOMEM;
  2449   2470     }
  2450         -  *res = sqlite3VdbeRecordCompare(m.n-lenRowid, m.z, pRec);
         2471  +  *res = sqlite3VdbeRecordCompare(m.n, m.z, 1, pRec);
  2451   2472     if( !pUnpacked ){
  2452   2473       sqlite3VdbeDeleteUnpackedRecord(pRec);
  2453   2474     }
  2454   2475     sqlite3VdbeMemRelease(&m);
  2455   2476     return SQLITE_OK;
  2456   2477   }
  2457   2478   

Changes to test/corrupt7.test.

    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.
    12     12   #
    13     13   # This file implements tests to make sure SQLite does not crash or
    14     14   # segfault if it sees a corrupt database file.  It specifically focuses
    15     15   # on corrupt cell offsets in a btree page.
    16     16   #
    17         -# $Id: corrupt7.test,v 1.4 2008/07/26 18:26:10 danielk1977 Exp $
           17  +# $Id: corrupt7.test,v 1.5 2008/08/13 14:07:41 drh Exp $
    18     18   
    19     19   set testdir [file dirname $argv0]
    20     20   source $testdir/tester.tcl
    21     21   
    22     22   # We must have the page_size pragma for these tests to work.
    23     23   #
    24     24   ifcapable !pager_pragmas {
................................................................................
    68     68     db close
    69     69     hexio_write test.db 1062 04
    70     70     sqlite3 db test.db
    71     71     db eval {PRAGMA integrity_check(1)}
    72     72   } {{*** in database main ***
    73     73   Corruption detected in cell 15 on page 2}}
    74     74   
    75         -do_test corrupt7-3.1 {
    76         -  execsql {
    77         -    DROP TABLE t1;
    78         -    CREATE TABLE t1(a, b);
    79         -    INSERT INTO t1 VALUES(1, 'one');
    80         -    INSERT INTO t1 VALUES(100, 'one hundred');
    81         -    INSERT INTO t1 VALUES(100000, 'one hundred thousand');
    82         -    CREATE INDEX i1 ON t1(b);
    83         -  }
    84         -  db close
    85         -
    86         -  # Locate the 3rd cell in the index.
    87         -  set cell_offset [hexio_get_int [hexio_read test.db [expr 1024*2 + 12] 2]]
    88         -  incr cell_offset [expr 1024*2]
    89         -  incr cell_offset 1
    90         -
    91         -  # This write corrupts the "header-size" field of the database record
    92         -  # stored in the index cell. At one point this was causing sqlite to 
    93         -  # reference invalid memory.
    94         -  hexio_write test.db $cell_offset FFFF7F
    95         -  
    96         -  sqlite3 db test.db
    97         -  catchsql {
    98         -    SELECT b FROM t1 WHERE b > 'o' AND b < 'p';
    99         -  }
   100         -} {1 {database disk image is malformed}}
           75  +# The code path that was causing the buffer overrun that this test
           76  +# case was checking for was removed.
           77  +#
           78  +#do_test corrupt7-3.1 {
           79  +#  execsql {
           80  +#    DROP TABLE t1;
           81  +#    CREATE TABLE t1(a, b);
           82  +#    INSERT INTO t1 VALUES(1, 'one');
           83  +#    INSERT INTO t1 VALUES(100, 'one hundred');
           84  +#    INSERT INTO t1 VALUES(100000, 'one hundred thousand');
           85  +#    CREATE INDEX i1 ON t1(b);
           86  +#  }
           87  +#  db close
           88  +#
           89  +#  # Locate the 3rd cell in the index.
           90  +#  set cell_offset [hexio_get_int [hexio_read test.db [expr 1024*2 + 12] 2]]
           91  +#  incr cell_offset [expr 1024*2]
           92  +#  incr cell_offset 1
           93  +#
           94  +#  # This write corrupts the "header-size" field of the database record
           95  +#  # stored in the index cell. At one point this was causing sqlite to 
           96  +#  # reference invalid memory.
           97  +#  hexio_write test.db $cell_offset FFFF7F
           98  +#  
           99  +#  sqlite3 db test.db
          100  +#  catchsql {
          101  +#    SELECT b FROM t1 WHERE b > 'o' AND b < 'p';
          102  +#  }
          103  +#} {1 {database disk image is malformed}}
   101    104   
   102    105   finish_test

Added test/tkt3292.test.

            1  +# 2008 August 12
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library. 
           12  +# Specifically, it tests the behavior of the sqlite3VdbeRecordCompare()
           13  +# routine in cases where the rowid is 0 or 1 in file format 4
           14  +# (meaning that the rowid has type code 8 or 9 with zero bytes of
           15  +# data).  Ticket #3292.
           16  +#
           17  +# $Id: tkt3292.test,v 1.1 2008/08/13 14:07:41 drh Exp $
           18  +
           19  +set testdir [file dirname $argv0]
           20  +source $testdir/tester.tcl
           21  +
           22  +do_test tkt3292-1.1 {
           23  +  execsql {
           24  +    PRAGMA legacy_file_format=OFF;
           25  +    CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT);
           26  +    INSERT INTO t1 VALUES(0, 1);
           27  +    INSERT INTO t1 VALUES(1, 1);
           28  +    INSERT INTO t1 VALUES(2, 1);
           29  +    CREATE INDEX i1 ON t1(b);
           30  +    SELECT * FROM t1 WHERE b>=1;
           31  +  }
           32  +} {0 1 1 1 2 1}
           33  +do_test tkt3292-1.2 {
           34  +  execsql {
           35  +    INSERT INTO t1 VALUES(3, 0);
           36  +    INSERT INTO t1 VALUES(4, 2);
           37  +    SELECT * FROM t1 WHERE b>=1;
           38  +  }
           39  +} {0 1 1 1 2 1 4 2}
           40  +
           41  +
           42  +do_test tkt3292-2.1 {
           43  +  execsql {
           44  +    CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c, d);
           45  +    INSERT INTO t2 VALUES(0, 1, 'hello', x'012345');
           46  +    INSERT INTO t2 VALUES(1, 1, 'hello', x'012345');
           47  +    INSERT INTO t2 VALUES(2, 1, 'hello', x'012345');
           48  +    CREATE INDEX i2 ON t2(b,c,d);
           49  +    SELECT a FROM t2 WHERE b=1 AND c='hello' AND d>=x'012345';
           50  +  }
           51  +} {0 1 2}
           52  +do_test tkt3292-2.2 {
           53  +  execsql {
           54  +    INSERT INTO t2 VALUES(3, 1, 'hello', x'012344');
           55  +    INSERT INTO t2 VALUES(4, 1, 'hello', x'012346');
           56  +    SELECT a FROM t2 WHERE b=1 AND c='hello' AND d>=x'012345';
           57  +  }
           58  +} {0 1 2 4}
           59  +
           60  +
           61  +finish_test