** This file contains C code routines that are called by the parser
** to handle SELECT statements.
**
-** $Id: select.c,v 1.12 2000/06/06 18:00:16 drh Exp $
+** $Id: select.c,v 1.13 2000/06/06 21:56:08 drh Exp $
*/
#include "sqliteInt.h"
pNew->pHaving = pHaving;
pNew->pOrderBy = pOrderBy;
pNew->isDistinct = isDistinct;
+ pNew->op = TK_SELECT;
return pNew;
}
** Delete the given Select structure and all of its substructures.
*/
void sqliteSelectDelete(Select *p){
+ if( p==0 ) return;
sqliteExprListDelete(p->pEList);
sqliteIdListDelete(p->pSrc);
sqliteExprDelete(p->pWhere);
sqliteExprListDelete(p->pGroupBy);
sqliteExprDelete(p->pHaving);
sqliteExprListDelete(p->pOrderBy);
+ sqliteSelectDelete(p->pPrior);
sqliteFree(p);
}
/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
+**
+** The pEList is used to determine the values for each column in the
+** result row. Except if pEList==NULL, then we just read nField
+** elements from the srcTab table.
*/
static int selectInnerLoop(
Parse *pParse, /* The parser context */
ExprList *pEList, /* List of values being extracted */
+ int srcTab, /* Pull data from this table */
+ int nField, /* Number of fields in the source table */
ExprList *pOrderBy, /* If not NULL, sort results using this key */
int distinct, /* If >=0, make sure results are distinct */
int eDest, /* How to dispose of the results */
/* Pull the requested fields.
*/
- for(i=0; i<pEList->nExpr; i++){
- sqliteExprCode(pParse, pEList->a[i].pExpr);
+ if( pEList ){
+ for(i=0; i<pEList->nExpr; i++){
+ sqliteExprCode(pParse, pEList->a[i].pExpr);
+ }
+ nField = pEList->nExpr;
+ }else{
+ for(i=0; i<nField; i++){
+ sqliteVdbeAddOp(v, OP_Field, srcTab, i, 0, 0);
+ }
}
/* If the current result is not distinct, skip the rest
sqliteVdbeAddOp(v, OP_String, 0, 0, "", lbl);
sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
}
+
/* If there is an ORDER BY clause, then store the results
** in a sorter.
*/
sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
}else
- /* If we are writing to a table, then write the results to the table.
+ /* In this mode, write each query result to the key of the temporary
+ ** table iParm.
*/
- if( eDest==SRT_Table ){
- sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
- sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
+ if( eDest==SRT_Union ){
+ sqliteVdbeAddOp(v, OP_MakeRecord, nField, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_String, iParm, 0, "", 0);
+ sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
+ }else
+
+ /* Construct a record from the query result, but instead of
+ ** saving that record, use it as a key to delete elements from
+ ** the temporary table iParm.
+ */
+ if( eDest==SRT_Except ){
+ assert( pEList->nExpr==1 );
+ sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
}else
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
}else
+
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
** of the scan loop.
/* If none of the above, send the data to the callback function.
*/
{
- sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_Callback, nField, 0, 0, 0);
+ }
+ return 0;
+}
+
+/*
+** Generate code that will tell the VDBE how many columns there
+** are in the result and the name for each column. This information
+** is used to provide "argc" and "azCol[]" values in the callback.
+*/
+static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){
+ int i;
+ sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
+ for(i=0; i<pEList->nExpr; i++){
+ Expr *p;
+ if( pEList->a[i].zName ){
+ char *zName = pEList->a[i].zName;
+ int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+ if( zName[0]=='\'' || zName[0]=='"' ){
+ sqliteVdbeDequoteP3(v, addr);
+ }
+ continue;
+ }
+ p = pEList->a[i].pExpr;
+ if( p->op!=TK_FIELD || pTabList==0 ){
+ char zName[30];
+ sprintf(zName, "field%d", i+1);
+ sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+ }else{
+ if( pTabList->nId>1 ){
+ char *zName = 0;
+ Table *pTab = pTabList->a[p->iTable].pTab;
+ char *zTab;
+
+ zTab = pTabList->a[p->iTable].zAlias;
+ if( zTab==0 ) zTab = pTab->zName;
+ sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0);
+ sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+ sqliteFree(zName);
+ }else{
+ Table *pTab = pTabList->a[0].pTab;
+ char *zName = pTab->aCol[p->iField].zName;
+ sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+ }
+ }
+ }
+}
+
+/*
+** This routine is called to process a query that is really the union
+** or intersection of two or more separate queries.
+*/
+static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
+ int rc;
+ Select *pPrior;
+ Vdbe *v;
+ int i;
+
+ /* Make sure we have a valid query engine. If not, create a new one.
+ */
+ v = pParse->pVdbe;
+ if( v==0 ){
+ v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+ }
+ if( v==0 ){
+ sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
+ pParse->nErr++;
+ return 1;
+ }
+
+ assert( p->pPrior!=0 );
+ pPrior = p->pPrior;
+ switch( p->op ){
+ case TK_ALL: {
+ rc = sqliteSelect(pParse, pPrior, eDest, iParm);
+ if( rc ) return rc;
+ p->pPrior = 0;
+ rc = sqliteSelect(pParse, p, eDest, iParm);
+ p->pPrior = pPrior;
+ break;
+ }
+ case TK_EXCEPT:
+ case TK_UNION: {
+ int unionTab;
+ int op;
+
+ if( eDest==SRT_Union ){
+ unionTab = iParm;
+ }else{
+ unionTab = pParse->nTab++;
+ sqliteVdbeAddOp(v, OP_Open, unionTab, 1, 0, 0);
+ sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0);
+ }
+ rc = sqliteSelect(pParse, pPrior, SRT_Union, unionTab);
+ if( rc ) return rc;
+ op = p->op==TK_EXCEPT ? SRT_Except : SRT_Union;
+ p->pPrior = 0;
+ rc = sqliteSelect(pParse, p, op, unionTab);
+ p->pPrior = pPrior;
+ if( rc ) return rc;
+ if( eDest!=SRT_Union ){
+ int iCont, iBreak;
+ assert( p->pEList );
+ generateColumnNames(v, 0, p->pEList);
+ iBreak = sqliteVdbeMakeLabel(v);
+ iCont = sqliteVdbeAddOp(v, OP_Next, unionTab, iBreak, 0, 0);
+ rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr,
+ 0, -1, eDest, iParm,
+ iCont, iBreak);
+ if( rc ) return 1;
+ sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
+ sqliteVdbeAddOp(v, OP_Close, unionTab, 0, 0, iBreak);
+ }
+ break;
+ }
+ case TK_INTERSECT: {
+ int tab1, tab2;
+ Select *pPrior;
+ int iCont, iBreak;
+
+ tab1 = pParse->nTab++;
+ tab2 = pParse->nTab++;
+ sqliteVdbeAddOp(v, OP_Open, tab1, 1, 0, 0);
+ sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0);
+ rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1);
+ if( rc ) return rc;
+ sqliteVdbeAddOp(v, OP_Open, tab2, 1, 0, 0);
+ sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0);
+ p->pPrior = 0;
+ rc = sqliteSelect(pParse, p, SRT_Union, tab2);
+ p->pPrior = pPrior;
+ if( rc ) return rc;
+ assert( p->pEList );
+ generateColumnNames(v, 0, p->pEList);
+ iBreak = sqliteVdbeMakeLabel(v);
+ iCont = sqliteVdbeAddOp(v, OP_Next, tab1, iBreak, 0, 0);
+ sqliteVdbeAddOp(v, OP_Key, tab1, 0, 0, 0);
+ sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont, 0, 0);
+ rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr,
+ 0, -1, eDest, iParm,
+ iCont, iBreak);
+ if( rc ) return 1;
+ sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
+ sqliteVdbeAddOp(v, OP_Close, tab2, 0, 0, iBreak);
+ sqliteVdbeAddOp(v, OP_Close, tab1, 0, 0, 0);
+ break;
+ }
+ }
+ assert( p->pEList && pPrior->pEList );
+ if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
+ sqliteSetString(&pParse->zErrMsg, "SELECTs have different numbers "
+ "of columns and therefore cannot be joined", 0);
+ pParse->nErr++;
+ return 1;
}
return 0;
}
**
** SRT_Set Store results as keys of a table with cursor iParm
**
-** SRT_Table Store results in a regular table with cursor iParm
+** SRT_Union Store results as a key in a temporary table iParm
+**
+** SRT_Except Remove results form the temporary talbe iParm.
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
int sqliteSelect(
Parse *pParse, /* The parser context */
Select *p, /* The SELECT statement being coded. */
- int eDest, /* One of SRT_Callback, SRT_Mem, SRT_Set, SRT_Table */
+ int eDest, /* One of: SRT_Callback Mem Set Union Except */
int iParm /* Save result in this memory location, if >=0 */
){
int i, j;
int isDistinct; /* True if the DISTINCT keyword is present */
int distinct; /* Table to use for the distinct set */
+ /* If there is are a sequence of queries, do the earlier ones first.
+ */
+ if( p->pPrior ){
+ return multiSelect(pParse, p, eDest, iParm);
+ }
+
+ /* Make local copies of the parameters for this query.
+ */
pEList = p->pEList;
pTabList = p->pSrc;
pWhere = p->pWhere;
Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
pExpr->iTable = i + pParse->nTab;
pExpr->iField = j;
- pEList = sqliteExprListAppend(pEList, pExpr, 0);
+ p->pEList = pEList = sqliteExprListAppend(pEList, pExpr, 0);
}
}
}
** step is skipped if the output is going to a table or a memory cell.
*/
if( eDest==SRT_Callback ){
- sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
- for(i=0; i<pEList->nExpr; i++){
- Expr *p;
- if( pEList->a[i].zName ){
- char *zName = pEList->a[i].zName;
- int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
- if( zName[0]=='\'' || zName[0]=='"' ){
- sqliteVdbeDequoteP3(v, addr);
- }
- continue;
- }
- p = pEList->a[i].pExpr;
- if( p->op!=TK_FIELD ){
- char zName[30];
- sprintf(zName, "field%d", i+1);
- sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
- }else{
- if( pTabList->nId>1 ){
- char *zName = 0;
- Table *pTab = pTabList->a[p->iTable].pTab;
- char *zTab;
-
- zTab = pTabList->a[p->iTable].zAlias;
- if( zTab==0 ) zTab = pTab->zName;
- sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0);
- sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
- sqliteFree(zName);
- }else{
- Table *pTab = pTabList->a[0].pTab;
- char *zName = pTab->aCol[p->iField].zName;
- sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
- }
- }
- }
+ generateColumnNames(v, pTabList, pEList);
}
/* Reset the aggregator
** aggregates
*/
if( !isAgg ){
- if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
+ if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
pWInfo->iContinue, pWInfo->iBreak) ){
return 1;
}
if( pHaving ){
sqliteExprIfFalse(pParse, pHaving, startagg);
}
- if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
+ if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
startagg, endagg) ){
return 1;
}
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.22 2000/06/06 19:18:24 drh Exp $
+** $Id: vdbe.c,v 1.23 2000/06/06 21:56:08 drh Exp $
*/
#include "sqliteInt.h"
#include <unistd.h>
struct VdbeTable {
DbbeTable *pTable; /* The table structure of the backend */
int index; /* The next index to extract */
+ int keyAsData; /* The OP_Field command works on key instead of data */
};
typedef struct VdbeTable VdbeTable;
static char *zOpName[] = { 0,
"Open", "Close", "Fetch", "New",
"Put", "Distinct", "Found", "NotFound",
- "Delete", "Field", "Key", "Rewind",
- "Next", "Destroy", "Reorganize", "ResetIdx",
- "NextIdx", "PutIdx", "DeleteIdx", "MemLoad",
- "MemStore", "ListOpen", "ListWrite", "ListRewind",
- "ListRead", "ListClose", "SortOpen", "SortPut",
- "SortMakeRec", "SortMakeKey", "Sort", "SortNext",
- "SortKey", "SortCallback", "SortClose", "FileOpen",
- "FileRead", "FileField", "FileClose", "AggReset",
- "AggFocus", "AggIncr", "AggNext", "AggSet",
- "AggGet", "SetInsert", "SetFound", "SetNotFound",
- "SetClear", "MakeRecord", "MakeKey", "Goto",
- "If", "Halt", "ColumnCount", "ColumnName",
- "Callback", "Integer", "String", "Null",
- "Pop", "Dup", "Pull", "Add",
- "AddImm", "Subtract", "Multiply", "Divide",
- "Min", "Max", "Like", "Glob",
- "Eq", "Ne", "Lt", "Le",
- "Gt", "Ge", "IsNull", "NotNull",
- "Negative", "And", "Or", "Not",
- "Concat", "Noop",
+ "Delete", "Field", "KeyAsData", "Key",
+ "Rewind", "Next", "Destroy", "Reorganize",
+ "ResetIdx", "NextIdx", "PutIdx", "DeleteIdx",
+ "MemLoad", "MemStore", "ListOpen", "ListWrite",
+ "ListRewind", "ListRead", "ListClose", "SortOpen",
+ "SortPut", "SortMakeRec", "SortMakeKey", "Sort",
+ "SortNext", "SortKey", "SortCallback", "SortClose",
+ "FileOpen", "FileRead", "FileField", "FileClose",
+ "AggReset", "AggFocus", "AggIncr", "AggNext",
+ "AggSet", "AggGet", "SetInsert", "SetFound",
+ "SetNotFound", "SetClear", "MakeRecord", "MakeKey",
+ "Goto", "If", "Halt", "ColumnCount",
+ "ColumnName", "Callback", "Integer", "String",
+ "Null", "Pop", "Dup", "Pull",
+ "Add", "AddImm", "Subtract", "Multiply",
+ "Divide", "Min", "Max", "Like",
+ "Glob", "Eq", "Ne", "Lt",
+ "Le", "Gt", "Ge", "IsNull",
+ "NotNull", "Negative", "And", "Or",
+ "Not", "Concat", "Noop",
};
/*
break;
}
+ /* Opcode: KeyAsData P1 P2 *
+ **
+ ** Turn the key-as-data mode for cursor P1 either on (if P2==1) or
+ ** off (if P2==0). In key-as-data mode, the OP_Fetch opcode pulls
+ ** data off of the key rather than the data. This is useful for
+ ** outer joins and stuff...
+ */
+ case OP_KeyAsData: {
+ int i = pOp->p1;
+ VdbeTable *pTab;
+ if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){
+ p->aTab[i].keyAsData = pOp->p2;
+ }
+ break;
+ }
+
/* Opcode: Field P1 P2 *
**
** Push onto the stack the value of the P2-th field from the
if( NeedStack(p, tos) ) goto no_mem;
if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){
- amt = sqliteDbbeDataLength(pTab);
- if( amt<=sizeof(int)*(p2+1) ){
- p->aStack[tos].flags = STK_Null;
- break;
- }
- pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2);
- if( *pAddr==0 ){
- p->aStack[tos].flags = STK_Null;
- break;
+ if( p->aTab[i].keyAsData ){
+ amt = sqliteDbbeKeyLength(pTab);
+ if( amt<=sizeof(int)*(p2+1) ){
+ p->aStack[tos].flags = STK_Null;
+ break;
+ }
+ pAddr = (int*)sqliteDbbeReadKey(pTab, sizeof(int)*p2);
+ if( *pAddr==0 ){
+ p->aStack[tos].flags = STK_Null;
+ break;
+ }
+ z = sqliteDbbeReadKey(pTab, *pAddr);
+ }else{
+ amt = sqliteDbbeDataLength(pTab);
+ if( amt<=sizeof(int)*(p2+1) ){
+ p->aStack[tos].flags = STK_Null;
+ break;
+ }
+ pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2);
+ if( *pAddr==0 ){
+ p->aStack[tos].flags = STK_Null;
+ break;
+ }
+ z = sqliteDbbeReadData(pTab, *pAddr);
}
- p->zStack[tos] = z = sqliteDbbeReadData(pTab, *pAddr);
+ p->zStack[tos] = z;
p->aStack[tos].n = strlen(z) + 1;
p->aStack[tos].flags = STK_Str;
}