diff --git a/manifest b/manifest index c6d822a639..d861563460 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sability\sto\sgroup\sFROM\sterms\susing\sparentheses.\s\sNames\sof\scolumns\sin\na\sjoin\sno\slonger\sinclude\sthe\stable\sname.\s(CVS\s1197) -D 2004-01-24T20:18:13 +C Modularize\sthe\scolumn\sname\sresolution\scode\sso\sthat\sit\sis\ssmaller,\sfaster,\nand\sready\sfor\ssome\senhancements\sthat\swill\sfix\slong-standing\sname\nresolutions\sproblems.\s(CVS\s1198) +D 2004-01-25T22:44:59 F Makefile.in 0515ff9218ad8d5a8f6220f0494b8ef94c67013b F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd @@ -31,7 +31,7 @@ F src/copy.c 9e47975ea96751c658bcf1a0c4f0bb7c6ee61e73 F src/date.c c6b7e3fa5364d50fe19641882194f3c75364a5af F src/delete.c 0778fe05df0a1d62ac27fd1a3dba237c186ff4d1 F src/encode.c 9e70ea1e4e746f23f18180949e94f1bb1c2220d3 -F src/expr.c e6a05bec1ca8d80d0901e5ff59c4ce3d9553f6cc +F src/expr.c 3928893e8a7576484cd2ed5548468ba978d8005a F src/func.c fd710743e4026dfebfd48c12d20b1a5c27318fa5 F src/hash.c 9b56ef3b291e25168f630d5643a4264ec011c70e F src/hash.h 3247573ab95b9dd90bcca0307a75d9a16da1ccc7 @@ -181,7 +181,7 @@ F www/speed.tcl 2f6b1155b99d39adb185f900456d1d592c4832b3 F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604 F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1 -P 3dc951951947a9188ecba1b84e48c65e34c4df16 -R c11f9dd26aca5e351dec881862f5d549 +P 3626f6d4a1adb4209d5bd9e6477343b52bddbdf2 +R dd7edf82e94deb20012095d659a5fe16 U drh -Z 2a2e36c650f4fa8bf25792ffa5a54e26 +Z cf295a8614ac26ab40dca8b761e6d91c diff --git a/manifest.uuid b/manifest.uuid index a96c152005..e552cac47c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3626f6d4a1adb4209d5bd9e6477343b52bddbdf2 \ No newline at end of file +d3648034b409822909d79eb5aa4e64cafa986541 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 8853fb9ecf..f70c8f4529 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.106 2004/01/16 16:42:54 drh Exp $ +** $Id: expr.c,v 1.107 2004/01/25 22:44:59 drh Exp $ */ #include "sqliteInt.h" #include @@ -374,6 +374,221 @@ int sqliteIsRowid(const char *z){ return 0; } +/* +** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up +** that name in the set of source tables in pSrcList and make the pExpr +** expression node refer back to that source column. The following changes +** are made to pExpr: +** +** pExpr->iDb Set the index in db->aDb[] of the database holding +** the table. +** pExpr->iTable Set to the cursor number for the table obtained +** from pSrcList. +** pExpr->iColumn Set to the column number within the table. +** pExpr->dataType Set to the appropriate data type for the column. +** pExpr->op Set to TK_COLUMN. +** pExpr->pLeft Any expression this points to is deleted +** pExpr->pRight Any expression this points to is deleted. +** +** The pDbToken is the name of the database (the "X"). This value may be +** NULL meaning that name is of the form Y.Z or Z. Any available database +** can be used. The pTableToken is the name of the table (the "Y"). This +** value can be NULL if pDbToken is also NULL. If pTableToken is NULL it +** means that the form of the name is Z and that columns from any table +** can be used. +** +** If the name cannot be resolved unambiguously, leave an error message +** in pParse and return non-zero. Return zero on success. +*/ +static int lookupName( + Parse *pParse, /* The parsing context */ + Token *pDbToken, /* Name of the database containing table, or NULL */ + Token *pTableToken, /* Name of table containing column, or NULL */ + Token *pColumnToken, /* Name of the column. */ + SrcList *pSrcList, /* List of tables used to resolve column names */ + ExprList *pEList, /* List of expressions used to resolve "AS" */ + Expr *pExpr /* Make this EXPR node point to the selected column */ +){ + char *zDb = 0; /* Name of the database. The "X" in X.Y.Z */ + char *zTab = 0; /* Name of the table. The "Y" in X.Y.Z or Y.Z */ + char *zCol = 0; /* Name of the column. The "Z" */ + int i, j; /* Loop counters */ + int cnt = 0; /* Number of matching column names */ + int cntTab = 0; /* Number of matching table names */ + sqlite *db; /* The database */ + + assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */ + if( pDbToken && pDbToken->z ){ + zDb = sqliteStrNDup(pDbToken->z, pDbToken->n); + sqliteDequote(zDb); + }else{ + zDb = 0; + } + if( pTableToken && pTableToken->z ){ + zTab = sqliteStrNDup(pTableToken->z, pTableToken->n); + sqliteDequote(zTab); + }else{ + assert( zDb==0 ); + zTab = 0; + } + zCol = sqliteStrNDup(pColumnToken->z, pColumnToken->n); + sqliteDequote(zCol); + if( sqlite_malloc_failed ){ + return 1; /* Leak memory (zDb and zTab) if malloc fails */ + } + assert( zTab==0 || pEList==0 ); + + pExpr->iTable = -1; + for(i=0; inSrc; i++){ + struct SrcList_item *pItem = &pSrcList->a[i]; + Table *pTab = pItem->pTab; + Column *pCol; + + if( pTab==0 ) continue; + assert( pTab->nCol>0 ); + if( zTab ){ + if( pItem->zAlias ){ + char *zTabName = pItem->zAlias; + if( sqliteStrICmp(zTabName, zTab)!=0 ) continue; + }else{ + char *zTabName = pTab->zName; + if( zTabName==0 || sqliteStrICmp(zTabName, zTab)!=0 ) continue; + if( zDb!=0 && sqliteStrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){ + continue; + } + } + } + if( 0==(cntTab++) ){ + pExpr->iTable = pItem->iCursor; + pExpr->iDb = pTab->iDb; + } + for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ + if( sqliteStrICmp(pCol->zName, zCol)==0 ){ + cnt++; + pExpr->iTable = pItem->iCursor; + pExpr->iDb = pTab->iDb; + /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ + pExpr->iColumn = j==pTab->iPKey ? -1 : j; + pExpr->dataType = pCol->sortOrder & SQLITE_SO_TYPEMASK; + break; + } + } + } + + /* If we have not already resolved the name, then maybe + ** it is a new.* or old.* trigger argument reference + */ + if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){ + TriggerStack *pTriggerStack = pParse->trigStack; + Table *pTab = 0; + if( pTriggerStack->newIdx != -1 && sqliteStrICmp("new", zTab) == 0 ){ + pExpr->iTable = pTriggerStack->newIdx; + assert( pTriggerStack->pTab ); + pTab = pTriggerStack->pTab; + }else if( pTriggerStack->oldIdx != -1 && sqliteStrICmp("old", zTab) == 0 ){ + pExpr->iTable = pTriggerStack->oldIdx; + assert( pTriggerStack->pTab ); + pTab = pTriggerStack->pTab; + } + + if( pTab ){ + int j; + Column *pCol = pTab->aCol; + + pExpr->iDb = pTab->iDb; + cntTab++; + for(j=0; j < pTab->nCol; j++, pCol++) { + if( sqliteStrICmp(pCol->zName, zCol)==0 ){ + cnt++; + pExpr->iColumn = j==pTab->iPKey ? -1 : j; + pExpr->dataType = pCol->sortOrder & SQLITE_SO_TYPEMASK; + break; + } + } + } + } + + /* + ** Perhaps the name is a reference to the ROWID + */ + if( cnt==0 && cntTab==1 && sqliteIsRowid(zCol) ){ + cnt = 1; + pExpr->iColumn = -1; + pExpr->dataType = SQLITE_SO_NUM; + } + + /* + ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z + ** might refer to an result-set alias. This happens, for example, when + ** we are resolving names in the WHERE clause of the following command: + ** + ** SELECT a+b AS x FROM table WHERE x<10; + ** + ** In cases like this, replace pExpr with a copy of the expression that + ** forms the result set entry ("a+b" in the example) and return immediately. + ** Note that the expression in the result set should have already been + ** resolved by the time the WHERE clause is resolved. + */ + if( cnt==0 && pEList!=0 ){ + for(j=0; jnExpr; j++){ + char *zAs = pEList->a[j].zName; + if( zAs!=0 && sqliteStrICmp(zAs, zCol)==0 ){ + assert( pExpr->pLeft==0 && pExpr->pRight==0 ); + pExpr->op = TK_AS; + pExpr->iColumn = j; + pExpr->pLeft = sqliteExprDup(pEList->a[j].pExpr); + sqliteFree(zCol); + assert( zTab==0 && zDb==0 ); + return 0; + } + } + } + + /* + ** If X and Y are NULL (in other words if only the column name Z is + ** supplied) and the value of Z is enclosed in double-quotes, then + ** Z is a string literal if it doesn't match any column names. In that + ** case, we need to return right away and not make any changes to + ** pExpr. + */ + if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){ + sqliteFree(zCol); + return 0; + } + + /* + ** cnt==0 means there was not match. cnt>1 means there were two or + ** more matches. Either way, we have an error. + */ + if( cnt!=1 ){ + char *z = 0; + char *zErr; + zErr = cnt==0 ? "no such column: %s" : "ambiguous column name: %s"; + if( zDb ){ + sqliteSetString(&z, zDb, ".", zTab, ".", zCol, 0); + }else if( zTab ){ + sqliteSetString(&z, zTab, ".", zCol, 0); + }else{ + z = sqliteStrDup(zCol); + } + sqliteErrorMsg(pParse, zErr, z); + sqliteFree(z); + } + + /* Clean up and return + */ + sqliteFree(zDb); + sqliteFree(zTab); + sqliteFree(zCol); + sqliteExprDelete(pExpr->pLeft); + pExpr->pLeft = 0; + sqliteExprDelete(pExpr->pRight); + pExpr->pRight = 0; + pExpr->op = TK_COLUMN; + sqliteAuthRead(pParse, pExpr, pSrcList); + return cnt!=1; +} + /* ** This routine walks an expression tree and resolves references to ** table columns. Nodes of the form ID.ID or ID resolve into an @@ -407,15 +622,15 @@ int sqliteIsRowid(const char *z){ */ int sqliteExprResolveIds( Parse *pParse, /* The parser context */ - SrcList *pTabList, /* List of tables used to resolve column names */ + SrcList *pSrcList, /* List of tables used to resolve column names */ ExprList *pEList, /* List of expressions used to resolve "AS" */ Expr *pExpr /* The expression to be analyzed. */ ){ int i; - if( pExpr==0 || pTabList==0 ) return 0; - for(i=0; inSrc; i++){ - assert( pTabList->a[i].iCursor>=0 && pTabList->a[i].iCursornTab ); + if( pExpr==0 || pSrcList==0 ) return 0; + for(i=0; inSrc; i++){ + assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursornTab ); } switch( pExpr->op ){ /* Double-quoted strings (ex: "abc") are used as identifiers if @@ -426,79 +641,11 @@ int sqliteExprResolveIds( if( pExpr->token.z[0]=='\'' ) break; /* Fall thru into the TK_ID case if this is a double-quoted string */ } - /* A lone identifier. Try and match it as follows: - ** - ** 1. To the name of a column of one of the tables in pTabList - ** - ** 2. To the right side of an AS keyword in the column list of - ** a SELECT statement. (For example, match against 'x' in - ** "SELECT a+b AS 'x' FROM t1".) - ** - ** 3. One of the special names "ROWID", "OID", or "_ROWID_". + /* A lone identifier is the name of a columnd. */ case TK_ID: { - int cnt = 0; /* Number of matches */ - char *z; - int iDb = -1; - - assert( pExpr->token.z ); - z = sqliteStrNDup(pExpr->token.z, pExpr->token.n); - sqliteDequote(z); - if( z==0 ) return 1; - for(i=0; inSrc; i++){ - int j; - Table *pTab = pTabList->a[i].pTab; - if( pTab==0 ) continue; - iDb = pTab->iDb; - assert( pTab->nCol>0 ); - for(j=0; jnCol; j++){ - if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){ - cnt++; - pExpr->iTable = pTabList->a[i].iCursor; - pExpr->iDb = pTab->iDb; - if( j==pTab->iPKey ){ - /* Substitute the record number for the INTEGER PRIMARY KEY */ - pExpr->iColumn = -1; - pExpr->dataType = SQLITE_SO_NUM; - }else{ - pExpr->iColumn = j; - pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK; - } - pExpr->op = TK_COLUMN; - } - } - } - if( cnt==0 && pEList!=0 ){ - int j; - for(j=0; jnExpr; j++){ - char *zAs = pEList->a[j].zName; - if( zAs!=0 && sqliteStrICmp(zAs, z)==0 ){ - cnt++; - assert( pExpr->pLeft==0 && pExpr->pRight==0 ); - pExpr->op = TK_AS; - pExpr->iColumn = j; - pExpr->pLeft = sqliteExprDup(pEList->a[j].pExpr); - } - } - } - if( cnt==0 && iDb>=0 && sqliteIsRowid(z) ){ - pExpr->iColumn = -1; - pExpr->iTable = pTabList->a[0].iCursor; - pExpr->iDb = iDb; - cnt = 1 + (pTabList->nSrc>1); - pExpr->op = TK_COLUMN; - pExpr->dataType = SQLITE_SO_NUM; - } - sqliteFree(z); - if( cnt==0 && pExpr->token.z[0]!='"' ){ - sqliteErrorMsg(pParse, "no such column: %T", &pExpr->token); + if( lookupName(pParse, 0, 0, &pExpr->token, pSrcList, pEList, pExpr) ){ return 1; - }else if( cnt>1 ){ - sqliteErrorMsg(pParse, "ambiguous column name: %T", &pExpr->token); - return 1; - } - if( pExpr->op==TK_COLUMN ){ - sqliteAuthRead(pParse, pExpr, pTabList); } break; } @@ -507,134 +654,32 @@ int sqliteExprResolveIds( ** Or a database, table and column: ID.ID.ID */ case TK_DOT: { - int cnt = 0; /* Number of matches */ - int cntTab = 0; /* Number of matching tables */ - int i; /* Loop counter */ - Expr *pLeft, *pRight; /* Left and right subbranches of the expr */ - char *zLeft, *zRight; /* Text of an identifier */ - char *zDb; /* Name of database holding table */ - sqlite *db = pParse->db; + Token *pColumn; + Token *pTable; + Token *pDb; + Expr *pRight; pRight = pExpr->pRight; if( pRight->op==TK_ID ){ - pLeft = pExpr->pLeft; - zDb = 0; + pDb = 0; + pTable = &pExpr->pLeft->token; + pColumn = &pRight->token; }else{ - Expr *pDb = pExpr->pLeft; - assert( pDb && pDb->op==TK_ID && pDb->token.z ); - zDb = sqliteStrNDup(pDb->token.z, pDb->token.n); - pLeft = pRight->pLeft; - pRight = pRight->pRight; + assert( pRight->op==TK_DOT ); + pDb = &pExpr->pLeft->token; + pTable = &pRight->pLeft->token; + pColumn = &pRight->pRight->token; } - assert( pLeft && pLeft->op==TK_ID && pLeft->token.z ); - assert( pRight && pRight->op==TK_ID && pRight->token.z ); - zLeft = sqliteStrNDup(pLeft->token.z, pLeft->token.n); - zRight = sqliteStrNDup(pRight->token.z, pRight->token.n); - if( zLeft==0 || zRight==0 ){ - sqliteFree(zLeft); - sqliteFree(zRight); - sqliteFree(zDb); + if( lookupName(pParse, pDb, pTable, pColumn, pSrcList, 0, pExpr) ){ return 1; } - sqliteDequote(zDb); - sqliteDequote(zLeft); - sqliteDequote(zRight); - pExpr->iTable = -1; - for(i=0; inSrc; i++){ - int j; - char *zTab; - Table *pTab = pTabList->a[i].pTab; - if( pTab==0 ) continue; - assert( pTab->nCol>0 ); - if( pTabList->a[i].zAlias ){ - zTab = pTabList->a[i].zAlias; - if( sqliteStrICmp(zTab, zLeft)!=0 ) continue; - }else{ - zTab = pTab->zName; - if( zTab==0 || sqliteStrICmp(zTab, zLeft)!=0 ) continue; - if( zDb!=0 && sqliteStrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){ - continue; - } - } - if( 0==(cntTab++) ){ - pExpr->iTable = pTabList->a[i].iCursor; - pExpr->iDb = pTab->iDb; - } - for(j=0; jnCol; j++){ - if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ - cnt++; - pExpr->iTable = pTabList->a[i].iCursor; - pExpr->iDb = pTab->iDb; - /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ - pExpr->iColumn = j==pTab->iPKey ? -1 : j; - pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK; - } - } - } - - /* If we have not already resolved this *.* expression, then maybe - * it is a new.* or old.* trigger argument reference */ - if( cnt == 0 && pParse->trigStack != 0 ){ - TriggerStack *pTriggerStack = pParse->trigStack; - int t = 0; - if( pTriggerStack->newIdx != -1 && sqliteStrICmp("new", zLeft) == 0 ){ - pExpr->iTable = pTriggerStack->newIdx; - assert( pTriggerStack->pTab ); - pExpr->iDb = pTriggerStack->pTab->iDb; - cntTab++; - t = 1; - } - if( pTriggerStack->oldIdx != -1 && sqliteStrICmp("old", zLeft) == 0 ){ - pExpr->iTable = pTriggerStack->oldIdx; - assert( pTriggerStack->pTab ); - pExpr->iDb = pTriggerStack->pTab->iDb; - cntTab++; - t = 1; - } - - if( t ){ - int j; - Table *pTab = pTriggerStack->pTab; - for(j=0; j < pTab->nCol; j++) { - if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ - cnt++; - pExpr->iColumn = j==pTab->iPKey ? -1 : j; - pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK; - } - } - } - } - - if( cnt==0 && cntTab==1 && sqliteIsRowid(zRight) ){ - cnt = 1; - pExpr->iColumn = -1; - pExpr->dataType = SQLITE_SO_NUM; - } - sqliteFree(zDb); - sqliteFree(zLeft); - sqliteFree(zRight); - if( cnt==0 ){ - sqliteErrorMsg(pParse, "no such column: %T.%T", - &pLeft->token, &pRight->token); - return 1; - }else if( cnt>1 ){ - sqliteErrorMsg(pParse, "ambiguous column name: %T.%T", - &pLeft->token, &pRight->token); - return 1; - } - sqliteExprDelete(pExpr->pLeft); - pExpr->pLeft = 0; - sqliteExprDelete(pExpr->pRight); - pExpr->pRight = 0; - pExpr->op = TK_COLUMN; - sqliteAuthRead(pParse, pExpr, pTabList); break; } case TK_IN: { Vdbe *v = sqliteGetVdbe(pParse); if( v==0 ) return 1; - if( sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){ + if( sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){ return 1; } if( pExpr->pSelect ){ @@ -704,11 +749,11 @@ int sqliteExprResolveIds( /* For all else, just recursively walk the tree */ default: { if( pExpr->pLeft - && sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){ + && sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){ return 1; } if( pExpr->pRight - && sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pRight) ){ + && sqliteExprResolveIds(pParse, pSrcList, pEList, pExpr->pRight) ){ return 1; } if( pExpr->pList ){ @@ -716,7 +761,7 @@ int sqliteExprResolveIds( ExprList *pList = pExpr->pList; for(i=0; inExpr; i++){ Expr *pArg = pList->a[i].pExpr; - if( sqliteExprResolveIds(pParse, pTabList, pEList, pArg) ){ + if( sqliteExprResolveIds(pParse, pSrcList, pEList, pArg) ){ return 1; } }