]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
added aggregate functions like count(*) (CVS 21)
authordrh <drh@noemail.net>
Wed, 31 May 2000 15:34:51 +0000 (15:34 +0000)
committerdrh <drh@noemail.net>
Wed, 31 May 2000 15:34:51 +0000 (15:34 +0000)
FossilOrigin-Name: dee7a8be88a95014534b90b96716d9e2e6b16579

15 files changed:
Makefile.in
manifest
manifest.uuid
src/build.c
src/delete.c [new file with mode: 0644]
src/expr.c [new file with mode: 0644]
src/insert.c [new file with mode: 0644]
src/parse.y
src/select.c [new file with mode: 0644]
src/sqliteInt.h
src/update.c [new file with mode: 0644]
src/vdbe.c
src/where.c
www/changes.tcl
www/index.tcl

index 8c277993d1225dd6db7281aeca8535690528c522..887254411e7248cb9eeba6873bf943dbc022d965 100644 (file)
@@ -47,7 +47,9 @@ LIBREADLINE = @TARGET_READLINE_LIBS@
 
 # Object files for the SQLite library.
 #
-LIBOBJ = build.o dbbe.o main.o parse.o tokenize.o util.o vdbe.o where.o
+LIBOBJ = build.o dbbe.o delete.o expr.o insert.o \
+         main.o parse.o select.o tokenize.o update.o \
+         util.o vdbe.o where.o
 
 # This is the default Makefile target.  The objects listed here
 # are what get build when you type just "make" with no arguments.
@@ -110,6 +112,21 @@ vdbe.o:    $(TOP)/src/vdbe.c $(HDR)
 where.o:       $(TOP)/src/where.c $(HDR)
        $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/where.c
 
+delete.o:      $(TOP)/src/delete.c $(HDR)
+       $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/delete.c
+
+expr.o:        $(TOP)/src/expr.c $(HDR)
+       $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/expr.c
+
+insert.o:      $(TOP)/src/insert.c $(HDR)
+       $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/insert.c
+
+select.o:      $(TOP)/src/select.c $(HDR)
+       $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/select.c
+
+update.o:      $(TOP)/src/update.c $(HDR)
+       $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/update.c
+
 gdbmdump:      $(TOP)/tool/gdbmdump.c
        $(TCC) $(GDBM_FLAGS) -o gdbmdump $(TOP)/tool/gdbmdump.c $(LIBGDBM)
 
index e1a4a8ad4b611dd7fbb97215975d7524840c9190..b09128221f713851b31639e581b539bcfc65462e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,25 +1,30 @@
-C :-)\s(CVS\s20)
-D 2000-05-31T02:27:49
+C added\saggregate\sfunctions\slike\scount(*)\s(CVS\s21)
+D 2000-05-31T15:34:52
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
-F Makefile.in dd79c78825935c5711ce45c372b0ac0f194b6d43
+F Makefile.in 7ac2fef265940d93a544cb454efa836451559a71
 F README 6b5960603c7f8bf42fc022b4b6436f242f238dbb
 F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
 F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
 F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
-F src/build.c c6b500077913cde55871d1f9c0d17d4ee4a86828
+F src/build.c 03f83e95d46e328a2ac08aace102b142ea38e6d7
 F src/dbbe.c dc9439f839d13e633158808e352056b531f17e1b
 F src/dbbe.h b678e31c32fa252e6fba830ad16ed8978d1521a9
+F src/delete.c 16ef3418b19be9ab39db836c693970ca7bbff605
+F src/expr.c 91970700e3e39b2b725b028c166f588a5bb0c038
+F src/insert.c bd34716d0bba5561f6b55101adbf16fa75f872e8
 F src/main.c 25cce7bce0eb3ba10bada7c05f4b38dc6dbbc86f
-F src/parse.y 05de7dec046dd8bd11f8cc3513ff8b27624618c8
+F src/parse.y bdfcd0a3fe7d6ad4b41dc2cbc0d04c4302f609b0
+F src/select.c 540fae91639d93ea1ef348882197554896841a2f
 F src/shell.c c5752d32cdeaa7d548d4f91177b697b023a00381
 F src/sqlite.h 2397c17a8f4ca90c09acab0100dc7e2f8f441b69
-F src/sqliteInt.h 9ac3f9e05bbc5913531473c86d4742343ae670c5
+F src/sqliteInt.h 81552acdedb0c3b256510a66c0f656d35d2ea2bd
 F src/tclsqlite.c 9efd29f79ded6a900aa3d142169c8bfe03b7affd
 F src/tokenize.c 5b066f314646d6c5396a253315e5e95d107e1800
+F src/update.c 9194f548dafc9884d79489874e22974ed67cd6a2
 F src/util.c 6b4327d7fbf684f8635155d4acb847ae991b3ebc
-F src/vdbe.c 74ff55bc2910e25bd811638233f6c02fb1a17fbc
+F src/vdbe.c 11d8e4f6e25044ceace5e7a5c160b74b0537492c
 F src/vdbe.h 02b470d344caed04451c896be7a775068dbdf076
-F src/where.c fd9faea693083c1bde83f824b98f7eb81c4762cc
+F src/where.c bed9a8360cbfbf712bdc397c8e22216a5e5f9800
 F test/all.test 66a8a5b8291a472157944edcdce51a320ebd1f35
 F test/copy.test 641bd3cfaab61c4ee32889587e21e4c70788a97a
 F test/delete.test 814d53e3b0d2d7069fb17e005d4041454d6585d4
@@ -36,10 +41,10 @@ F tool/opNames.awk 2bd9071a138e4e2be13dc98fe066398a61219e1e
 F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
 F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
 F www/c_interface.tcl f875864edf7974157d1c257ca08de854660882a5
-F www/changes.tcl 38ff869ccbf99388d53abd08aeea4e24e2bb23b7
-F www/index.tcl 57a97afafe04ab53d1996ba3a61ac41fa8453f5a
+F www/changes.tcl f6d75118ac266313ebcdafa79b779b82dde44cc0
+F www/index.tcl 600e85c207929bedb9c6fd221aa7875fd8f43edf
 F www/sqlite.tcl 7deb564df188ad4523adecfe2365de6d09f6dfd9
-P 03725ce5ae871247789ece0f2c3426f74ba575e7
-R bab7f299b23f92a0096c17a89a463018
+P 01d85b35e9c4ca5619ad21a4232a8f8bf9ec3538
+R 05da1ee70a5c666d9e2d499f8f42ff62
 U drh
-Z e30b944ebaee88defeb667112902d5c8
+Z d21127e72e51c417dbc3dc88c1114902
index 1b7f2dfd7550de7ffbe22f93f30d7117a793ab2e..a38dfab2c3cc087c44a9ea29f8dc43fdec3d37f7 100644 (file)
@@ -1 +1 @@
-01d85b35e9c4ca5619ad21a4232a8f8bf9ec3538
\ No newline at end of file
+dee7a8be88a95014534b90b96716d9e2e6b16579
\ No newline at end of file
index fd0aaeef3153b3465f10744ad64a676b2ea48800..380ce94a0628bafa55ab203f9c70a50e3f4f0f62 100644 (file)
@@ -24,7 +24,7 @@
 ** This file contains C code routines that are called by the parser
 ** when syntax rules are reduced.
 **
