📄 select.c
字号:
#include "eDbInit.h"
void eDbSelectDelete(Select *p){
if( p==0 ) return;
eDbExprListDelete(p->pEList);
eDbSrcListDelete(p->pSrc);
eDbExprDelete(p->pWhere);
eDbExprListDelete(p->pGroupBy);
eDbExprDelete(p->pHaving);
eDbExprListDelete(p->pOrderBy);
eDbSelectDelete(p->pPrior);
eDbFree(p->zSelect);
eDbFree(p);
}
/*
** Allocate a new Select structure and return a pointer to that
** structure.
*/
Select *eDbSelectNew(
ExprList *pEList, /* which columns to include in the result */
SrcList *pSrc, /* the FROM clause -- which tables to scan */
Expr *pWhere, /* the WHERE clause */
ExprList *pGroupBy, /* the GROUP BY clause */
Expr *pHaving, /* the HAVING clause */
ExprList *pOrderBy, /* the ORDER BY clause */
int isDistinct /* true if the DISTINCT keyword is present */
){
Select *pNew;
pNew = eDbMalloc( sizeof(*pNew) );
if( pNew==0 ){
eDbExprListDelete(pEList);
eDbSrcListDelete(pSrc);
eDbExprDelete(pWhere);
eDbExprListDelete(pGroupBy);
eDbExprDelete(pHaving);
eDbExprListDelete(pOrderBy);
}else{
if( pEList==0 ){
pEList = eDbExprListAppend(0, eDbExpr(TK_ALL,0,0,0), 0);
}
pNew->pEList = pEList;
pNew->pSrc = pSrc;
pNew->pWhere = pWhere;
pNew->pGroupBy = pGroupBy;
pNew->pHaving = pHaving;
pNew->pOrderBy = pOrderBy;
pNew->isDistinct = isDistinct;
pNew->op = TK_SELECT;
}
return pNew;
}
/*
** Return the index of a column in a table. Return -1 if the column
** is not contained in the table.
*/
static int columnIndex(Table *pTab, const char *zCol){
int i;
for(i=0; i<pTab->nCol; i++){
if( eDbStrICmp(pTab->aCol[i].zName, zCol)==0 ) return i;
}
return -1;
}
/*
** For the given SELECT statement, do three things.
**
** (1) Fill in the pTabList->a[].pTab fields in the SrcList that
** defines the set of tables that should be scanned. For views,
** fill pTabList->a[].pSelect with a copy of the SELECT statement
** that implements the view. A copy is made of the view's SELECT
** statement so that we can freely modify or delete that statement
** without worrying about messing up the presistent representation
** of the view.
**
** (2) Add terms to the WHERE clause to accomodate the NATURAL keyword
** on joins and the ON and USING clause of joins.
**
** (3) Scan the list of columns in the result set (pEList) looking
** for instances of the "*" operator or the TABLE.* operator.
** If found, expand each "*" to be every column in every table
** and TABLE.* to be every column in TABLE.
**
** Return 0 on success. If there are problems, leave an error message
** in pParse and return non-zero.
*/
static int fillInColumnList(Parser *pParse, Select *p){
int i, j, k, rc;
SrcList *pTabList;
ExprList *pEList;
Table *pTab;
if( p==0 || p->pSrc==0 ) return 1;
pTabList = p->pSrc;
pEList = p->pEList;
/* Look up every table in the table list.
*/
for(i=0; i<pTabList->nSrc; i++){
if( pTabList->a[i].pTab ){
/* This routine has run before! No need to continue */
return 0;
}
/* An ordinary table or view name in the FROM clause */
pTabList->a[i].pTab = pTab =
eDbLocateTable(pParse,pTabList->a[i].zName,pTabList->a[i].zDatabase);
if( pTab==0 ){
return 1;
}
}
/* For every "*" that occurs in the column list, insert the names of
** all columns in all tables. And for every TABLE.* insert the names
** of all columns in TABLE. The parser inserted a special expression
** with the TK_ALL operator for each "*" that it found in the column list.
** The following code just has to locate the TK_ALL expressions and expand
** each one to the list of all columns in all tables.
**
** The first loop just checks to see if there are any "*" operators
** that need expanding.
*/
for(k=0; k<pEList->nExpr; k++){
Expr *pE = pEList->a[k].pExpr;
if( pE->op==TK_ALL ) break;
if( pE->op==TK_DOT && pE->pRight && pE->pRight->op==TK_ALL
&& pE->pLeft && pE->pLeft->op==TK_ID ) break;
}
rc = 0;
if( k<pEList->nExpr ){
/*
** If we get here it means the result set contains one or more "*"
** operators that need to be expanded. Loop through each expression
** in the result set and expand them one by one.
*/
struct ExprList_item *a = pEList->a;
ExprList *pNew = 0;
for(k=0; k<pEList->nExpr; k++){
Expr *pE = a[k].pExpr;
if( pE->op!=TK_ALL &&
(pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){
/* This particular expression does not need to be expanded.
*/
pNew = eDbExprListAppend(pNew, a[k].pExpr, 0);
pNew->a[pNew->nExpr-1].zName = a[k].zName;
a[k].pExpr = 0;
a[k].zName = 0;
}else{
/* This expression is a "*" or a "TABLE.*" and needs to be
** expanded. */
int tableSeen = 0; /* Set to 1 when TABLE matches */
Token *pName; /* text of name of TABLE */
if( pE->op==TK_DOT && pE->pLeft ){
pName = &pE->pLeft->token;
}else{
pName = 0;
}
for(i=0; i<pTabList->nSrc; i++){
Table *pTab = pTabList->a[i].pTab;
char *zTabName = pTabList->a[i].zAlias;
if( zTabName==0 || zTabName[0]==0 ){
zTabName = pTab->zName;
}
if( pName && (zTabName==0 || zTabName[0]==0 ||
eDbStrNICmp(pName->z, zTabName, pName->n)!=0 ||
zTabName[pName->n]!=0) ){
continue;
}
tableSeen = 1;
for(j=0; j<pTab->nCol; j++){
Expr *pExpr, *pLeft, *pRight;
char *zName = pTab->aCol[j].zName;
if( i>0 && (pTabList->a[i-1].jointype & JT_NATURAL)!=0 &&
columnIndex(pTabList->a[i-1].pTab, zName)>=0 ){
/* In a NATURAL join, omit the join columns from the
** table on the right */
continue;
}
if( i>0 && eDbIdListIndex(pTabList->a[i-1].pUsing, zName)>=0 ){
/* In a join with a USING clause, omit columns in the
** using clause from the table on the right. */
continue;
}
pRight = eDbExpr(TK_ID, 0, 0, 0);
if( pRight==0 ) break;
pRight->token.z = zName;
pRight->token.n = strlen(zName);
pRight->token.dyn = 0;
if( zTabName && pTabList->nSrc>1 ){
pLeft = eDbExpr(TK_ID, 0, 0, 0);
pExpr = eDbExpr(TK_DOT, pLeft, pRight, 0);
if( pExpr==0 ) break;
pLeft->token.z = zTabName;
pLeft->token.n = strlen(zTabName);
pLeft->token.dyn = 0;
eDbSetString((char**)&pExpr->span.z, zTabName, ".", zName, 0);
pExpr->span.n = strlen(pExpr->span.z);
pExpr->span.dyn = 1;
pExpr->token.z = 0;
pExpr->token.n = 0;
pExpr->token.dyn = 0;
}else{
pExpr = pRight;
pExpr->span = pExpr->token;
}
pNew = eDbExprListAppend(pNew, pExpr, 0);
}
}
if( !tableSeen ){
if( pName ){
char *z=0;
eDbSetNString(&z,pName->z,pName->n,0);
eDbErrorMsg(pParse, "no such table: %s", z);
eDbFree(z);
}else{
eDbErrorMsg(pParse, "no tables specified");
}
rc = 1;
}
}
}
eDbExprListDelete(pEList);
p->pEList = pNew;
}
return rc;
}
/*
** Generate code for the given SELECT statement.
**
** The results are distributed in various ways depending on the
** value of eDest and iParm.
**
** eDest Value Result
** ------------ -------------------------------------------
** SRT_Callback Invoke the callback for each row of the result.
**
** SRT_Mem Store first result in memory cell iParm
**
** SRT_Set Store results as keys of a table with cursor iParm
**
** SRT_Union Store results as a key in a temporary table iParm
**
** SRT_Except Remove results from the temporary table iParm.
**
** SRT_Table Store results in temporary table iParm
**
** The table above is incomplete. Additional eDist value have be added
** since this comment was written. See the selectInnerLoop() function for
** a complete listing of the allowed values of eDest and their meanings.
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
** pParse->zErrMsg.
**
** This routine does NOT free the Select structure passed in. The
** calling function needs to do that.
**
** The pParent, parentTab, and *pParentAgg fields are filled in if this
** SELECT is a subquery. This routine may try to combine this SELECT
** with its parent to form a single flat query. In so doing, it might
** change the parent query from a non-aggregate to an aggregate query.
** For that reason, the pParentAgg flag is passed as a pointer, so it
** can be changed.
**
** Example 1: The meaning of the pParent parameter.
**
** SELECT * FROM t1 JOIN (SELECT x, count(*) FROM t2) JOIN t3;
** \ \_______ subquery _______/ /
** \ /
** \____________________ outer query ___________________/
**
** This routine is called for the outer query first. For that call,
** pParent will be NULL. During the processing of the outer query, this
** routine is called recursively to handle the subquery. For the recursive
** call, pParent will point to the outer query. Because the subquery is
** the second element in a three-way join, the parentTab parameter will
** be 1 (the 2nd value of a 0-indexed array.)
*/
int eDbSelect(Parser *pParse,Select *p){
int i;
int isAgg;
ExprList *pEList; /* List of columns to extract. */
SrcList *pTabList; /* List of tables to select from */
Expr *pWhere; /* The WHERE clause. May be NULL */
ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */
ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
Expr *pHaving; /* The HAVING clause. May be NULL */
int isDistinct; /* True if the DISTINCT keyword is present */
int rc = 1; /* Value to return from this function */
ParseResult *pRes = eDbMalloc(sizeof(ParseResult));
if( eDb_malloc_failed || pParse->nErr || p==0 ) return 1;
/* Make local copies of the parameters for this query.
*/
pTabList = p->pSrc;
pWhere = p->pWhere;
pOrderBy = p->pOrderBy;
pGroupBy = p->pGroupBy;
pHaving = p->pHaving;
isDistinct = p->isDistinct;
if( pTabList->nSrc > 3){
eDbErrorMsg(pParse,"total of tables must be no more than 3!\n");
goto select_end;
}
/* Allocate cursors for each table in the FROM clause
*/
eDbSrcListAssignCursors(pParse, pTabList);
/*
** Do not even attempt to generate any code if we have already seen
** errors before this routine starts.
*/
if( pParse->nErr>0 ) goto select_end;
/* Expand any "*" terms in the result set. (For example the "*" in
** "SELECT * FROM t1") The fillInColumnlist() routine also does some
** other housekeeping - see the header comment for details.
*/
if( fillInColumnList(pParse, p) ){
goto select_end;
}
pWhere = p->pWhere;
pEList = p->pEList;
if( pEList==0 ) goto select_end;
/* At this point, we should have allocated all the cursors that we
** need to handle subquerys and temporary tables.
**
** Resolve the column names and do a semantics check on all the expressions.
*/
for(i=0; i<pEList->nExpr; i++){
if( eDbExprResolveIds(pParse, pTabList, 0, pEList->a[i].pExpr) ){
goto select_end;
}
if( eDbExprCheck(pParse, pEList->a[i].pExpr, 1, &isAgg) ){
goto select_end;
}
}
if( pWhere ){
if( eDbExprResolveIds(pParse, pTabList, pEList, pWhere) ){
goto select_end;
}
if( eDbExprCheck(pParse, pWhere, 0, 0) ){
goto select_end;
}
eDbDealwithWhereClause(pTabList,pWhere);
}
if( pHaving ){
/*
if( pGroupBy==0 ){
eDbErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
goto select_end;
}
if( eDbExprResolveIds(pParse, pTabList, pEList, pHaving) ){
goto select_end;
}
if( eDbExprCheck(pParse, pHaving, 1, &isAgg) ){
goto select_end;
}
*/
}
if( pOrderBy ){
/*
for(i=0; i<pOrderBy->nExpr; i++){
int iCol;
Expr *pE = pOrderBy->a[i].pExpr;
if( eDbExprIsInteger(pE, &iCol) && iCol>0 && iCol<=pEList->nExpr ){
eDbExprDelete(pE);
pE = pOrderBy->a[i].pExpr = eDbExprDup(pEList->a[iCol-1].pExpr);
}
if( eDbExprResolveIds(pParse, pTabList, pEList, pE) ){
goto select_end;
}
if( eDbExprCheck(pParse, pE, isAgg, 0) ){
goto select_end;
}
if( eDbExprIsConstant(pE) ){
if( eDbExprIsInteger(pE, &iCol)==0 ){
eDbErrorMsg(pParse,
"ORDER BY terms must not be non-integer constants");
goto select_end;
}else if( iCol<=0 || iCol>pEList->nExpr ){
eDbErrorMsg(pParse,
"ORDER BY column number %d out of range - should be "
"between 1 and %d", iCol, pEList->nExpr);
goto select_end;
}
}
}
*/
}/*END if( pOrderBy )*/
if( pGroupBy ){
/*
for(i=0; i<pGroupBy->nExpr; i++){
int iCol;
Expr *pE = pGroupBy->a[i].pExpr;
if( eDbExprIsInteger(pE, &iCol) && iCol>0 && iCol<=pEList->nExpr ){
eDbExprDelete(pE);
pE = pGroupBy->a[i].pExpr = eDbExprDup(pEList->a[iCol-1].pExpr);
}
if( eDbExprResolveIds(pParse, pTabList, pEList, pE) ){
goto select_end;
}
if( eDbExprCheck(pParse, pE, isAgg, 0) ){
goto select_end;
}
if( eDbExprIsConstant(pE) ){
if( eDbExprIsInteger(pE, &iCol)==0 ){
eDbErrorMsg(pParse,
"GROUP BY terms must not be non-integer constants");
goto select_end;
}else if( iCol<=0 || iCol>pEList->nExpr ){
eDbErrorMsg(pParse,
"GROUP BY column number %d out of range - should be "
"between 1 and %d", iCol, pEList->nExpr);
goto select_end;
}
}
}
*/
}
rc = 0;
pRes->pSelect = p;
pRes->op = Op_Select;
pParse->pResult = pRes;
/* Control jumps to here if an error is encountered above, or upon
** successful coding of the SELECT.
*/
return eDb_OK;
select_end:
eDbSelectDelete(p);
return rc;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -