Added support for the COUNT_CHANGES pragma in order to help out the

ODBC driver.  Fixed a but on count(*) when applied to empty tables. (CVS 289)

FossilOrigin-Name: 747bf1b30b74cfd0e9c27e7c0bc5172637f35520
This commit is contained in:
drh
2001-10-15 00:44:35 +00:00
parent f2d6a64495
commit 1bee3d7b43
19 changed files with 349 additions and 101 deletions
+11 -3
View File
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.33 2001/10/06 16:33:02 drh Exp $
** $Id: btree.c,v 1.34 2001/10/15 00:44:35 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -597,8 +597,8 @@ static void pageDestructor(void *pData){
** until the first page is loaded.
**
** zFilename is the name of the database file. If zFilename is NULL
** a new database with a random name is created. The database will be
** destroyed when sqliteBtreeClose() is called.
** a new database with a random name is created. This randomly named
** database file will be deleted when sqliteBtreeClose() is called.
*/
int sqliteBtreeOpen(
const char *zFilename, /* Name of the file containing the BTree database */
@@ -807,6 +807,13 @@ int sqliteBtreeRollback(Btree *pBt){
** Create a new cursor for the BTree whose root is on the page
** iTable. The act of acquiring a cursor gets a read lock on
** the database file.
**
** If wrFlag==0, then the cursor can only be used for reading.
** If wrFlag==1, then the cursor can be used for reading or writing.
** A read/write cursor requires exclusive access to its table. There
** cannot be two or more cursors open on the same table is any one of
** cursors is a read/write cursor. But there can be two or more
** read-only cursors open on the same table.
*/
int sqliteBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
int rc;
@@ -1357,6 +1364,7 @@ int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){
int sqliteBtreeNext(BtCursor *pCur, int *pRes){
int rc;
if( pCur->pPage==0 ){
if( pRes ) *pRes = 1;
return SQLITE_ABORT;
}
if( pCur->bSkipNext ){
+9 -1
View File
@@ -25,7 +25,7 @@
** ROLLBACK
** PRAGMA
**
** $Id: build.c,v 1.49 2001/10/13 02:59:09 drh Exp $
** $Id: build.c,v 1.50 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -1441,6 +1441,14 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
}
}else
if( sqliteStrICmp(zLeft, "count_changes")==0 ){
if( getBoolean(zRight) ){
db->flags |= SQLITE_CountRows;
}else{
db->flags &= ~SQLITE_CountRows;
}
}else
if( sqliteStrICmp(zLeft, "table_info")==0 ){
Table *pTab;
Vdbe *v;
+35 -3
View File
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements.
**
** $Id: delete.c,v 1.17 2001/10/13 01:06:48 drh Exp $
** $Id: delete.c,v 1.18 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -33,6 +33,8 @@ void sqliteDeleteFrom(
Index *pIdx; /* For looping over indices of the table */
int base; /* Index of the first available table cursor */
sqlite *db; /* Main database structure */
int openOp; /* Opcode used to open a cursor to the table */
if( pParse->nErr || sqlite_malloc_failed ){
pTabList = 0;
@@ -86,11 +88,31 @@ void sqliteDeleteFrom(
pParse->schemaVerified = 1;
}
/* Initialize the counter of the number of rows deleted, if
** we are counting rows.
*/
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
}
/* Special case: A DELETE without a WHERE clause deletes everything.
** It is easier just to erase the whole table.
*/
if( pWhere==0 ){
if( db->flags & SQLITE_CountRows ){
/* If counting rows deleted, just count the total number of
** entries in the table. */
int endOfLoop = sqliteVdbeMakeLabel(v);
int addr;
openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
sqliteVdbeAddOp(v, openOp, 0, pTab->tnum);
sqliteVdbeAddOp(v, OP_Rewind, 0, 0);
addr = sqliteVdbeAddOp(v, OP_Next, 0, endOfLoop);
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
sqliteVdbeResolveLabel(v, endOfLoop);
sqliteVdbeAddOp(v, OP_Close, 0, 0);
}
sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->isTemp);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pTab->isTemp);
@@ -101,8 +123,6 @@ void sqliteDeleteFrom(
** the table an pick which records to delete.
*/
else{
int openOp;
/* Begin the database scan
*/
sqliteVdbeAddOp(v, OP_ListOpen, 0, 0);
@@ -112,6 +132,9 @@ void sqliteDeleteFrom(
/* Remember the key of every item to be deleted.
*/
sqliteVdbeAddOp(v, OP_ListWrite, 0, 0);
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
}
/* End the database scan loop.
*/
@@ -151,6 +174,15 @@ void sqliteDeleteFrom(
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}
/*
** Return the number of rows that were deleted.
*/
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_ColumnCount, 1, 0);
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
sqliteVdbeChangeP3(v, -1, "rows deleted", P3_STATIC);
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
}
delete_from_cleanup:
sqliteIdListDelete(pTabList);
+25 -1
View File
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
**
** $Id: insert.c,v 1.23 2001/10/13 01:06:48 drh Exp $
** $Id: insert.c,v 1.24 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -170,6 +170,9 @@ void sqliteInsert(
** and the loop is not used.
*/
if( srcTab>=0 ){
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_Integer, 0, 0); /* Initialize the row count */
}
sqliteVdbeAddOp(v, OP_Rewind, srcTab, 0);
iBreak = sqliteVdbeMakeLabel(v);
iCont = sqliteVdbeAddOp(v, OP_Next, srcTab, iBreak);
@@ -200,6 +203,7 @@ void sqliteInsert(
}
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_Put, base, 0);
/* Create appropriate entries for the new data row in all indices
** of the table.
@@ -230,6 +234,14 @@ void sqliteInsert(
sqliteVdbeAddOp(v, OP_PutIdx, idx+base, pIdx->isUnique);
}
/* If inserting from a SELECT, keep a count of the number of
** rows inserted.
*/
if( srcTab>=0 && (db->flags & SQLITE_CountRows)!=0 ){
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
}
/* The bottom of the loop, if the data source is a SELECT statement
*/
if( srcTab>=0 ){
@@ -241,6 +253,18 @@ void sqliteInsert(
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}
/*
** Return the number of rows inserted.
*/
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_ColumnCount, 1, 0);
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
sqliteVdbeChangeP3(v, -1, "rows inserted", P3_STATIC);
if( srcTab<0 ){
sqliteVdbeAddOp(v, OP_Integer, 1, 0);
}
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
}
insert_cleanup:
if( pList ) sqliteExprListDelete(pList);
+1 -1
View File
@@ -68,7 +68,7 @@
** To work around the problem, SQLite has to manage file locks internally
** on its own. Whenever a new database is opened, we have to find the
** specific inode of the database file (the inode is determined by the
** st_dev and st_ino fields of the stat structure the stat() fills in)
** st_dev and st_ino fields of the stat structure that fstat() fills in)
** and check for locks already existing on that inode. When locks are
** created or removed, we have to look at our own internal record of the
** locks to see if another thread has previously set a lock on that same
+35 -26
View File
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.39 2001/10/13 01:06:48 drh Exp $
** $Id: select.c,v 1.40 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -239,6 +239,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0);
for(i=0; i<pEList->nExpr; i++){
Expr *p;
int showFullNames;
if( pEList->a[i].zName ){
char *zName = pEList->a[i].zName;
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
@@ -247,17 +248,13 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
}
p = pEList->a[i].pExpr;
if( p==0 ) continue;
if( p->span.z && p->span.z[0] ){
showFullNames = (pParse->db->flags & SQLITE_FullColNames)!=0;
if( p->span.z && p->span.z[0] && !showFullNames ){
int addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0);
sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
sqliteVdbeCompressSpace(v, addr);
}else if( p->op!=TK_COLUMN || pTabList==0 ){
char zName[30];
sprintf(zName, "column%d", i+1);
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
sqliteVdbeChangeP3(v, -1, zName, strlen(zName));
}else{
if( pTabList->nId>1 || (pParse->db->flags & SQLITE_FullColNames)!=0 ){
}else if( p->op==TK_COLUMN && pTabList ){
if( pTabList->nId>1 || showFullNames ){
char *zName = 0;
Table *pTab = pTabList->a[p->iTable].pTab;
char *zTab;
@@ -274,6 +271,16 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
sqliteVdbeChangeP3(v, -1, zName, P3_STATIC);
}
}else if( p->span.z && p->span.z[0] ){
int addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0);
sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n);
sqliteVdbeCompressSpace(v, addr);
}else{
char zName[30];
assert( p->op!=TK_COLUMN || pTabList==0 );
sprintf(zName, "column%d", i+1);
sqliteVdbeAddOp(v, OP_ColumnName, i, 0);
sqliteVdbeChangeP3(v, -1, zName, strlen(zName));
}
}
}
@@ -867,6 +874,23 @@ int sqliteSelect(
*/
if( isAgg ){
sqliteVdbeAddOp(v, OP_AggReset, 0, pParse->nAgg);
if( pGroupBy==0 ){
sqliteVdbeAddOp(v, OP_String, 0, 0);
sqliteVdbeChangeP3(v, -1, "", P3_STATIC);
sqliteVdbeAddOp(v, OP_AggFocus, 0, 0);
for(i=0; i<pParse->nAgg; i++){
Expr *pE;
if( !pParse->aAgg[i].isAgg ) continue;
pE = pParse->aAgg[i].pExpr;
assert( pE==0 || pE->op==TK_AGG_FUNCTION );
assert( pE==0 || (pE->pList!=0 && pE->pList->nExpr==1) );
if( pE==0 || pE->iColumn==FN_Sum ){
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
sqliteVdbeAddOp(v, OP_AggSet, 0, i);
continue;
}
}
}
}
/* Initialize the memory cell to NULL
@@ -898,28 +922,13 @@ int sqliteSelect(
** processing.
*/
else{
int doFocus;
if( pGroupBy ){
int lbl1;
for(i=0; i<pGroupBy->nExpr; i++){
sqliteExprCode(pParse, pGroupBy->a[i].pExpr);
}
sqliteVdbeAddOp(v, OP_MakeKey, pGroupBy->nExpr, 0);
doFocus = 1;
}else{
doFocus = 0;
for(i=0; i<pParse->nAgg; i++){
if( !pParse->aAgg[i].isAgg ){
doFocus = 1;
break;
}
}
if( doFocus ){
sqliteVdbeAddOp(v, OP_String, 0, 0);
sqliteVdbeChangeP3(v, -1, "", P3_STATIC);
}
}
if( doFocus ){
int lbl1 = sqliteVdbeMakeLabel(v);
lbl1 = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_AggFocus, 0, lbl1);
for(i=0; i<pParse->nAgg; i++){
if( pParse->aAgg[i].isAgg ) continue;
+4 -1
View File
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.61 2001/10/13 02:59:09 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.62 2001/10/15 00:44:36 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@@ -163,6 +163,9 @@ struct sqlite {
#define SQLITE_InTrans 0x00000008 /* True if in a transaction */
#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */
#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
#define SQLITE_CountRows 0x00000040 /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
/* the count using a callback. */
/*
** Current file format version
+23 -1
View File
@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.18 2001/10/13 01:06:48 drh Exp $
** $Id: update.c,v 1.19 2001/10/15 00:44:36 drh Exp $
*/
#include "sqliteInt.h"
@@ -155,6 +155,12 @@ void sqliteUpdate(
*/
sqliteWhereEnd(pWInfo);
/* Initialize the count of updated rows
*/
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_Integer, 0, 0);
}
/* Rewind the list of records that need to be updated and
** open every index that needs updating.
*/
@@ -216,6 +222,12 @@ void sqliteUpdate(
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
sqliteVdbeAddOp(v, OP_Put, base, 0);
/* Increment the count of rows affected by the update
*/
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
}
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
*/
@@ -226,6 +238,16 @@ void sqliteUpdate(
sqliteVdbeAddOp(v, OP_Commit, 0, 0);
}
/*
** Return the number of rows that were changed.
*/
if( db->flags & SQLITE_CountRows ){
sqliteVdbeAddOp(v, OP_ColumnCount, 1, 0);
sqliteVdbeAddOp(v, OP_ColumnName, 0, 0);
sqliteVdbeChangeP3(v, -1, "rows updated", P3_STATIC);
sqliteVdbeAddOp(v, OP_Callback, 1, 0);
}
update_cleanup:
sqliteFree(apIdx);
sqliteFree(aXRef);