Use the VList object to capture the mapping between SQL parameter names and

variable numbers in a single memory allocation.

FossilOrigin-Name: 99fddf2ecb7202bfdf5ddc4cc1c4e155c883f7ec
This commit is contained in:
drh 2016-12-23 13:54:22 +00:00
commit cfdc235ec3
10 changed files with 169 additions and 81 deletions

View File

@ -1,5 +1,5 @@
C Fix\sharmless\scompiler\swarnings.
D 2016-12-22T14:53:25.934
C Use\sthe\sVList\sobject\sto\scapture\sthe\smapping\sbetween\sSQL\sparameter\snames\sand\nvariable\snumbers\sin\sa\ssingle\smemory\sallocation.
D 2016-12-23T13:54:22.985
F Makefile.in 41bd4cad981487345c4a84081074bcdb876e4b2e
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
@ -341,7 +341,7 @@ F src/ctime.c 9f2296a4e5d26ebf0e0d95a0af4628f1ea694e7a
F src/date.c b48378aeac68fa20c811404955a9b62108df47d8
F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
F src/delete.c c8bc10d145c9666a34ae906250326fdaa8d58fa5
F src/expr.c c7c11fbe8bd4d27d04f1370efcce5a05b4fca229
F src/expr.c a90e37bc542abe33890cafccacbf8a7db9cb5401
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 2e9aabe1aee76273aff8a84ee92c464e095400ae
F src/func.c 43916c1d8e6da5d107d91d2b212577d4f69a876a
@ -393,7 +393,7 @@ F src/shell.c bf79a69d1f9b53de958498752b78e9906c5d0aff
F src/sqlite.h.in e8e2d108d82647f0a812fdb74accf91c1ec08ddc
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
F src/sqliteInt.h adbe80409c2926d91e5485789e5bf69313cd54b0
F src/sqliteInt.h 2075e22d50833ca2d9956d0b7a6bfb845ad05dd2
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
@ -448,18 +448,18 @@ F src/test_windirent.c 600398db0198ca1c77ca183831bf456746b6f5c4
F src/test_windirent.h 7edc57e2faa727026dbd5d010dd0e2e665d5aa01
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 6eb0752c4fd191f789190c3708ee5b0060fd2829
F src/tokenize.c 5c2f516876fc27fbd7753913f032f49eb89e83b5
F src/treeview.c 4e44ade3bfe59d82005039f72e09333ce2b4162c
F src/trigger.c c9f0810043b265724fdb1bdd466894f984dfc182
F src/update.c 1da7c462110bffed442a42884cb0d528c1db46d8
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
F src/util.c e68e8ced7328f22d2cf7b4c898c394a0de34cdf1
F src/util.c 1534060bc034cdc51381c040c8bd6252dbcb64c9
F src/vacuum.c 33c174b28886b2faf26e503b5a49a1c01a9b1c16
F src/vdbe.c 0106e0d4f0d39aced35c150a6919d2878df6b1e0
F src/vdbe.c 54b12d95dbf10533ab2584acbf31ae12b8bfe171
F src/vdbe.h 50ee139f9c68fff91be1d717ed3a6abbd496919c
F src/vdbeInt.h 42e498dbe96475dbb3fda3d85d8fd2a87eff60a2
F src/vdbeapi.c 129a2a1103e29fe839e137c641ff42b8066667b5
F src/vdbeaux.c 7f67112cc57b2fa37b777fbf03eb32c96b337cfe
F src/vdbeInt.h 281cb70332dc8b593b8c7afe776f3a2ba7d4255e
F src/vdbeapi.c d6ebaa465f070eb1af8ba4e7b34583ece87bdd24
F src/vdbeaux.c 12919c5aa354296d6b9ba808bca3e9dda836858c
F src/vdbeblob.c f4f98ea672b242f807c08c92c7faaa79e5091b65
F src/vdbemem.c d3fd85b7b7ef3eb75de29c6d7e1d10d3ca78b4fd
F src/vdbesort.c eda25cb2d1727efca6f7862fea32b8aa33c0face
@ -1539,7 +1539,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 52a12e47de88744187bd1c71f2820885cde414a8
R d04805544fe17af2729028773b1348a0
P fa86db2f915c0a189c3be02b1aaa7f24d339d7d6 aa23d7eaf69f5ecbf9500b2353846094cae41e6c
R 2f816100f2d74d42d3a1978c26f09af2
T +closed aa23d7eaf69f5ecbf9500b2353846094cae41e6c
U drh
Z 33ba125ec9059ffd904b774699d11e2c
Z bdddf7c19647dc703ac75fbc8ecd14da

