diff options
author | drh <drh@noemail.net> | 2004-09-07 16:19:52 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2004-09-07 16:19:52 +0000 |
commit | fa6bc0000fc5b52903cbcb36bacc78833d320e4e (patch) | |
tree | 0affec0b5d7fbaecde46d403133d36ce45dbc300 /src | |
parent | 1807ce37b84a2a41638f01e59c501a6800bb7883 (diff) | |
download | sqlite-fa6bc0000fc5b52903cbcb36bacc78833d320e4e.tar.gz sqlite-fa6bc0000fc5b52903cbcb36bacc78833d320e4e.zip |
Wildcards with the same name map into the same variable number. New
api sqlite3_bind_parameter_index() added to map wildcard names into
wildcard index numbers. Support for "?nnn" wildcards. (CVS 1945)
FossilOrigin-Name: 435b3f301fbb6953adc974c7f03589b06e9114c3
Diffstat (limited to 'src')
-rw-r--r-- | src/expr.c | 71 | ||||
-rw-r--r-- | src/parse.y | 6 | ||||
-rw-r--r-- | src/sqlite.h.in | 11 | ||||
-rw-r--r-- | src/sqliteInt.h | 11 | ||||
-rw-r--r-- | src/test1.c | 30 | ||||
-rw-r--r-- | src/tokenize.c | 24 | ||||
-rw-r--r-- | src/vdbeapi.c | 47 |
7 files changed, 171 insertions, 29 deletions
diff --git a/src/expr.c b/src/expr.c index b72474a61..86cb54ad3 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.160 2004/09/06 17:24:13 drh Exp $ +** $Id: expr.c,v 1.161 2004/09/07 16:19:53 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -274,6 +274,75 @@ Expr *sqlite3ExprFunction(ExprList *pList, Token *pToken){ } /* +** Assign a variable number to an expression that encodes a wildcard +** in the original SQL statement. +** +** Wildcards consisting of a single "?" are assigned the next sequential +** variable number. +** +** Wildcards of the form "?nnn" are assigned the number "nnn". We make +** sure "nnn" is not too be to avoid a denial of service attack when +** the SQL statement comes from an external source. +** +** Wildcards of the form ":aaa" or "$aaa" are assigned the same number +** as the previous instance of the same wildcard. Or if this is the first +** instance of the wildcard, the next sequenial variable number is +** assigned. +*/ +void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ + Token *pToken; + if( pExpr==0 ) return; + pToken = &pExpr->token; + assert( pToken->n>=1 ); + assert( pToken->z!=0 ); + assert( pToken->z[0]!=0 ); + if( pToken->n==1 ){ + /* Wildcard of the form "?". Assign the next variable number */ + pExpr->iTable = ++pParse->nVar; + }else if( pToken->z[0]=='?' ){ + /* Wildcard of the form "?nnn". Convert "nnn" to an integer and + ** use it as the variable number */ + int i; + pExpr->iTable = i = atoi(&pToken->z[1]); + if( i<1 || i>SQLITE_MAX_VARIABLE_NUMBER ){ + sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", + SQLITE_MAX_VARIABLE_NUMBER); + } + if( i>pParse->nVar ){ + pParse->nVar = i; + } + }else{ + /* Wildcards of the form ":aaa" or "$aaa". Reuse the same variable + ** number as the prior appearance of the same name, or if the name + ** has never appeared before, reuse the same variable number + */ + int i, n; + n = pToken->n; + for(i=0; i<pParse->nVarExpr; i++){ + Expr *pE; + if( (pE = pParse->apVarExpr[i])!=0 + && pE->token.n==n + && memcmp(pE->token.z, pToken->z, n)==0 ){ + pExpr->iTable = pE->iTable; + break; + } + } + if( i>=pParse->nVarExpr ){ + pExpr->iTable = ++pParse->nVar; + if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){ + pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10; + pParse->apVarExpr = sqliteRealloc(pParse->apVarExpr, + pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0]) ); + } + if( !sqlite3_malloc_failed ){ + assert( pParse->apVarExpr!=0 ); + pParse->apVarExpr[pParse->nVarExpr++] = pExpr; + } + } + } +} + +/* ** Recursively delete an expression tree. */ void sqlite3ExprDelete(Expr *p){ diff --git a/src/parse.y b/src/parse.y index a22f9e5d1..37a864aac 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.135 2004/08/25 04:07:02 drh Exp $ +** @(#) $Id: parse.y,v 1.136 2004/09/07 16:19:54 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -560,9 +560,7 @@ expr(A) ::= BLOB(X). {A = sqlite3Expr(@X, 0, 0, &X);} expr(A) ::= VARIABLE(X). { Token *pToken = &X; Expr *pExpr = A = sqlite3Expr(TK_VARIABLE, 0, 0, pToken); - if( pExpr ){ - pExpr->iTable = ++pParse->nVar; - } + sqlite3ExprAssignVarNumber(pParse, pExpr); } expr(A) ::= ID(X) LP exprlist(Y) RP(E). { A = sqlite3ExprFunction(Y, &X); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index ae4ce1978..218a91cd4 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.118 2004/09/06 17:34:13 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.119 2004/09/07 16:19:54 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -640,13 +640,20 @@ int sqlite3_bind_parameter_count(sqlite3_stmt*); /* ** Return the name of the i-th parameter. Ordinary wildcards "?" are -** nameless and a NULL is returned. For wildcards of the form :N: or +** nameless and a NULL is returned. For wildcards of the form :N or ** $vvvv the complete text of the wildcard is returned. ** NULL is returned if the index is out of range. */ const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* +** Return the index of a parameter with the given name. The name +** must match exactly. If no parameter with the given name is found, +** return 0. +*/ +int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); + +/* ** Return the number of columns in the result set returned by the compiled ** SQL statement. This routine returns 0 if pStmt is an SQL statement ** that does not return data (for example an UPDATE). diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 28f9f36c1..0fd1029fa 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.319 2004/09/06 17:24:13 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.320 2004/09/07 16:19:54 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -68,6 +68,11 @@ #define MAX_ATTACHED 10 /* +** The maximum value of a ?nnn wildcard that the parser will accept. +*/ +#define SQLITE_MAX_VARIABLE_NUMBER 999 + +/* ** When building SQLite for embedded systems where memory is scarce, ** you can define one or more of the following macros to omit extra ** features of the library and thus keep the size of the library to @@ -990,6 +995,9 @@ struct Parse { int nSet; /* Number of sets used so far */ int nAgg; /* Number of aggregate expressions */ int nVar; /* Number of '?' variables seen in the SQL so far */ + int nVarExpr; /* Number of used slots in apVarExpr[] */ + int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ + Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ AggExpr *aAgg; /* An array of aggregate expressions */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ @@ -1209,6 +1217,7 @@ Expr *sqlite3Expr(int, Expr*, Expr*, Token*); Expr *sqlite3ExprAnd(Expr*, Expr*); void sqlite3ExprSpan(Expr*,Token*,Token*); Expr *sqlite3ExprFunction(ExprList*, Token*); +void sqlite3ExprAssignVarNumber(Parse*, Expr*); void sqlite3ExprDelete(Expr*); ExprList *sqlite3ExprListAppend(ExprList*,Expr*,Token*); void sqlite3ExprListDelete(ExprList*); diff --git a/src/test1.c b/src/test1.c index cbd3e7372..a8a8b17e0 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.101 2004/09/06 17:24:13 drh Exp $ +** $Id: test1.c,v 1.102 2004/09/07 16:19:54 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -1646,6 +1646,33 @@ static int test_bind_parameter_name( } /* +** Usage: sqlite3_bind_parameter_index STMT NAME +** +** Return the index of the wildcard called NAME. Return 0 if there is +** no such wildcard. +*/ +static int test_bind_parameter_index( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + sqlite3_stmt *pStmt; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "STMT NAME"); + return TCL_ERROR; + } + if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; + Tcl_SetObjResult(interp, + Tcl_NewIntObj( + sqlite3_bind_parameter_index(pStmt,Tcl_GetString(objv[2])) + ) + ); + return TCL_OK; +} + +/* ** Usage: sqlite3_errcode DB ** ** Return the string representation of the most recent sqlite3_* API @@ -2463,6 +2490,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "sqlite3_bind_blob", test_bind_blob ,0 }, { "sqlite3_bind_parameter_count", test_bind_parameter_count, 0}, { "sqlite3_bind_parameter_name", test_bind_parameter_name, 0}, + { "sqlite3_bind_parameter_index", test_bind_parameter_index, 0}, { "sqlite3_errcode", test_errcode ,0 }, { "sqlite3_errmsg", test_errmsg ,0 }, { "sqlite3_errmsg16", test_errmsg16 ,0 }, diff --git a/src/tokenize.c b/src/tokenize.c index f3bda6014..7601cf34e 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -15,7 +15,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.85 2004/09/06 17:24:13 drh Exp $ +** $Id: tokenize.c,v 1.86 2004/09/07 16:19:54 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -371,7 +371,8 @@ static int sqliteGetToken(const unsigned char *z, int *tokenType){ } case '?': { *tokenType = TK_VARIABLE; - return 1; + for(i=1; isdigit(z[i]); i++){} + return i; } case ':': { for(i=1; (z[i]&0x80)!=0 || isIdChar[z[i]]; i++){} @@ -474,7 +475,13 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){ sqlite3SetString(pzErrMsg, "out of memory", (char*)0); return 1; } - pParse->sLastToken.dyn = 0; + assert( pParse->sLastToken.dyn==0 ); + assert( pParse->pNewTable==0 ); + assert( pParse->pNewTrigger==0 ); + assert( pParse->nVar==0 ); + assert( pParse->nVarExpr==0 ); + assert( pParse->nVarExprAlloc==0 ); + assert( pParse->apVarExpr==0 ); pParse->zTail = pParse->zSql = zSql; while( sqlite3_malloc_failed==0 && zSql[i]!=0 ){ assert( i>=0 ); @@ -541,14 +548,9 @@ abort_parse: sqlite3VdbeDelete(pParse->pVdbe); pParse->pVdbe = 0; } - if( pParse->pNewTable ){ - sqlite3DeleteTable(pParse->db, pParse->pNewTable); - pParse->pNewTable = 0; - } - if( pParse->pNewTrigger ){ - sqlite3DeleteTrigger(pParse->pNewTrigger); - pParse->pNewTrigger = 0; - } + sqlite3DeleteTable(pParse->db, pParse->pNewTable); + sqlite3DeleteTrigger(pParse->pNewTrigger); + sqliteFree(pParse->apVarExpr); if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){ pParse->rc = SQLITE_ERROR; } diff --git a/src/vdbeapi.c b/src/vdbeapi.c index b238c7fe7..f5abe0ee6 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -525,16 +525,11 @@ int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){ } /* -** Return the name of a wildcard parameter. Return NULL if the index -** is out of range or if the wildcard is unnamed. -** -** The result is always UTF-8. +** Create a mapping from variable numbers to variable names +** in the Vdbe.azVar[] array, if such a mapping does not already +** exist. */ -const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ - Vdbe *p = (Vdbe*)pStmt; - if( p==0 || i<1 || i>p->nVar ){ - return 0; - } +static void createVarMap(Vdbe *p){ if( !p->okVar ){ int j; Op *pOp; @@ -546,5 +541,39 @@ const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ } p->okVar = 1; } +} + +/* +** Return the name of a wildcard parameter. Return NULL if the index +** is out of range or if the wildcard is unnamed. +** +** The result is always UTF-8. +*/ +const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){ + Vdbe *p = (Vdbe*)pStmt; + if( p==0 || i<1 || i>p->nVar ){ + return 0; + } + createVarMap(p); return p->azVar[i-1]; } + +/* +** Given a wildcard parameter name, return the index of the variable +** with that name. If there is no variable with the given name, +** return 0. +*/ +int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ + Vdbe *p = (Vdbe*)pStmt; + int i; + if( p==0 ){ + return 0; + } + createVarMap(p); + for(i=0; i<p->nVar; i++){ + if( strcmp(p->azVar[i],zName)==0 ){ + return i+1; + } + } + return 0; +} |