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:
+11
-3
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user