/ Check-in [f4d9089c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment::-) (CVS 62)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f4d9089c5d69b16fee5feb49b02e524499e6328d
User & Date: drh 2000-06-06 21:56:08
Context
2000-06-06
22:13
:-) (CVS 63) check-in: 65d2100d user: drh tags: trunk
21:56
:-) (CVS 62) check-in: f4d9089c user: drh tags: trunk
19:18
:-) (CVS 61) check-in: 25984b4d user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/parse.y.

    22     22   **
    23     23   *************************************************************************
    24     24   ** This file contains SQLite's grammar for SQL.  Process this file
    25     25   ** using the lemon parser generator to generate C code that runs
    26     26   ** the parser.  Lemon will also generate a header file containing
    27     27   ** numeric codes for all of the tokens.
    28     28   **
    29         -** @(#) $Id: parse.y,v 1.13 2000/06/06 17:27:05 drh Exp $
           29  +** @(#) $Id: parse.y,v 1.14 2000/06/06 21:56:08 drh Exp $
    30     30   */
    31     31   %token_prefix TK_
    32     32   %token_type {Token}
    33     33   %extra_argument {Parse *pParse}
    34     34   %syntax_error {
    35     35     sqliteSetNString(&pParse->zErrMsg,"syntax error near \"",0,TOKEN.z,TOKEN.n,
    36     36                      "\"", 1, 0);
................................................................................
   135    135   cmd ::= select(X).  {
   136    136     sqliteSelect(pParse, X, SRT_Callback, 0);
   137    137     sqliteSelectDelete(X);
   138    138   }
   139    139   
   140    140   %type select {Select*}
   141    141   %destructor select {sqliteSelectDelete($$);}
          142  +%type oneselect {Select*}
          143  +%destructor oneselect {sqliteSelectDelete($$);}
   142    144   
   143         -select(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
   144         -              groupby_opt(P) having_opt(Q) orderby_opt(Z). {
          145  +select(A) ::= oneselect(X).                      {A = X;}
          146  +select(A) ::= select(X) joinop(Y) oneselect(Z).  {
          147  +    Z->op = Y;
          148  +    Z->pPrior = X;
          149  +    A = Z;
          150  +}
          151  +%type joinop {int}
          152  +joinop(A) ::= UNION.      {A = TK_UNION;}
          153  +joinop(A) ::= UNION ALL.  {A = TK_ALL;}
          154  +joinop(A) ::= INTERSECT.  {A = TK_INTERSECT;}
          155  +joinop(A) ::= EXCEPT.     {A = TK_EXCEPT;}
          156  +oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
          157  +                 groupby_opt(P) having_opt(Q) orderby_opt(Z). {
   145    158     A = sqliteSelectNew(W,X,Y,P,Q,Z,D);
   146    159   }
   147    160   
   148    161   // The "distinct" nonterminal is true (1) if the DISTINCT keyword is
   149    162   // present and false (0) if it is not.
   150    163   //
   151    164   %type distinct {int}
................................................................................
   217    230   groupby_opt(A) ::= .     {A = 0;}
   218    231   groupby_opt(A) ::= GROUP BY exprlist(X).  {A = X;}
   219    232   
   220    233   %type having_opt {Expr*}
   221    234   %destructor having_opt {sqliteExprDelete($$);}
   222    235   having_opt(A) ::= .      {A = 0;}
   223    236   having_opt(A) ::= HAVING expr(X).  {A = X;}
          237  +
   224    238   
   225    239   cmd ::= DELETE FROM ID(X) where_opt(Y).
   226    240       {sqliteDeleteFrom(pParse, &X, Y);}
   227    241   
   228    242   %type where_opt {Expr*}
   229    243   %destructor where_opt {sqliteExprDelete($$);}
   230    244   

Changes to src/select.c.

    20     20   **   drh@hwaci.com
    21     21   **   http://www.hwaci.com/drh/
    22     22   **
    23     23   *************************************************************************
    24     24   ** This file contains C code routines that are called by the parser
    25     25   ** to handle SELECT statements.
    26     26   **
    27         -** $Id: select.c,v 1.12 2000/06/06 18:00:16 drh Exp $
           27  +** $Id: select.c,v 1.13 2000/06/06 21:56:08 drh Exp $
    28     28   */
    29     29   #include "sqliteInt.h"
    30     30   
    31     31   /*
    32     32   ** Allocate a new Select structure and return a pointer to that
    33     33   ** structure.
    34     34   */
................................................................................
    47     47     pNew->pEList = pEList;
    48     48     pNew->pSrc = pSrc;
    49     49     pNew->pWhere = pWhere;
    50     50     pNew->pGroupBy = pGroupBy;
    51     51     pNew->pHaving = pHaving;
    52     52     pNew->pOrderBy = pOrderBy;
    53     53     pNew->isDistinct = isDistinct;
           54  +  pNew->op = TK_SELECT;
    54     55     return pNew;
    55     56   }
    56     57   
    57     58   /*
    58     59   ** Delete the given Select structure and all of its substructures.
    59     60   */
    60     61   void sqliteSelectDelete(Select *p){
           62  +  if( p==0 ) return;
    61     63     sqliteExprListDelete(p->pEList);
    62     64     sqliteIdListDelete(p->pSrc);
    63     65     sqliteExprDelete(p->pWhere);
    64     66     sqliteExprListDelete(p->pGroupBy);
    65     67     sqliteExprDelete(p->pHaving);
    66     68     sqliteExprListDelete(p->pOrderBy);
           69  +  sqliteSelectDelete(p->pPrior);
    67     70     sqliteFree(p);
    68     71   }
    69     72   
    70     73   /*
    71     74   ** Delete the aggregate information from the parse structure.
    72     75   */
    73     76   void sqliteParseInfoReset(Parse *pParse){
................................................................................
    77     80     pParse->iAggCount = -1;
    78     81     pParse->useAgg = 0;
    79     82   }
    80     83   
    81     84   /*
    82     85   ** This routine generates the code for the inside of the inner loop
    83     86   ** of a SELECT.
           87  +**
           88  +** The pEList is used to determine the values for each column in the
           89  +** result row.  Except  if pEList==NULL, then we just read nField
           90  +** elements from the srcTab table.
    84     91   */
    85     92   static int selectInnerLoop(
    86     93     Parse *pParse,          /* The parser context */
    87     94     ExprList *pEList,       /* List of values being extracted */
           95  +  int srcTab,             /* Pull data from this table */
           96  +  int nField,             /* Number of fields in the source table */
    88     97     ExprList *pOrderBy,     /* If not NULL, sort results using this key */
    89     98     int distinct,           /* If >=0, make sure results are distinct */
    90     99     int eDest,              /* How to dispose of the results */
    91    100     int iParm,              /* An argument to the disposal method */
    92    101     int iContinue,          /* Jump here to continue with next row */
    93    102     int iBreak              /* Jump here to break out of the inner loop */
    94    103   ){
    95    104     Vdbe *v = pParse->pVdbe;
    96    105     int i;
    97    106   
    98    107     /* Pull the requested fields.
    99    108     */
   100         -  for(i=0; i<pEList->nExpr; i++){
   101         -    sqliteExprCode(pParse, pEList->a[i].pExpr);
          109  +  if( pEList ){
          110  +    for(i=0; i<pEList->nExpr; i++){
          111  +      sqliteExprCode(pParse, pEList->a[i].pExpr);
          112  +    }
          113  +    nField = pEList->nExpr;
          114  +  }else{
          115  +    for(i=0; i<nField; i++){
          116  +      sqliteVdbeAddOp(v, OP_Field, srcTab, i, 0, 0);
          117  +    }
   102    118     }
   103    119   
   104    120     /* If the current result is not distinct, skip the rest
   105    121     ** of the processing for the current row.
   106    122     */
   107    123     if( distinct>=0 ){
   108    124       int lbl = sqliteVdbeMakeLabel(v);
................................................................................
   109    125       sqliteVdbeAddOp(v, OP_MakeKey, pEList->nExpr, 1, 0, 0);
   110    126       sqliteVdbeAddOp(v, OP_Distinct, distinct, lbl, 0, 0);
   111    127       sqliteVdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0, 0, 0);
   112    128       sqliteVdbeAddOp(v, OP_Goto, 0, iContinue, 0, 0);
   113    129       sqliteVdbeAddOp(v, OP_String, 0, 0, "", lbl);
   114    130       sqliteVdbeAddOp(v, OP_Put, distinct, 0, 0, 0);
   115    131     }
          132  +
   116    133     /* If there is an ORDER BY clause, then store the results
   117    134     ** in a sorter.
   118    135     */
   119    136     if( pOrderBy ){
   120    137       char *zSortOrder;
   121    138       sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
   122    139       zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
................................................................................
   126    143         sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
   127    144       }
   128    145       zSortOrder[pOrderBy->nExpr] = 0;
   129    146       sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0);
   130    147       sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
   131    148     }else 
   132    149   
   133         -  /* If we are writing to a table, then write the results to the table.
          150  +  /* In this mode, write each query result to the key of the temporary
          151  +  ** table iParm.
   134    152     */
   135         -  if( eDest==SRT_Table ){
   136         -    sqliteVdbeAddOp(v, OP_MakeRecord, pEList->nExpr, 0, 0, 0);
   137         -    sqliteVdbeAddOp(v, OP_New, iParm, 0, 0, 0);
   138         -    sqliteVdbeAddOp(v, OP_Pull, 1, 0, 0, 0);
          153  +  if( eDest==SRT_Union ){
          154  +    sqliteVdbeAddOp(v, OP_MakeRecord, nField, 0, 0, 0);
          155  +    sqliteVdbeAddOp(v, OP_String, iParm, 0, "", 0);
          156  +    sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
          157  +  }else 
          158  +
          159  +  /* Construct a record from the query result, but instead of
          160  +  ** saving that record, use it as a key to delete elements from
          161  +  ** the temporary table iParm.
          162  +  */
          163  +  if( eDest==SRT_Except ){
          164  +    assert( pEList->nExpr==1 );
          165  +    sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
   139    166       sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
   140    167     }else 
   141    168   
   142    169     /* If we are creating a set for an "expr IN (SELECT ...)" construct,
   143    170     ** then there should be a single item on the stack.  Write this
   144    171     ** item into the set table with bogus data.
   145    172     */
   146    173     if( eDest==SRT_Set ){
   147    174       assert( pEList->nExpr==1 );
   148    175       sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
   149    176       sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
   150    177     }else 
          178  +
   151    179   
   152    180     /* If this is a scalar select that is part of an expression, then
   153    181     ** store the results in the appropriate memory cell and break out
   154    182     ** of the scan loop.
   155    183     */
   156    184     if( eDest==SRT_Mem ){
   157    185       sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
   158    186       sqliteVdbeAddOp(v, OP_Goto, 0, iBreak, 0, 0);
   159    187     }else
   160    188   
   161    189     /* If none of the above, send the data to the callback function.
   162    190     */
   163    191     {
   164         -    sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
          192  +    sqliteVdbeAddOp(v, OP_Callback, nField, 0, 0, 0);
          193  +  }
          194  +  return 0;
          195  +}
          196  +
          197  +/*
          198  +** Generate code that will tell the VDBE how many columns there
          199  +** are in the result and the name for each column.  This information
          200  +** is used to provide "argc" and "azCol[]" values in the callback.
          201  +*/
          202  +static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){
          203  +  int i;
          204  +  sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
          205  +  for(i=0; i<pEList->nExpr; i++){
          206  +    Expr *p;
          207  +    if( pEList->a[i].zName ){
          208  +      char *zName = pEList->a[i].zName;
          209  +      int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
          210  +      if( zName[0]=='\'' || zName[0]=='"' ){
          211  +        sqliteVdbeDequoteP3(v, addr);
          212  +      }
          213  +      continue;
          214  +    }
          215  +    p = pEList->a[i].pExpr;
          216  +    if( p->op!=TK_FIELD || pTabList==0 ){
          217  +      char zName[30];
          218  +      sprintf(zName, "field%d", i+1);
          219  +      sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
          220  +    }else{
          221  +      if( pTabList->nId>1 ){
          222  +        char *zName = 0;
          223  +        Table *pTab = pTabList->a[p->iTable].pTab;
          224  +        char *zTab;
          225  + 
          226  +        zTab = pTabList->a[p->iTable].zAlias;
          227  +        if( zTab==0 ) zTab = pTab->zName;
          228  +        sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0);
          229  +        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
          230  +        sqliteFree(zName);
          231  +      }else{
          232  +        Table *pTab = pTabList->a[0].pTab;
          233  +        char *zName = pTab->aCol[p->iField].zName;
          234  +        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
          235  +      }
          236  +    }
          237  +  }
          238  +}
          239  +
          240  +/*
          241  +** This routine is called to process a query that is really the union
          242  +** or intersection of two or more separate queries.
          243  +*/
          244  +static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
          245  +  int rc;
          246  +  Select *pPrior;
          247  +  Vdbe *v;
          248  +  int i;
          249  +
          250  +  /* Make sure we have a valid query engine.  If not, create a new one.
          251  +  */
          252  +  v = pParse->pVdbe;
          253  +  if( v==0 ){
          254  +    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
          255  +  }
          256  +  if( v==0 ){
          257  +    sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
          258  +    pParse->nErr++;
          259  +    return 1;
          260  +  }
          261  +
          262  +  assert( p->pPrior!=0 );
          263  +  pPrior = p->pPrior;
          264  +  switch( p->op ){
          265  +    case TK_ALL: {
          266  +      rc = sqliteSelect(pParse, pPrior, eDest, iParm);
          267  +      if( rc ) return rc;
          268  +      p->pPrior = 0;
          269  +      rc = sqliteSelect(pParse, p, eDest, iParm);
          270  +      p->pPrior = pPrior;
          271  +      break;
          272  +    }
          273  +    case TK_EXCEPT:
          274  +    case TK_UNION: {
          275  +      int unionTab;
          276  +      int op;
          277  +
          278  +      if( eDest==SRT_Union ){
          279  +        unionTab = iParm;
          280  +      }else{
          281  +        unionTab = pParse->nTab++;          
          282  +        sqliteVdbeAddOp(v, OP_Open, unionTab, 1, 0, 0);
          283  +        sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0);
          284  +      }
          285  +      rc = sqliteSelect(pParse, pPrior, SRT_Union, unionTab);
          286  +      if( rc ) return rc;
          287  +      op = p->op==TK_EXCEPT ? SRT_Except : SRT_Union;
          288  +      p->pPrior = 0;
          289  +      rc = sqliteSelect(pParse, p, op, unionTab);
          290  +      p->pPrior = pPrior;
          291  +      if( rc ) return rc;
          292  +      if( eDest!=SRT_Union ){
          293  +        int iCont, iBreak;
          294  +        assert( p->pEList );
          295  +        generateColumnNames(v, 0, p->pEList);
          296  +        iBreak = sqliteVdbeMakeLabel(v);
          297  +        iCont = sqliteVdbeAddOp(v, OP_Next, unionTab, iBreak, 0, 0);
          298  +        rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr,
          299  +                             0, -1, eDest, iParm, 
          300  +                             iCont, iBreak);
          301  +        if( rc ) return 1;
          302  +        sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
          303  +        sqliteVdbeAddOp(v, OP_Close, unionTab, 0, 0, iBreak);
          304  +      }
          305  +      break;
          306  +    }
          307  +    case TK_INTERSECT: {
          308  +      int tab1, tab2;
          309  +      Select *pPrior;
          310  +      int iCont, iBreak;
          311  +
          312  +      tab1 = pParse->nTab++;
          313  +      tab2 = pParse->nTab++;
          314  +      sqliteVdbeAddOp(v, OP_Open, tab1, 1, 0, 0);
          315  +      sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0);
          316  +      rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1);
          317  +      if( rc ) return rc;
          318  +      sqliteVdbeAddOp(v, OP_Open, tab2, 1, 0, 0);
          319  +      sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0);
          320  +      p->pPrior = 0;
          321  +      rc = sqliteSelect(pParse, p, SRT_Union, tab2);
          322  +      p->pPrior = pPrior;
          323  +      if( rc ) return rc;
          324  +      assert( p->pEList );
          325  +      generateColumnNames(v, 0, p->pEList);
          326  +      iBreak = sqliteVdbeMakeLabel(v);
          327  +      iCont = sqliteVdbeAddOp(v, OP_Next, tab1, iBreak, 0, 0);
          328  +      sqliteVdbeAddOp(v, OP_Key, tab1, 0, 0, 0);
          329  +      sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont, 0, 0);
          330  +      rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr,
          331  +                             0, -1, eDest, iParm, 
          332  +                             iCont, iBreak);
          333  +      if( rc ) return 1;
          334  +      sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
          335  +      sqliteVdbeAddOp(v, OP_Close, tab2, 0, 0, iBreak);
          336  +      sqliteVdbeAddOp(v, OP_Close, tab1, 0, 0, 0);
          337  +      break;
          338  +    }
          339  +  }
          340  +  assert( p->pEList && pPrior->pEList );
          341  +  if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
          342  +    sqliteSetString(&pParse->zErrMsg, "SELECTs have different numbers "
          343  +       "of columns and therefore cannot be joined", 0);
          344  +    pParse->nErr++;
          345  +    return 1;
   165    346     }
   166    347     return 0;
   167    348   }
   168    349   
   169    350   /*
   170    351   ** Generate code for the given SELECT statement.
   171    352   **
................................................................................
   176    357   **     ------------    -------------------------------------------
   177    358   **     SRT_Callback    Invoke the callback for each row of the result.
   178    359   **
   179    360   **     SRT_Mem         Store first result in memory cell iParm
   180    361   **
   181    362   **     SRT_Set         Store results as keys of a table with cursor iParm
   182    363   **
   183         -**     SRT_Table       Store results in a regular table with cursor iParm
          364  +**     SRT_Union       Store results as a key in a temporary table iParm
          365  +**
          366  +**     SRT_Except      Remove results form the temporary talbe iParm.
   184    367   **
   185    368   ** This routine returns the number of errors.  If any errors are
   186    369   ** encountered, then an appropriate error message is left in
   187    370   ** pParse->zErrMsg.
   188    371   **
   189    372   ** This routine does NOT free the Select structure passed in.  The
   190    373   ** calling function needs to do that.
   191    374   */
   192    375   int sqliteSelect(
   193    376     Parse *pParse,         /* The parser context */
   194    377     Select *p,             /* The SELECT statement being coded. */
   195         -  int eDest,             /* One of SRT_Callback, SRT_Mem, SRT_Set, SRT_Table */
          378  +  int eDest,             /* One of: SRT_Callback Mem Set Union Except */
   196    379     int iParm              /* Save result in this memory location, if >=0 */
   197    380   ){
   198    381     int i, j;
   199    382     WhereInfo *pWInfo;
   200    383     Vdbe *v;
   201    384     int isAgg = 0;         /* True for select lists like "count(*)" */
   202    385     ExprList *pEList;      /* List of fields to extract.  NULL means "*" */
................................................................................
   204    387     Expr *pWhere;          /* The WHERE clause.  May be NULL */
   205    388     ExprList *pOrderBy;    /* The ORDER BY clause.  May be NULL */
   206    389     ExprList *pGroupBy;    /* The GROUP BY clause.  May be NULL */
   207    390     Expr *pHaving;         /* The HAVING clause.  May be NULL */
   208    391     int isDistinct;        /* True if the DISTINCT keyword is present */
   209    392     int distinct;          /* Table to use for the distinct set */
   210    393   
          394  +  /* If there is are a sequence of queries, do the earlier ones first.
          395  +  */
          396  +  if( p->pPrior ){
          397  +    return multiSelect(pParse, p, eDest, iParm);
          398  +  }
          399  +
          400  +  /* Make local copies of the parameters for this query.
          401  +  */
   211    402     pEList = p->pEList;
   212    403     pTabList = p->pSrc;
   213    404     pWhere = p->pWhere;
   214    405     pOrderBy = p->pOrderBy;
   215    406     pGroupBy = p->pGroupBy;
   216    407     pHaving = p->pHaving;
   217    408     isDistinct = p->isDistinct;
................................................................................
   251    442     if( pEList==0 ){
   252    443       for(i=0; i<pTabList->nId; i++){
   253    444         Table *pTab = pTabList->a[i].pTab;
   254    445         for(j=0; j<pTab->nCol; j++){
   255    446           Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
   256    447           pExpr->iTable = i + pParse->nTab;
   257    448           pExpr->iField = j;
   258         -        pEList = sqliteExprListAppend(pEList, pExpr, 0);
          449  +        p->pEList = pEList = sqliteExprListAppend(pEList, pExpr, 0);
   259    450         }
   260    451       }
   261    452     }
   262    453   
   263    454     /* If writing to memory or generating a set
   264    455     ** only a single column may be output.
   265    456     */
................................................................................
   384    575       sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0);
   385    576     }
   386    577   
   387    578     /* Identify column names if we will be using in the callback.  This
   388    579     ** step is skipped if the output is going to a table or a memory cell.
   389    580     */
   390    581     if( eDest==SRT_Callback ){
   391         -    sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
   392         -    for(i=0; i<pEList->nExpr; i++){
   393         -      Expr *p;
   394         -      if( pEList->a[i].zName ){
   395         -        char *zName = pEList->a[i].zName;
   396         -        int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
   397         -        if( zName[0]=='\'' || zName[0]=='"' ){
   398         -          sqliteVdbeDequoteP3(v, addr);
   399         -        }
   400         -        continue;
   401         -      }
   402         -      p = pEList->a[i].pExpr;
   403         -      if( p->op!=TK_FIELD ){
   404         -        char zName[30];
   405         -        sprintf(zName, "field%d", i+1);
   406         -        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
   407         -      }else{
   408         -        if( pTabList->nId>1 ){
   409         -          char *zName = 0;
   410         -          Table *pTab = pTabList->a[p->iTable].pTab;
   411         -          char *zTab;
   412         -  
   413         -          zTab = pTabList->a[p->iTable].zAlias;
   414         -          if( zTab==0 ) zTab = pTab->zName;
   415         -          sqliteSetString(&zName, zTab, ".", pTab->aCol[p->iField].zName, 0);
   416         -          sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
   417         -          sqliteFree(zName);
   418         -        }else{
   419         -          Table *pTab = pTabList->a[0].pTab;
   420         -          char *zName = pTab->aCol[p->iField].zName;
   421         -          sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
   422         -        }
   423         -      }
   424         -    }
          582  +    generateColumnNames(v, pTabList, pEList);
   425    583     }
   426    584   
   427    585     /* Reset the aggregator
   428    586     */
   429    587     if( isAgg ){
   430    588       sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg, 0, 0);
   431    589     }
