aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/expr.c103
-rw-r--r--src/func.c284
-rw-r--r--src/main.c4
-rw-r--r--src/sqlite.h.in9
-rw-r--r--src/sqliteInt.h6
-rw-r--r--src/utf.c177
-rw-r--r--src/util.c53
-rw-r--r--src/vdbe.c4
-rw-r--r--src/vdbeapi.c1
-rw-r--r--src/vdbeaux.c5
10 files changed, 474 insertions, 172 deletions
diff --git a/src/expr.c b/src/expr.c
index b3ba9f47f..dd2a8cef3 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.134 2004/06/05 10:22:17 danielk1977 Exp $
+** $Id: expr.c,v 1.135 2004/06/06 09:44:04 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -968,11 +968,12 @@ int sqlite3ExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
int nId; /* Number of characters in function name */
const char *zId; /* The function name. */
FuncDef *pDef;
+ int iPrefEnc = (pParse->db->enc==TEXT_Utf8)?0:1;
getFunctionName(pExpr, &zId, &nId);
- pDef = sqlite3FindFunction(pParse->db, zId, nId, n, 0);
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, n, iPrefEnc, 0);
if( pDef==0 ){
- pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, 0);
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, iPrefEnc, 0);
if( pDef==0 ){
no_such_func = 1;
}else{
@@ -1233,12 +1234,15 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
const char *zId;
int p2 = 0;
int i;
+ int iPrefEnc = (pParse->db->enc==TEXT_Utf8)?0:1;
getFunctionName(pExpr, &zId, &nId);
- pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, 0);
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, iPrefEnc, 0);
assert( pDef!=0 );
nExpr = sqlite3ExprCodeExprList(pParse, pList);
for(i=0; i<nExpr && i<32; i++){
- p2 &= (1<<i);
+ if( sqlite3ExprIsConstant(pList->a[i].pExpr) ){
+ p2 |= (1<<i);
+ }
}
sqlite3VdbeOp3(v, OP_Function, nExpr, p2, (char*)pDef, P3_FUNCDEF);
break;
@@ -1645,13 +1649,14 @@ int sqlite3ExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
}
}
if( i>=pParse->nAgg ){
+ int iPrefEnc = (pParse->db->enc==TEXT_Utf8)?0:1;
i = appendAggInfo(pParse);
if( i<0 ) return 1;
pParse->aAgg[i].isAgg = 1;
pParse->aAgg[i].pExpr = pExpr;
pParse->aAgg[i].pFunc = sqlite3FindFunction(pParse->db,
pExpr->token.z, pExpr->token.n,
- pExpr->pList ? pExpr->pList->nExpr : 0, 0);
+ pExpr->pList ? pExpr->pList->nExpr : 0, iPrefEnc, 0);
}
pExpr->iAgg = i;
break;
@@ -1677,9 +1682,10 @@ int sqlite3ExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
}
/*
-** Locate a user function given a name and a number of arguments.
-** Return a pointer to the FuncDef structure that defines that
-** function, or return NULL if the function does not exist.
+** Locate a user function given a name, a number of arguments and a flag
+** indicating whether the function prefers UTF-16 over UTF-8. Return a
+** pointer to the FuncDef structure that defines that function, or return
+** NULL if the function does not exist.
**
** If the createFlag argument is true, then a new (blank) FuncDef
** structure is created and liked into the "db" structure if a
@@ -1690,39 +1696,70 @@ int sqlite3ExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
** If createFlag is false and nArg is -1, then the first valid
** function found is returned. A function is valid if either xFunc
** or xStep is non-zero.
+**
+** If createFlag is false, then a function with the required name and
+** number of arguments may be returned even if the eTextRep flag does not
+** match that requested.
*/
FuncDef *sqlite3FindFunction(
sqlite *db, /* An open database */
const char *zName, /* Name of the function. Not null-terminated */
int nName, /* Number of characters in the name */
int nArg, /* Number of arguments. -1 means any number */
+ int eTextRep, /* True to retrieve UTF-16 versions. */
int createFlag /* Create new entry if true and does not otherwise exist */
){
- FuncDef *pFirst, *p, *pMaybe;
- pFirst = p = (FuncDef*)sqlite3HashFind(&db->aFunc, zName, nName);
- if( p && !createFlag && nArg<0 ){
- while( p && p->xFunc==0 && p->xStep==0 ){ p = p->pNext; }
- return p;
- }
- pMaybe = 0;
- while( p && p->nArg!=nArg ){
- if( p->nArg<0 && !createFlag && (p->xFunc || p->xStep) ) pMaybe = p;
- p = p->pNext;
- }
- if( p && !createFlag && p->xFunc==0 && p->xStep==0 ){
- return 0;
+ FuncDef *p; /* Iterator variable */
+ FuncDef *pFirst; /* First function with this name */
+ FuncDef *pBest = 0; /* Best match found so far */
+ int matchqual = 0;
+
+ /* Normalize argument values to simplify comparisons below. */
+ if( eTextRep ) eTextRep = 1;
+ if( nArg<-1 ) nArg = -1;
+
+ pFirst = (FuncDef*)sqlite3HashFind(&db->aFunc, zName, nName);
+ for(p=pFirst; p; p=p->pNext){
+ if( 1 || p->xFunc || p->xStep ){
+ if( p->nArg==nArg && p->iPrefEnc==eTextRep ){
+ /* A perfect match. */
+ pBest = p;
+ matchqual = 4;
+ break;
+ }
+ if( p->nArg==nArg ){
+ /* Number of arguments matches, but not the text encoding */
+ pBest = p;
+ matchqual = 3;
+ }
+ else if( (p->nArg<0) || (nArg<0) ){
+ if( matchqual<2 && p->iPrefEnc==eTextRep ){
+ /* Matched a varargs function with correct text encoding */
+ pBest = p;
+ matchqual = 2;
+ }
+ if( matchqual<1 ){
+ /* Matched a varargs function with incorrect text encoding */
+ pBest = p;
+ matchqual = 1;
+ }
+ }
+ }
}
- if( p==0 && pMaybe ){
- assert( createFlag==0 );
- return pMaybe;
+
+ if( createFlag && matchqual<4 &&
+ (pBest = sqliteMalloc(sizeof(*pBest)+nName+1)) ){
+ pBest->nArg = nArg;
+ pBest->pNext = pFirst;
+ pBest->zName = (char*)&pBest[1];
+ memcpy(pBest->zName, zName, nName);
+ pBest->zName[nName] = 0;
+ sqlite3HashInsert(&db->aFunc, pBest->zName, nName, (void*)pBest);
}
- if( p==0 && createFlag && (p = sqliteMalloc(sizeof(*p)+nName+1))!=0 ){
- p->nArg = nArg;
- p->pNext = pFirst;
- p->zName = (char*)&p[1];
- memcpy(p->zName, zName, nName);
- p->zName[nName] = 0;
- sqlite3HashInsert(&db->aFunc, p->zName, nName, (void*)p);
+
+ if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
+ return pBest;
}
- return p;
+ return 0;
}
+
diff --git a/src/func.c b/src/func.c
index 706bfcf75..e6cd5d0f7 100644
--- a/src/func.c
+++ b/src/func.c
@@ -16,7 +16,7 @@
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** $Id: func.c,v 1.62 2004/05/31 18:51:58 drh Exp $
+** $Id: func.c,v 1.63 2004/06/06 09:44:04 danielk1977 Exp $
*/
#include <ctype.h>
#include <math.h>
@@ -293,23 +293,236 @@ static void last_statement_change_count(
}
/*
+** A LIKE pattern compiles to an instance of the following structure. Refer
+** to the comment for compileLike() function for details.
+*/
+struct LikePattern {
+ int nState;
+ struct LikeState {
+ int val; /* Unicode codepoint or -1 for any char i.e. '_' */
+ int failstate; /* State to jump to if next char is not val */
+ } aState[0];
+};
+typedef struct LikePattern LikePattern;
+
+void deleteLike(void *pLike){
+ sqliteFree(pLike);
+}
+
+
+/* #define TRACE_LIKE */
+
+#if defined(TRACE_LIKE) && !defined(NDEBUG)
+char *dumpLike(LikePattern *pLike){
+ int i;
+ int k = 0;
+ char *zBuf = (char *)sqliteMalloc(pLike->nState*40);
+
+ k += sprintf(&zBuf[k], "%d states - ", pLike->nState);
+ for(i=0; i<pLike->nState; i++){
+ k += sprintf(&zBuf[k], " %d:(%d, %d)", i, pLike->aState[i].val,
+ pLike->aState[i].failstate);
+ }
+ return zBuf;
+}
+#endif
+
+/*
+** This function compiles an SQL 'LIKE' pattern into a state machine,
+** represented by a LikePattern structure.
+**
+** Each state of the state-machine has two attributes, 'val' and
+** 'failstate'. The val attribute is either the value of a unicode
+** codepoint, or -1, indicating a '_' wildcard (match any single
+** character). The failstate is either the number of another state
+** or -1, indicating jump to 'no match'.
+**
+** To see if a string matches a pattern the pattern is
+** compiled to a state machine that is executed according to the algorithm
+** below. The string is assumed to be terminated by a 'NUL' character
+** (unicode codepoint 0).
+**
+** 1 S = 0
+** 2 DO
+** 3 C = <Next character from input string>
+** 4 IF( C matches <State S val> )
+** 5 S = S+1
+** 6 ELSE IF( S != <State S failstate> )
+** 7 S = <State S failstate>
+** 8 <Rewind Input string 1 character>
+** 9 WHILE( (C != NUL) AND (S != FAILED) )
+** 10
+** 11 IF( S == <number of states> )
+** 12 RETURN MATCH
+** 13 ELSE
+** 14 RETURN NO-MATCH
+**
+** In practice there is a small optimization to avoid the <Rewind>
+** operation in line 8 of the description above.
+**
+** For example, the following pattern, 'X%ABabc%_Y' is compiled to
+** the state machine below.
+**
+** State Val FailState
+** -------------------------------
+** 0 120 (x) -1 (NO MATCH)
+** 1 97 (a) 1
+** 2 98 (b) 1
+** 3 97 (a) 1
+** 4 98 (b) 2
+** 5 99 (c) 3
+** 6 -1 (_) 6
+** 7 121 (y) 7
+** 8 0 (NUL) 7
+**
+** The algorithms implemented to compile and execute the state machine were
+** first presented in "Fast pattern matching in strings", Knuth, Morris and
+** Pratt, 1977.
+**
+*/
+LikePattern *compileLike(sqlite3_value *pPattern, u8 enc){
+ LikePattern *pLike;
+ struct LikeState *aState;
+ int pc_state = -1; /* State number of previous '%' wild card */
+ int n = 0;
+ int c;
+
+ int offset = 0;
+ const char *zLike;
+
+ if( enc==TEXT_Utf8 ){
+ zLike = sqlite3_value_text(pPattern);
+ n = sqlite3_value_bytes(pPattern) + 1;
+ }else{
+ zLike = sqlite3_value_text16(pPattern);
+ n = sqlite3_value_bytes16(pPattern)/2 + 1;
+ }
+
+ pLike = (LikePattern *)
+ sqliteMalloc(sizeof(LikePattern)+n*sizeof(struct LikeState));
+ aState = pLike->aState;
+
+ n = 0;
+ do {
+ c = sqlite3ReadUniChar(zLike, &offset, &enc, 1);
+ if( c==95 ){ /* A '_' wildcard */
+ aState[n].val = -1;
+ n++;
+ }else if( c==37 ){ /* A '%' wildcard */
+ aState[n].failstate = n;
+ pc_state = n;
+ }else{ /* A regular character */
+ aState[n].val = c;
+
+ assert( pc_state<=n );
+ if( pc_state<0 ){
+ aState[n].failstate = -1;
+ }else if( pc_state==n ){
+ aState[n].failstate = pc_state;
+ }else{
+ int k = pLike->aState[n-1].failstate;
+ while( k>pc_state && aState[k+1].val!=-1 && aState[k+1].val!=c ){
+ k = aState[k].failstate;
+ }
+ if( k!=pc_state && aState[k+1].val==c ){
+ assert( k==pc_state );
+ k++;
+ }
+ aState[n].failstate = k;
+ }
+ n++;
+ }
+ }while( c );
+ pLike->nState = n;
+#if defined(TRACE_LIKE) && !defined(NDEBUG)
+ {
+ char *zCompiled = dumpLike(pLike);
+ printf("Pattern=\"%s\" Compiled=\"%s\"\n", zPattern, zCompiled);
+ sqliteFree(zCompiled);
+ }
+#endif
+ return pLike;
+}
+
+/*
** Implementation of the like() SQL function. This function implements
** the build-in LIKE operator. The first argument to the function is the
-** string and the second argument is the pattern. So, the SQL statements:
+** pattern and the second argument is the string. So, the SQL statements:
**
** A LIKE B
**
-** is implemented as like(A,B).
+** is implemented as like(B,A).
+**
+** If the pointer retrieved by via a call to sqlite3_user_data() is
+** not NULL, then this function uses UTF-16. Otherwise UTF-8.
*/
static void likeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- const unsigned char *zA = sqlite3_value_text(argv[0]);
- const unsigned char *zB = sqlite3_value_text(argv[1]);
- if( zA && zB ){
- sqlite3_result_int(context, sqlite3LikeCompare(zA, zB));
+ int s;
+ int c;
+ int nc;
+ u8 enc;
+ int offset = 0;
+ const unsigned char *zString;
+ LikePattern *pLike = sqlite3_get_auxdata(context, 0);
+
+ /* If either argument is NULL, the result is NULL */
+ if( sqlite3_value_type(argv[1])==SQLITE_NULL ||
+ sqlite3_value_type(argv[0])==SQLITE_NULL ){
+ return;
+ }
+
+ /* If the user-data pointer is NULL, use UTF-8. Otherwise UTF-16. */
+ if( sqlite3_user_data(context) ){
+ enc = TEXT_Utf16;
+ zString = (const unsigned char *)sqlite3_value_text16(argv[1]);
+ }else{
+ enc = TEXT_Utf8;
+ zString = sqlite3_value_text(argv[1]);
+ }
+
+ /* If the LIKE pattern has not been compiled, compile it now. */
+ if( !pLike ){
+ pLike = compileLike(argv[0], enc);
+ if( !pLike ){
+ sqlite3_result_error(context, "out of memory", -1);
+ return;
+ }
+ sqlite3_set_auxdata(context, 0, pLike, deleteLike);
+ }
+
+ s = 0;
+ nc = 1;
+ do {
+ int val = pLike->aState[s].val;
+ if( nc ) c = sqlite3ReadUniChar(zString, &offset, &enc, 1);
+
+#if defined(TRACE_LIKE) && !defined(NDEBUG)
+ printf("State=%d:(%d, %d) Input=%d\n",
+ s, pLike->aState[s].val,
+ pLike->aState[s].failstate, c);
+#endif
+
+ if( val==-1 || val==c ){
+ s++;
+ nc = 1;
+ }else{
+ if( pLike->aState[s].failstate==s ){
+ nc = 1;
+ }else{
+ nc = 0;
+ s = pLike->aState[s].failstate;
+ }
+ }
+ }while( c && s>=0 );
+
+ if( s==pLike->nState ){
+ sqlite3_result_int(context, 1);
+ }else{
+ sqlite3_result_int(context, 0);
}
}
@@ -642,39 +855,40 @@ void sqlite3RegisterBuiltinFunctions(sqlite *db){
char *zName;
signed char nArg;
u8 argType; /* 0: none. 1: db 2: (-1) */
+ u8 eTextRep; /* 1: UTF-16. 0: UTF-8 */
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
} aFuncs[] = {
- { "min", -1, 0, minmaxFunc },
- { "min", 0, 0, 0 },
- { "max", -1, 2, minmaxFunc },
- { "max", 0, 2, 0 },
- { "typeof", 1, 0, typeofFunc },
- { "classof", 1, 0, typeofFunc }, /* FIX ME: hack */
- { "length", 1, 0, lengthFunc },
- { "substr", 3, 0, substrFunc },
- { "abs", 1, 0, absFunc },
- { "round", 1, 0, roundFunc },
- { "round", 2, 0, roundFunc },
- { "upper", 1, 0, upperFunc },
- { "lower", 1, 0, lowerFunc },
- { "coalesce", -1, 0, ifnullFunc },
- { "coalesce", 0, 0, 0 },
- { "coalesce", 1, 0, 0 },
- { "ifnull", 2, 0, ifnullFunc },
- { "random", -1, 0, randomFunc },
- { "like", 2, 0, likeFunc },
- { "glob", 2, 0, globFunc },
- { "nullif", 2, 0, nullifFunc },
- { "sqlite_version", 0, 0, versionFunc},
- { "quote", 1, 0, quoteFunc },
- { "last_insert_rowid", 0, 1, last_insert_rowid },
- { "change_count", 0, 1, change_count },
- { "last_statement_change_count", 0, 1, last_statement_change_count },
+ { "min", -1, 0, 0, minmaxFunc },
+ { "min", 0, 0, 0, 0 },
+ { "max", -1, 2, 0, minmaxFunc },
+ { "max", 0, 2, 0, 0 },
+ { "typeof", 1, 0, 0, typeofFunc },
+ { "length", 1, 0, 0, lengthFunc },
+ { "substr", 3, 0, 0, substrFunc },
+ { "abs", 1, 0, 0, absFunc },
+ { "round", 1, 0, 0, roundFunc },
+ { "round", 2, 0, 0, roundFunc },
+ { "upper", 1, 0, 0, upperFunc },
+ { "lower", 1, 0, 0, lowerFunc },
+ { "coalesce", -1, 0, 0, ifnullFunc },
+ { "coalesce", 0, 0, 0, 0 },
+ { "coalesce", 1, 0, 0, 0 },
+ { "ifnull", 2, 0, 0, ifnullFunc },
+ { "random", -1, 0, 0, randomFunc },
+ { "like", 2, 0, 0, likeFunc }, /* UTF-8 */
+ { "like", 2, 2, 1, likeFunc }, /* UTF-16 */
+ { "glob", 2, 0, 0, globFunc },
+ { "nullif", 2, 0, 0, nullifFunc },
+ { "sqlite_version", 0, 0, 0, versionFunc},
+ { "quote", 1, 0, 0, quoteFunc },
+ { "last_insert_rowid", 0, 1, 0, last_insert_rowid },
+ { "change_count", 0, 1, 0, change_count },
+ { "last_statement_change_count", 0, 1, 0, last_statement_change_count },
#ifdef SQLITE_SOUNDEX
- { "soundex", 1, 0, soundexFunc},
+ { "soundex", 1, 0, 0, soundexFunc},
#endif
#ifdef SQLITE_TEST
- { "randstr", 2, 0, randStr },
+ { "randstr", 2, 0, 0, randStr },
#endif
};
static struct {
diff --git a/src/main.c b/src/main.c
index ce93f2eff..c0dada0a6 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.204 2004/06/04 06:22:01 danielk1977 Exp $
+** $Id: main.c,v 1.205 2004/06/06 09:44:04 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -656,7 +656,7 @@ int sqlite3_create_function(
return SQLITE_ERROR;
}
- p = sqlite3FindFunction(db, zFunctionName, nName, nArg, 1);
+ p = sqlite3FindFunction(db, zFunctionName, nName, nArg, eTextRep, 1);
if( p==0 ) return 1;
p->xFunc = xFunc;
p->xStep = xStep;
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index e710ada5d..0b6cd99dc 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.92 2004/06/05 10:22:18 danielk1977 Exp $
+** @(#) $Id: sqlite.h.in,v 1.93 2004/06/06 09:44:04 danielk1977 Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
@@ -835,6 +835,13 @@ int sqlite3_reset(sqlite3_stmt *pStmt);
** aggregate takes. If this parameter is negative, then the function or
** aggregate may take any number of arguments.
**
+** If the fourth parameter is non-zero, this indicates that the function is
+** more likely to handle text in UTF-16 encoding than UTF-8. This does not
+** change the behaviour of the programming interface. However, if two
+** versions of the same function are registered, one with eTextRep non-zero
+** and the other zero, SQLite invokes the version likely to minimize
+** conversions between unicode encodings.
+**
** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are
** pointers to user implemented C functions that implement the user
** function or aggregate. A scalar function requires an implementation of
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index aa95275db..8d9815f6c 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.268 2004/06/04 06:22:02 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.269 2004/06/06 09:44:05 danielk1977 Exp $
*/
#include "config.h"
#include "sqlite3.h"
@@ -1265,7 +1265,6 @@ void sqlite3UnlinkAndDeleteIndex(sqlite*,Index*);
void sqlite3Vacuum(Parse*, Token*);
int sqlite3RunVacuum(char**, sqlite*);
int sqlite3GlobCompare(const unsigned char*,const unsigned char*);
-int sqlite3LikeCompare(const unsigned char*,const unsigned char*);
char *sqlite3TableNameFromToken(Token*);
int sqlite3ExprCheck(Parse*, Expr*, int, int*);
int sqlite3ExprType(Expr*);
@@ -1297,7 +1296,7 @@ ExprList *sqlite3ExprListDup(ExprList*);
SrcList *sqlite3SrcListDup(SrcList*);
IdList *sqlite3IdListDup(IdList*);
Select *sqlite3SelectDup(Select*);
-FuncDef *sqlite3FindFunction(sqlite*,const char*,int,int,int);
+FuncDef *sqlite3FindFunction(sqlite*,const char*,int,int,int,int);
void sqlite3RegisterBuiltinFunctions(sqlite*);
void sqlite3RegisterDateTimeFunctions(sqlite*);
int sqlite3SafetyOn(sqlite*);
@@ -1373,3 +1372,4 @@ u8 sqlite3UtfReadBom(const void *zData, int nData);
void *sqlite3HexToBlob(const char *z);
int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
const char *sqlite3ErrStr(int);
+int sqlite3ReadUniChar(const char *zStr, int *pOffset, u8 *pEnc, int fold);
diff --git a/src/utf.c b/src/utf.c
index 65dd05e4a..4da418b6a 100644
--- a/src/utf.c
+++ b/src/utf.c
@@ -12,7 +12,7 @@
** This file contains routines used to translate between UTF-8,
** UTF-16, UTF-16BE, and UTF-16LE.
**
-** $Id: utf.c,v 1.16 2004/06/02 00:29:24 danielk1977 Exp $
+** $Id: utf.c,v 1.17 2004/06/06 09:44:05 danielk1977 Exp $
**
** Notes on UTF-8:
**
@@ -75,6 +75,138 @@ struct UtfString {
#define READ_16(pZ,big_endian) (big_endian?BE16(pZ):LE16(pZ))
/*
+** The following macro, LOWERCASE(x), takes an integer representing a
+** unicode code point. The value returned is the same code point folded to
+** lower case, if applicable. SQLite currently understands the upper/lower
+** case relationship between the 26 characters used in the English
+** language only.
+**
+** This means that characters with umlauts etc. will not be folded
+** correctly (unless they are encoded as composite characters, which would
+** doubtless cause much trouble).
+*/
+#define LOWERCASE(x) (x<91?(int)(UpperToLower[x]):x);
+static unsigned char UpperToLower[91] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
+ 122,
+};
+
+/*
+** The first parameter, zStr, points at a unicode string. This routine
+** reads a single character from the string and returns the codepoint value
+** of the character read.
+**
+** The value of *pEnc is the string encoding. If *pEnc is TEXT_Utf16le or
+** TEXT_Utf16be, and the first character read is a byte-order-mark, then
+** the value of *pEnc is modified if necessary. In this case the next
+** character is read and it's code-point value returned.
+**
+** The value of *pOffset is the byte-offset in zStr from which to begin
+** reading. It is incremented by the number of bytes read by this function.
+**
+** If the fourth parameter, fold, is non-zero, then codepoint values are
+** folded to lower-case before being returned. See comments for macro
+** LOWERCASE(x) for details.
+*/
+int sqlite3ReadUniChar(const char *zStr, int *pOffset, u8 *pEnc, int fold){
+ int ret = 0;
+
+ switch( *pEnc ){
+ case TEXT_Utf8: {
+ struct Utf8TblRow {
+ u8 b1_mask;
+ u8 b1_masked_val;
+ u8 b1_value_mask;
+ int trailing_bytes;
+ };
+ static const struct Utf8TblRow utf8tbl[] = {
+ { 0x80, 0x00, 0x7F, 0 },
+ { 0xE0, 0xC0, 0x1F, 1 },
+ { 0xF0, 0xE0, 0x0F, 2 },
+ { 0xF8, 0xF0, 0x0E, 3 },
+ { 0, 0, 0, 0}
+ };
+
+ u8 b1; /* First byte of the potentially multi-byte utf-8 character */
+ int ii;
+ struct Utf8TblRow const *pRow;
+
+ pRow = &(utf8tbl[0]);
+
+ b1 = zStr[(*pOffset)++];
+ while( pRow->b1_mask && (b1&pRow->b1_mask)!=pRow->b1_masked_val ){
+ pRow++;
+ }
+ if( !pRow->b1_mask ){
+ return (int)0xFFFD;
+ }
+
+ ret = (u32)(b1&pRow->b1_value_mask);
+ for( ii=0; ii<pRow->trailing_bytes; ii++ ){
+ u8 b = zStr[(*pOffset)++];
+ if( (b&0xC0)!=0x80 ){
+ return (int)0xFFFD;
+ }
+ ret = (ret<<6) + (u32)(b&0x3F);
+ }
+
+ break;
+ }
+
+ case TEXT_Utf16le:
+ case TEXT_Utf16be: {
+ u32 code_point; /* the first code-point in the character */
+ u32 code_point2; /* the second code-point in the character, if any */
+
+ code_point = READ_16(&zStr[*pOffset], (*pEnc==TEXT_Utf16be));
+ *pOffset += 2;
+
+ /* If this is a non-surrogate code-point, just cast it to an int and
+ ** this is the code-point value.
+ */
+ if( code_point<0xD800 || code_point>0xE000 ){
+ ret = code_point;
+ break;
+ }
+
+ /* If this is a trailing surrogate code-point, then the string is
+ ** malformed; return the replacement character.
+ */
+ if( code_point>0xDBFF ){
+ return (int)0xFFFD;
+ }
+
+ /* The code-point just read is a leading surrogate code-point. If their
+ ** is not enough data left or the next code-point is not a trailing
+ ** surrogate, return the replacement character.
+ */
+ code_point2 = READ_16(&zStr[*pOffset], (*pEnc==TEXT_Utf16be));
+ *pOffset += 2;
+ if( code_point2<0xDC00 || code_point>0xDFFF ){
+ return (int)0xFFFD;
+ }
+
+ ret = (
+ (((code_point&0x03C0)+0x0040)<<16) + /* uuuuu */
+ ((code_point&0x003F)<<10) + /* xxxxxx */
+ (code_point2&0x03FF) /* yy yyyyyyyy */
+ );
+ }
+ default:
+ assert(0);
+ }
+
+ if( fold ){
+ return LOWERCASE(ret);
+ }
+ return ret;
+}
+
+/*
** Read the BOM from the start of *pStr, if one is present. Return zero
** for little-endian, non-zero for big-endian. If no BOM is present, return
** the value of the parameter "big_endian".
@@ -133,47 +265,8 @@ u8 sqlite3UtfReadBom(const void *zData, int nData){
** strings, the unicode replacement character U+FFFD may be returned.
*/
static u32 readUtf8(UtfString *pStr){
- struct Utf8TblRow {
- u8 b1_mask;
- u8 b1_masked_val;
- u8 b1_value_mask;
- int trailing_bytes;
- };
- static const struct Utf8TblRow utf8tbl[] = {
- { 0x80, 0x00, 0x7F, 0 },
- { 0xE0, 0xC0, 0x1F, 1 },
- { 0xF0, 0xE0, 0x0F, 2 },
- { 0xF8, 0xF0, 0x0E, 3 },
- { 0, 0, 0, 0}
- };
-
- u8 b1; /* First byte of the potentially multi-byte utf-8 character */
- u32 ret = 0; /* Return value */
- int ii;
- struct Utf8TblRow const *pRow;
-
- pRow = &(utf8tbl[0]);
-
- b1 = pStr->pZ[pStr->c];
- pStr->c++;
- while( pRow->b1_mask && (b1&pRow->b1_mask)!=pRow->b1_masked_val ){
- pRow++;
- }
- if( !pRow->b1_mask ){
- return 0xFFFD;
- }
-
- ret = (u32)(b1&pRow->b1_value_mask);
- for( ii=0; ii<pRow->trailing_bytes; ii++ ){
- u8 b = pStr->pZ[pStr->c+ii];
- if( (b&0xC0)!=0x80 ){
- return 0xFFFD;
- }
- ret = (ret<<6) + (u32)(b&0x3F);
- }
-
- pStr->c += pRow->trailing_bytes;
- return ret;
+ u8 enc = TEXT_Utf8;
+ return sqlite3ReadUniChar(pStr->pZ, &pStr->c, &enc, 0);
}
/*
diff --git a/src/util.c b/src/util.c
index 00f20f6dc..637782d38 100644
--- a/src/util.c
+++ b/src/util.c
@@ -14,7 +14,7 @@
** This file contains functions for allocating memory, comparing
** strings, and stuff like that.
**
-** $Id: util.c,v 1.96 2004/06/02 00:41:10 drh Exp $
+** $Id: util.c,v 1.97 2004/06/06 09:44:05 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <stdarg.h>
@@ -1056,57 +1056,6 @@ sqlite3GlobCompare(const unsigned char *zPattern, const unsigned char *zString){
}
/*
-** Compare two UTF-8 strings for equality using the "LIKE" operator of
-** SQL. The '%' character matches any sequence of 0 or more
-** characters and '_' matches any single character. Case is
-** not significant.
-**
-** This routine is just an adaptation of the sqlite3GlobCompare()
-** routine above.
-*/
-int
-sqlite3LikeCompare(const unsigned char *zPattern, const unsigned char *zString){
- register int c;
- int c2;
-
- while( (c = UpperToLower[*zPattern])!=0 ){
- switch( c ){
- case '%': {
- while( (c=zPattern[1]) == '%' || c == '_' ){
- if( c=='_' ){
- if( *zString==0 ) return 0;
- sqliteNextChar(zString);
- }
- zPattern++;
- }
- if( c==0 ) return 1;
- c = UpperToLower[c];
- while( (c2=UpperToLower[*zString])!=0 ){
- while( c2 != 0 && c2 != c ){ c2 = UpperToLower[*++zString]; }
- if( c2==0 ) return 0;
- if( sqlite3LikeCompare(&zPattern[1],zString) ) return 1;
- sqliteNextChar(zString);
- }
- return 0;
- }
- case '_': {
- if( *zString==0 ) return 0;
- sqliteNextChar(zString);
- zPattern++;
- break;
- }
- default: {
- if( c != UpperToLower[*zString] ) return 0;
- zPattern++;
- zString++;
- break;
- }
- }
- }
- return *zString==0;
-}
-
-/*
** Change the sqlite.magic from SQLITE_MAGIC_OPEN to SQLITE_MAGIC_BUSY.
** Return an error (non-zero) if the magic was not SQLITE_MAGIC_OPEN
** when this routine is called.
diff --git a/src/vdbe.c b/src/vdbe.c
index 29ac27cff..848d7cccf 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -43,7 +43,7 @@
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
-** $Id: vdbe.c,v 1.358 2004/06/05 10:22:18 danielk1977 Exp $
+** $Id: vdbe.c,v 1.359 2004/06/06 09:44:05 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -1273,7 +1273,7 @@ case OP_Function: {
*/
if( ctx.pVdbeFunc ){
int mask = pOp->p2;
- for(i=0; i<n; i++){
+ for(i=0; i<ctx.pVdbeFunc->nAux; i++){
struct AuxData *pAux = &ctx.pVdbeFunc->apAux[i];
if( (i>31 || !(mask&(1<<i))) && pAux->pAux ){
pAux->xDelete(pAux->pAux);
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 1e40fad75..ac7d976bb 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -248,6 +248,7 @@ void sqlite3_set_auxdata(
pCtx->pVdbeFunc = sqliteRealloc(pCtx->pVdbeFunc, nMalloc);
if( !pCtx->pVdbeFunc ) return;
pCtx->pVdbeFunc->nAux = iArg+1;
+ pCtx->pVdbeFunc->pFunc = pCtx->pFunc;
}
pAuxData = &pCtx->pVdbeFunc->apAux[iArg];
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index d3a982ad6..e17a1cbcc 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1235,9 +1235,10 @@ void sqlite3VdbeDelete(Vdbe *p){
sqliteFree(pOp->p3);
}
if( pOp->p3type==P3_VDBEFUNC ){
+ int j;
VdbeFunc *pVdbeFunc = (VdbeFunc *)pOp->p3;
- for(i=0; i<pVdbeFunc->nAux; i++){
- struct AuxData *pAuxData = &pVdbeFunc->apAux[i].pAux;
+ for(j=0; j<pVdbeFunc->nAux; j++){
+ struct AuxData *pAuxData = &pVdbeFunc->apAux[j].pAux;
if( pAuxData->pAux && pAuxData->xDelete ){
pAuxData->xDelete(pAuxData->pAux);
}