From cce7d1761422e206689062685c79664e469a57ed Mon Sep 17 00:00:00 2001
From: drh
Date: Wed, 31 May 2000 15:34:51 +0000
Subject: [PATCH] added aggregate functions like count(*) (CVS 21)
FossilOrigin-Name: dee7a8be88a95014534b90b96716d9e2e6b16579
---
Makefile.in | 19 +-
manifest | 31 ++-
manifest.uuid | 2 +-
src/build.c | 715 +-----------------------------------------------
src/delete.c | 132 +++++++++
src/expr.c | 527 +++++++++++++++++++++++++++++++++++
src/insert.c | 158 +++++++++++
src/parse.y | 9 +-
src/select.c | 271 ++++++++++++++++++
src/sqliteInt.h | 16 +-
src/update.c | 225 +++++++++++++++
src/vdbe.c | 17 +-
src/where.c | 239 +---------------
www/changes.tcl | 5 +
www/index.tcl | 24 +-
15 files changed, 1395 insertions(+), 995 deletions(-)
create mode 100644 src/delete.c
create mode 100644 src/expr.c
create mode 100644 src/insert.c
create mode 100644 src/select.c
create mode 100644 src/update.c
diff --git a/Makefile.in b/Makefile.in
index 8c277993d1..887254411e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -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)
diff --git a/manifest b/manifest
index e1a4a8ad4b..b09128221f 100644
--- 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
diff --git a/manifest.uuid b/manifest.uuid
index 1b7f2dfd75..a38dfab2c3 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-01d85b35e9c4ca5619ad21a4232a8f8bf9ec3538
\ No newline at end of file
+dee7a8be88a95014534b90b96716d9e2e6b16579
\ No newline at end of file
diff --git a/src/build.c b/src/build.c
index fd0aaeef31..380ce94a06 100644
--- a/src/build.c
+++ b/src/build.c
@@ -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; inId; i++){
- pField->a[i].idx = -1;
- }
- for(i=0; inId; i++){
- for(j=0; jnCol; 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; inCol; i++){
- if( pField==0 ){
- j = i;
- }else{
- for(j=0; jnId; 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; inField; i++){
- int idx = pIdx->aiField[i];
- if( pField==0 ){
- j = idx;
- }else{
- for(j=0; jnId; 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; inId; i++){
- int j;
- Table *pTab = pTabList->a[i].pTab;
- if( pTab==0 ) continue;
- for(j=0; jnCol; 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; inId; 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; jnCol; 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; inExpr; 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; inId; 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; inId; i++){
- Table *pTab = pTabList->a[i].pTab;
- for(j=0; jnCol; 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; inExpr; 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; inExpr; 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; inExpr; 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; inExpr; 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; inExpr; 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; inId; 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; jnField; 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; inId; 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; inCol; 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; inExpr; i++){
- if( sqliteExprResolveIds(pParse, pTabList, pChanges->a[i].pExpr) ){
- goto update_cleanup;
- }
- for(j=0; jnCol; 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; inField; i++){
- if( aXRef[pIdx->aiField[i]]>=0 ) break;
- }
- if( inField ) 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; inField; i++){
- if( aXRef[pIdx->aiField[i]]>=0 ) break;
- }
- if( inField ) 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; izName, 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; inField; 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; inCol; 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; inCol, 0, 0, 0); /* The KEY */
- pIdx = apIdx[i];
- for(j=0; jnField; 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
index 0000000000..3ab5f59404
--- /dev/null
+++ b/src/delete.c
@@ -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; inId; 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; jnField; 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
index 0000000000..01ed63d1cf
--- /dev/null
+++ b/src/expr.c
@@ -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; inId; i++){
+ int j;
+ Table *pTab = pTabList->a[i].pTab;
+ if( pTab==0 ) continue;
+ for(j=0; jnCol; 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; inId; 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; jnCol; 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; inExpr; 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; in
+ && 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 && ipList->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; inExpr; 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
index 0000000000..071e642c7d
--- /dev/null
+++ b/src/insert.c
@@ -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; inId; i++){
+ pField->a[i].idx = -1;
+ }
+ for(i=0; inId; i++){
+ for(j=0; jnCol; 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; inCol; i++){
+ if( pField==0 ){
+ j = i;
+ }else{
+ for(j=0; jnId; 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; inField; i++){
+ int idx = pIdx->aiField[i];
+ if( pField==0 ){
+ j = idx;
+ }else{
+ for(j=0; jnId; 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);
+}
diff --git a/src/parse.y b/src/parse.y
index 89d003575c..750e172013 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -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
index 0000000000..1a9416e348
--- /dev/null
+++ b/src/select.c
@@ -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; inId; 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; inId; i++){
+ Table *pTab = pTabList->a[i].pTab;
+ for(j=0; jnCol; 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; inExpr; 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; inExpr; 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; inExpr; 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; inExpr; 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; inExpr; 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; inExpr; 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; inExpr; 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; ia[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;
+}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index c64a147379..2c1491d8d3 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -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"
@@ -61,6 +61,16 @@
*/
#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
index 0000000000..a19846a91d
--- /dev/null
+++ b/src/update.c
@@ -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; inId; 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; inCol; 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; inExpr; 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; jnCol; 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; inField; i++){
+ if( aXRef[pIdx->aiField[i]]>=0 ) break;
+ }
+ if( inField ) 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; inField; i++){
+ if( aXRef[pIdx->aiField[i]]>=0 ) break;
+ }
+ if( inField ) 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; izName, 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; inField; 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; inCol; 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; inCol, 0, 0, 0); /* The KEY */
+ pIdx = apIdx[i];
+ for(j=0; jnField; 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;
+}
diff --git a/src/vdbe.c b/src/vdbe.c
index 62ed2e9565..195210a30d 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -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--;
diff --git a/src/where.c b/src/where.c
index 217bfd342b..188d172ba1 100644
--- a/src/where.c
+++ b/src/where.c
@@ -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;
- }
- }
-}
diff --git a/www/changes.tcl b/www/changes.tcl
index fa52187e88..dfc8c63e6d 100644
--- a/www/changes.tcl
+++ b/www/changes.tcl
@@ -17,6 +17,11 @@ proc chng {date desc} {
puts "
"
}
+chan {2000 May 31} {
+Added support for aggregate functions (Ex: COUNT(*), MIN(...))
+to the SELECT statement.
+}
+
chng {2000 May 30} {
Added the LIKE operator.
Added a GLOB operator: similar to LIKE
diff --git a/www/index.tcl b/www/index.tcl
index 24a87a44c8..93bf168cf8 100644
--- a/www/index.tcl
+++ b/www/index.tcl
@@ -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 {
SQLite: An SQL Frontend For GDBM
@@ -50,31 +50,13 @@ see sqlite.html.
A history of changes to SQLite is found
here.
-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:
-
-
-
-- CREATE TABLE
-- CREATE INDEX
-- DROP TABLE
-- DROP INDEX
-- INSERT INTO
-- UPDATE
-- SELECT
-- DELETE FROM
-
-
-
-A few of the many SQL features that SQLite does not (currently)
+
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:
-- ALTER TABLE
- The GROUP BY or HAVING clauses of a SELECT
-- The COUNT(), MAX(), MIN(), and AVG() functions
- Constraints
- Nested queries
- Transactions or rollback