................................................................................
   445    603     pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
   446    604     if( pWInfo==0 ) return 1;
   447    605   
   448    606     /* Use the standard inner loop if we are not dealing with
   449    607     ** aggregates
   450    608     */
   451    609     if( !isAgg ){
   452         -    if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
          610  +    if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
   453    611                       pWInfo->iContinue, pWInfo->iBreak) ){
   454    612          return 1;
   455    613       }
   456    614     }
   457    615   
   458    616     /* If we are dealing with aggregates, then to the special aggregate
   459    617     ** processing.  
................................................................................
   524    682       int endagg = sqliteVdbeMakeLabel(v);
   525    683       int startagg;
   526    684       startagg = sqliteVdbeAddOp(v, OP_AggNext, 0, endagg, 0, 0);
   527    685       pParse->useAgg = 1;
   528    686       if( pHaving ){
   529    687         sqliteExprIfFalse(pParse, pHaving, startagg);
   530    688       }
   531         -    if( selectInnerLoop(pParse, pEList, pOrderBy, distinct, eDest, iParm,
          689  +    if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
   532    690                       startagg, endagg) ){
   533    691         return 1;
   534    692       }
   535    693       sqliteVdbeAddOp(v, OP_Goto, 0, startagg, 0, 0);
   536    694       sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, endagg);
   537    695       pParse->useAgg = 0;
   538    696     }

Changes to src/sqliteInt.h.

    19     19   ** Author contact information:
    20     20   **   drh@hwaci.com
    21     21   **   http://www.hwaci.com/drh/
    22     22   **
    23     23   *************************************************************************
    24     24   ** Internal interface definitions for SQLite.
    25     25   **
    26         -** @(#) $Id: sqliteInt.h,v 1.18 2000/06/06 17:27:05 drh Exp $
           26  +** @(#) $Id: sqliteInt.h,v 1.19 2000/06/06 21:56:08 drh Exp $
    27     27   */
    28     28   #include "sqlite.h"
    29     29   #include "dbbe.h"
    30     30   #include "vdbe.h"
    31     31   #include "parse.h"
    32     32   #include <gdbm.h>
    33     33   #include <stdio.h>