-** $Id: build.c,v 1.10 2000/05/31 02:27:49 drh Exp $
+** $Id: build.c,v 1.11 2000/05/31 15:34:52 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -188,7 +188,7 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){
 ** Space to hold the name is obtained from sqliteMalloc() and must
 ** be freed by the calling function.
 */
-static char *sqliteTableNameFromToken(Token *pName){
+char *sqliteTableNameFromToken(Token *pName){
   char *zName = 0;
   sqliteSetNString(&zName, pName->z, pName->n, 0);
   sqliteDequote(zName);
@@ -316,7 +316,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
 ** Given a token, look up a table with that name.  If not found, leave
 ** an error for the parser to find and return NULL.
 */
-static Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
+Table *sqliteTableFromToken(Parse *pParse, Token *pTok){
   char *zName = sqliteTableNameFromToken(pTok);
   Table *pTab = sqliteFindTable(pParse->db, zName);
   sqliteFree(zName);
@@ -736,715 +736,6 @@ void sqliteIdListDelete(IdList *pList){
   sqliteFree(pList);
 }
 
-/*
-** This routine is call to handle SQL of the following form:
-**
-**    insert into TABLE (IDLIST) values(EXPRLIST)
-**
-** The parameters are the table name and the expression list.
-*/
-void sqliteInsert(
-  Parse *pParse,        /* Parser context */
-  Token *pTableName,    /* Name of table into which we are inserting */
-  ExprList *pList,      /* List of values to be inserted */
-  IdList *pField        /* Field name corresponding to pList.  Might be NULL */
-){
-  Table *pTab;
-  char *zTab;
-  int i, j;
-  Vdbe *v;
-
-  zTab = sqliteTableNameFromToken(pTableName);
-  pTab = sqliteFindTable(pParse->db, zTab);
-  sqliteFree(zTab);
-  if( pTab==0 ){
-    sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0, 
-        pTableName->z, pTableName->n, 0);
-    pParse->nErr++;
-    goto insert_cleanup;
-  }
-  if( pTab->readOnly ){
-    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
-        " may not be modified", 0);
-    pParse->nErr++;
-    goto insert_cleanup;
-  }
-  if( pField==0 && pList->nExpr!=pTab->nCol ){
-    char zNum1[30];
-    char zNum2[30];
-    sprintf(zNum1,"%d", pList->nExpr);
-    sprintf(zNum2,"%d", pTab->nCol);
-    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
-       " has ", zNum2, " columns but ",
-       zNum1, " values were supplied", 0);
-    pParse->nErr++;
-    goto insert_cleanup;
-  }
-  if( pField!=0 && pList->nExpr!=pField->nId ){
-    char zNum1[30];
-    char zNum2[30];
-    sprintf(zNum1,"%d", pList->nExpr);
-    sprintf(zNum2,"%d", pField->nId);
-    sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
-       zNum2, " columns", 0);
-    pParse->nErr++;
-    goto insert_cleanup;
-  }
-  if( pField ){
-    for(i=0; i<pField->nId; i++){
-      pField->a[i].idx = -1;
-    }
-    for(i=0; i<pField->nId; i++){
-      for(j=0; j<pTab->nCol; j++){
-        if( sqliteStrICmp(pField->a[i].zName, pTab->azCol[j])==0 ){
-          pField->a[i].idx = j;
-          break;
-        }
-      }
-      if( j>=pTab->nCol ){
-        sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
-           " has no column named ", pField->a[i].zName, 0);
-        pParse->nErr++;
-        goto insert_cleanup;
-      }
-    }
-  }
-  v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  if( v ){
-    Index *pIdx;
-    sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
-    sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0);
-    if( pTab->pIndex ){
-      sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
-    }
-    for(i=0; i<pTab->nCol; i++){
-      if( pField==0 ){
-        j = i;
-      }else{
-        for(j=0; j<pField->nId; j++){
-          if( pField->a[j].idx==i ) break;
-        }
-      }
-      if( pField && j>=pField->nId ){
-        sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
-      }else{
-        sqliteExprCode(pParse, pList->a[j].pExpr);
-      }
-    }
-    sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
-    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-      if( pIdx->pNext ){
-        sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
-      }
-      sqliteVdbeAddOp(v, OP_Open, 0, 0, pIdx->zName, 0);
-      for(i=0; i<pIdx->nField; i++){
-        int idx = pIdx->aiField[i];
-        if( pField==0 ){
-          j = idx;
-        }else{
-          for(j=0; j<pField->nId; j++){
-            if( pField->a[j].idx==idx ) break;
-          }
-        }
-        if( pField && j>=pField->nId ){
-          sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
-        }else{
-          sqliteExprCode(pParse, pList->a[j].pExpr);
-        }
-      }
-      sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_PutIdx, 0, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
-    }
-  }
-
-insert_cleanup:
-  sqliteExprListDelete(pList);
-  sqliteIdListDelete(pField);
-}
-
-/*
-** This routine walks an expression tree and resolves references to
-** table fields.  Nodes of the form ID.ID or ID resolve into an
-** index to the table in the table list and a field offset.  The opcode
-** for such nodes is changed to TK_FIELD.  The iTable value is changed
-** to the index of the referenced table in pTabList, and the iField value
-** is changed to the index of the field of the referenced table.
-**
-** Unknown fields or tables provoke an error.  The function returns
-** the number of errors seen and leaves an error message on pParse->zErrMsg.
-*/
-int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
-  if( pExpr==0 ) return 0;
-  switch( pExpr->op ){
-    /* A lone identifier */
-    case TK_ID: {
-      int cnt = 0;   /* Number of matches */
-      int i;         /* Loop counter */
-      char *z = 0;
-      sqliteSetNString(&z, pExpr->token.z, pExpr->token.n, 0);
-      for(i=0; i<pTabList->nId; i++){
-        int j;
-        Table *pTab = pTabList->a[i].pTab;
-        if( pTab==0 ) continue;
-        for(j=0; j<pTab->nCol; j++){
-          if( sqliteStrICmp(pTab->azCol[j], z)==0 ){
-            cnt++;
-            pExpr->iTable = i;
-            pExpr->iField = j;
-          }
-        }
-      }
-      sqliteFree(z);
-      if( cnt==0 ){
-        sqliteSetNString(&pParse->zErrMsg, "no such field: ", -1,  
-          pExpr->token.z, pExpr->token.n, 0);
-        pParse->nErr++;
-        return 1;
-      }else if( cnt>1 ){
-        sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: ", -1,  
-          pExpr->token.z, pExpr->token.n, 0);
-        pParse->nErr++;
-        return 1;
-      }
-      pExpr->op = TK_FIELD;
-      break; 
-    }
-  
-    /* A table name and field name:  ID.ID */
-    case TK_DOT: {
-      int cnt = 0;   /* Number of matches */
-      int i;         /* Loop counter */
-      Expr *pLeft, *pRight;    /* Left and right subbranches of the expr */
-      int n;                   /* Length of an identifier */
-      char *zLeft, *zRight;    /* Text of an identifier */
-
-      pLeft = pExpr->pLeft;
-      pRight = pExpr->pRight;
-      assert( pLeft && pLeft->op==TK_ID );
-      assert( pRight && pRight->op==TK_ID );
-      zLeft = 0;
-      sqliteSetNString(&zLeft, pLeft->token.z, pLeft->token.n, 0);
-      zRight = 0;
-      sqliteSetNString(&zRight, pRight->token.z, pRight->token.n, 0);
-      for(i=0; i<pTabList->nId; i++){
-        int j;
-        char *zTab;
-        Table *pTab = pTabList->a[i].pTab;
-        if( pTab==0 ) continue;
-        if( pTabList->a[i].zAlias ){
-          zTab = pTabList->a[i].zAlias;
-        }else{
-          zTab = pTab->zName;
-        }
-        if( sqliteStrICmp(zTab, zLeft)!=0 ) continue;
-        for(j=0; j<pTab->nCol; j++){
-          if( sqliteStrICmp(pTab->azCol[j], zRight)==0 ){
-            cnt++;
-            pExpr->iTable = i;
-            pExpr->iField = j;
-          }
-        }
-      }
-      sqliteFree(zLeft);
-      sqliteFree(zRight);
-      if( cnt==0 ){
-        sqliteSetNString(&pParse->zErrMsg, "no such field: ", -1,  
-          pLeft->token.z, pLeft->token.n, ".", 1, 
-          pRight->token.z, pRight->token.n, 0);
-        pParse->nErr++;
-        return 1;
-      }else if( cnt>1 ){
-        sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: ", -1,  
-          pLeft->token.z, pLeft->token.n, ".", 1,
-          pRight->token.z, pRight->token.n, 0);
-        pParse->nErr++;
-        return 1;
-      }
-      sqliteExprDelete(pLeft);
-      pExpr->pLeft = 0;
-      sqliteExprDelete(pRight);
-      pExpr->pRight = 0;
-      pExpr->op = TK_FIELD;
-      break;
-    }
-
-    /* For all else, just recursively walk the tree */
-    default: {
-      if( pExpr->pLeft 
-            && sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){
-        return 1;
-      }
-      if( pExpr->pRight 
-            && sqliteExprResolveIds(pParse, pTabList, pExpr->pRight) ){
-        return 1;
-      }
-      if( pExpr->pList ){
-        int i;
-        ExprList *pList = pExpr->pList;
-        for(i=0; i<pList->nExpr; i++){
-          if( sqliteExprResolveIds(pParse, pTabList, pList->a[i].pExpr) ){
-            return 1;
-          }
-        }
-      }
-    }
-  }
-  return 0;
-}
-
-/*
-** Process a SELECT statement.
-*/
-void sqliteSelect(
-  Parse *pParse,         /* The parser context */
-  ExprList *pEList,      /* List of fields to extract.  NULL means "*" */
-  IdList *pTabList,      /* List of tables to select from */
-  Expr *pWhere,          /* The WHERE clause.  May be NULL */
-  ExprList *pOrderBy     /* The ORDER BY clause.  May be NULL */
-){
-  int i, j;
-  WhereInfo *pWInfo;
-  Vdbe *v;
-
-  if( pParse->nErr>0 ) goto select_cleanup;
-
-  /* Look up every table in the table list.
-  */
-  for(i=0; i<pTabList->nId; i++){
-    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
-    if( pTabList->a[i].pTab==0 ){
-      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
-         pTabList->a[i].zName, 0);
-      pParse->nErr++;
-      goto select_cleanup;
-    }
-  }
-
-  /* If the list of fields to retrieve is "*" then replace it with
-  ** a list of all fields from all tables.
-  */
-  if( pEList==0 ){
-    for(i=0; i<pTabList->nId; i++){
-      Table *pTab = pTabList->a[i].pTab;
-      for(j=0; j<pTab->nCol; j++){
-        Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
-        pExpr->iTable = i;
-        pExpr->iField = j;
-        pEList = sqliteExprListAppend(pEList, pExpr, 0);
-      }
-    }
-  }
-
-  /* Resolve the field names in all the expressions.
-  */
-  for(i=0; i<pEList->nExpr; i++){
-    if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){
-      goto select_cleanup;
-    }
-  }
-  if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){
-    goto select_cleanup;
-  }
-  if( pOrderBy ){
-    for(i=0; i<pOrderBy->nExpr; i++){
-      if( sqliteExprResolveIds(pParse, pTabList, pOrderBy->a[i].pExpr) ){
-        goto select_cleanup;
-      }
-    }
-  }
-
-  /* Begin generating code.
-  */
-  v = pParse->pVdbe;
-  if( v==0 ){
-    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  }
-  if( v==0 ) goto select_cleanup;
-  if( pOrderBy ){
-    sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0);
-  }
-
-
-  /* Identify column names
-  */
-  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;
-        sqliteSetString(&zName, pTab->zName, ".", 
-               pTab->azCol[p->iField], 0);
-        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
-        sqliteFree(zName);
-      }else{
-        Table *pTab = pTabList->a[0].pTab;
-        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->azCol[p->iField], 0);
-      }
-    }
-  }
-
-  /* Begin the database scan
-  */  
-  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
-  if( pWInfo==0 ) goto select_cleanup;
-
-  /* Pull the requested fields.
-  */
-  for(i=0; i<pEList->nExpr; i++){
-    sqliteExprCode(pParse, pEList->a[i].pExpr);
-  }
-  
-  /* If there is no ORDER BY clause, then we can invoke the callback
-  ** right away.  If there is an ORDER BY, then we need to put the
-  ** data into an appropriate sorter record.
-  */
-  if( pOrderBy==0 ){
-    sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
-  }else{
-    char *zSortOrder;
-    sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
-    zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
-    if( zSortOrder==0 ) goto select_cleanup;
-    for(i=0; i<pOrderBy->nExpr; i++){
-      zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
-      sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
-    }
-    zSortOrder[pOrderBy->nExpr] = 0;
-    sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0);
-    sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
-  }
-
-  /* End the database scan loop.
-  */
-  sqliteWhereEnd(pWInfo);
-
-  /* If there is an ORDER BY clause, then we need to sort the results
-  ** and send them to the callback one by one.
-  */
-  if( pOrderBy ){
-    int end = sqliteVdbeMakeLabel(v);
-    int addr;
-    sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
-    addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
-    sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
-    sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end);
-  }
-
-  /* Always execute the following code before exiting, in order to
-  ** release resources.
-  */
-select_cleanup:
-  sqliteExprListDelete(pEList);
-  sqliteIdListDelete(pTabList);
-  sqliteExprDelete(pWhere);
-  sqliteExprListDelete(pOrderBy);
-  return;
-}
-
-/*
-** Process a DELETE FROM statement.
-*/
-void sqliteDeleteFrom(
-  Parse *pParse,         /* The parser context */
-  Token *pTableName,     /* The table from which we should delete things */
-  Expr *pWhere           /* The WHERE clause.  May be null */
-){
-  Vdbe *v;               /* The virtual database engine */
-  Table *pTab;           /* The table from which records will be deleted */
-  IdList *pTabList;      /* An ID list holding pTab and nothing else */
-  int end, addr;         /* A couple addresses of generated code */
-  int i;                 /* Loop counter */
-  WhereInfo *pWInfo;     /* Information about the WHERE clause */
-  Index *pIdx;           /* For looping over indices of the table */
-
-  /* Locate the table which we want to update.  This table has to be
-  ** put in an IdList structure because some of the subroutines will
-  ** will be calling are designed to work with multiple tables and expect
-  ** an IdList* parameter instead of just a Table* parameger.
-  */
-  pTabList = sqliteIdListAppend(0, pTableName);
-  for(i=0; i<pTabList->nId; i++){
-    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
-    if( pTabList->a[i].pTab==0 ){
-      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
-         pTabList->a[i].zName, 0);
-      pParse->nErr++;
-      goto delete_from_cleanup;
-    }
-    if( pTabList->a[i].pTab->readOnly ){
-      sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName,
-        " may not be modified", 0);
-      pParse->nErr++;
-      goto delete_from_cleanup;
-    }
-  }
-  pTab = pTabList->a[0].pTab;
-
-  /* Resolve the field names in all the expressions.
-  */
-  if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){
-    goto delete_from_cleanup;
-  }
-
-  /* Begin generating code.
-  */
-  v = pParse->pVdbe;
-  if( v==0 ){
-    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  }
-  if( v==0 ) goto delete_from_cleanup;
-
-  /* Begin the database scan
-  */
-  sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
-  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
-  if( pWInfo==0 ) goto delete_from_cleanup;
-
-  /* Remember the index of every item to be deleted.
-  */
-  sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
-
-  /* End the database scan loop.
-  */
-  sqliteWhereEnd(pWInfo);
-
-  /* Delete every item identified in the list.
-  */
-  sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
-  for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
-    sqliteVdbeAddOp(v, OP_Open, i, 0, pIdx->zName, 0);
-  }
-  end = sqliteVdbeMakeLabel(v);
-  addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
-  if( pTab->pIndex ){
-    sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0);
-    for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
-      int j;
-      sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
-      for(j=0; j<pIdx->nField; j++){
-        sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0);
-      }
-      sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_DeleteIdx, i, 0, 0, 0);
-    }
-  }
-  sqliteVdbeAddOp(v, OP_Delete, 0, 0, 0, 0);
-  sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
-  sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
-
-delete_from_cleanup:
-  sqliteIdListDelete(pTabList);
-  sqliteExprDelete(pWhere);
-  return;
-}
-
-/*
-** Process an UPDATE statement.
-*/
-void sqliteUpdate(
-  Parse *pParse,         /* The parser context */
-  Token *pTableName,     /* The table in which we should change things */
-  ExprList *pChanges,    /* Things to be changed */
-  Expr *pWhere           /* The WHERE clause.  May be null */
-){
-  int i, j;              /* Loop counters */
-  Table *pTab;           /* The table to be updated */
-  IdList *pTabList = 0;  /* List containing only pTab */
-  int end, addr;         /* A couple of addresses in the generated code */
-  WhereInfo *pWInfo;     /* Information about the WHERE clause */
-  Vdbe *v;               /* The virtual database engine */
-  Index *pIdx;           /* For looping over indices */
-  int nIdx;              /* Number of indices that need updating */
-  Index **apIdx = 0;     /* An array of indices that need updating too */
-  int *aXRef = 0;        /* aXRef[i] is the index in pChanges->a[] of the
-                         ** an expression for the i-th field of the table.
-                         ** aXRef[i]==-1 if the i-th field is not changed. */
-
-  /* Locate the table which we want to update.  This table has to be
-  ** put in an IdList structure because some of the subroutines will
-  ** will be calling are designed to work with multiple tables and expect
-  ** an IdList* parameter instead of just a Table* parameger.
-  */
-  pTabList = sqliteIdListAppend(0, pTableName);
-  for(i=0; i<pTabList->nId; i++){
-    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
-    if( pTabList->a[i].pTab==0 ){
-      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
-         pTabList->a[i].zName, 0);
-      pParse->nErr++;
-      goto update_cleanup;
-    }
-    if( pTabList->a[i].pTab->readOnly ){
-      sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName,
-        " may not be modified", 0);
-      pParse->nErr++;
-      goto update_cleanup;
-    }
-  }
-  pTab = pTabList->a[0].pTab;
-  aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
-  if( aXRef==0 ) goto update_cleanup;
-  for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
-
-  /* Resolve the field names in all the expressions in both the
-  ** WHERE clause and in the new values.  Also find the field index
-  ** for each field to be updated in the pChanges array.
-  */
-  if( pWhere && sqliteExprResolveIds(pParse, pTabList, pWhere) ){
-    goto update_cleanup;
-  }
-  for(i=0; i<pChanges->nExpr; i++){
-    if( sqliteExprResolveIds(pParse, pTabList, pChanges->a[i].pExpr) ){
-      goto update_cleanup;
-    }
-    for(j=0; j<pTab->nCol; j++){
-      if( strcmp(pTab->azCol[j], pChanges->a[i].zName)==0 ){
-        pChanges->a[i].idx = j;
-        aXRef[j] = i;
-        break;
-      }
-    }
-    if( j>=pTab->nCol ){
-      sqliteSetString(&pParse->zErrMsg, "no such field: ", 
-         pChanges->a[i].zName, 0);
-      pParse->nErr++;
-      goto update_cleanup;
-    }
-  }
-
-  /* Allocate memory for the array apIdx[] and fill it pointers to every
-  ** index that needs to be updated.  Indices only need updating if their
-  ** key includes one of the fields named in pChanges.
-  */
-  for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-    for(i=0; i<pIdx->nField; i++){
-      if( aXRef[pIdx->aiField[i]]>=0 ) break;
-    }
-    if( i<pIdx->nField ) nIdx++;
-  }
-  apIdx = sqliteMalloc( sizeof(Index*) * nIdx );
-  if( apIdx==0 ) goto update_cleanup;
-  for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-    for(i=0; i<pIdx->nField; i++){
-      if( aXRef[pIdx->aiField[i]]>=0 ) break;
-    }
-    if( i<pIdx->nField ) apIdx[nIdx++] = pIdx;
-  }
-
-  /* Begin generating code.
-  */
-  v = pParse->pVdbe;
-  if( v==0 ){
-    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  }
-  if( v==0 ) goto update_cleanup;
-
-  /* Begin the database scan
-  */
-  sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
-  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
-  if( pWInfo==0 ) goto update_cleanup;
-
-  /* Remember the index of every item to be updated.
-  */
-  sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
-
-  /* End the database scan loop.
-  */
-  sqliteWhereEnd(pWInfo);
-
-  /* Rewind the list of records that need to be updated and
-  ** open every index that needs updating.
-  */
-  sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
-  for(i=0; i<nIdx; i++){
-    sqliteVdbeAddOp(v, OP_Open, i+1, 0, apIdx[i]->zName, 0);
-  }
-
-  /* Loop over every record that needs updating.  We have to load
-  ** the old data for each record to be updated because some fields
-  ** might not change and we will need to copy the old value, therefore.
-  ** Also, the old data is needed to delete the old index entires.
-  */
-  end = sqliteVdbeMakeLabel(v);
-  addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
-  sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
-  sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0);
-
-  /* Delete the old indices for the current record.
-  */
-  for(i=0; i<nIdx; i++){
-    sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
-    pIdx = apIdx[i];
-    for(j=0; j<pIdx->nField; j++){
-      sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0);
-    }
-    sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_DeleteIdx, i+1, 0, 0, 0);
-  }
-
-  /* Compute a completely new data for this record.  
-  */
-  for(i=0; i<pTab->nCol; i++){
-    j = aXRef[i];
-    if( j<0 ){
-      sqliteVdbeAddOp(v, OP_Field, 0, i, 0, 0);
-    }else{
-      sqliteExprCode(pParse, pChanges->a[j].pExpr);
-    }
-  }
-
-  /* Insert new index entries that correspond to the new data
-  */
-  for(i=0; i<nIdx; i++){
-    sqliteVdbeAddOp(v, OP_Dup, pTab->nCol, 0, 0, 0); /* The KEY */
-    pIdx = apIdx[i];
-    for(j=0; j<pIdx->nField; j++){
-      sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiField[j], 0, 0, 0);
-    }
-    sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_PutIdx, i+1, 0, 0, 0);
-  }
-
-  /* Write the new data back into the database.
-  */
-  sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
-  sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
-
-  /* Repeat the above with the next record to be updated, until
-  ** all record selected by the WHERE clause have been updated.
-  */
-  sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
-  sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
-
-update_cleanup:
-  sqliteFree(apIdx);
-  sqliteFree(aXRef);
-  sqliteIdListDelete(pTabList);
-  sqliteExprListDelete(pChanges);
-  sqliteExprDelete(pWhere);
-  return;
-}
 
 /*
 ** The COPY command is for compatibility with PostgreSQL and specificially
diff --git a/src/delete.c b/src/delete.c
new file mode 100644 (file)
index 0000000..3ab5f59
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** General Public License for more details.
+** 
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA  02111-1307, USA.
+**
+** Author contact information:
+**   drh@hwaci.com
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle DELETE FROM statements.
+**
+** $Id: delete.c,v 1.1 2000/05/31 15:34:53 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** Process a DELETE FROM statement.
+*/
+void sqliteDeleteFrom(
+  Parse *pParse,         /* The parser context */
+  Token *pTableName,     /* The table from which we should delete things */
+  Expr *pWhere           /* The WHERE clause.  May be null */
+){
+  Vdbe *v;               /* The virtual database engine */
+  Table *pTab;           /* The table from which records will be deleted */
+  IdList *pTabList;      /* An ID list holding pTab and nothing else */
+  int end, addr;         /* A couple addresses of generated code */
+  int i;                 /* Loop counter */
+  WhereInfo *pWInfo;     /* Information about the WHERE clause */
+  Index *pIdx;           /* For looping over indices of the table */
+
+  /* Locate the table which we want to update.  This table has to be
+  ** put in an IdList structure because some of the subroutines will
+  ** will be calling are designed to work with multiple tables and expect
+  ** an IdList* parameter instead of just a Table* parameger.
+  */
+  pTabList = sqliteIdListAppend(0, pTableName);
+  for(i=0; i<pTabList->nId; i++){
+    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
+    if( pTabList->a[i].pTab==0 ){
+      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
+         pTabList->a[i].zName, 0);
+      pParse->nErr++;
+      goto delete_from_cleanup;
+    }
+    if( pTabList->a[i].pTab->readOnly ){
+      sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName,
+        " may not be modified", 0);
+      pParse->nErr++;
+      goto delete_from_cleanup;
+    }
+  }
+  pTab = pTabList->a[0].pTab;
+
+  /* Resolve the field names in all the expressions.
+  */
+  if( pWhere ){
+    if( sqliteExprResolveIds(pParse, pTabList, pWhere) ){
+      goto delete_from_cleanup;
+    }
+    if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
+      goto delete_from_cleanup;
+    }
+  }
+
+  /* Begin generating code.
+  */
+  v = pParse->pVdbe;
+  if( v==0 ){
+    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  }
+  if( v==0 ) goto delete_from_cleanup;
+
+  /* Begin the database scan
+  */
+  sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
+  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
+  if( pWInfo==0 ) goto delete_from_cleanup;
+
+  /* Remember the index of every item to be deleted.
+  */
+  sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
+
+  /* End the database scan loop.
+  */
+  sqliteWhereEnd(pWInfo);
+
+  /* Delete every item identified in the list.
+  */
+  sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
+  for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+    sqliteVdbeAddOp(v, OP_Open, i, 0, pIdx->zName, 0);
+  }
+  end = sqliteVdbeMakeLabel(v);
+  addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
+  if( pTab->pIndex ){
+    sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0);
+    for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+      int j;
+      sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+      for(j=0; j<pIdx->nField; j++){
+        sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0);
+      }
+      sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_DeleteIdx, i, 0, 0, 0);
+    }
+  }
+  sqliteVdbeAddOp(v, OP_Delete, 0, 0, 0, 0);
+  sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
+  sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
+
+delete_from_cleanup:
+  sqliteIdListDelete(pTabList);
+  sqliteExprDelete(pWhere);
+  return;
+}
diff --git a/src/expr.c b/src/expr.c
new file mode 100644 (file)
index 0000000..01ed63d
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** General Public License for more details.
+** 
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA  02111-1307, USA.
+**
+** Author contact information:
+**   drh@hwaci.com
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code routines used for processing expressions
+**
+** $Id: expr.c,v 1.1 2000/05/31 15:34:53 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This routine walks an expression tree and resolves references to
+** table fields.  Nodes of the form ID.ID or ID resolve into an
+** index to the table in the table list and a field offset.  The opcode
+** for such nodes is changed to TK_FIELD.  The iTable value is changed
+** to the index of the referenced table in pTabList, and the iField value
+** is changed to the index of the field of the referenced table.
+**
+** Unknown fields or tables provoke an error.  The function returns
+** the number of errors seen and leaves an error message on pParse->zErrMsg.
+*/
+int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
+  if( pExpr==0 ) return 0;
+  switch( pExpr->op ){
+    /* A lone identifier */
+    case TK_ID: {
+      int cnt = 0;   /* Number of matches */
+      int i;         /* Loop counter */
+      char *z = 0;
+      sqliteSetNString(&z, pExpr->token.z, pExpr->token.n, 0);
+      for(i=0; i<pTabList->nId; i++){
+        int j;
+        Table *pTab = pTabList->a[i].pTab;
+        if( pTab==0 ) continue;
+        for(j=0; j<pTab->nCol; j++){
+          if( sqliteStrICmp(pTab->azCol[j], z)==0 ){
+            cnt++;
+            pExpr->iTable = i;
+            pExpr->iField = j;
+          }
+        }
+      }
+      sqliteFree(z);
+      if( cnt==0 ){
+        sqliteSetNString(&pParse->zErrMsg, "no such field: ", -1,  
+          pExpr->token.z, pExpr->token.n, 0);
+        pParse->nErr++;
+        return 1;
+      }else if( cnt>1 ){
+        sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: ", -1,  
+          pExpr->token.z, pExpr->token.n, 0);
+        pParse->nErr++;
+        return 1;
+      }
+      pExpr->op = TK_FIELD;
+      break; 
+    }
+  
+    /* A table name and field name:  ID.ID */
+    case TK_DOT: {
+      int cnt = 0;   /* Number of matches */
+      int i;         /* Loop counter */
+      Expr *pLeft, *pRight;    /* Left and right subbranches of the expr */
+      int n;                   /* Length of an identifier */
+      char *zLeft, *zRight;    /* Text of an identifier */
+
+      pLeft = pExpr->pLeft;
+      pRight = pExpr->pRight;
+      assert( pLeft && pLeft->op==TK_ID );
+      assert( pRight && pRight->op==TK_ID );
+      zLeft = 0;
+      sqliteSetNString(&zLeft, pLeft->token.z, pLeft->token.n, 0);
+      zRight = 0;
+      sqliteSetNString(&zRight, pRight->token.z, pRight->token.n, 0);
+      for(i=0; i<pTabList->nId; i++){
+        int j;
+        char *zTab;
+        Table *pTab = pTabList->a[i].pTab;
+        if( pTab==0 ) continue;
+        if( pTabList->a[i].zAlias ){
+          zTab = pTabList->a[i].zAlias;
+        }else{
+          zTab = pTab->zName;
+        }
+        if( sqliteStrICmp(zTab, zLeft)!=0 ) continue;
+        for(j=0; j<pTab->nCol; j++){
+          if( sqliteStrICmp(pTab->azCol[j], zRight)==0 ){
+            cnt++;
+            pExpr->iTable = i;
+            pExpr->iField = j;
+          }
+        }
+      }
+      sqliteFree(zLeft);
+      sqliteFree(zRight);
+      if( cnt==0 ){
+        sqliteSetNString(&pParse->zErrMsg, "no such field: ", -1,  
+          pLeft->token.z, pLeft->token.n, ".", 1, 
+          pRight->token.z, pRight->token.n, 0);
+        pParse->nErr++;
+        return 1;
+      }else if( cnt>1 ){
+        sqliteSetNString(&pParse->zErrMsg, "ambiguous field name: ", -1,  
+          pLeft->token.z, pLeft->token.n, ".", 1,
+          pRight->token.z, pRight->token.n, 0);
+        pParse->nErr++;
+        return 1;
+      }
+      sqliteExprDelete(pLeft);
+      pExpr->pLeft = 0;
+      sqliteExprDelete(pRight);
+      pExpr->pRight = 0;
+      pExpr->op = TK_FIELD;
+      break;
+    }
+
+    /* For all else, just recursively walk the tree */
+    default: {
+      if( pExpr->pLeft 
+            && sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){
+        return 1;
+      }
+      if( pExpr->pRight 
+            && sqliteExprResolveIds(pParse, pTabList, pExpr->pRight) ){
+        return 1;
+      }
+      if( pExpr->pList ){
+        int i;
+        ExprList *pList = pExpr->pList;
+        for(i=0; i<pList->nExpr; i++){
+          if( sqliteExprResolveIds(pParse, pTabList, pList->a[i].pExpr) ){
+            return 1;
+          }
+        }
+      }
+    }
+  }
+  return 0;
+}
+
+#if 0 /* NOT USED */
+/*
+** Compare a token against a string.  Return TRUE if they match.
+*/
+static int sqliteTokenCmp(Token *pToken, const char *zStr){
+  int n = strlen(zStr);
+  if( n!=pToken->n ) return 0;
+  return sqliteStrNICmp(pToken->z, zStr, n)==0;
+}
+#endif
+
+/*
+** Convert a function name into its integer identifier.  Return the
+** identifier.  Return FN_Unknown if the function name is unknown.
+*/
+int sqliteFuncId(Token *pToken){
+  static const struct {
+     char *zName;
+     int len;
+     int id;
+  } aFunc[] = {
+     { "count",  5, FN_Count },
+     { "min",    3, FN_Min   },
+     { "max",    3, FN_Max   },
+     { "sum",    3, FN_Sum   },
+  };
+  int i;
+  for(i=0; i<ArraySize(aFunc); i++){
+    if( aFunc[i].len==pToken->n 
+     && sqliteStrNICmp(pToken->z, aFunc[i].zName, aFunc[i].len)==0 ){
+       return aFunc[i].id;
+    }
+  }
+  return FN_Unknown;
+}
+
+/*
+** Error check the functions in an expression.  Make sure all
+** function names are recognized and all functions have the correct
+** number of arguments.  Leave an error message in pParse->zErrMsg
+** if anything is amiss.  Return the number of errors.
+**
+** if pIsAgg is not null and this expression is an aggregate function
+** (like count(*) or max(value)) then write a 1 into *pIsAgg.
+*/
+int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
+  int nErr = 0;
+  if( pExpr==0 ) return 0;
+  if( pIsAgg ) *pIsAgg = 0;
+  switch( pExpr->op ){
+    case TK_FUNCTION: {
+      int id = sqliteFuncId(&pExpr->token);
+      int n = pExpr->pList ? pExpr->pList->nExpr : 0;
+      int no_such_func = 0;
+      int too_many_args = 0;
+      int too_few_args = 0;
+      int is_agg = 0;
+      int i;
+      switch( id ){
+        case FN_Unknown: { 
+          no_such_func = 1;
+          break;
+        }
+        case FN_Count: { 
+          no_such_func = !allowAgg;
+          too_many_args = n>1;
+          is_agg = 1;
+          break;
+        }
+        case FN_Max:
+        case FN_Min: {
+          too_few_args = allowAgg ? n<1 : n<2;
+          is_agg = n==1;
+          break;
+        }
+        case FN_Sum: {
+          no_such_func = !allowAgg;
+          too_many_args = n>1;
+          too_few_args = n<1;
+          is_agg = 1;
+          break;
+        }
+        default: break;
+      }
+      if( no_such_func ){
+        sqliteSetNString(&pParse->zErrMsg, "no such function: ", -1,
+           pExpr->token.z, pExpr->token.n, 0);
+        pParse->nErr++;
+        nErr++;
+      }else if( too_many_args ){
+        sqliteSetNString(&pParse->zErrMsg, "too many arguments to function ",-1,
+           pExpr->token.z, pExpr->token.n, "()", 2, 0);
+        pParse->nErr++;
+        nErr++;
+      }else if( too_few_args ){
+        sqliteSetNString(&pParse->zErrMsg, "too few arguments to function ",-1,
+           pExpr->token.z, pExpr->token.n, "()", 2, 0);
+        pParse->nErr++;
+        nErr++;
+      }
+      if( is_agg && pIsAgg ) *pIsAgg = 1;
+      for(i=0; nErr==0 && i<n; i++){
+        nErr = sqliteExprCheck(pParse, pExpr->pList->a[i].pExpr, 0, 0);
+      }
+    }
+    default: {
+      if( pExpr->pLeft ){
+        nErr = sqliteExprCheck(pParse, pExpr->pLeft, 0, 0);
+      }
+      if( nErr==0 && pExpr->pRight ){
+        nErr = sqliteExprCheck(pParse, pExpr->pRight, 0, 0);
+      }
+      break;
+    }
+  }
+  return nErr;
+}
+
+/*
+** Generate code into the current Vdbe to evaluate the given
+** expression and leave the result on the stack.
+*/
+void sqliteExprCode(Parse *pParse, Expr *pExpr){
+  Vdbe *v = pParse->pVdbe;
+  int op;
+  switch( pExpr->op ){
+    case TK_PLUS:     op = OP_Add;      break;
+    case TK_MINUS:    op = OP_Subtract; break;
+    case TK_STAR:     op = OP_Multiply; break;
+    case TK_SLASH:    op = OP_Divide;   break;
+    case TK_AND:      op = OP_And;      break;
+    case TK_OR:       op = OP_Or;       break;
+    case TK_LT:       op = OP_Lt;       break;
+    case TK_LE:       op = OP_Le;       break;
+    case TK_GT:       op = OP_Gt;       break;
+    case TK_GE:       op = OP_Ge;       break;
+    case TK_NE:       op = OP_Ne;       break;
+    case TK_EQ:       op = OP_Eq;       break;
+    case TK_LIKE:     op = OP_Like;     break;
+    case TK_GLOB:     op = OP_Glob;     break;
+    case TK_ISNULL:   op = OP_IsNull;   break;
+    case TK_NOTNULL:  op = OP_NotNull;  break;
+    case TK_NOT:      op = OP_Not;      break;
+    case TK_UMINUS:   op = OP_Negative; break;
+    default: break;
+  }
+  switch( pExpr->op ){
+    case TK_FIELD: {
+      sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
+      break;
+    }
+    case TK_INTEGER: {
+      int i = atoi(pExpr->token.z);
+      sqliteVdbeAddOp(v, OP_Integer, i, 0, 0, 0);
+      break;
+    }
+    case TK_FLOAT: {
+      int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
+      sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
+      break;
+    }
+    case TK_STRING: {
+      int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
+      sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
+      sqliteVdbeDequoteP3(v, addr);
+      break;
+    }
+    case TK_NULL: {
+      sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+      break;
+    }
+    case TK_AND:
+    case TK_OR:
+    case TK_PLUS:
+    case TK_STAR:
+    case TK_MINUS:
+    case TK_SLASH: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteExprCode(pParse, pExpr->pRight);
+      sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
+      break;
+    }
+    case TK_LT:
+    case TK_LE:
+    case TK_GT:
+    case TK_GE:
+    case TK_NE:
+    case TK_EQ: 
+    case TK_LIKE: 
+    case TK_GLOB: {
+      int dest;
+      sqliteVdbeAddOp(v, OP_Integer, 1, 0, 0, 0);
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteExprCode(pParse, pExpr->pRight);
+      dest = sqliteVdbeCurrentAddr(v) + 2;
+      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+      sqliteVdbeAddOp(v, OP_AddImm, -1, 0, 0, 0);
+      break;
+    }
+    case TK_NOT:
+    case TK_UMINUS: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
+      break;
+    }
+    case TK_ISNULL:
+    case TK_NOTNULL: {
+      int dest;
+      sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+      sqliteExprCode(pParse, pExpr->pLeft);
+      dest = sqliteVdbeCurrentAddr(v) + 2;
+      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+      sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
+      break;
+    }
+    case TK_FUNCTION: {
+      int id = sqliteFuncId(&pExpr->token);
+      int op;
+      int i;
+      ExprList *pList = pExpr->pList;
+      op = id==FN_Min ? OP_Min : OP_Max;
+      for(i=0; i<pList->nExpr; i++){
+        sqliteExprCode(pParse, pList->a[i].pExpr);
+        if( i>0 ){
+          sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
+        }
+      }
+      break;
+    }
+  }
+  return;
+}
+
+/*
+** Generate code for a boolean expression such that a jump is made
+** to the label "dest" if the expression is true but execution
+** continues straight thru if the expression is false.
+*/
+void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
+  Vdbe *v = pParse->pVdbe;
+  int op = 0;
+  switch( pExpr->op ){
+    case TK_LT:       op = OP_Lt;       break;
+    case TK_LE:       op = OP_Le;       break;
+    case TK_GT:       op = OP_Gt;       break;
+    case TK_GE:       op = OP_Ge;       break;
+    case TK_NE:       op = OP_Ne;       break;
+    case TK_EQ:       op = OP_Eq;       break;
+    case TK_LIKE:     op = OP_Like;     break;
+    case TK_GLOB:     op = OP_Glob;     break;
+    case TK_ISNULL:   op = OP_IsNull;   break;
+    case TK_NOTNULL:  op = OP_NotNull;  break;
+    default:  break;
+  }
+  switch( pExpr->op ){
+    case TK_AND: {
+      int d2 = sqliteVdbeMakeLabel(v);
+      sqliteExprIfFalse(pParse, pExpr->pLeft, d2);
+      sqliteExprIfTrue(pParse, pExpr->pRight, dest);
+      sqliteVdbeResolveLabel(v, d2);
+      break;
+    }
+    case TK_OR: {
+      sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
+      sqliteExprIfTrue(pParse, pExpr->pRight, dest);
+      break;
+    }
+    case TK_NOT: {
+      sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
+      break;
+    }
+    case TK_LT:
+    case TK_LE:
+    case TK_GT:
+    case TK_GE:
+    case TK_NE:
+    case TK_EQ:
+    case TK_LIKE:
+    case TK_GLOB: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteExprCode(pParse, pExpr->pRight);
+      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+      break;
+    }
+    case TK_ISNULL:
+    case TK_NOTNULL: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+      break;
+    }
+    default: {
+      sqliteExprCode(pParse, pExpr);
+      sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
+      break;
+    }
+  }
+}
+
+/*
+** Generate code for boolean expression such that a jump is made
+** to the label "dest" if the expression is false but execution
+** continues straight thru if the expression is true.
+*/
+void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
+  Vdbe *v = pParse->pVdbe;
+  int op = 0;
+  switch( pExpr->op ){
+    case TK_LT:       op = OP_Ge;       break;
+    case TK_LE:       op = OP_Gt;       break;
+    case TK_GT:       op = OP_Le;       break;
+    case TK_GE:       op = OP_Lt;       break;
+    case TK_NE:       op = OP_Eq;       break;
+    case TK_EQ:       op = OP_Ne;       break;
+    case TK_LIKE:     op = OP_Like;     break;
+    case TK_GLOB:     op = OP_Glob;     break;
+    case TK_ISNULL:   op = OP_NotNull;  break;
+    case TK_NOTNULL:  op = OP_IsNull;   break;
+    default:  break;
+  }
+  switch( pExpr->op ){
+    case TK_AND: {
+      sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
+      sqliteExprIfFalse(pParse, pExpr->pRight, dest);
+      break;
+    }
+    case TK_OR: {
+      int d2 = sqliteVdbeMakeLabel(v);
+      sqliteExprIfTrue(pParse, pExpr->pLeft, d2);
+      sqliteExprIfFalse(pParse, pExpr->pRight, dest);
+      sqliteVdbeResolveLabel(v, d2);
+      break;
+    }
+    case TK_NOT: {
+      sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
+      break;
+    }
+    case TK_LT:
+    case TK_LE:
+    case TK_GT:
+    case TK_GE:
+    case TK_NE:
+    case TK_EQ: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteExprCode(pParse, pExpr->pRight);
+      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+      break;
+    }
+    case TK_LIKE:
+    case TK_GLOB: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteExprCode(pParse, pExpr->pRight);
+      sqliteVdbeAddOp(v, op, 1, dest, 0, 0);
+      break;
+    }
+    case TK_ISNULL:
+    case TK_NOTNULL: {
+      sqliteExprCode(pParse, pExpr->pLeft);
+      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
+      break;
+    }
+    default: {
+      sqliteExprCode(pParse, pExpr);
+      sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
+      break;
+    }
+  }
+}
diff --git a/src/insert.c b/src/insert.c
new file mode 100644 (file)
index 0000000..071e642
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** General Public License for more details.
+** 
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA  02111-1307, USA.
+**
+** Author contact information:
+**   drh@hwaci.com
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle INSERT statements.
+**
+** $Id: insert.c,v 1.1 2000/05/31 15:34:53 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** This routine is call to handle SQL of the following form:
+**
+**    insert into TABLE (IDLIST) values(EXPRLIST)
+**
+** The parameters are the table name and the expression list.
+*/
+void sqliteInsert(
+  Parse *pParse,        /* Parser context */
+  Token *pTableName,    /* Name of table into which we are inserting */
+  ExprList *pList,      /* List of values to be inserted */
+  IdList *pField        /* Field name corresponding to pList.  Might be NULL */
+){
+  Table *pTab;
+  char *zTab;
+  int i, j;
+  Vdbe *v;
+
+  zTab = sqliteTableNameFromToken(pTableName);
+  pTab = sqliteFindTable(pParse->db, zTab);
+  sqliteFree(zTab);
+  if( pTab==0 ){
+    sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0, 
+        pTableName->z, pTableName->n, 0);
+    pParse->nErr++;
+    goto insert_cleanup;
+  }
+  if( pTab->readOnly ){
+    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
+        " may not be modified", 0);
+    pParse->nErr++;
+    goto insert_cleanup;
+  }
+  if( pField==0 && pList->nExpr!=pTab->nCol ){
+    char zNum1[30];
+    char zNum2[30];
+    sprintf(zNum1,"%d", pList->nExpr);
+    sprintf(zNum2,"%d", pTab->nCol);
+    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
+       " has ", zNum2, " columns but ",
+       zNum1, " values were supplied", 0);
+    pParse->nErr++;
+    goto insert_cleanup;
+  }
+  if( pField!=0 && pList->nExpr!=pField->nId ){
+    char zNum1[30];
+    char zNum2[30];
+    sprintf(zNum1,"%d", pList->nExpr);
+    sprintf(zNum2,"%d", pField->nId);
+    sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
+       zNum2, " columns", 0);
+    pParse->nErr++;
+    goto insert_cleanup;
+  }
+  if( pField ){
+    for(i=0; i<pField->nId; i++){
+      pField->a[i].idx = -1;
+    }
+    for(i=0; i<pField->nId; i++){
+      for(j=0; j<pTab->nCol; j++){
+        if( sqliteStrICmp(pField->a[i].zName, pTab->azCol[j])==0 ){
+          pField->a[i].idx = j;
+          break;
+        }
+      }
+      if( j>=pTab->nCol ){
+        sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
+           " has no column named ", pField->a[i].zName, 0);
+        pParse->nErr++;
+        goto insert_cleanup;
+      }
+    }
+  }
+  v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  if( v ){
+    Index *pIdx;
+    sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
+    sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0);
+    if( pTab->pIndex ){
+      sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+    }
+    for(i=0; i<pTab->nCol; i++){
+      if( pField==0 ){
+        j = i;
+      }else{
+        for(j=0; j<pField->nId; j++){
+          if( pField->a[j].idx==i ) break;
+        }
+      }
+      if( pField && j>=pField->nId ){
+        sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+      }else{
+        sqliteExprCode(pParse, pList->a[j].pExpr);
+      }
+    }
+    sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
+    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+      if( pIdx->pNext ){
+        sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+      }
+      sqliteVdbeAddOp(v, OP_Open, 0, 0, pIdx->zName, 0);
+      for(i=0; i<pIdx->nField; i++){
+        int idx = pIdx->aiField[i];
+        if( pField==0 ){
+          j = idx;
+        }else{
+          for(j=0; j<pField->nId; j++){
+            if( pField->a[j].idx==idx ) break;
+          }
+        }
+        if( pField && j>=pField->nId ){
+          sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+        }else{
+          sqliteExprCode(pParse, pList->a[j].pExpr);
+        }
+      }
+      sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_PutIdx, 0, 0, 0, 0);
+      sqliteVdbeAddOp(v, OP_Close, 0, 0, 0, 0);
+    }
+  }
+
+insert_cleanup:
+  sqliteExprListDelete(pList);
+  sqliteIdListDelete(pField);
+}
index 89d003575c19aca3ddf292bd75ef84a59f8f6257..750e1720132476071a48020c0d7d099a2329f110 100644 (file)
@@ -26,7 +26,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.3 2000/05/31 02:27:49 drh Exp $
+** @(#) $Id: parse.y,v 1.4 2000/05/31 15:34:53 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -254,8 +254,8 @@ expr(A) ::= ID(X) DOT ID(Y). {Expr *temp1 = sqliteExpr(TK_ID, 0, 0, &X);
 expr(A) ::= INTEGER(X).      {A = sqliteExpr(TK_INTEGER, 0, 0, &X);}
 expr(A) ::= FLOAT(X).        {A = sqliteExpr(TK_FLOAT, 0, 0, &X);}
 expr(A) ::= STRING(X).       {A = sqliteExpr(TK_STRING, 0, 0, &X);}
-// expr(A) ::= ID(X) LP exprlist(Y) RP.  {A = sqliteExprFunction(Y, &X);}
-// expr(A) ::= ID(X) LP STAR RP.         {A = sqliteExprFunction(0, &X);}
+expr(A) ::= ID(X) LP exprlist(Y) RP.  {A = sqliteExprFunction(Y, &X);}
+expr(A) ::= ID(X) LP STAR RP.         {A = sqliteExprFunction(0, &X);}
 expr(A) ::= expr(X) AND expr(Y).   {A = sqliteExpr(TK_AND, X, Y, 0);}
 expr(A) ::= expr(X) OR expr(Y).    {A = sqliteExpr(TK_OR, X, Y, 0);}
 expr(A) ::= expr(X) LT expr(Y).    {A = sqliteExpr(TK_LT, X, Y, 0);}
@@ -282,13 +282,12 @@ expr(A) ::= PLUS expr(X). [NOT]    {A = X;}
 %type expritem {Expr*}
 %destructor expritem {sqliteExprDelete($$);}
 
-/*
 exprlist(A) ::= exprlist(X) COMMA expritem(Y). 
    {A = sqliteExprListAppend(X,Y,0);}
 exprlist(A) ::= expritem(X).            {A = sqliteExprListAppend(0,X,0);}
 expritem(A) ::= expr(X).                {A = X;}
 expritem(A) ::= .                       {A = 0;}
-*/
+
 
 cmd ::= CREATE(S) uniqueflag INDEX ID(X) ON ID(Y) LP idxlist(Z) RP(E).
     {sqliteCreateIndex(pParse, &X, &Y, Z, &S, &E);}
diff --git a/src/select.c b/src/select.c
new file mode 100644 (file)
index 0000000..1a9416e
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** General Public License for more details.
+** 
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA  02111-1307, USA.
+**
+** Author contact information:
+**   drh@hwaci.com
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle SELECT statements.
+**
+** $Id: select.c,v 1.1 2000/05/31 15:34:53 drh Exp $
+*/
+#include "sqliteInt.h"
+
+
+/*
+** Process a SELECT statement.
+*/
+void sqliteSelect(
+  Parse *pParse,         /* The parser context */
+  ExprList *pEList,      /* List of fields to extract.  NULL means "*" */
+  IdList *pTabList,      /* List of tables to select from */
+  Expr *pWhere,          /* The WHERE clause.  May be NULL */
+  ExprList *pOrderBy     /* The ORDER BY clause.  May be NULL */
+){
+  int i, j;
+  WhereInfo *pWInfo;
+  Vdbe *v;
+  int isAgg = 0;         /* True for select lists like "count(*)" */
+
+  if( pParse->nErr>0 ) goto select_cleanup;
+
+  /* Look up every table in the table list.
+  */
+  for(i=0; i<pTabList->nId; i++){
+    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
+    if( pTabList->a[i].pTab==0 ){
+      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
+         pTabList->a[i].zName, 0);
+      pParse->nErr++;
+      goto select_cleanup;
+    }
+  }
+
+  /* If the list of fields to retrieve is "*" then replace it with
+  ** a list of all fields from all tables.
+  */
+  if( pEList==0 ){
+    for(i=0; i<pTabList->nId; i++){
+      Table *pTab = pTabList->a[i].pTab;
+      for(j=0; j<pTab->nCol; j++){
+        Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
+        pExpr->iTable = i;
+        pExpr->iField = j;
+        pEList = sqliteExprListAppend(pEList, pExpr, 0);
+      }
+    }
+  }
+
+  /* Resolve the field names and do a semantics check on all the expressions.
+  */
+  for(i=0; i<pEList->nExpr; i++){
+    if( sqliteExprResolveIds(pParse, pTabList, pEList->a[i].pExpr) ){
+      goto select_cleanup;
+    }
+    if( sqliteExprCheck(pParse, pEList->a[i].pExpr, 1, &pEList->a[i].isAgg) ){
+      goto select_cleanup;
+    }
+  }
+  if( pEList->nExpr>0 ){
+    isAgg = pEList->a[0].isAgg;
+    for(i=1; i<pEList->nExpr; i++){
+      if( pEList->a[i].isAgg!=isAgg ){
+        sqliteSetString(&pParse->zErrMsg, "some selected items are aggregates "
+          "and others are not", 0);
+        pParse->nErr++;
+        goto select_cleanup;
+      }
+    }
+  }
+  if( pWhere ){
+    if( sqliteExprResolveIds(pParse, pTabList, pWhere) ){
+      goto select_cleanup;
+    }
+    if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
+      goto select_cleanup;
+    }
+  }
+  if( pOrderBy ){
+    for(i=0; i<pOrderBy->nExpr; i++){
+      if( sqliteExprResolveIds(pParse, pTabList, pOrderBy->a[i].pExpr) ){
+        goto select_cleanup;
+      }
+      if( sqliteExprCheck(pParse, pOrderBy->a[i].pExpr, 0, 0) ){
+        goto select_cleanup;
+      }
+    }
+  }
+
+  /* ORDER BY is ignored if this is an aggregate query like count(*)
+  ** since only one row will be returned.
+  */
+  if( isAgg && pOrderBy ){
+    sqliteExprListDelete(pOrderBy);
+    pOrderBy = 0;
+  }
+
+  /* Begin generating code.
+  */
+  v = pParse->pVdbe;
+  if( v==0 ){
+    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  }
+  if( v==0 ) goto select_cleanup;
+  if( pOrderBy ){
+    sqliteVdbeAddOp(v, OP_SortOpen, 0, 0, 0, 0);
+  }
+
+  /* Identify column names
+  */
+  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;
+        sqliteSetString(&zName, pTab->zName, ".", 
+               pTab->azCol[p->iField], 0);
+        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
+        sqliteFree(zName);
+      }else{
+        Table *pTab = pTabList->a[0].pTab;
+        sqliteVdbeAddOp(v, OP_ColumnName, i, 0, pTab->azCol[p->iField], 0);
+      }
+    }
+  }
+
+  /* Initialize the stack to contain aggregate seed values
+  */
+  if( isAgg ){
+    for(i=0; i<pEList->nExpr; i++){
+      Expr *p = pEList->a[i].pExpr;
+      switch( sqliteFuncId(&p->token) ){
+        case FN_Min:
+        case FN_Max: {
+          sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
+          break;
+        }
+        default: {
+          sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
+          break;
+        }
+      }
+    }
+  }
+
+  /* Begin the database scan
+  */  
+  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0);
+  if( pWInfo==0 ) goto select_cleanup;
+
+  /* Pull the requested fields.
+  */
+  for(i=0; i<pEList->nExpr; i++){
+    sqliteExprCode(pParse, pEList->a[i].pExpr);
+  }
+  
+  /* If there is no ORDER BY clause, then we can invoke the callback
+  ** right away.  If there is an ORDER BY, then we need to put the
+  ** data into an appropriate sorter record.
+  */
+  if( pOrderBy ){
+    char *zSortOrder;
+    sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
+    zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
+    if( zSortOrder==0 ) goto select_cleanup;
+    for(i=0; i<pOrderBy->nExpr; i++){
+      zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
+      sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
+    }
+    zSortOrder[pOrderBy->nExpr] = 0;
+    sqliteVdbeAddOp(v, OP_SortMakeKey, pOrderBy->nExpr, 0, zSortOrder, 0);
+    sqliteVdbeAddOp(v, OP_SortPut, 0, 0, 0, 0);
+  }else if( isAgg ){
+    int n = pEList->nExpr;
+    for(i=0; i<n; i++){
+      Expr *p = pEList->a[i].pExpr;
+      int id = sqliteFuncId(&p->token);
+      int op, p1;
+      if( n>1 ){
+        sqliteVdbeAddOp(v, OP_Pull, n-1, 0, 0, 0);
+      }
+      if( id!=FN_Count && p->pList && p->pList->nExpr>=1 ){
+        sqliteExprCode(pParse, p->pList->a[0].pExpr);
+      }
+      switch( sqliteFuncId(&p->token) ){
+        case FN_Count: op = OP_AddImm; p1 = 1; break;
+        case FN_Sum:   op = OP_Add;    p1 = 0; break;
+        case FN_Min:   op = OP_Min;    p1 = 1; break;
+        case FN_Max:   op = OP_Max;    p1 = 0; break;
+      }
+      sqliteVdbeAddOp(v, op, p1, 0, 0, 0);
+    }
+  }else{
+    sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
+  }
+
+  /* End the database scan loop.
+  */
+  sqliteWhereEnd(pWInfo);
+
+  /* If there is an ORDER BY clause, then we need to sort the results
+  ** and send them to the callback one by one.
+  */
+  if( pOrderBy ){
+    int end = sqliteVdbeMakeLabel(v);
+    int addr;
+    sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
+    addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
+    sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
+    sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, end);
+  }
+
+  /* If this is an aggregate, then we need to invoke the callback
+  ** exactly once.
+  */
+  if( isAgg ){
+    sqliteVdbeAddOp(v, OP_Callback, pEList->nExpr, 0, 0, 0);
+  }
+
+  /* Always execute the following code before exiting, in order to
+  ** release resources.
+  */
+select_cleanup:
+  sqliteExprListDelete(pEList);
+  sqliteIdListDelete(pTabList);
+  sqliteExprDelete(pWhere);
+  sqliteExprListDelete(pOrderBy);
+  return;
+}
index c64a147379d28e91de1645e1dd6ee65a42b838ba..2c1491d8d39d4f5bf4149f9703ba3da915516485 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.4 2000/05/31 02:27:49 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.5 2000/05/31 15:34:53 drh Exp $
 */
 #include "sqlite.h"
 #include "dbbe.h"
 */
 #define ArraySize(X)    (sizeof(X)/sizeof(X[0]))
 
