aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2004-09-07 16:19:52 +0000
committerdrh <drh@noemail.net>2004-09-07 16:19:52 +0000
commitfa6bc0000fc5b52903cbcb36bacc78833d320e4e (patch)
tree0affec0b5d7fbaecde46d403133d36ce45dbc300 /src
parent1807ce37b84a2a41638f01e59c501a6800bb7883 (diff)
downloadsqlite-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.c71
-rw-r--r--src/parse.y6
-rw-r--r--src/sqlite.h.in11
-rw-r--r--src/sqliteInt.h11
-rw-r--r--src/test1.c30
-rw-r--r--src/tokenize.c24
-rw-r--r--src/vdbeapi.c47
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;
+}