................................................................................
   224    224     int isDistinct;        /* True if the DISTINCT keyword is present */
   225    225     ExprList *pEList;      /* The fields of the result */
   226    226     IdList *pSrc;          /* The FROM clause */
   227    227     Expr *pWhere;          /* The WHERE clause */
   228    228     ExprList *pGroupBy;    /* The GROUP BY clause */
   229    229     Expr *pHaving;         /* The HAVING clause */
   230    230     ExprList *pOrderBy;    /* The ORDER BY clause */
          231  +  int op;                /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
          232  +  Select *pPrior;        /* Prior select to which this one joins */
   231    233   };
   232    234   
   233    235   /*
   234    236   ** The results of a select can be distributed in several ways.
   235    237   */
   236    238   #define SRT_Callback     1  /* Invoke a callback with each row of result */
   237    239   #define SRT_Mem          2  /* Store result in a memory cell */
   238         -#define SRT_Set          3  /* Store result in a table for use with "IN" */
   239         -#define SRT_Table        4  /* Store result in a regular table */
          240  +#define SRT_Set          3  /* Store result as unique keys in a table */
          241  +#define SRT_Union        5  /* Store result as keys in a table */
          242  +#define SRT_Except       6  /* Remove result from a UNION table */
   240    243   
   241    244   /*
   242    245   ** When a SELECT uses aggregate functions (like "count(*)" or "avg(f1)")
   243    246   ** we have to do some additional analysis of expressions.  An instance
   244    247   ** of the following structure holds information about a single subexpression
   245    248   ** somewhere in the SELECT statement.  An array of these structures holds
   246    249   ** all the information we need to generate code for aggregate

Changes to src/tokenize.c.

    23     23   *************************************************************************
    24     24   ** An tokenizer for SQL
    25     25   **
    26     26   ** This file contains C code that splits an SQL input string up into
    27     27   ** individual tokens and sends those tokens one-by-one over to the
    28     28   ** parser for analysis.
    29     29   **
    30         -** $Id: tokenize.c,v 1.7 2000/06/06 17:27:06 drh Exp $
           30  +** $Id: tokenize.c,v 1.8 2000/06/06 21:56:08 drh Exp $
    31     31   */
    32     32   #include "sqliteInt.h"
    33     33   #include <ctype.h>
    34     34   #include <stdlib.h>
    35     35   
    36     36   /*
    37     37   ** All the keywords of the SQL language are stored as in a hash
................................................................................
    61     61     { "CREATE",            0, TK_CREATE,           0 },
    62     62     { "DEFAULT",           0, TK_DEFAULT,          0 },
    63     63     { "DELETE",            0, TK_DELETE,           0 },
    64     64     { "DELIMITERS",        0, TK_DELIMITERS,       0 },
    65     65     { "DESC",              0, TK_DESC,             0 },
    66     66     { "DISTINCT",          0, TK_DISTINCT,         0 },
    67     67     { "DROP",              0, TK_DROP,             0 },
           68  +  { "EXCEPT",            0, TK_EXCEPT,           0 },
    68     69     { "EXPLAIN",           0, TK_EXPLAIN,          0 },
    69     70     { "FROM",              0, TK_FROM,             0 },
    70     71     { "GLOB",              0, TK_GLOB,             0 },
    71     72     { "GROUP",             0, TK_GROUP,            0 },
    72     73     { "HAVING",            0, TK_HAVING,           0 },
    73     74     { "IN",                0, TK_IN,               0 },
    74     75     { "INDEX",             0, TK_INDEX,            0 },
    75     76     { "INSERT",            0, TK_INSERT,           0 },
           77  +  { "INTERSECT",         0, TK_INTERSECT,        0 },
    76     78     { "INTO",              0, TK_INTO,             0 },
    77     79     { "IS",                0, TK_IS,               0 },
    78     80     { "ISNULL",            0, TK_ISNULL,           0 },
    79     81     { "KEY",               0, TK_KEY,              0 },
    80     82     { "LIKE",              0, TK_LIKE,             0 },
    81     83     { "NOT",               0, TK_NOT,              0 },
    82     84     { "NOTNULL",           0, TK_NOTNULL,          0 },
................................................................................
    84     86     { "ON",                0, TK_ON,               0 },
    85     87     { "OR",                0, TK_OR,               0 },
    86     88     { "ORDER",             0, TK_ORDER,            0 },
    87     89     { "PRIMARY",           0, TK_PRIMARY,          0 },
    88     90     { "SELECT",            0, TK_SELECT,           0 },
    89     91     { "SET",               0, TK_SET,              0 },
    90     92     { "TABLE",             0, TK_TABLE,            0 },
           93  +  { "UNION",             0, TK_UNION,            0 },
    91     94     { "UNIQUE",            0, TK_UNIQUE,           0 },
    92     95     { "UPDATE",            0, TK_UPDATE,           0 },
    93     96     { "USING",             0, TK_USING,            0 },
    94     97     { "VACUUM",            0, TK_VACUUM,           0 },
    95     98     { "VALUES",            0, TK_VALUES,           0 },
    96     99     { "WHERE",             0, TK_WHERE,            0 },
    97    100   };

Changes to src/vdbe.c.

    37     37   ** inplicit conversion from one type to the other occurs as necessary.
    38     38   ** 
    39     39   ** Most of the code in this file is taken up by the sqliteVdbeExec()
    40     40   ** function which does the work of interpreting a VDBE program.
    41     41   ** But other routines are also provided to help in building up
    42     42   ** a program instruction by instruction.
    43     43   **
    44         -** $Id: vdbe.c,v 1.22 2000/06/06 19:18:24 drh Exp $
           44  +** $Id: vdbe.c,v 1.23 2000/06/06 21:56:08 drh Exp $
    45     45   */
    46     46   #include "sqliteInt.h"
    47     47   #include <unistd.h>
    48     48   
    49     49   /*
    50     50   ** SQL is translated into a sequence of instructions to be
    51     51   ** executed by a virtual machine.  Each instruction is an instance
................................................................................
    56     56   /*
    57     57   ** Every table that the virtual machine has open is represented by an
    58     58   ** instance of the following structure.
    59     59   */
    60     60   struct VdbeTable {
    61     61     DbbeTable *pTable;    /* The table structure of the backend */
    62     62     int index;            /* The next index to extract */
           63  +  int keyAsData;        /* The OP_Field command works on key instead of data */
    63     64   };
    64     65   typedef struct VdbeTable VdbeTable;
    65     66   
    66     67   /*
    67     68   ** A sorter builds a list of elements to be sorted.  Each element of
    68     69   ** the list is an instance of the following structure.
    69     70   */