+/*
+** Integer identifiers for functions.
+*/
+#define FN_Unknown    0
+#define FN_Count      1
+#define FN_Min        2
+#define FN_Max        3
+#define FN_Sum        4
+#define FN_Avg        5
+
 /*
 ** Forward references to structures
 */
@@ -151,6 +161,7 @@ struct ExprList {
     Expr *pExpr;           /* The list of expressions */
     char *zName;           /* Token associated with this expression */
     int idx;               /* ... */
+    int isAgg;             /* True if this is an aggregate like count(*) */
   } *a;                  /* One entry for each expression */
 };
 
@@ -251,3 +262,6 @@ void sqliteCopy(Parse*, Token*, Token*, Token*);
 void sqliteVacuum(Parse*, Token*);
 int sqliteGlobCompare(const char*,const char*);
 int sqliteLikeCompare(const unsigned char*,const unsigned char*);
+char *sqliteTableNameFromToken(Token*);
+int sqliteExprCheck(Parse*, Expr*, int, int*);
+int sqliteFuncId(Token*);
diff --git a/src/update.c b/src/update.c
new file mode 100644 (file)
index 0000000..a19846a
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+** Copyright (c) 1999, 2000 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the GNU General Public
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+** General Public License for more details.
+** 
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA  02111-1307, USA.
+**
+** Author contact information:
+**   drh@hwaci.com
+**   http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle UPDATE statements.
+**
+** $Id: update.c,v 1.1 2000/05/31 15:34:53 drh Exp $
+*/
+#include "sqliteInt.h"
+
+/*
+** Process an UPDATE statement.
+*/
+void sqliteUpdate(
+  Parse *pParse,         /* The parser context */
+  Token *pTableName,     /* The table in which we should change things */
+  ExprList *pChanges,    /* Things to be changed */
+  Expr *pWhere           /* The WHERE clause.  May be null */
+){
+  int i, j;              /* Loop counters */
+  Table *pTab;           /* The table to be updated */
+  IdList *pTabList = 0;  /* List containing only pTab */
+  int end, addr;         /* A couple of addresses in the generated code */
+  WhereInfo *pWInfo;     /* Information about the WHERE clause */
+  Vdbe *v;               /* The virtual database engine */
+  Index *pIdx;           /* For looping over indices */
+  int nIdx;              /* Number of indices that need updating */
+  Index **apIdx = 0;     /* An array of indices that need updating too */
+  int *aXRef = 0;        /* aXRef[i] is the index in pChanges->a[] of the
+                         ** an expression for the i-th field of the table.
+                         ** aXRef[i]==-1 if the i-th field is not changed. */
+
+  /* Locate the table which we want to update.  This table has to be
+  ** put in an IdList structure because some of the subroutines will
+  ** will be calling are designed to work with multiple tables and expect
+  ** an IdList* parameter instead of just a Table* parameger.
+  */
+  pTabList = sqliteIdListAppend(0, pTableName);
+  for(i=0; i<pTabList->nId; i++){
+    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
+    if( pTabList->a[i].pTab==0 ){
+      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
+         pTabList->a[i].zName, 0);
+      pParse->nErr++;
+      goto update_cleanup;
+    }
+    if( pTabList->a[i].pTab->readOnly ){
+      sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName,
+        " may not be modified", 0);
+      pParse->nErr++;
+      goto update_cleanup;
+    }
+  }
+  pTab = pTabList->a[0].pTab;
+  aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
+  if( aXRef==0 ) goto update_cleanup;
+  for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
+
+  /* Resolve the field names in all the expressions in both the
+  ** WHERE clause and in the new values.  Also find the field index
+  ** for each field to be updated in the pChanges array.
+  */
+  if( pWhere ){
+    if( sqliteExprResolveIds(pParse, pTabList, pWhere) ){
+      goto update_cleanup;
+    }
+    if( sqliteExprCheck(pParse, pWhere, 0, 0) ){
+      goto update_cleanup;
+    }
+  }
+  for(i=0; i<pChanges->nExpr; i++){
+    if( sqliteExprResolveIds(pParse, pTabList, pChanges->a[i].pExpr) ){
+      goto update_cleanup;
+    }
+    if( sqliteExprCheck(pParse, pChanges->a[i].pExpr, 0, 0) ){
+      goto update_cleanup;
+    }
+    for(j=0; j<pTab->nCol; j++){
+      if( strcmp(pTab->azCol[j], pChanges->a[i].zName)==0 ){
+        pChanges->a[i].idx = j;
+        aXRef[j] = i;
+        break;
+      }
+    }
+    if( j>=pTab->nCol ){
+      sqliteSetString(&pParse->zErrMsg, "no such field: ", 
+         pChanges->a[i].zName, 0);
+      pParse->nErr++;
+      goto update_cleanup;
+    }
+  }
+
+  /* Allocate memory for the array apIdx[] and fill it pointers to every
+  ** index that needs to be updated.  Indices only need updating if their
+  ** key includes one of the fields named in pChanges.
+  */
+  for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+    for(i=0; i<pIdx->nField; i++){
+      if( aXRef[pIdx->aiField[i]]>=0 ) break;
+    }
+    if( i<pIdx->nField ) nIdx++;
+  }
+  apIdx = sqliteMalloc( sizeof(Index*) * nIdx );
+  if( apIdx==0 ) goto update_cleanup;
+  for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+    for(i=0; i<pIdx->nField; i++){
+      if( aXRef[pIdx->aiField[i]]>=0 ) break;
+    }
+    if( i<pIdx->nField ) apIdx[nIdx++] = pIdx;
+  }
+
+  /* Begin generating code.
+  */
+  v = pParse->pVdbe;
+  if( v==0 ){
+    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  }
+  if( v==0 ) goto update_cleanup;
+
+  /* Begin the database scan
+  */
+  sqliteVdbeAddOp(v, OP_ListOpen, 0, 0, 0, 0);
+  pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1);
+  if( pWInfo==0 ) goto update_cleanup;
+
+  /* Remember the index of every item to be updated.
+  */
+  sqliteVdbeAddOp(v, OP_ListWrite, 0, 0, 0, 0);
+
+  /* End the database scan loop.
+  */
+  sqliteWhereEnd(pWInfo);
+
+  /* Rewind the list of records that need to be updated and
+  ** open every index that needs updating.
+  */
+  sqliteVdbeAddOp(v, OP_ListRewind, 0, 0, 0, 0);
+  for(i=0; i<nIdx; i++){
+    sqliteVdbeAddOp(v, OP_Open, i+1, 0, apIdx[i]->zName, 0);
+  }
+
+  /* Loop over every record that needs updating.  We have to load
+  ** the old data for each record to be updated because some fields
+  ** might not change and we will need to copy the old value, therefore.
+  ** Also, the old data is needed to delete the old index entires.
+  */
+  end = sqliteVdbeMakeLabel(v);
+  addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end, 0, 0);
+  sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+  sqliteVdbeAddOp(v, OP_Fetch, 0, 0, 0, 0);
+
+  /* Delete the old indices for the current record.
+  */
+  for(i=0; i<nIdx; i++){
+    sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
+    pIdx = apIdx[i];
+    for(j=0; j<pIdx->nField; j++){
+      sqliteVdbeAddOp(v, OP_Field, 0, pIdx->aiField[j], 0, 0);
+    }
+    sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_DeleteIdx, i+1, 0, 0, 0);
+  }
+
+  /* Compute a completely new data for this record.  
+  */
+  for(i=0; i<pTab->nCol; i++){
+    j = aXRef[i];
+    if( j<0 ){
+      sqliteVdbeAddOp(v, OP_Field, 0, i, 0, 0);
+    }else{
+      sqliteExprCode(pParse, pChanges->a[j].pExpr);
+    }
+  }
+
+  /* Insert new index entries that correspond to the new data
+  */
+  for(i=0; i<nIdx; i++){
+    sqliteVdbeAddOp(v, OP_Dup, pTab->nCol, 0, 0, 0); /* The KEY */
+    pIdx = apIdx[i];
+    for(j=0; j<pIdx->nField; j++){
+      sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-pIdx->aiField[j], 0, 0, 0);
+    }
+    sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nField, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_PutIdx, i+1, 0, 0, 0);
+  }
+
+  /* Write the new data back into the database.
+  */
+  sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
+  sqliteVdbeAddOp(v, OP_Put, 0, 0, 0, 0);
+
+  /* Repeat the above with the next record to be updated, until
+  ** all record selected by the WHERE clause have been updated.
+  */
+  sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
+  sqliteVdbeAddOp(v, OP_ListClose, 0, 0, 0, end);
+
+update_cleanup:
+  sqliteFree(apIdx);
+  sqliteFree(aXRef);
+  sqliteIdListDelete(pTabList);
+  sqliteExprListDelete(pChanges);
+  sqliteExprDelete(pWhere);
+  return;
+}
index 62ed2e9565ce420c6b6906dbc692497cd83bb60e..195210a30dbab9c7ec7aad64daaed128d7c52495 100644 (file)
@@ -41,7 +41,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.3 2000/05/31 02:27:50 drh Exp $
+** $Id: vdbe.c,v 1.4 2000/05/31 15:34:53 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -879,6 +879,7 @@ int sqliteVdbeExec(
             sqliteFree(p->zStack[nos]);
             p->zStack[nos] = p->zStack[tos];
             p->iStack[nos] = p->iStack[tos];
+            p->zStack[tos] = 0;
           }
         }
         p->tos--;
