added aggregate functions like count(*) (CVS 21)

FossilOrigin-Name: dee7a8be88a95014534b90b96716d9e2e6b16579
This commit is contained in:
drh 2000-05-31 15:34:51 +00:00
parent dce2cbe65f
commit cce7d17614
15 changed files with 1395 additions and 995 deletions

View 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)

View File

@ -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

View File

@ -1 +1 @@
01d85b35e9c4ca5619ad21a4232a8f8bf9ec3538
dee7a8be88a95014534b90b96716d9e2e6b16579

View 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

132
src/delete.c Normal file
View File

@ -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;
}

527
src/expr.c Normal file
View File

@ -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;
}
}
}

158
src/insert.c Normal file
View File

@ -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);
}

View 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);}

271
src/select.c Normal file
View File

@ -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;
}

View 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"
@ -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*);

225
src/update.c Normal file
View File

@ -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;
}

View 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--;

View 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;
}
}
}

View 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>

View 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>