diff --git a/manifest b/manifest index 81e9c98694..ad38bf3d0a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Misc\sfixes\sfor\stest\scases\sfailing\sdue\sto\sthe\snew\slocking\smodel.\s(CVS\s1561) -D 2004-06-10T05:59:25 +C Add\sthe\ssqlite3_collation_needed()\sAPI\sand\sfix\ssome\serror\shandling\scases\ninvolving\sunknown\scollation\ssequences.\s(CVS\s1562) +D 2004-06-10T10:50:08 F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -27,17 +27,17 @@ F src/attach.c 93b8ecec4a8d7b4e9f2479e2327d90c9d01765e8 F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79 F src/btree.c 281af87aa117de024f5b6c2728a2339cba9ef584 F src/btree.h 589427ac13bb544d298cd99726e2572a6fe4bdaa -F src/build.c 5d958f480d71f56981f262de6994a4d2ee6e5f75 +F src/build.c 4b1a23d919fe01549702f7f1bfe7f8b656e77a17 F src/date.c 8e6fa3173386fb29fdef012ee08a853c1e9908b2 -F src/delete.c b30f08250c9ed53a25a13c7c04599c1e8753992d +F src/delete.c 911221aadb35d610c84fadb32e71c52990827e58 F src/encode.c a876af473d1d636faa3dca51c7571f2e007eea37 -F src/expr.c 3aea8faac17debea4f5c2659351c27d5660453a0 +F src/expr.c 34e63e960ab8ca9e4fc4a1f41b0a3b77df2ae167 F src/func.c ffbdfa4cad2a16a41390c2ce923ef8b0f173d777 F src/hash.c 440c2f8cb373ee1b4e13a0988489c7cd95d55b6f F src/hash.h 762d95f1e567664d1eafc1687de755626be962fb -F src/insert.c 4268d9e3959cc845ea243fb4ec7507269404dad9 +F src/insert.c 68c7f3ddd6a7f1e5596d6996da1a2861b3789a3a F src/legacy.c ad23746f15f67e34577621b1875f639c94839e1f -F src/main.c cb41777e75f6b95a2af42f439c78e761a49cdffa +F src/main.c 335b4cd48af0011017e33a411aea307553114e67 F src/md5.c 4302e84ae516c616bb079c4e6d038c0addb33481 F src/os.h 23c69c5084e71b5fe199ff1c4e35a4aded0f1380 F src/os_common.h 6393ac67a3a7b4aea19ff17529980ecf77eb2348 @@ -50,15 +50,15 @@ F src/os_win.h 004eec47b1780fcaf07420ddc2072294b698d48c F src/pager.c d852730901441babf6cd16fc528dd6eecc2b2eab F src/pager.h ca8f293e1d623a7c628a1c5e0c6cf43d5bbb80bf F src/parse.y 097438674976355a10cf177bd97326c548820b86 -F src/pragma.c 6ab13748a415bf8e8f2dd79e5f713fbe72dfd3f4 +F src/pragma.c 0bc3adea28df802074996bec067d506d55d28f84 F src/printf.c 63b15f1ea9fe3daa066bb7430fd20d4a2d717dc8 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 -F src/select.c 1f8355e702f109f6771f82a9bfe7aac4c82cbaf2 +F src/select.c 6cb407796dde0e8f27450ead68856eb9f8188789 F src/shell.c ca519519dcbbc582f6d88f7d0e7583b857fd3469 -F src/sqlite.h.in 00ce6b80cf4dffa9bf7a028d80d1ffba708b175a -F src/sqliteInt.h cd9db5ca4a2ba59cf1692fcbd1ea7318c50f0c4f +F src/sqlite.h.in 2b6afe1de6935d3dfbd6042f46a62f1b7c3b3992 +F src/sqliteInt.h 6be535d420f99c57f29f13c3c2d6a3497432b366 F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2 -F src/tclsqlite.c 6383ba7b620b276d49b40d48872502e0adb1b685 +F src/tclsqlite.c e974c0b2479ed37334aeb268de331e0a1b21b5a8 F src/test1.c f78d6ac0675bc5db48dac9c5379c965bdadb9113 F src/test2.c 05f810c90cf6262d5f352860e87d41a3f34207f9 F src/test3.c beafd0ccf7b9ae784744be1b1e66ffe8f64c25da @@ -66,7 +66,7 @@ F src/test4.c a921a69821fd30209589228e64f94e9f715b6fe2 F src/test5.c 862784cd7a68e7d36f00287aac6e413ca996eaf8 F src/tokenize.c 183c5d7da11affab5d70d903d33409c8c0ce6c5b F src/trigger.c d1a4d7a59b34c811bf6070d64d0497baa0140dcf -F src/update.c 259f06e7b22c684b2d3dda54a18185892d6e9573 +F src/update.c 168b6d523087ca4545b74ec9f3102b1f3c6b1e38 F src/utf.c c2c8e445bfea724f3502609d6389fe66651f02ab F src/util.c e8629f04d920ae968fced709dc7a3a2c62b65ac4 F src/vacuum.c b921eb778842592e1fb48a9d4cef7e861103878f @@ -218,7 +218,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248 F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4 -P adb2bd61436927d37b23bae857089d62e12397af -R e8b127567100f827cb21e845b744b7ae +P 71e98d0d089576433c4b06dcba1c57063bd366f5 +R 3d4ef809458f49528c446bf513dcad92 U danielk1977 -Z 7c95f2d087a4836fb9faba67d8f7b64b +Z b84856b016de718871cfb8d15d69b3b5 diff --git a/manifest.uuid b/manifest.uuid index cb65bce093..9472c0a770 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -71e98d0d089576433c4b06dcba1c57063bd366f5 \ No newline at end of file +edf069b9f4044ed2a80962c7722052bf1b80bf45 \ No newline at end of file diff --git a/src/build.c b/src/build.c index d9f95e52c0..1f09c250a9 100644 --- a/src/build.c +++ b/src/build.c @@ -23,7 +23,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.214 2004/06/10 02:16:02 danielk1977 Exp $ +** $Id: build.c,v 1.215 2004/06/10 10:50:08 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -884,7 +884,8 @@ static CollSeq * findCollSeqEntry( pColl[1].enc = TEXT_Utf16le; pColl[2].zName = (char*)&pColl[3]; pColl[2].enc = TEXT_Utf16be; - memcpy(pColl[0].zName, zName, nName+1); + memcpy(pColl[0].zName, zName, nName); + pColl[0].zName[nName] = 0; sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl); } } @@ -922,6 +923,110 @@ CollSeq *sqlite3FindCollSeq( return pColl; } +static void callCollNeeded(sqlite *db, const char *zName, int nName){ + /* No collation sequence of this type for this encoding is registered. + ** Call the collation factory to see if it can supply us with one. + */ + char *zExternal = 0; + assert( !db->xCollNeeded || !db->xCollNeeded16 ); + if( nName<0 ) nName = strlen(zName); + if( db->xCollNeeded ){ + zExternal = sqliteStrNDup(zName, nName); + if( !zExternal ) return; + db->xCollNeeded(db->pCollNeededArg, db, (int)db->enc, zExternal); + } + if( db->xCollNeeded16 ){ + if( SQLITE_BIGENDIAN ){ + zExternal = sqlite3utf8to16be(zName, nName); + }else{ + zExternal = sqlite3utf8to16le(zName, nName); + } + if( !zExternal ) return; + db->xCollNeeded16(db->pCollNeededArg, db, (int)db->enc, zExternal); + } + if( zExternal ) sqliteFree(zExternal); +} + +static int synthCollSeq(Parse *pParse, CollSeq *pColl){ + /* The collation factory failed to deliver a function but there may be + ** other versions of this collation function (for other text encodings) + ** available. Use one of these instead. Avoid a UTF-8 <-> UTF-16 + ** conversion if possible. + */ + CollSeq *pColl2 = 0; + char *z = pColl->zName; + int n = strlen(z); + switch( pParse->db->enc ){ + case TEXT_Utf16le: + pColl2 = sqlite3FindCollSeq(pParse->db, TEXT_Utf16be, z, n, 0); + assert( pColl2 ); + if( pColl2->xCmp ) break; + pColl2 = sqlite3FindCollSeq(pParse->db, TEXT_Utf8, z, n, 0); + assert( pColl2 ); + break; + + case TEXT_Utf16be: + pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf16le, z, n, 0); + assert( pColl2 ); + if( pColl2->xCmp ) break; + pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf8, z, n, 0); + assert( pColl2 ); + break; + + case TEXT_Utf8: + pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf16be, z, n, 0); + assert( pColl2 ); + if( pColl2->xCmp ) break; + pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf16le, z, n, 0); + assert( pColl2 ); + break; + } + if( pColl2->xCmp ){ + memcpy(pColl, pColl2, sizeof(CollSeq)); + }else{ + if( pParse->nErr==0 ){ + sqlite3SetNString(&pParse->zErrMsg, "no such collation sequence: ", + -1, z, n, 0); + } + pParse->nErr++; + return SQLITE_ERROR; + } + return SQLITE_OK; +} + +/* +** This routine is called on a collation sequence before it is used to +** check that it is defined. An undefined collation sequence exists when +** a database is loaded that contains references to collation sequences +** that have not been defined by sqlite3_create_collation() etc. +** +** If required, this routine calls the 'collation needed' callback to +** request a definition of the collating sequence. If this doesn't work, +** an equivalent collating sequence that uses a text encoding different +** from the main database is substituted, if one is available. +*/ +int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ + if( pColl && !pColl->xCmp ){ + callCollNeeded(pParse->db, pColl->zName, strlen(pColl->zName)); + if( !pColl->xCmp && synthCollSeq(pParse, pColl) ){ + return SQLITE_ERROR; + } + } + return SQLITE_OK; +} + +int sqlite3CheckIndexCollSeq(Parse *pParse, Index *pIdx){ + if( pIdx ){ + int i; + for(i=0; inColumn; i++){ + if( sqlite3CheckCollSeq(pParse, pIdx->keyInfo.aColl[i]) ){ + return SQLITE_ERROR; + } + } + } + return SQLITE_OK; +} + /* ** This function returns the collation sequence for database native text ** encoding identified by the string zName, length nName. @@ -938,64 +1043,33 @@ CollSeq *sqlite3FindCollSeq( */ CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){ u8 enc = pParse->db->enc; - CollSeq *pColl = sqlite3FindCollSeq(pParse->db, enc, zName, nName, 0); - if( !pColl || !pColl->xCmp ){ + u8 initbusy = pParse->db->init.busy; + CollSeq *pColl = sqlite3FindCollSeq(pParse->db, enc, zName, nName, initbusy); + if( !initbusy && (!pColl || !pColl->xCmp) ){ /* No collation sequence of this type for this encoding is registered. ** Call the collation factory to see if it can supply us with one. */ - - /* FIX ME: Actually call collation factory, then call - ** sqlite3FindCollSeq() again. */ + callCollNeeded(pParse->db, zName, nName); pColl = sqlite3FindCollSeq(pParse->db, enc, zName, nName, 0); - if( pColl && !pColl->xCmp ){ - /* The collation factory failed to deliver a function but there are - ** other versions of this collation function (for other text - ** encodings) available. Use one of these instead. Avoid a - ** UTF-8 <-> UTF-16 conversion if possible. + /* There may be a version of the collation sequence that requires + ** translation between encodings. Search for it with synthCollSeq(). */ - CollSeq *pColl2 = 0; - switch( enc ){ - case TEXT_Utf16le: - pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf16be,zName,nName,0); - assert( pColl2 ); - if( pColl2->xCmp ) break; - pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf8,zName,nName,0); - assert( pColl2 ); - break; - - case TEXT_Utf16be: - pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf16le,zName,nName,0); - assert( pColl2 ); - if( pColl2->xCmp ) break; - pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf8,zName,nName,0); - assert( pColl2 ); - break; - - case TEXT_Utf8: - pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf16be,zName,nName,0); - assert( pColl2 ); - if( pColl2->xCmp ) break; - pColl2 = sqlite3FindCollSeq(pParse->db,TEXT_Utf16le,zName,nName,0); - assert( pColl2 ); - break; - } - - if( pColl2->xCmp ){ - memcpy(pColl, pColl2, sizeof(CollSeq)); + if( synthCollSeq(pParse, pColl) ){ + return 0; } } } /* If nothing has been found, write the error message into pParse */ - if( !pColl || !pColl->xCmp ){ + if( !initbusy && (!pColl || !pColl->xCmp) ){ if( pParse->nErr==0 ){ sqlite3SetNString(&pParse->zErrMsg, "no such collation sequence: ", -1, zName, nName, 0); } pParse->nErr++; + pColl = 0; } - return pColl; } @@ -1943,6 +2017,11 @@ void sqlite3CreateIndex( pIndex->keyInfo.aColl[i] = pTab->aCol[j].pColl; } assert( pIndex->keyInfo.aColl[i] ); + if( !db->init.busy && + sqlite3CheckCollSeq(pParse, pIndex->keyInfo.aColl[i]) + ){ + goto exit_create_index; + } } pIndex->keyInfo.nField = pList->nExpr; @@ -2448,7 +2527,6 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ ** specified auxiliary database and the temp database are made writable. */ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ - sqlite *db = pParse->db; Vdbe *v = sqlite3GetVdbe(pParse); if( v==0 ) return; sqlite3CodeVerifySchema(pParse, iDb); diff --git a/src/delete.c b/src/delete.c index 82bb8575b8..b9cc3f05c7 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.72 2004/06/03 16:08:41 danielk1977 Exp $ +** $Id: delete.c,v 1.73 2004/06/10 10:50:15 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -193,6 +193,13 @@ void sqlite3DeleteFrom( ** the table and pick which records to delete. */ else{ + /* Ensure all required collation sequences are available. */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( sqlite3CheckIndexCollSeq(pParse, pIdx) ){ + goto delete_from_cleanup; + } + } + /* Begin the database scan */ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0); diff --git a/src/expr.c b/src/expr.c index b341f83a63..762b696c48 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.137 2004/06/09 09:55:18 danielk1977 Exp $ +** $Id: expr.c,v 1.138 2004/06/10 10:50:17 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -59,14 +59,18 @@ char sqlite3ExprAffinity(Expr *pExpr){ ** Return the default collation sequence for the expression pExpr. If ** there is no default collation type, return 0. */ -CollSeq *sqlite3ExprCollSeq(Expr *pExpr){ +CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ + CollSeq *pColl = 0; if( pExpr ){ - if( pExpr->pColl ) return pExpr->pColl; - if( pExpr->op==TK_AS ){ - return sqlite3ExprCollSeq(pExpr->pLeft); + pColl = pExpr->pColl; + if( pExpr->op==TK_AS && !pColl ){ + return sqlite3ExprCollSeq(pParse, pExpr->pLeft); } } - return 0; + if( sqlite3CheckCollSeq(pParse, pColl) ){ + pColl = 0; + } + return pColl; } /* @@ -157,10 +161,10 @@ static int binaryCompareP1(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){ ** is used, or the default (BINARY) if neither expression has a collating ** type. */ -static CollSeq* binaryCompareCollSeq(Expr *pLeft, Expr *pRight){ - CollSeq *pColl = sqlite3ExprCollSeq(pLeft); +static CollSeq* binaryCompareCollSeq(Parse *pParse, Expr *pLeft, Expr *pRight){ + CollSeq *pColl = sqlite3ExprCollSeq(pParse, pLeft); if( !pColl ){ - pColl = sqlite3ExprCollSeq(pRight); + pColl = sqlite3ExprCollSeq(pParse, pRight); } return pColl; } @@ -868,7 +872,7 @@ int sqlite3ExprResolveIds( assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0); if( pExpr->pSelect->pEList && pExpr->pSelect->pEList->nExpr>0 ){ - keyInfo.aColl[0] = binaryCompareCollSeq(pExpr->pLeft, + keyInfo.aColl[0] = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pSelect->pEList->a[0].pExpr); } }else if( pExpr->pList ){ @@ -1195,7 +1199,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ case TK_NE: case TK_EQ: { int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pRight, 0); - CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pRight); + CollSeq *p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight); sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3ExprCode(pParse, pExpr->pRight); sqlite3VdbeOp3(v, op, p1, 0, (void *)p3, P3_COLLSEQ); @@ -1326,12 +1330,12 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ sqlite3VdbeAddOp(v, OP_Dup, 0, 0); sqlite3ExprCode(pParse, pExpr->pList->a[0].pExpr); p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[0].pExpr, 0); - p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[0].pExpr); + p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pList->a[0].pExpr); sqlite3VdbeOp3(v, OP_Ge, p1, 0, (void *)p3, P3_COLLSEQ); sqlite3VdbeAddOp(v, OP_Pull, 1, 0); sqlite3ExprCode(pParse, pExpr->pList->a[1].pExpr); p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[1].pExpr, 0); - p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[1].pExpr); + p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pList->a[1].pExpr); sqlite3VdbeOp3(v, OP_Le, p1, 0, (void *)p3, P3_COLLSEQ); sqlite3VdbeAddOp(v, OP_And, 0, 0); break; @@ -1360,7 +1364,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ sqlite3ExprCode(pParse, pExpr->pList->a[i].pExpr); if( pExpr->pLeft ){ int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[i].pExpr, 1); - CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft, + CollSeq *p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pList->a[i].pExpr); sqlite3VdbeAddOp(v, OP_Dup, 1, 1); jumpInst = sqlite3VdbeOp3(v, OP_Ne, p1, 0, (void *)p3, P3_COLLSEQ); @@ -1476,7 +1480,7 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_NE: case TK_EQ: { int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pRight, jumpIfNull); - CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pRight); + CollSeq *p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight); sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3ExprCode(pParse, pExpr->pRight); sqlite3VdbeOp3(v, op, p1, dest, (void *)p3, P3_COLLSEQ); @@ -1502,12 +1506,12 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ sqlite3VdbeAddOp(v, OP_Dup, 0, 0); sqlite3ExprCode(pParse, pExpr->pList->a[0].pExpr); p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[0].pExpr, !jumpIfNull); - p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[0].pExpr); + p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pList->a[0].pExpr); addr = sqlite3VdbeOp3(v, OP_Lt, p1, 0, (void *)p3, P3_COLLSEQ); sqlite3ExprCode(pParse, pExpr->pList->a[1].pExpr); p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[1].pExpr, jumpIfNull); - p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[1].pExpr); + p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pList->a[1].pExpr); sqlite3VdbeOp3(v, OP_Le, p1, dest, (void *)p3, P3_COLLSEQ); sqlite3VdbeAddOp(v, OP_Integer, 0, 0); @@ -1570,7 +1574,7 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ case TK_NE: case TK_EQ: { int p1 = binaryCompareP1(pExpr->pLeft, pExpr->pRight, jumpIfNull); - CollSeq *p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pRight); + CollSeq *p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight); sqlite3ExprCode(pParse, pExpr->pLeft); sqlite3ExprCode(pParse, pExpr->pRight); sqlite3VdbeOp3(v, op, p1, dest, (void *)p3, P3_COLLSEQ); @@ -1597,13 +1601,13 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ sqlite3ExprCode(pParse, pExpr->pList->a[0].pExpr); addr = sqlite3VdbeCurrentAddr(v); p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[0].pExpr, !jumpIfNull); - p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[0].pExpr); + p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pList->a[0].pExpr); sqlite3VdbeOp3(v, OP_Ge, p1, addr+3, (void *)p3, P3_COLLSEQ); sqlite3VdbeAddOp(v, OP_Pop, 1, 0); sqlite3VdbeAddOp(v, OP_Goto, 0, dest); sqlite3ExprCode(pParse, pExpr->pList->a[1].pExpr); p1 = binaryCompareP1(pExpr->pLeft, pExpr->pList->a[1].pExpr, jumpIfNull); - p3 = binaryCompareCollSeq(pExpr->pLeft, pExpr->pList->a[1].pExpr); + p3 = binaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pList->a[1].pExpr); sqlite3VdbeOp3(v, OP_Gt, p1, dest, (void *)p3, P3_COLLSEQ); break; } diff --git a/src/insert.c b/src/insert.c index 7b71705fdf..969f768ce2 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.109 2004/05/31 08:55:34 danielk1977 Exp $ +** $Id: insert.c,v 1.110 2004/06/10 10:50:21 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -234,6 +234,13 @@ void sqlite3Insert( goto insert_cleanup; } + /* Ensure all required collation sequences are available. */ + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( sqlite3CheckIndexCollSeq(pParse, pIdx) ){ + goto insert_cleanup; + } + } + /* Allocate a VDBE */ v = sqlite3GetVdbe(pParse); diff --git a/src/main.c b/src/main.c index 7825477285..ac8831a09f 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.213 2004/06/10 02:16:02 danielk1977 Exp $ +** $Id: main.c,v 1.214 2004/06/10 10:50:22 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -1217,3 +1217,25 @@ int sqlite3_create_collation16( sqliteFree(zName8); return rc; } + +int sqlite3_collation_needed( + sqlite3 *db, + void *pCollNeededArg, + void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*) +){ + db->xCollNeeded = xCollNeeded; + db->xCollNeeded16 = 0; + db->pCollNeededArg = pCollNeededArg; + return SQLITE_OK; +} +int sqlite3_collation_needed16( + sqlite3 *db, + void *pCollNeededArg, + void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*) +){ + db->xCollNeeded = 0; + db->xCollNeeded16 = xCollNeeded16; + db->pCollNeededArg = pCollNeededArg; + return SQLITE_OK; +} + diff --git a/src/pragma.c b/src/pragma.c index 05a1d034df..bc1e313e33 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.41 2004/06/10 01:30:59 drh Exp $ +** $Id: pragma.c,v 1.42 2004/06/10 10:50:25 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -655,6 +655,7 @@ void sqlite3Pragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){ sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0); cnt++; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) return; sqlite3VdbeAddOp(v, OP_Integer, pIdx->tnum, 0); cnt++; } diff --git a/src/select.c b/src/select.c index d868a85bbe..011493e5ed 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.185 2004/06/09 09:55:18 danielk1977 Exp $ +** $Id: select.c,v 1.186 2004/06/10 10:50:25 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -550,7 +550,7 @@ static void generateSortTail( ** is stored in pOrderBy->a[i].zName. Otherwise, use the default ** collation type for the expression. */ - pInfo->aColl[i] = sqlite3ExprCollSeq(pOrderBy->a[i].pExpr); + pInfo->aColl[i] = sqlite3ExprCollSeq(pParse, pOrderBy->a[i].pExpr); if( !pInfo->aColl[i] ){ pInfo->aColl[i] = db->pDfltColl; } @@ -825,7 +825,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){ if( zType ){ pTab->aCol[i].affinity = sqlite3AffinityType(zType, strlen(zType)); } - pTab->aCol[i].pColl = sqlite3ExprCollSeq(p); + pTab->aCol[i].pColl = sqlite3ExprCollSeq(pParse, p); if( !pTab->aCol[i].pColl ){ pTab->aCol[i].pColl = pParse->db->pDfltColl; } @@ -2233,21 +2233,6 @@ int sqlite3Select( } } - /* If there is an ORDER BY clause, resolve any collation sequences - ** names that have been explicitly specified. - */ - if( pOrderBy ){ - for(i=0; inExpr; i++){ - if( pOrderBy->a[i].zName ){ - pOrderBy->a[i].pExpr->pColl = - sqlite3LocateCollSeq(pParse, pOrderBy->a[i].zName, -1); - } - } - if( pParse->nErr ){ - goto select_end; - } - } - /* Begin generating code. */ v = sqlite3GetVdbe(pParse); @@ -2323,6 +2308,21 @@ int sqlite3Select( return rc; } + /* If there is an ORDER BY clause, resolve any collation sequences + ** names that have been explicitly specified. + */ + if( pOrderBy ){ + for(i=0; inExpr; i++){ + if( pOrderBy->a[i].zName ){ + pOrderBy->a[i].pExpr->pColl = + sqlite3LocateCollSeq(pParse, pOrderBy->a[i].zName, -1); + } + } + if( pParse->nErr ){ + goto select_end; + } + } + /* Set the limiter. */ computeLimitRegisters(pParse, p); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 9147f96ea3..e139e7cbf2 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.96 2004/06/10 02:16:02 danielk1977 Exp $ +** @(#) $Id: sqlite.h.in,v 1.97 2004/06/10 10:50:30 danielk1977 Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ @@ -967,21 +967,84 @@ void sqlite3_result_value(sqlite3_context*, sqlite3_value*); #define SQLITE_UTF16LE 2 #define SQLITE_UTF16BE 3 +/* +** These two functions are used to add new collation sequences to the +** sqlite3 handle specified as the first argument. +** +** The name of the new collation sequence is specified as a UTF-8 string +** for sqlite3_create_collation() and a UTF-16 string for +** sqlite3_create_collation16(). In both cases the name is passed as the +** second function argument. +** +** The third argument must be one of the constants SQLITE_UTF8, +** SQLITE_UTF16LE or SQLITE_UTF16BE, indicating that the user-supplied +** routine expects to be passed pointers to strings encoded using UTF-8, +** UTF-16 little-endian or UTF-16 big-endian respectively. +** +** A pointer to the user supplied routine must be passed as the fifth +** argument. If it is NULL, this is the same as deleting the collation +** sequence (so that SQLite cannot call it anymore). Each time the user +** supplied function is invoked, it is passed a copy of the void* passed as +** the fourth argument to sqlite3_create_collation() or +** sqlite3_create_collation16() as its first parameter. +** +** The remaining arguments to the user-supplied routine are two strings, +** each represented by a [length, data] pair and encoded in the encoding +** that was passed as the third argument when the collation sequence was +** registered. The user routine should return negative, zero or positive if +** the first string is less than, equal to, or greater than the second +** string. i.e. (STRING1 - STRING2). +*/ int sqlite3_create_collation( sqlite3*, const char *zName, - int enc, + int eTextRep, void*, int(*xCompare)(void*,int,const void*,int,const void*) ); int sqlite3_create_collation16( sqlite3*, const char *zName, - int enc, + int eTextRep, void*, int(*xCompare)(void*,int,const void*,int,const void*) ); +/* +** To avoid having to register all collation sequences before a database +** can be used, a single callback function may be registered with the +** database handle to be called whenever an undefined collation sequence is +** required. +** +** If the function is registered using the sqlite3_collation_needed() API, +** then it is passed the names of undefined collation sequences as strings +** encoded in UTF-8. If sqlite3_collation_needed16() is used, the names +** are passed as UTF-16 in machine native byte order. A call to either +** function replaces any existing callback. +** +** When the user-function is invoked, the first argument passed is a copy +** of the second argument to sqlite3_collation_needed() or +** sqlite3_collation_needed16(). The second argument is the database +** handle. The third argument is one of SQLITE_UTF8, SQLITE_UTF16BE or +** SQLITE_UTF16LE, indicating the most desirable form of the collation +** sequence function required. The fourth parameter is the name of the +** required collation sequence. +** +** The collation sequence is returned to SQLite by a collation-needed +** callback using the sqlite3_create_collation() or +** sqlite3_create_collation16() APIs, described above. +*/ +int sqlite3_collation_needed( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const char*) +); +int sqlite3_collation_needed16( + sqlite3*, + void*, + void(*)(void*,sqlite3*,int eTextRep,const void*) +); + #ifdef __cplusplus } /* End of the 'extern "C"' block */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4f7e49911c..57e139bce9 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.279 2004/06/10 02:16:02 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.280 2004/06/10 10:50:32 danielk1977 Exp $ */ #include "config.h" #include "sqlite3.h" @@ -423,6 +423,9 @@ struct sqlite { u8 enc; /* Text encoding for this database. */ u8 autoCommit; /* The auto-commit flag. */ int nMaster; /* Length of master journal name. -1=unknown */ + void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); + void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); + void *pCollNeededArg; }; /* @@ -1395,4 +1398,6 @@ int sqlite3ReadUniChar(const char *zStr, int *pOffset, u8 *pEnc, int fold); int sqlite3ReadSchema(sqlite *db, char **); CollSeq *sqlite3FindCollSeq(sqlite *,u8 enc, const char *,int,int); CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName); -CollSeq *sqlite3ExprCollSeq(Expr *pExpr); +CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); +int sqlite3CheckCollSeq(Parse *, CollSeq *); +int sqlite3CheckIndexCollSeq(Parse *, Index *); diff --git a/src/tclsqlite.c b/src/tclsqlite.c index ecfcc2c0bc..af4131a719 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.82 2004/06/10 02:16:02 danielk1977 Exp $ +** $Id: tclsqlite.c,v 1.83 2004/06/10 10:50:38 danielk1977 Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -69,7 +69,8 @@ struct SqliteDb { SqlFunc *pFunc; /* List of SQL functions */ SqlCollate *pCollate; /* List of SQL collation functions */ int rc; /* Return code of most recent sqlite3_exec() */ - int nChange; /* Database changes for the most recent eval */ + int nChange; /* Database changes for the most recent eval */ + Tcl_Obj *pCollateNeeded; /* Collation needed script */ }; /* @@ -217,6 +218,20 @@ static int DbCommitHandler(void *cd){ return 0; } +static void tclCollateNeeded( + void *pCtx, + sqlite *db, + int enc, + const char *zName +){ + SqliteDb *pDb = (SqliteDb *)pCtx; + Tcl_Obj *pScript = Tcl_DuplicateObj(pDb->pCollateNeeded); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zName, -1)); + Tcl_EvalObjEx(pDb->interp, pScript, 0); + Tcl_DecrRefCount(pScript); +} + /* ** This routine is called to evaluate an SQL collation function implemented ** using TCL script. @@ -382,7 +397,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ "errorcode", "eval", "function", "last_insert_rowid", "last_statement_changes", "onecolumn", "progress", "rekey", "timeout", - "trace", "collate", + "trace", "collate", "collation_needed", 0 }; enum DB_enum { @@ -391,7 +406,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ DB_ERRORCODE, DB_EVAL, DB_FUNCTION, DB_LAST_INSERT_ROWID, DB_LAST_STATEMENT_CHANGES, DB_ONECOLUMN, DB_PROGRESS, DB_REKEY, DB_TIMEOUT, - DB_TRACE, DB_COLLATE + DB_TRACE, DB_COLLATE, DB_COLLATION_NEEDED }; if( objc<2 ){ @@ -924,6 +939,26 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ break; } + /* + ** $db collate_needed SCRIPT + ** + ** Create a new SQL collation function called NAME. Whenever + ** that function is called, invoke SCRIPT to evaluate the function. + */ + case DB_COLLATION_NEEDED: { + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 2, objv, "SCRIPT"); + return TCL_ERROR; + } + if( pDb->pCollateNeeded ){ + Tcl_DecrRefCount(pDb->pCollateNeeded); + } + pDb->pCollateNeeded = Tcl_DuplicateObj(objv[2]); + Tcl_IncrRefCount(pDb->pCollateNeeded); + sqlite3_collation_needed(pDb->db, pDb, tclCollateNeeded); + break; + } + } /* End of the SWITCH statement */ return rc; } @@ -1051,6 +1086,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){ Md5_Register(p->db); } #endif + p->interp = interp; return TCL_OK; } diff --git a/src/update.c b/src/update.c index c38f09b869..f38f2b8496 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.81 2004/05/29 11:24:50 danielk1977 Exp $ +** $Id: update.c,v 1.82 2004/06/10 10:50:45 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -179,6 +179,7 @@ void sqlite3Update( } } if( inColumn ){ + if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) goto update_cleanup; apIdx[nIdx++] = pIdx; aIdxUsed[j] = 1; }else{