]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the sqlite3_collation_needed() API and fix some error handling cases
authordanielk1977 <danielk1977@noemail.net>
Thu, 10 Jun 2004 10:50:08 +0000 (10:50 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 10 Jun 2004 10:50:08 +0000 (10:50 +0000)
involving unknown collation sequences. (CVS 1562)

FossilOrigin-Name: edf069b9f4044ed2a80962c7722052bf1b80bf45

13 files changed:
manifest
manifest.uuid
src/build.c
src/delete.c
src/expr.c
src/insert.c
src/main.c
src/pragma.c
src/select.c
src/sqlite.h.in
src/sqliteInt.h
src/tclsqlite.c
src/update.c

index 81e9c986944b466bfe71900076fb5627339d56d2..ad38bf3d0a4d93b342f2c823ce60a4319598ea52 100644 (file)
--- 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
index cb65bce093ac308c7c14e0bceafe8c2f84623448..9472c0a770f543af8309696d6feb9d15e98d2ba6 100644 (file)
@@ -1 +1 @@
-71e98d0d089576433c4b06dcba1c57063bd366f5
\ No newline at end of file
+edf069b9f4044ed2a80962c7722052bf1b80bf45
\ No newline at end of file
index d9f95e52c02d36a7d16f6e0616151ff55d48e8d0..1f09c250a96fe53cd3d8c253abd5ad10129c1384 100644 (file)
@@ -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 <ctype.h>
@@ -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; i<pIdx->nColumn; 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);
index 82bb8575b8e4875df50924431b61726bb4d2cab5..b9cc3f05c7df968c4ea520f14f837fa1e0127ea3 100644 (file)
@@ -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);
index b341f83a63983fb47acc754a7fd697be4d780581..762b696c486290bda5ec0e0990f39899422ff1b6 100644 (file)
@@ -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 <ctype.h>
@@ -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;
     }
index 7b71705fdf2d04c799b876c3463cbf7f313fad51..969f768ce2860aebed0181cd41968f2ddff5b1e6 100644 (file)
@@ -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);
index 7825477285d320d2703b0cd58f3e3ae7db0af239..ac8831a09f288b5aeef4123e8c2ba5f6fb4c4672 100644 (file)
@@ -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;
+}
+
index 05a1d034df99efcd4ebc452793c594479014250f..bc1e313e33f88bb4745482e51710f10c89683664 100644 (file)
@@ -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 <ctype.h>
@@ -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++;
         }
index d868a85bbeaf0a4b43bcb1b7a414433aa6d11bb6..011493e5ed9d8e71eacdfc21c27bdc9afda844f4 100644 (file)
@@ -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; i<pOrderBy->nExpr; 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; i<pOrderBy->nExpr; 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);
index 9147f96ea3eac6d07c7c91858b15f9f48eb05273..e139e7cbf201c876e1cd9cd20273d2a11e8e55b3 100644 (file)
@@ -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 */
index 4f7e49911cf714036197c43e9cb18800534c98c4..57e139bce901b2702a776747a20e54993c02bbe9 100644 (file)
@@ -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 *);
index ecfcc2c0bca278d77e9579a2857efe615ad6301f..af4131a71984575764230533187d2727767a4961 100644 (file)
@@ -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;
 }
 
index c38f09b869bb2717feb75f39e8d7f92ea7fb040c..f38f2b8496789e30394df73cd3010b184483fcb0 100644 (file)
@@ -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( i<pIdx->nColumn ){
+      if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) goto update_cleanup;
       apIdx[nIdx++] = pIdx;
       aIdxUsed[j] = 1;
     }else{