................................................................................
   731    732   ** change, be sure to change this array to match.  You can use the
   732    733   ** "opNames.awk" awk script which is part of the source tree to regenerate
   733    734   ** this array, then copy and paste it into this file, if you want.
   734    735   */
   735    736   static char *zOpName[] = { 0,
   736    737     "Open",           "Close",          "Fetch",          "New",
   737    738     "Put",            "Distinct",       "Found",          "NotFound",
   738         -  "Delete",         "Field",          "Key",            "Rewind",
   739         -  "Next",           "Destroy",        "Reorganize",     "ResetIdx",
   740         -  "NextIdx",        "PutIdx",         "DeleteIdx",      "MemLoad",
   741         -  "MemStore",       "ListOpen",       "ListWrite",      "ListRewind",
   742         -  "ListRead",       "ListClose",      "SortOpen",       "SortPut",
   743         -  "SortMakeRec",    "SortMakeKey",    "Sort",           "SortNext",
   744         -  "SortKey",        "SortCallback",   "SortClose",      "FileOpen",
   745         -  "FileRead",       "FileField",      "FileClose",      "AggReset",
   746         -  "AggFocus",       "AggIncr",        "AggNext",        "AggSet",
   747         -  "AggGet",         "SetInsert",      "SetFound",       "SetNotFound",
   748         -  "SetClear",       "MakeRecord",     "MakeKey",        "Goto",
   749         -  "If",             "Halt",           "ColumnCount",    "ColumnName",
   750         -  "Callback",       "Integer",        "String",         "Null",
   751         -  "Pop",            "Dup",            "Pull",           "Add",
   752         -  "AddImm",         "Subtract",       "Multiply",       "Divide",
   753         -  "Min",            "Max",            "Like",           "Glob",
   754         -  "Eq",             "Ne",             "Lt",             "Le",
   755         -  "Gt",             "Ge",             "IsNull",         "NotNull",
   756         -  "Negative",       "And",            "Or",             "Not",
   757         -  "Concat",         "Noop",         
          739  +  "Delete",         "Field",          "KeyAsData",      "Key",
          740  +  "Rewind",         "Next",           "Destroy",        "Reorganize",
          741  +  "ResetIdx",       "NextIdx",        "PutIdx",         "DeleteIdx",
          742  +  "MemLoad",        "MemStore",       "ListOpen",       "ListWrite",
          743  +  "ListRewind",     "ListRead",       "ListClose",      "SortOpen",
          744  +  "SortPut",        "SortMakeRec",    "SortMakeKey",    "Sort",
          745  +  "SortNext",       "SortKey",        "SortCallback",   "SortClose",
          746  +  "FileOpen",       "FileRead",       "FileField",      "FileClose",
          747  +  "AggReset",       "AggFocus",       "AggIncr",        "AggNext",
          748  +  "AggSet",         "AggGet",         "SetInsert",      "SetFound",
          749  +  "SetNotFound",    "SetClear",       "MakeRecord",     "MakeKey",
          750  +  "Goto",           "If",             "Halt",           "ColumnCount",
          751  +  "ColumnName",     "Callback",       "Integer",        "String",
          752  +  "Null",           "Pop",            "Dup",            "Pull",
          753  +  "Add",            "AddImm",         "Subtract",       "Multiply",
          754  +  "Divide",         "Min",            "Max",            "Like",
          755  +  "Glob",           "Eq",             "Ne",             "Lt",
          756  +  "Le",             "Gt",             "Ge",             "IsNull",
          757  +  "NotNull",        "Negative",       "And",            "Or",
          758  +  "Not",            "Concat",         "Noop",         
   758    759   };
   759    760   
   760    761   /*
   761    762   ** Given the name of an opcode, return its number.  Return 0 if
   762    763   ** there is no match.
   763    764   **
   764    765   ** This routine is used for testing and debugging.
................................................................................
  1877   1878               zKey = p->zStack[tos];
  1878   1879             }
  1879   1880             sqliteDbbeDelete(p->aTab[i].pTable, nKey, zKey);
  1880   1881           }
  1881   1882           PopStack(p, 1);
  1882   1883           break;
  1883   1884         }
         1885  +
         1886  +      /* Opcode: KeyAsData P1 P2 *
         1887  +      **
         1888  +      ** Turn the key-as-data mode for cursor P1 either on (if P2==1) or
         1889  +      ** off (if P2==0).  In key-as-data mode, the OP_Fetch opcode pulls
         1890  +      ** data off of the key rather than the data.  This is useful for
         1891  +      ** outer joins and stuff...
         1892  +      */
         1893  +      case OP_KeyAsData: {
         1894  +        int i = pOp->p1;
         1895  +        VdbeTable *pTab;
         1896  +        if( i>=0 && i<p->nTable && p->aTab[i].pTable!=0 ){
         1897  +          p->aTab[i].keyAsData = pOp->p2;
         1898  +        }
         1899  +        break;
         1900  +      }
  1884   1901   
  1885   1902         /* Opcode: Field P1 P2 *
  1886   1903         **
  1887   1904         ** Push onto the stack the value of the P2-th field from the
  1888   1905         ** most recent Fetch from table P1.
  1889   1906         ** 
  1890   1907         ** The value pushed is just a pointer to the data in the cursor.
................................................................................
  1899   1916           int p2 = pOp->p2;
  1900   1917           int tos = ++p->tos;
  1901   1918           DbbeTable *pTab;
  1902   1919           char *z;
  1903   1920   
  1904   1921           if( NeedStack(p, tos) ) goto no_mem;
  1905   1922           if( i>=0 && i<p->nTable && (pTab = p->aTab[i].pTable)!=0 ){
  1906         -          amt = sqliteDbbeDataLength(pTab);
  1907         -          if( amt<=sizeof(int)*(p2+1) ){
  1908         -            p->aStack[tos].flags = STK_Null;
  1909         -            break;
         1923  +          if( p->aTab[i].keyAsData ){
         1924  +            amt = sqliteDbbeKeyLength(pTab);
         1925  +            if( amt<=sizeof(int)*(p2+1) ){
         1926  +              p->aStack[tos].flags = STK_Null;
         1927  +              break;
         1928  +            }
         1929  +            pAddr = (int*)sqliteDbbeReadKey(pTab, sizeof(int)*p2);
         1930  +            if( *pAddr==0 ){
         1931  +              p->aStack[tos].flags = STK_Null;
         1932  +              break;
         1933  +            }
         1934  +            z = sqliteDbbeReadKey(pTab, *pAddr);
         1935  +          }else{
         1936  +            amt = sqliteDbbeDataLength(pTab);
         1937  +            if( amt<=sizeof(int)*(p2+1) ){
         1938  +              p->aStack[tos].flags = STK_Null;
         1939  +              break;
         1940  +            }
         1941  +            pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2);
         1942  +            if( *pAddr==0 ){
         1943  +              p->aStack[tos].flags = STK_Null;
         1944  +              break;
         1945  +            }
         1946  +            z = sqliteDbbeReadData(pTab, *pAddr);
  1910   1947             }
  1911         -          pAddr = (int*)sqliteDbbeReadData(pTab, sizeof(int)*p2);
  1912         -          if( *pAddr==0 ){
  1913         -            p->aStack[tos].flags = STK_Null;
  1914         -            break;
  1915         -          }
  1916         -          p->zStack[tos] = z = sqliteDbbeReadData(pTab, *pAddr);
         1948  +          p->zStack[tos] = z;
  1917   1949             p->aStack[tos].n = strlen(z) + 1;
  1918   1950             p->aStack[tos].flags = STK_Str;
  1919   1951           }
  1920   1952           break;
  1921   1953         }
  1922   1954   
  1923   1955         /* Opcode: Key P1 * *

Changes to src/vdbe.h.

    23     23   *************************************************************************
    24     24   ** Header file for the Virtual DataBase Engine (VDBE)
    25     25   **
    26     26   ** This header defines the interface to the virtual database engine
    27     27   ** or VDBE.  The VDBE implements an abstract machine that runs a
    28     28   ** simple program to access and modify the underlying database.
    29     29   **
    30         -** $Id: vdbe.h,v 1.8 2000/06/06 01:50:44 drh Exp $
           30  +** $Id: vdbe.h,v 1.9 2000/06/06 21:56:08 drh Exp $
    31     31   */
    32     32   #ifndef _SQLITE_VDBE_H_
    33     33   #define _SQLITE_VDBE_H_
    34     34   #include <stdio.h>
    35     35   
    36     36   /*
    37     37   ** A single VDBE is an opaque structure named "Vdbe".  Only routines
................................................................................
    77     77   #define OP_New                 4
    78     78   #define OP_Put                 5
    79     79   #define OP_Distinct            6
    80     80   #define OP_Found               7
    81     81   #define OP_NotFound            8
    82     82   #define OP_Delete              9
    83     83   #define OP_Field              10
    84         -#define OP_Key                11
    85         -#define OP_Rewind             12
    86         -#define OP_Next               13
           84  +#define OP_KeyAsData          11
           85  +#define OP_Key                12
           86  +#define OP_Rewind             13
           87  +#define OP_Next               14
           88  +
           89  +#define OP_Destroy            15
           90  +#define OP_Reorganize         16
           91  +
           92  +#define OP_ResetIdx           17
           93  +#define OP_NextIdx            18
           94  +#define OP_PutIdx             19
           95  +#define OP_DeleteIdx          20
           96  +
           97  +#define OP_MemLoad            21
           98  +#define OP_MemStore           22
           99  +
          100  +#define OP_ListOpen           23
          101  +#define OP_ListWrite          24
          102  +#define OP_ListRewind         25
          103  +#define OP_ListRead           26
          104  +#define OP_ListClose          27
          105  +
          106  +#define OP_SortOpen           28
          107  +#define OP_SortPut            29
          108  +#define OP_SortMakeRec        30
          109  +#define OP_SortMakeKey        31
          110  +#define OP_Sort               32
          111  +#define OP_SortNext           33
          112  +#define OP_SortKey            34
          113  +#define OP_SortCallback       35
          114  +#define OP_SortClose          36
          115  +
          116  +#define OP_FileOpen           37
          117  +#define OP_FileRead           38
          118  +#define OP_FileField          39
          119  +#define OP_FileClose          40
          120  +
          121  +#define OP_AggReset           41
          122  +#define OP_AggFocus           42
          123  +#define OP_AggIncr            43
          124  +#define OP_AggNext            44
          125  +#define OP_AggSet             45
          126  +#define OP_AggGet             46
          127  +
          128  +#define OP_SetInsert          47
          129  +#define OP_SetFound           48
          130  +#define OP_SetNotFound        49
          131  +#define OP_SetClear           50
    87    132   
    88         -#define OP_Destroy            14
    89         -#define OP_Reorganize         15
          133  +#define OP_MakeRecord         51
          134  +#define OP_MakeKey            52
    90    135   
    91         -#define OP_ResetIdx           16
    92         -#define OP_NextIdx            17
    93         -#define OP_PutIdx             18
    94         -#define OP_DeleteIdx          19
          136  +#define OP_Goto               53
          137  +#define OP_If                 54
          138  +#define OP_Halt               55
    95    139   
    96         -#define OP_MemLoad            20
    97         -#define OP_MemStore           21
    98         -
    99         -#define OP_ListOpen           22
   100         -#define OP_ListWrite          23
   101         -#define OP_ListRewind         24
   102         -#define OP_ListRead           25
   103         -#define OP_ListClose          26
          140  +#define OP_ColumnCount        56
          141  +#define OP_ColumnName         57
          142  +#define OP_Callback           58
   104    143   
   105         -#define OP_SortOpen           27
   106         -#define OP_SortPut            28
   107         -#define OP_SortMakeRec        29
   108         -#define OP_SortMakeKey        30
   109         -#define OP_Sort               31
   110         -#define OP_SortNext           32
   111         -#define OP_SortKey            33
   112         -#define OP_SortCallback       34
   113         -#define OP_SortClose          35
          144  +#define OP_Integer            59
          145  +#define OP_String             60
          146  +#define OP_Null               61
          147  +#define OP_Pop                62
          148  +#define OP_Dup                63
          149  +#define OP_Pull               64
   114    150   
   115         -#define OP_FileOpen           36
   116         -#define OP_FileRead           37
   117         -#define OP_FileField          38
   118         -#define OP_FileClose          39
   119         -
   120         -#define OP_AggReset           40
   121         -#define OP_AggFocus           41
   122         -#define OP_AggIncr            42
   123         -#define OP_AggNext            43
   124         -#define OP_AggSet             44
   125         -#define OP_AggGet             45
   126         -
   127         -#define OP_SetInsert          46
   128         -#define OP_SetFound           47
   129         -#define OP_SetNotFound        48
   130         -#define OP_SetClear           49
          151  +#define OP_Add                65
          152  +#define OP_AddImm             66
          153  +#define OP_Subtract           67
          154  +#define OP_Multiply           68
          155  +#define OP_Divide             69
          156  +#define OP_Min                70
          157  +#define OP_Max                71
          158  +#define OP_Like               72
          159  +#define OP_Glob               73
          160  +#define OP_Eq                 74
          161  +#define OP_Ne                 75
          162  +#define OP_Lt                 76
          163  +#define OP_Le                 77
          164  +#define OP_Gt                 78
          165  +#define OP_Ge                 79
          166  +#define OP_IsNull             80
          167  +#define OP_NotNull            81
          168  +#define OP_Negative           82
          169  +#define OP_And                83
          170  +#define OP_Or                 84
          171  +#define OP_Not                85
          172  +#define OP_Concat             86
          173  +#define OP_Noop               87
   131    174   
   132         -#define OP_MakeRecord         50
   133         -#define OP_MakeKey            51
   134         -
   135         -#define OP_Goto               52
   136         -#define OP_If                 53
   137         -#define OP_Halt               54
   138         -
   139         -#define OP_ColumnCount        55
   140         -#define OP_ColumnName         56
   141         -#define OP_Callback           57
   142         -
   143         -#define OP_Integer            58
   144         -#define OP_String             59
   145         -#define OP_Null               60
   146         -#define OP_Pop                61
   147         -#define OP_Dup                62
   148         -#define OP_Pull               63
   149         -
   150         -#define OP_Add                64
   151         -#define OP_AddImm             65
   152         -#define OP_Subtract           66
   153         -#define OP_Multiply           67
   154         -#define OP_Divide             68
   155         -#define OP_Min                69
   156         -#define OP_Max                70
   157         -#define OP_Like               71
   158         -#define OP_Glob               72
   159         -#define OP_Eq                 73
   160         -#define OP_Ne                 74
   161         -#define OP_Lt                 75
   162         -#define OP_Le                 76
   163         -#define OP_Gt                 77
   164         -#define OP_Ge                 78
   165         -#define OP_IsNull             79
   166         -#define OP_NotNull            80
   167         -#define OP_Negative           81
   168         -#define OP_And                82
   169         -#define OP_Or                 83
   170         -#define OP_Not                84
   171         -#define OP_Concat             85
   172         -#define OP_Noop               86
   173         -
   174         -#define OP_MAX                86
          175  +#define OP_MAX                87
   175    176   
   176    177   /*
   177    178   ** Prototypes for the VDBE interface.  See comments on the implementation
   178    179   ** for a description of what each of these routines does.
   179    180   */
   180    181   Vdbe *sqliteVdbeCreate(Dbbe*);
   181    182   int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int);