added aggregate functions like count(*) (CVS 21)
FossilOrigin-Name: dee7a8be88a95014534b90b96716d9e2e6b16579
This commit is contained in:
parent
dce2cbe65f
commit
cce7d17614
19
Makefile.in
19
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)
|
||||
|
||||
|
31
manifest
31
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
|
||||
|
@ -1 +1 @@
|
||||
01d85b35e9c4ca5619ad21a4232a8f8bf9ec3538
|
||||
dee7a8be88a95014534b90b96716d9e2e6b16579
|
715
src/build.c
715
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; 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
132
src/delete.c
Normal 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
527
src/expr.c
Normal 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
158
src/insert.c
Normal 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);
|
||||
}
|
@ -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
271
src/select.c
Normal 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;
|
||||
}
|
@ -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
225
src/update.c
Normal 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;
|
||||
}
|
17
src/vdbe.c
17
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--;
|
||||
|
239
src/where.c
239
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user