From 491791a8272be96576072dc0ede07adf2132bbf2 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 18 Jul 2002 00:34:09 +0000 Subject: [PATCH] Fix for ticket #107: Fix a design defect in indices that was causing queries to fail when using an index on a column containing an empty string. This fix is an incompatible file-format change. (CVS 681) FossilOrigin-Name: 20d152fcddb4fa53556a9c93c7a869600a7c5183 --- VERSION | 2 +- manifest | 30 ++++++++++---------- manifest.uuid | 2 +- src/build.c | 4 +-- src/delete.c | 4 +-- src/expr.c | 8 +++--- src/insert.c | 4 +-- src/main.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- src/select.c | 8 +++--- src/sqliteInt.h | 5 ++-- src/vdbe.c | 68 +++++++++++++++++++++++---------------------- test/index.test | 68 ++++++++++++++++++++++++++++++++++++++++++++- 12 files changed, 206 insertions(+), 71 deletions(-) diff --git a/VERSION b/VERSION index da6b0a8f16..e70b4523ae 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.5.6 +2.6.0 diff --git a/manifest b/manifest index 170316e6e9..65e3a042ee 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Fix\sfor\sticket\s#105:\sFix\sthe\sUPDATE\scommand\sso\sthat\sit\sworks\sproperly\swith\nindexed\stables\swhen\sthere\sis\sa\ssubquery\sin\sthe\sWHERE\sclause.\s\sAdd\stests\nto\sverify\scorrect\soperation.\s(CVS\s680) -D 2002-07-16T17:22:51 +C Fix\sfor\sticket\s#107:\sFix\sa\sdesign\sdefect\sin\sindices\sthat\swas\scausing\squeries\nto\sfail\swhen\susing\san\sindex\son\sa\scolumn\scontaining\san\sempty\sstring.\s\sThis\nfix\sis\san\sincompatible\sfile-format\schange.\s(CVS\s681) +D 2002-07-18T00:34:10 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 -F VERSION 0afb45a36f2b97c8455247659f1155967b8bb883 +F VERSION b36b90fdfe67f5f38d70b444e4430701df184330 F aclocal.m4 11faa843caa38fd451bc6aeb43e248d1723a269d F config.guess f38b1e93d1e0fa6f5a6913e9e7b12774b9232588 F config.sub f14b07d544ca26b5d698259045136b783e18fc7f @@ -20,15 +20,15 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/btree.c db8cd1bd46cd30a1763c3cc80602571d1b30a329 F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 -F src/build.c 81d0f42ae58af35d6331402e71a4fb2d2898586c -F src/delete.c 215492ffcea4262a993e55f3c4a67dc9fea4da9c +F src/build.c 2f81c837284840448f21c90ef7c9c6c6c0d4d8a0 +F src/delete.c bc35d6aa7e7b2a4f88510a17e060abb355a53bd6 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760 -F src/expr.c 5c3b241a680dff98afdf5f0ba6e14a3b19669914 +F src/expr.c 8a6b669ba5d6cd2810e8671f918ddb0fac3dd1b1 F src/func.c e45cd908b9b723d9b91473d09e12c23f786b3fc2 F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8 -F src/insert.c 4511e06abce1688664ce90cbf09fa13433b82c43 -F src/main.c d9ff20d7412b20cb97c21a65e13c1c174653b50d +F src/insert.c 9bc794863ea2988a7b8667ef010b3c46b26dba38 +F src/main.c dbe691d2b6e0b2b0e0e87ca42c04506ead1c0550 F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b F src/os.c edb22daad525f49681f41c76683a16c1d39755c7 F src/os.h 5b9a69875c880d1665ae040cbca1f7b9c82198ab @@ -37,11 +37,11 @@ F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e F src/parse.y 5307e1a7b26241991934d4b50ae70980f3f2aca9 F src/printf.c 06f4c8725063e0faf0e34824ab70feace7146bf7 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c f4e7221a319da25f549a434a6c664eedcbce4dec +F src/select.c a43eabfc2e3e4d67660027f016889935f706deab F src/shell.c 37a8405aec5740726c4ee18826c1ff5fd2c29b96 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 75c5bbb066d0faf34424b7d1babf8b44d5b31af2 -F src/sqliteInt.h d18d098aa9121d2415a9c0d1d3e09a10dde39385 +F src/sqliteInt.h 0d0b7b7b2b6829eb5c2f63489b11c44ba966fc75 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/tclsqlite.c c502819c209011659e1bbb428cbac5670cce7f79 F src/test1.c 456cb080db85056be723e770435d9509afc3a83a @@ -52,7 +52,7 @@ F src/tokenize.c b5500e193a82b5b9888fbf947efd90d3b4858178 F src/trigger.c d88ab4d68d68955c217b38fb6717e090fbbf54a4 F src/update.c ddba82d1b0d1cb34d862d8ad943012f88e2b8495 F src/util.c 7a99e754c44dd220e881122e30581c08b6d6adef -F src/vdbe.c 0169270bb73e8dec4174b90dffc7070c4cabe039 +F src/vdbe.c 7433a7811fbbb7cfec4f12c142913b4d8a02231e F src/vdbe.h a9292f2b5fcecef924fa255fb74609e9cbc776c2 F src/where.c 6a43aa6c80eab12221eeca754cba852a9ecd1e13 F test/all.test f296d27fff6aca72348af15092154f879d1fc7d4 @@ -66,7 +66,7 @@ F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8 F test/expr.test 5fadd0bc87c223b424ce6752b576c1df346abf1f F test/func.test cae5f27e09736cfdcc978c3719ac1345405b848c F test/in.test e59461f1702b7387880bf08a0ce6bb777925d282 -F test/index.test 1a69532e7868eddac61c679fd03f46b5666214da +F test/index.test 7503d903c4dffecbb415010298720720db8618e0 F test/insert.test a122afb86911e77c181d912348866a5b1a61eeab F test/insert2.test c288375a64dad3295044714f0dfed4a193cf067f F test/intpkey.test 69a6a9b41e541f27a2ffcd20264fb35adc3c2680 @@ -141,7 +141,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 93710f7ed7e1baa6acbf4bc32982e046f61ffa44 -R a496b407b95e3e3391653a1442fdf78c +P bbca16f88d00cd33ac7229edf3ee4623eff6e62f +R 3b401eb81b101736a092f34a44910d1f U drh -Z af2183151817c8e8381633943b4d70fd +Z 3a1e3e849570ce46d4051f5d0915780a diff --git a/manifest.uuid b/manifest.uuid index db31b261c9..7de7eb6145 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bbca16f88d00cd33ac7229edf3ee4623eff6e62f \ No newline at end of file +20d152fcddb4fa53556a9c93c7a869600a7c5183 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 999acf33bf..3b2d3bf89e 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.105 2002/07/13 17:23:21 drh Exp $ +** $Id: build.c,v 1.106 2002/07/18 00:34:11 drh Exp $ */ #include "sqliteInt.h" #include @@ -1398,7 +1398,7 @@ void sqliteCreateIndex( sqliteVdbeAddOp(v, OP_Column, 2, pIndex->aiColumn[i]); } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0); - if( db->file_format>=3 ) sqliteAddIdxKeyType(v, pIndex); + if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIndex); sqliteVdbeAddOp(v, OP_IdxPut, 1, pIndex->onError!=OE_None); sqliteVdbeAddOp(v, OP_Next, 2, lbl1); sqliteVdbeResolveLabel(v, lbl2); diff --git a/src/delete.c b/src/delete.c index 8e91e25eed..393b9480c5 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.39 2002/07/05 21:42:37 drh Exp $ +** $Id: delete.c,v 1.40 2002/07/18 00:34:11 drh Exp $ */ #include "sqliteInt.h" @@ -381,7 +381,7 @@ void sqliteGenerateRowIndexDelete( } } sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); - if( db->file_format>=3 ) sqliteAddIdxKeyType(v, pIdx); + if( db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx); sqliteVdbeAddOp(v, OP_IdxDelete, base+i, 0); } } diff --git a/src/expr.c b/src/expr.c index 4798ddb3b8..43b4f7ee0d 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.78 2002/07/16 02:05:44 drh Exp $ +** $Id: expr.c,v 1.79 2002/07/18 00:34:12 drh Exp $ */ #include "sqliteInt.h" #include @@ -999,7 +999,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ case TK_GE: case TK_NE: case TK_EQ: { - if( pParse->db->file_format>=3 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ + if( pParse->db->file_format>=4 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ op += 6; /* Convert numeric opcodes to text opcodes */ } /* Fall through into the next case */ @@ -1232,7 +1232,7 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_EQ: { sqliteExprCode(pParse, pExpr->pLeft); sqliteExprCode(pParse, pExpr->pRight); - if( pParse->db->file_format>=3 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ + if( pParse->db->file_format>=4 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ op += 6; /* Convert numeric opcodes to text opcodes */ } sqliteVdbeAddOp(v, op, jumpIfNull, dest); @@ -1325,7 +1325,7 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_GE: case TK_NE: case TK_EQ: { - if( pParse->db->file_format>=3 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ + if( pParse->db->file_format>=4 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){ op += 6; /* Convert numeric opcodes to text opcodes */ } sqliteExprCode(pParse, pExpr->pLeft); diff --git a/src/insert.c b/src/insert.c index a6b2e858ed..d2e8d71268 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.63 2002/07/05 21:42:37 drh Exp $ +** $Id: insert.c,v 1.64 2002/07/18 00:34:12 drh Exp $ */ #include "sqliteInt.h" @@ -616,7 +616,7 @@ void sqliteGenerateConstraintChecks( } } jumpInst1 = sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0); - if( pParse->db->file_format>=3 ) sqliteAddIdxKeyType(v, pIdx); + if( pParse->db->file_format>=4 ) sqliteAddIdxKeyType(v, pIdx); onError = pIdx->onError; if( onError==OE_None ) continue; if( overrideError!=OE_Default ){ diff --git a/src/main.c b/src/main.c index 28b98d9cc4..6db127f83f 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.86 2002/07/13 17:23:21 drh Exp $ +** $Id: main.c,v 1.87 2002/07/18 00:34:12 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -89,6 +89,31 @@ int sqliteInitCallback(void *pDb, int argc, char **argv, char **azColName){ return nErr; } +/* +** This is a callback procedure used to reconstruct a table. The +** name of the table to be reconstructed is passed in as argv[0]. +** +** This routine is used to automatically upgrade a database from +** format version 1 or 2 to version 3. The correct operation of +** this routine relys on the fact that no indices are used when +** copying a table out to a temporary file. +*/ +static int +upgrade_3_callback(void *pDb, int argc, char **argv, char **NotUsed){ + sqlite *db = (sqlite*)pDb; + int rc; + + rc = sqlite_exec_printf(db, + "CREATE TEMP TABLE sqlite_x AS SELECT * FROM '%q'; " + "DELETE FROM '%q'; " + "INSERT INTO '%q' SELECT * FROM sqlite_x; " + "DROP TABLE sqlite_x;", + 0, 0, 0, argv[0], argv[0], argv[0]); + return rc!=SQLITE_OK; +} + + + /* ** Attempt to read the database schema and initialize internal ** data structures. Return one of the SQLITE_ error codes to @@ -206,12 +231,14 @@ int sqliteInit(sqlite *db, char **pzErrMsg){ /* ** file_format==1 Version 2.1.0. ** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY. - ** file_format==3 Version 2.6.0. Add support for separate numeric and + ** file_format==3 Version 2.6.0. Fix empty-string index bug. + ** file_format==4 Version 2.7.0. Add support for separate numeric and ** text datatypes. */ if( db->file_format==0 ){ - db->file_format = 2; - }else if( db->file_format>2 ){ + /* This happens if the database was initially empty */ + db->file_format = 3; + }else if( db->file_format>3 ){ sqliteBtreeCloseCursor(curMain); sqliteSetString(pzErrMsg, "unsupported file format", 0); rc = SQLITE_ERROR; @@ -313,6 +340,40 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){ sqliteFree(*pzErrMsg); *pzErrMsg = 0; } + + /* If the database is in formats 1 or 2, then upgrade it to + ** version 3. This will reconstruct all indices. If the + ** upgrade fails for any reason (ex: out of disk space, database + ** is read only, interrupt receive, etc.) then refuse to open. + */ + if( db->file_format<3 ){ + char *zErr; + int meta[SQLITE_N_BTREE_META]; + + db->file_format = 3; + rc = sqlite_exec(db, + "BEGIN; SELECT name FROM sqlite_master WHERE type='table';", + upgrade_3_callback, + db, + &zErr); + if( rc==SQLITE_OK ){ + sqliteBtreeGetMeta(db->pBe, meta); + meta[2] = 3; + sqliteBtreeUpdateMeta(db->pBe, meta); + sqlite_exec(db, "COMMIT", 0, 0, 0); + } + if( rc!=SQLITE_OK ){ + sqliteSetString(pzErrMsg, + "unable to upgrade database to the version 2.6 format", + zErr ? ": " : 0, zErr, 0); + sqliteFree(zErr); + sqliteStrRealloc(pzErrMsg); + sqlite_close(db); + return 0; + } + } + + /* Return a pointer to the newly opened database structure */ return db; no_mem_on_open: @@ -502,6 +563,11 @@ int sqlite_exec( return rc; } } + if( db->file_format<3 ){ + sqliteSafetyOff(db); + sqliteSetString(pzErrMsg, "obsolete database file format", 0); + return SQLITE_ERROR; + } if( db->recursionDepth==0 ){ db->nChange = 0; } db->recursionDepth++; memset(&sParse, 0, sizeof(sParse)); diff --git a/src/select.c b/src/select.c index 61be40e9c1..89511db34d 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.105 2002/07/11 12:18:17 drh Exp $ +** $Id: select.c,v 1.106 2002/07/18 00:34:12 drh Exp $ */ #include "sqliteInt.h" @@ -322,7 +322,7 @@ static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){ type = SQLITE_SO_TEXT; }else if( (order & SQLITE_SO_TYPEMASK)==SQLITE_SO_NUM ){ type = SQLITE_SO_NUM; - }else if( pParse->db->file_format>=3 ){ + }else if( pParse->db->file_format>=4 ){ type = sqliteExprType(pOrderBy->a[i].pExpr); }else{ type = SQLITE_SO_NUM; @@ -429,7 +429,7 @@ static int selectInnerLoop( sqliteVdbeAddOp(v, OP_IsNull, -pEList->nExpr, sqliteVdbeCurrentAddr(v)+7); #endif sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1); - if( pParse->db->file_format>=3 ) sqliteAddKeyType(v, pEList); + if( pParse->db->file_format>=4 ) sqliteAddKeyType(v, pEList); sqliteVdbeAddOp(v, OP_Distinct, distinct, sqliteVdbeCurrentAddr(v)+3); sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0); sqliteVdbeAddOp(v, OP_Goto, 0, iContinue); @@ -1968,7 +1968,7 @@ int sqliteSelect( sqliteExprCode(pParse, pGroupBy->a[i].pExpr); } sqliteVdbeAddOp(v, OP_MakeKey, pGroupBy->nExpr, 0); - if( pParse->db->file_format>=3 ) sqliteAddKeyType(v, pGroupBy); + if( pParse->db->file_format>=4 ) sqliteAddKeyType(v, pGroupBy); lbl1 = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1); for(i=0; inAgg; i++){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 16b271ef38..4b48ecc529 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.138 2002/07/13 17:23:21 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.139 2002/07/18 00:34:12 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -187,7 +187,8 @@ typedef struct TriggerStack TriggerStack; ** ** file_format==1 Version 2.1.0. ** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY. -** file_format==3 Version 2.6.0. Add support for separate numeric and +** file_format==3 Version 2.6.0. Fix empty-string index bug. +** file_format==4 Version 2.7.0. Add support for separate numeric and ** text datatypes. */ struct sqlite { diff --git a/src/vdbe.c b/src/vdbe.c index 3700a4baa6..9e6161b821 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.164 2002/07/05 21:42:37 drh Exp $ +** $Id: vdbe.c,v 1.165 2002/07/18 00:34:12 drh Exp $ */ #include "sqliteInt.h" #include @@ -2634,6 +2634,20 @@ case OP_MakeRecord: { ** text. The first character corresponds to the lowest element on the ** stack. If P3 is NULL then all arguments are assumed to be numeric. ** +** The key is a concatenation of fields. Each field is terminated by +** a single 0x00 character. A NULL field is introduced by an 'a' and +** is followed immediately by its 0x00 terminator. A numeric field is +** introduced by a single character 'b' and is followed by a sequence +** of characters that represent the number such that a comparison of +** the character string using memcpy() sorts the numbers in numerical +** order. The character strings for numbers are generated using the +** sqliteRealToSortable() function. A text field is introduced by a +** 'c' character and is followed by the exact text of the field. The +** use of an 'a', 'b', or 'c' character at the beginning of each field +** guarantees that NULL sort before numbers and that numbers sort +** before text. 0x00 characters do not occur except as separators +** between fields. +** ** See also: MakeIdxKey, SortMakeKey */ /* Opcode: MakeIdxKey P1 P2 P3 @@ -2687,41 +2701,24 @@ case OP_MakeKey: { containsNull = 1; }else if( pOp->p3 && pOp->p3[j]=='t' ){ Stringify(p, i); - }else if( flags & STK_Real ){ - z = aStack[i].z; - sqliteRealToSortable(aStack[i].r, &z[1]); - z[0] = 0; + aStack[i].flags &= ~(STK_Int|STK_Real); + nByte += aStack[i].n+1; + }else if( (flags & (STK_Real|STK_Int))!=0 || isNumber(zStack[i]) ){ + if( (flags & (STK_Real|STK_Int))==STK_Int ){ + aStack[i].r = aStack[i].i; + }else if( (flags & (STK_Real|STK_Int))==0 ){ + aStack[i].r = atof(zStack[i]); + } Release(p, i); - len = strlen(&z[1]); + z = aStack[i].z; + sqliteRealToSortable(aStack[i].r, z); + len = strlen(z); zStack[i] = 0; aStack[i].flags = STK_Real; - aStack[i].n = len+2; - nByte += aStack[i].n; - }else if( flags & STK_Int ){ - z = aStack[i].z; - aStack[i].r = aStack[i].i; - sqliteRealToSortable(aStack[i].r, &z[1]); - z[0] = 0; - Release(p, i); - len = strlen(&z[1]); - zStack[i] = 0; - aStack[i].flags = STK_Int; - aStack[i].n = len+2; - nByte += aStack[i].n; + aStack[i].n = len+1; + nByte += aStack[i].n+1; }else{ - assert( flags & STK_Str ); - if( isNumber(zStack[i]) ){ - aStack[i].r = atof(zStack[i]); - Release(p, i); - z = aStack[i].z; - sqliteRealToSortable(aStack[i].r, &z[1]); - z[0] = 0; - len = strlen(&z[1]); - zStack[i] = 0; - aStack[i].flags = STK_Real; - aStack[i].n = len+2; - } - nByte += aStack[i].n; + nByte += aStack[i].n+1; } } if( nByte+sizeof(u32)>MAX_BYTES_PER_ROW ){ @@ -2734,9 +2731,14 @@ case OP_MakeKey: { j = 0; for(i=p->tos-nField+1; i<=p->tos; i++){ if( aStack[i].flags & STK_Null ){ - zNewKey[j++] = 0; + zNewKey[j++] = 'a'; zNewKey[j++] = 0; }else{ + if( aStack[i].flags & (STK_Int|STK_Real) ){ + zNewKey[j++] = 'b'; + }else{ + zNewKey[j++] = 'c'; + } memcpy(&zNewKey[j], zStack[i] ? zStack[i] : aStack[i].z, aStack[i].n); j += aStack[i].n; } diff --git a/test/index.test b/test/index.test index d4ca515e00..10e65618e2 100644 --- a/test/index.test +++ b/test/index.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the CREATE INDEX statement. # -# $Id: index.test,v 1.18 2002/07/13 03:11:54 drh Exp $ +# $Id: index.test,v 1.19 2002/07/18 00:34:13 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -415,4 +415,70 @@ do_test index-13.4 { } } {1 2 3 a b c} +# Check the sort order of data in an index. +# +do_test index-14.1 { + execsql { + CREATE TABLE t6(a,b,c); + CREATE INDEX t6i1 ON t6(a,b); + INSERT INTO t6 VALUES('','',1); + INSERT INTO t6 VALUES('',NULL,2); + INSERT INTO t6 VALUES(NULL,'',3); + INSERT INTO t6 VALUES('abc',123,4); + INSERT INTO t6 VALUES(123,'abc',5); + SELECT c FROM t6 ORDER BY a,b; + } +} {3 5 2 1 4} +do_test index-14.2 { + execsql { + SELECT c FROM t6 WHERE a=''; + } +} {2 1} +do_test index-14.3 { + execsql { + SELECT c FROM t6 WHERE b=''; + } +} {1 3} +do_test index-14.4 { + execsql { + SELECT c FROM t6 WHERE a>''; + } +} {4} +do_test index-14.5 { + execsql { + SELECT c FROM t6 WHERE a>=''; + } +} {2 1 4} +do_test index-14.6 { + execsql { + SELECT c FROM t6 WHERE a>123; + } +} {2 1 4} +do_test index-14.7 { + execsql { + SELECT c FROM t6 WHERE a>=123; + } +} {5 2 1 4} +do_test index-14.8 { + execsql { + SELECT c FROM t6 WHERE a<'abc'; + } +} {3 5 2 1} +do_test index-14.9 { + execsql { + SELECT c FROM t6 WHERE a<='abc'; + } +} {3 5 2 1 4} +do_test index-14.10 { + execsql { + SELECT c FROM t6 WHERE a<=''; + } +} {3 5 2 1} +do_test index-14.11 { + execsql { + SELECT c FROM t6 WHERE a<''; + } +} {3 5} + + finish_test