View File

@ -1 +1 @@
fa86db2f915c0a189c3be02b1aaa7f24d339d7d6
99fddf2ecb7202bfdf5ddc4cc1c4e155c883f7ec

View File

@ -934,7 +934,7 @@ Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){
** 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
** sure "nnn" is not too big to avoid a denial of service attack when
** the SQL statement comes from an external source.
**
** Wildcards of the form ":aaa", "@aaa", or "$aaa" are assigned the same number
@ -945,6 +945,7 @@ Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){
void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){
sqlite3 *db = pParse->db;
const char *z;
ynVar x;
if( pExpr==0 ) return;
assert( !ExprHasProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) );
@ -955,9 +956,9 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){
if( z[1]==0 ){
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
x = (ynVar)(++pParse->nVar);
}else{
ynVar x;
int doAdd = 0;
if( z[0]=='?' ){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
@ -973,40 +974,29 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
return;
}
if( i>pParse->nVar ){
pParse->nVar = (int)i;
if( x>pParse->nVar ){
pParse->nVar = (int)x;
doAdd = 1;
}else if( sqlite3VListNumToName(pParse->pVList, x)==0 ){
doAdd = 1;
}
}else{
/* Wildcards like ":aaa", "$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
*/
ynVar i;
for(i=x=0; i<pParse->nzVar; i++){
if( pParse->azVar[i] && strcmp(pParse->azVar[i],z)==0 ){
x = (ynVar)i+1;
break;
}
x = (ynVar)sqlite3VListNameToNum(pParse->pVList, z, n);
if( x==0 ){
x = (ynVar)(++pParse->nVar);
doAdd = 1;
}
if( x==0 ) x = (ynVar)(++pParse->nVar);
}
pExpr->iColumn = x;
if( x>pParse->nzVar ){
char **a;
a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
if( a==0 ){
assert( db->mallocFailed ); /* Error reported through mallocFailed */
return;
}
pParse->azVar = a;
memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
pParse->nzVar = x;
if( doAdd ){
pParse->pVList = sqlite3VListAdd(db, pParse->pVList, z, n, x);
}
if( pParse->azVar[x-1]==0 ){
pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n);
}
}
if( pParse->nVar>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
}
pExpr->iColumn = x;
if( x>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "too many SQL variables");
}
}
@ -3430,9 +3420,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
assert( pExpr->u.zToken[0]!=0 );
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target);
if( pExpr->u.zToken[1]!=0 ){
assert( pExpr->u.zToken[0]=='?'
|| strcmp(pExpr->u.zToken, pParse->azVar[pExpr->iColumn-1])==0 );
sqlite3VdbeAppendP4(v, pParse->azVar[pExpr->iColumn-1], P4_STATIC);
const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn);
assert( pExpr->u.zToken[0]=='?' || strcmp(pExpr->u.zToken, z)==0 );
pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */
sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC);
}
return target;
}

View File

