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
diff --git a/src/expr.c b/src/expr.c
index b72474a..86cb54a 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 @@
 }
 
 /*
+** 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 a22f9e5..37a864a 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) ::= 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 ae4ce19..218a91c 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 @@
 
 /*
 ** 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 28f9f36..0fd1029 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 @@
   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 *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 cbd3e73..a8a8b17 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 @@
 }
 
 /*
+** 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 @@
      { "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 f3bda60..7601cf3 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 @@
     }
     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 @@
     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 @@
     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 b238c7f..f5abe0e 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -525,16 +525,11 @@
 }
 
 /*
-** 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 @@
     }
     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;
+}