@@ -888,7 +889,10 @@ int sqliteVdbeExec(
       /* Opcode: Min * * *
       **
       ** Pop the top two elements from the stack then push back the
-      ** smaller of the two.
+      ** smaller of the two. 
+      **
+      ** If P1==1, always choose TOS for the min and decrement P1.
+      ** This is self-altering code...
       */
       case OP_Min: {
         int tos = p->tos;
@@ -901,10 +905,17 @@ int sqliteVdbeExec(
         }else{
           Stringify(p, tos);
           Stringify(p, nos);
-          if( sqliteCompare(p->zStack[nos], p->zStack[tos])>0 ){
+          if( pOp->p1==1 ){
+            sqliteFree(p->zStack[nos]);
+            p->zStack[nos] = p->zStack[tos];
+            p->iStack[nos] = p->iStack[tos];
+            p->zStack[tos] = 0;
+            pOp->p1 = 0;
+          }else if( sqliteCompare(p->zStack[nos], p->zStack[tos])>0 ){
             sqliteFree(p->zStack[nos]);
             p->zStack[nos] = p->zStack[tos];
             p->iStack[nos] = p->iStack[tos];
+            p->zStack[tos] = 0;
           }
         }
         p->tos--;
index 217bfd342b177f77042368e2376ce549de9eef45..188d172ba1a681a836ba46d911961ae14b99690e 100644 (file)
@@ -25,7 +25,7 @@
 ** the WHERE clause of SQL statements.  Also found here are subroutines
 ** to generate VDBE code to evaluate expressions.
 **
-** $Id: where.c,v 1.4 2000/05/31 02:27:50 drh Exp $
+** $Id: where.c,v 1.5 2000/05/31 15:34:54 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -353,240 +353,3 @@ void sqliteWhereEnd(WhereInfo *pWInfo){
   sqliteFree(pWInfo);
   return;
 }
-
-/*
-** Generate code into the current Vdbe to evaluate the given
-** expression and leave the result on the stack.
-*/
-void sqliteExprCode(Parse *pParse, Expr *pExpr){
-  Vdbe *v = pParse->pVdbe;
-  int op;
-  switch( pExpr->op ){
-    case TK_PLUS:     op = OP_Add;      break;
-    case TK_MINUS:    op = OP_Subtract; break;
-    case TK_STAR:     op = OP_Multiply; break;
-    case TK_SLASH:    op = OP_Divide;   break;
-    case TK_AND:      op = OP_And;      break;
-    case TK_OR:       op = OP_Or;       break;
-    case TK_LT:       op = OP_Lt;       break;
-    case TK_LE:       op = OP_Le;       break;
-    case TK_GT:       op = OP_Gt;       break;
-    case TK_GE:       op = OP_Ge;       break;
-    case TK_NE:       op = OP_Ne;       break;
-    case TK_EQ:       op = OP_Eq;       break;
-    case TK_LIKE:     op = OP_Like;     break;
-    case TK_GLOB:     op = OP_Glob;     break;
-    case TK_ISNULL:   op = OP_IsNull;   break;
-    case TK_NOTNULL:  op = OP_NotNull;  break;
-    case TK_NOT:      op = OP_Not;      break;
-    case TK_UMINUS:   op = OP_Negative; break;
-    default: break;
-  }
-  switch( pExpr->op ){
-    case TK_FIELD: {
-      sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iField, 0, 0);
-      break;
-    }
-    case TK_INTEGER: {
-      int i = atoi(pExpr->token.z);
-      sqliteVdbeAddOp(v, OP_Integer, i, 0, 0, 0);
-      break;
-    }
-    case TK_FLOAT: {
-      int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
-      sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
-      break;
-    }
-    case TK_STRING: {
-      int addr = sqliteVdbeAddOp(v, OP_String, 0, 0, 0, 0);
-      sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
-      sqliteVdbeDequoteP3(v, addr);
-      break;
-    }
-    case TK_NULL: {
-      sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
-      break;
-    }
-    case TK_AND:
-    case TK_OR:
-    case TK_PLUS:
-    case TK_STAR:
-    case TK_MINUS:
-    case TK_SLASH: {
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteExprCode(pParse, pExpr->pRight);
-      sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
-      break;
-    }
-    case TK_LT:
-    case TK_LE:
-    case TK_GT:
-    case TK_GE:
-    case TK_NE:
-    case TK_EQ: 
-    case TK_LIKE: 
-    case TK_GLOB: {
-      int dest;
-      sqliteVdbeAddOp(v, OP_Integer, 1, 0, 0, 0);
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteExprCode(pParse, pExpr->pRight);
-      dest = sqliteVdbeCurrentAddr(v) + 2;
-      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
-      sqliteVdbeAddOp(v, OP_AddImm, -1, 0, 0, 0);
-      break;
-    }
-    case TK_NOT:
-    case TK_UMINUS: {
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteVdbeAddOp(v, op, 0, 0, 0, 0);
-      break;
-    }
-    case TK_ISNULL:
-    case TK_NOTNULL: {
-      int dest;
-      sqliteVdbeAddOp(v, OP_Integer, 0, 0, 0, 0);
-      sqliteExprCode(pParse, pExpr->pLeft);
-      dest = sqliteVdbeCurrentAddr(v) + 2;
-      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
-      sqliteVdbeAddOp(v, OP_AddImm, 1, 0, 0, 0);
-      break;
-    }
-  }
-  return;
-}
-
-/*
-** Generate code for a boolean expression such that a jump is made
-** to the label "dest" if the expression is true but execution
-** continues straight thru if the expression is false.
-*/
-void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest){
-  Vdbe *v = pParse->pVdbe;
-  int op = 0;
-  switch( pExpr->op ){
-    case TK_LT:       op = OP_Lt;       break;
-    case TK_LE:       op = OP_Le;       break;
-    case TK_GT:       op = OP_Gt;       break;
-    case TK_GE:       op = OP_Ge;       break;
-    case TK_NE:       op = OP_Ne;       break;
-    case TK_EQ:       op = OP_Eq;       break;
-    case TK_LIKE:     op = OP_Like;     break;
-    case TK_GLOB:     op = OP_Glob;     break;
-    case TK_ISNULL:   op = OP_IsNull;   break;
-    case TK_NOTNULL:  op = OP_NotNull;  break;
-    default:  break;
-  }
-  switch( pExpr->op ){
-    case TK_AND: {
-      int d2 = sqliteVdbeMakeLabel(v);
-      sqliteExprIfFalse(pParse, pExpr->pLeft, d2);
-      sqliteExprIfTrue(pParse, pExpr->pRight, dest);
-      sqliteVdbeResolveLabel(v, d2);
-      break;
-    }
-    case TK_OR: {
-      sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
-      sqliteExprIfTrue(pParse, pExpr->pRight, dest);
-      break;
-    }
-    case TK_NOT: {
-      sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
-      break;
-    }
-    case TK_LT:
-    case TK_LE:
-    case TK_GT:
-    case TK_GE:
-    case TK_NE:
-    case TK_EQ:
-    case TK_LIKE:
-    case TK_GLOB: {
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteExprCode(pParse, pExpr->pRight);
-      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
-      break;
-    }
-    case TK_ISNULL:
-    case TK_NOTNULL: {
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
-      break;
-    }
-    default: {
-      sqliteExprCode(pParse, pExpr);
-      sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
-      break;
-    }
-  }
-}
-
-/*
-** Generate code for boolean expression such that a jump is made
-** to the label "dest" if the expression is false but execution
-** continues straight thru if the expression is true.
-*/
-void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
-  Vdbe *v = pParse->pVdbe;
-  int op = 0;
-  switch( pExpr->op ){
-    case TK_LT:       op = OP_Ge;       break;
-    case TK_LE:       op = OP_Gt;       break;
-    case TK_GT:       op = OP_Le;       break;
-    case TK_GE:       op = OP_Lt;       break;
-    case TK_NE:       op = OP_Eq;       break;
-    case TK_EQ:       op = OP_Ne;       break;
-    case TK_LIKE:     op = OP_Like;     break;
-    case TK_GLOB:     op = OP_Glob;     break;
-    case TK_ISNULL:   op = OP_NotNull;  break;
-    case TK_NOTNULL:  op = OP_IsNull;   break;
-    default:  break;
-  }
-  switch( pExpr->op ){
-    case TK_AND: {
-      sqliteExprIfFalse(pParse, pExpr->pLeft, dest);
-      sqliteExprIfFalse(pParse, pExpr->pRight, dest);
-      break;
-    }
-    case TK_OR: {
-      int d2 = sqliteVdbeMakeLabel(v);
-      sqliteExprIfTrue(pParse, pExpr->pLeft, d2);
-      sqliteExprIfFalse(pParse, pExpr->pRight, dest);
-      sqliteVdbeResolveLabel(v, d2);
-      break;
-    }
-    case TK_NOT: {
-      sqliteExprIfTrue(pParse, pExpr->pLeft, dest);
-      break;
-    }
-    case TK_LT:
-    case TK_LE:
-    case TK_GT:
-    case TK_GE:
-    case TK_NE:
-    case TK_EQ: {
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteExprCode(pParse, pExpr->pRight);
-      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
-      break;
-    }
-    case TK_LIKE:
-    case TK_GLOB: {
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteExprCode(pParse, pExpr->pRight);
-      sqliteVdbeAddOp(v, op, 1, dest, 0, 0);
-      break;
-    }
-    case TK_ISNULL:
-    case TK_NOTNULL: {
-      sqliteExprCode(pParse, pExpr->pLeft);
-      sqliteVdbeAddOp(v, op, 0, dest, 0, 0);
-      break;
-    }
-    default: {
-      sqliteExprCode(pParse, pExpr);
-      sqliteVdbeAddOp(v, OP_Not, 0, 0, 0, 0);
-      sqliteVdbeAddOp(v, OP_If, 0, dest, 0, 0);
-      break;
-    }
-  }
-}
index fa52187e88beef1b8a8212019bd0ba4f7777c7ff..dfc8c63e6dd8ce145101a6bd7b9afb6451d9e446 100644 (file)
@@ -17,6 +17,11 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
+chan {2000 May 31} {
+<li>Added support for aggregate functions (Ex: <b>COUNT(*)<b>, <b>MIN(...)</b>)
+to the SELECT statement.</li>
+}
+
 chng {2000 May 30} {
 <li>Added the <b>LIKE</b> operator.</li>
 <li>Added a <b>GLOB</b> operator: similar to <B>LIKE</B> 
index 24a87a44c858c8b7d601874444aa09b7fae5e2bf..93bf168cf864a6f58c97aa2382f5c96dfa164b84 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this TCL script to generate HTML for the index.html file.
 #
-set rcsid {$Id: index.tcl,v 1.5 2000/05/31 02:27:50 drh Exp $}
+set rcsid {$Id: index.tcl,v 1.6 2000/05/31 15:34:54 drh Exp $}
 
 puts {<html>
 <head><title>SQLite: An SQL Frontend For GDBM</title></head>
@@ -50,31 +50,13 @@ see <a href="sqlite.html">sqlite.html</a>.</p>
 <p>A history of changes to SQLite is found
 <a href="changes.html">here</a>.</p>
 
-<p>SQLite does not try to implement every feature of SQL.  But it
-does strive to implement to most commonly used features.  SQLite
-currently understands the following SQL commands:</p>
-
-<p>
-<ul>
-<li>CREATE TABLE</li>
-<li>CREATE INDEX</li>
-<li>DROP TABLE</li>
-<li>DROP INDEX</li>
-<li>INSERT INTO</li>
-<li>UPDATE</li>
-<li>SELECT</li>
-<li>DELETE FROM</li>
-</ul>
-</p>
-
-<p>A few of the many SQL features that SQLite does not (currently) 
+<p>SQLite does not try to implement every feature of SQL. 
+A few of the many SQL features that SQLite does not (currently) 
 implement are as follows:</p>
 
 <p>
 <ul>
-<li>ALTER TABLE</li>
 <li>The GROUP BY or HAVING clauses of a SELECT</li>
-<li>The COUNT(), MAX(), MIN(), and AVG() functions</li>
 <li>Constraints</li>
 <li>Nested queries</li>
 <li>Transactions or rollback</li>