@ -1046,6 +1046,14 @@ typedef struct Walker Walker;
typedef struct WhereInfo WhereInfo;
typedef struct With With;
/* A VList object records a mapping between parameters/variables/wildcards
** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer
** variable number associated with that parameter. See the format description
** on the sqlite3VListAdd() routine for more information. A VList is really
** just an array of integers.
*/
typedef int VList;
/*
** Defer sourcing vdbe.h and btree.h until after the "u8" and
** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
@ -2952,7 +2960,6 @@ struct Parse {
Token sLastToken; /* The last token parsed */
ynVar nVar; /* Number of '?' variables seen in the SQL so far */
int nzVar; /* Number of available slots in azVar[] */
u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
u8 explain; /* True if the EXPLAIN flag is found on the query */
#ifndef SQLITE_OMIT_VIRTUALTABLE
@ -2964,7 +2971,7 @@ struct Parse {
int iSelectId; /* ID of current select for EXPLAIN output */
int iNextSelectId; /* Next available select ID for EXPLAIN output */
#endif
char **azVar; /* Pointers to names of parameters */
VList *pVList; /* Mapping between variable names and numbers */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
const char *zTail; /* All SQL text past the last semicolon parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */
@ -3864,6 +3871,9 @@ LogEst sqlite3LogEstFromDouble(double);
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
u64 sqlite3LogEstToInt(LogEst);
#endif
VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int);
const char *sqlite3VListNumToName(VList*,int);
int sqlite3VListNameToNum(VList*,const char*,int);
/*
** Routines to read and write variable-length integers. These used to

View File

@ -500,8 +500,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
assert( pParse->pNewTable==0 );
assert( pParse->pNewTrigger==0 );
assert( pParse->nVar==0 );
assert( pParse->nzVar==0 );
assert( pParse->azVar==0 );
assert( pParse->pVList==0 );
while( 1 ){
assert( i>=0 );
if( zSql[i]!=0 ){
@ -588,8 +587,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
sqlite3DbFree(db, pParse->azVar);
sqlite3DbFree(db, pParse->pVList);
while( pParse->pAinc ){
AutoincInfo *p = pParse->pAinc;
pParse->pAinc = p->pNext;

View File

@ -1453,3 +1453,106 @@ u64 sqlite3LogEstToInt(LogEst x){
return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x);
}
#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */
/*
** Add a new name/number pair to a VList. This might require that the
** VList object be reallocated, so return the new VList. If an OOM
** error occurs, the original VList returned and the
** db->mallocFailed flag is set.
**
** A VList is really just an array of integers. To destroy a VList,
** simply pass it to sqlite3DbFree().
**
** The first integer is the number of integers allocated for the whole
** VList. The second integer is the number of integers actually used.
** Each name/number pair is encoded by subsequent groups of 3 or more
** integers.
**
** Each name/number pair starts with two integers which are the numeric
** value for the pair and the size of the name/number pair, respectively.
** The text name overlays one or more following integers. The text name
** is always zero-terminated.
**
** Conceptually:
**
** struct VList {
** int nAlloc; // Number of allocated slots
** int nUsed; // Number of used slots
** struct VListEntry {
** int iValue; // Value for this entry
** int nSlot; // Slots used by this entry
** // ... variable name goes here
** } a[0];
** }
**
** During code generation, pointers to the variable names within the
** VList are taken. When that happens, nAlloc is set to zero as an
** indication that the VList may never again be enlarged, since the
** accompanying realloc() would invalidate the pointers.
*/
VList *sqlite3VListAdd(
sqlite3 *db, /* The database connection used for malloc() */
VList *pIn, /* The input VList. Might be NULL */
const char *zName, /* Name of symbol to add */
int nName, /* Bytes of text in zName */
int iVal /* Value to associate with zName */
){
int nInt; /* number of sizeof(int) objects needed for zName */
char *z; /* Pointer to where zName will be stored */
int i; /* Index in pIn[] where zName is stored */
nInt = nName/4 + 3;
assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */
if( pIn==0 || pIn[1]+nInt > pIn[0] ){
/* Enlarge the allocation */
int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt;
VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int));
if( pOut==0 ) return pIn;
if( pIn==0 ) pOut[1] = 2;
pIn = pOut;
pIn[0] = nAlloc;
}
i = pIn[1];
pIn[i] = iVal;
pIn[i+1] = nInt;
z = (char*)&pIn[i+2];
pIn[1] = i+nInt;
assert( pIn[1]<=pIn[0] );
memcpy(z, zName, nName);
z[nName] = 0;
return pIn;
}
/*
** Return a pointer to the name of a variable in the given VList that
** has the value iVal. Or return a NULL if there is no such variable in
** the list
*/
const char *sqlite3VListNumToName(VList *pIn, int iVal){
int i, mx;
if( pIn==0 ) return 0;
mx = pIn[1];
i = 2;
do{
if( pIn[i]==iVal ) return (char*)&pIn[i+2];
i += pIn[i+1];
}while( i<mx );
return 0;
}
/*
** Return the number of the variable named zName, if it is in VList.
** or return 0 if there is no such variable.
*/
int sqlite3VListNameToNum(VList *pIn, const char *zName, int nName){
int i, mx;
if( pIn==0 ) return 0;
mx = pIn[1];
i = 2;
do{
const char *z = (const char*)&pIn[i+2];
if( strncmp(z,zName,nName)==0 && z[nName]==0 ) return pIn[i];
i += pIn[i+1];
}while( i<mx );
return 0;
}

View File

@ -1190,7 +1190,7 @@ case OP_Variable: { /* out2 */
Mem *pVar; /* Value being transferred */
assert( pOp->p1>0 && pOp->p1<=p->nVar );
assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] );
assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) );
pVar = &p->aVar[pOp->p1 - 1];
if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;

View File

@ -346,7 +346,6 @@ struct Vdbe {
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
Parse *pParse; /* Parsing context used to create this Vdbe */
ynVar nVar; /* Number of entries in aVar[] */
ynVar nzVar; /* Number of entries in azVar[] */
u32 magic; /* Magic number for sanity checking */
int nMem; /* Number of memory locations currently allocated */
int nCursor; /* Number of slots in apCsr[] */
@ -371,7 +370,7 @@ struct Vdbe {
char *zErrMsg; /* Error message written here */
VdbeCursor **apCsr; /* One element of this array for each open cursor */
Mem *aVar; /* Values for the OP_Variable opcode. */
char **azVar; /* Name of variables */
VList *pVList; /* Name of variables */
#ifndef SQLITE_OMIT_TRACE
i64 startTime; /* Time when query started - used for profiling */
#endif

View File

@ -1470,10 +1470,8 @@ int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
*/
const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){
Vdbe *p = (Vdbe*)pStmt;
if( p==0 || i<1 || i>p->nzVar ){
return 0;
}
return p->azVar[i-1];
if( p==0 ) return 0;
return sqlite3VListNumToName(p->pVList, i);
}
/*
@ -1482,19 +1480,8 @@ const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){
** return 0.
*/
int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){
int i;
if( p==0 ){
return 0;
}
if( zName ){
for(i=0; i<p->nzVar; i++){
const char *z = p->azVar[i];
if( z && strncmp(z,zName,nName)==0 && z[nName]==0 ){
return i+1;
}
}
}
return 0;
if( p==0 || zName==0 ) return 0;
return sqlite3VListNameToNum(p->pVList, zName, nName);
}
int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){
return sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlite3Strlen30(zName));

View File

@ -1975,10 +1975,8 @@ void sqlite3VdbeMakeReady(
x.nFree = x.nNeeded;
}while( !db->mallocFailed );
p->nzVar = pParse->nzVar;
p->azVar = pParse->azVar;
pParse->nzVar = 0;
pParse->azVar = 0;
p->pVList = pParse->pVList;
pParse->pVList = 0;
p->explain = pParse->explain;
if( db->mallocFailed ){
p->nVar = 0;
@ -2972,7 +2970,6 @@ void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, int mask){
*/
void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
SubProgram *pSub, *pNext;
int i;
assert( p->db==0 || p->db==db );
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
for(pSub=p->pProgram; pSub; pSub=pNext){
@ -2982,18 +2979,20 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
}
if( p->magic!=VDBE_MAGIC_INIT ){
releaseMemArray(p->aVar, p->nVar);
for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
sqlite3DbFree(db, p->azVar);
sqlite3DbFree(db, p->pVList);
sqlite3DbFree(db, p->pFree);
}
vdbeFreeOpArray(db, p->aOp, p->nOp);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
for(i=0; i<p->nScan; i++){
sqlite3DbFree(db, p->aScan[i].zName);
{
int i;
for(i=0; i<p->nScan; i++){
sqlite3DbFree(db, p->aScan[i].zName);
}
sqlite3DbFree(db, p->aScan);
}
sqlite3DbFree(db, p->aScan);
#endif
}