diff --git a/manifest b/manifest
index 5d1dd7e794..4bafe2bff4 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bug\sfixes\sfrom\sOleg\sOleinick\s(CVS\s195)
-D 2001-04-03T16:53:22
+C Added\stransaction\ssupport\s(CVS\s196)
+D 2001-04-04T11:48:57
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in fd8815aa01a7181f60f786158b7737a35413189e
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
@@ -9,11 +9,11 @@ F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
F doc/report1.txt 734cbae63b1310cc643fe5e9e3da1ab55a79b99e
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
-F src/build.c 7aa5879bf58ea6bbff22c26c59d1130021fa6ca4
+F src/build.c 4c5eede16695d5e74bb3004e51923492b66eae62
F src/dbbe.c b178f0959f6bac5ef8a109484c1571053f31abe5
-F src/dbbe.h 4b33f0cf884dfab49e39a422b2dcaf7a2a0e626c
-F src/dbbegdbm.c c4b2857e242ff8b4e8a5ac2d95e2e35f462ce8eb
-F src/dbbemem.c f0007eff4a00f28126c093f37f8e7dd2fcaa123b
+F src/dbbe.h 7235b15c6c5d8be0c4da469cef9620cee70b1cc8
+F src/dbbegdbm.c d044b9e3a463608ac4f35283c78ac372d5da64c6
+F src/dbbemem.c fa84058f79dd5e6af1ccbb0d41c85e05a6bc19ac
F src/delete.c 7aa9dcb86d5e98c3eb9dee00a459e0ef9b73fbe3
F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a
F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7
@@ -23,48 +23,50 @@ F src/ex/dbbemird.c b00aef85656fa0a101dac2c32e12922ad106715a
F src/ex/pg.c 2bbf6a94f37226d06337868b6bf4d7affc60197f
F src/ex/pg.h 23a4ac807b0546ec2bb6239ec8bd3e06926572cd
F src/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7
-F src/expr.c 49bc261fdc4f4fb91c74cd668a9a952c00e85931
+F src/expr.c cdf54a3b8a24ef99b3b7808a5a55af17d404bc67
F src/insert.c 4bc1cab84f7805d560a1417734a532843e30b762
F src/main.c 5afe29c425b875acede20f609485866eb5b276f6
F src/pager.h 889c5cf517ad30704e295540793c893ac843fd5f
-F src/parse.y 25ee4d8efccc4b247c32fe4ab194e3dd8fd5a4ee
+F src/parse.y 1ba81d3b75f37ca868aa0ab990bb977fd41519eb
F src/printf.c af0dc65c293427272e1949c7807b1d88f10004fd
F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
-F src/select.c faac634ef0c717bc82ca112a4531a257886f2c7a
+F src/select.c a6bfdaa92d4614e79bf18129283c5163faa291fc
F src/shell.c 441e20913cde0bb71281f4027623c623530241cd
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 3b446fcbed6005f0ab89632f3356c4708b349e88
-F src/sqliteInt.h 9887d207b98362392668410a11c59b3e334f51a1
+F src/sqliteInt.h 7872fa85719adff8e458f4a27d56a0ea3e8a3dd1
F src/table.c 5be76051a8ed6f6bfa641f4adc52529efa34fbf9
F src/tclsqlite.c f654b0399ea8a29262637dbe71fdfe7c26bd9032
-F src/tokenize.c c7ad428f38e56342eb2025320480b5ae9ece1b90
+F src/tokenize.c 8fc3936eefad84f1fff19e0892ed0542eb9ac7b3
F src/update.c 8365b3922ea098330d1e20862d6e64911e4e03d0
F src/util.c f4573201fc2b581dbf601c53787349310b7da150
-F src/vdbe.c aa14a8aef0229fd5cfa32c3957dc627555f42be8
-F src/vdbe.h 031b7dd7d6f94c51dc37cdf26efe43d1619bb672
-F src/where.c 478fde7c930969ca428de2d80b137959d25ee2fb
+F src/vdbe.c 53de79aa212997a8615659d7a7e6eb12aa77255d
+F src/vdbe.h dc1205da434c6a9da03b5d6b089270bbc8e6d437
+F src/where.c 459bf37ac7849599da400420984b3306484b4cbb
F test/all.test 15cac2f6b2d4c55bf896212aff3cc9d6597b0490
F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
F test/dbbe.test a022fe2d983848f786e17ef1fc6809cfd37fb02c
F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf
-F test/expr.test 48273bf48a15d226c35829f702af4254c0ff6795
+F test/expr.test 83b29f29f58df80d185d163b7fab5c658a1bd29a
F test/func.test 02aed8845b98bde1043dda97455de1d37238ebb3
-F test/in.test 2c560c0f55fb777029fd9bb5378f2997582aa603
-F test/index.test ee060ef8912be47ba616e50cce7985259a68d58a
-F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c
+F test/in.test ea48016c4fcc479d315932ae2b8568146686ffaf
+F test/index.test b189ac11bf8d4fbcf87402f4028c25c8a6d91bb5
+F test/insert.test dbd3bd189edb61fddbe66c236694ef23352429f1
F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
F test/lock.test bca7d53de73138b1f670a2fbdb1f481ff7eaa45a
-F test/main.test 5b0ed3d586c15b9136b9fd4916dcc95086639387
-F test/select1.test 68ff778c24fc8982e63dda37acb5b0396913adf7
+F test/main.test da635f9e078cd21ddf074e727381a715064489ff
+F test/rowid.test 128453599def7435e988216f7fe89c7450b8a9a3
+F test/select1.test 824d9d5007dffd6a45edde79e89c0a04c36e3ebe
F test/select2.test 04ac3bd69298f58c7d0883159bab42ab9ad6021c
F test/select3.test a9234b8424b6c6d71de534f43b91ade9be68e9cc
F test/select4.test cb5374d7c87680e294ac749307459a5cc547609d
F test/select5.test e2b9d51d88cbd6c307c2c05b0ef55fe7ba811ac2
-F test/sort.test d582086c4bb7df3fbf50aa72e69d7e235e9f8e31
+F test/sort.test 838cd862642ed9a2c47e1a17b5c33da452b4552e
F test/subselect.test bf8b251a92fb091973c1c469ce499dc9648a41d5
-F test/table.test eaa25951c0f18615763cd3dc248ea4bc38739c05
+F test/table.test c1704fead1af27d67850a934d531848ce5bee4a7
F test/tclsqlite.test d2aa55926874783b2401f0146e839f773c6796e1
-F test/tester.tcl 01f881142be3bd8713abcea06747652067dafb78
+F test/tester.tcl c77fd7a4fb1f3812e469be6229ee330baaffc911
+F test/trans.test 82556605d48f56ad4679e95478d70546a763f26a
F test/update.test 72c0c93310483b86dc904a992220c5b84c7ce100
F test/vacuum.test b95d8119a0a83dc6c4ac63888f8872f06199e065
F test/where.test bbab5a308055fb6087dc23d600b4ad2b72797397
@@ -73,7 +75,7 @@ F tool/gdbmstat.c 56a9033531e5f5a48413f6ec436d5fb0341632c1
F tool/lemon.c e007bfdbc79a51a4cd7c8a5f81f517cebd121150
F tool/lempar.c 943b476d44b319eed525e46bb29e15f2c5986b37
F tool/memleak.awk a0a11dd84bf4582acc81c3c61271021ae49b3f15
-F tool/opNames.awk 2bd9071a138e4e2be13dc98fe066398a61219e1e
+F tool/opNames.awk 5ba1f48aa854ee3b7c3d2b54233665bc3e649ea2
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
F tool/renumberOps.awk 6d067177ad5f8d711b79577b462da9b3634bd0a9
F tool/report1.txt 9eae07f26a8fc53889b45fc833a66a33daa22816
@@ -81,18 +83,18 @@ F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8
F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6
F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be
F www/c_interface.tcl 11be2d5826eb7d6efd629751d3b483c1ed78ba14
-F www/changes.tcl 1be73dbd1d45471fdef05f627e8332206768f179
+F www/changes.tcl 2f8108b1c19f6b1428cd89aeb4da0f446af5a8b6
F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e
F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
F www/index.tcl 0ca6421e6e82b17ed0c1779d46463211498f9d12
-F www/lang.tcl e3905bec9f0d0fd47d9838e991cab7d6f7aff47d
+F www/lang.tcl 7fec414487ebee2cbb17c90addf5a026cd10396a
F www/mingw.tcl fc5f4ba9d336b6e8c97347cc6496d6162461ef60
F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
-P 833291c22734b2ac2342da84710320eb28f5d8cc
-R b3979afeb1b2a68085466c778c74b02f
+P 1f0197d504fa2bde15b287ac6c0102cacdb1e482
+R 0c956d20e6dabe568cbda0cb58ce5c9e
U drh
-Z ceda48d7a4b9b3c738f23f47b38664dd
+Z 9500ff4dc470566bf1bdd893cda52c35
diff --git a/manifest.uuid b/manifest.uuid
index d89cf8b8eb..87d2bca303 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-1f0197d504fa2bde15b287ac6c0102cacdb1e482
\ No newline at end of file
+35a8feed0d10e780c477f7440fbe80637fcf9906
\ No newline at end of file
diff --git a/src/build.c b/src/build.c
index a003cacb03..9c71383bd4 100644
--- a/src/build.c
+++ b/src/build.c
@@ -33,7 +33,7 @@
** COPY
** VACUUM
**
-** $Id: build.c,v 1.25 2001/01/15 22:51:09 drh Exp $
+** $Id: build.c,v 1.26 2001/04/04 11:48:57 drh Exp $
*/
#include "sqliteInt.h"
@@ -924,3 +924,63 @@ vacuum_cleanup:
sqliteFree(zName);
return;
}
+
+/*
+** Begin a transaction
+*/
+void sqliteBeginTransaction(Parse *pParse){
+ int rc;
+ DbbeMethods *pM;
+ sqlite *db;
+ if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return;
+ if( db->flags & SQLITE_InTrans ) return;
+ pM = pParse->db->pBe->x;
+ if( pM && pM->BeginTransaction ){
+ rc = (*pM->BeginTransaction)(pParse->db->pBe);
+ }else{
+ rc = SQLITE_OK;
+ }
+ if( rc==SQLITE_OK ){
+ db->flags |= SQLITE_InTrans;
+ }
+}
+
+/*
+** Commit a transaction
+*/
+void sqliteCommitTransaction(Parse *pParse){
+ int rc;
+ DbbeMethods *pM;
+ sqlite *db;
+ if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return;
+ if( (db->flags & SQLITE_InTrans)==0 ) return;
+ pM = pParse->db->pBe->x;
+ if( pM && pM->Commit ){
+ rc = (*pM->Commit)(pParse->db->pBe);
+ }else{
+ rc = SQLITE_OK;
+ }
+ if( rc==SQLITE_OK ){
+ db->flags &= ~SQLITE_InTrans;
+ }
+}
+
+/*
+** Rollback a transaction
+*/
+void sqliteRollbackTransaction(Parse *pParse){
+ int rc;
+ DbbeMethods *pM;
+ sqlite *db;
+ if( pParse==0 || (db=pParse->db)==0 || db->pBe==0 ) return;
+ if( (db->flags & SQLITE_InTrans)==0 ) return;
+ pM = pParse->db->pBe->x;
+ if( pM && pM->Rollback ){
+ rc = (*pM->Rollback)(pParse->db->pBe);
+ }else{
+ rc = SQLITE_OK;
+ }
+ if( rc==SQLITE_OK ){
+ db->flags &= ~SQLITE_InTrans;
+ }
+}
diff --git a/src/dbbe.h b/src/dbbe.h
index 31c6252f5c..41442e4a44 100644
--- a/src/dbbe.h
+++ b/src/dbbe.h
@@ -28,7 +28,7 @@
** This library was originally designed to support the following
** backends: GDBM, NDBM, SDBM, Berkeley DB.
**
-** $Id: dbbe.h,v 1.12 2001/04/03 16:53:22 drh Exp $
+** $Id: dbbe.h,v 1.13 2001/04/04 11:48:57 drh Exp $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
@@ -151,6 +151,15 @@ struct DbbeMethods {
/* Remove an entry from the table */
int (*Delete)(DbbeCursor*, int nKey, char *pKey);
+
+ /* Begin a transaction. */
+ int (*BeginTransaction)(Dbbe*);
+
+ /* Commit a transaction. */
+ int (*Commit)(Dbbe*);
+
+ /* Rollback a transaction. */
+ int (*Rollback)(Dbbe*);
};
/*
diff --git a/src/dbbegdbm.c b/src/dbbegdbm.c
index 8aba06b32f..c91fd6eb27 100644
--- a/src/dbbegdbm.c
+++ b/src/dbbegdbm.c
@@ -30,7 +30,7 @@
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
-** $Id: dbbegdbm.c,v 1.5 2001/04/03 16:53:22 drh Exp $
+** $Id: dbbegdbm.c,v 1.6 2001/04/04 11:48:57 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -68,6 +68,7 @@ typedef struct Dbbex Dbbex;
struct Dbbex {
Dbbe dbbe; /* The base class */
int write; /* True for write permission */
+ int inTrans; /* Currently in a transaction */
BeFile *pOpen; /* List of open files */
char *zDir; /* Directory hold the database */
};
@@ -178,6 +179,7 @@ static int sqliteGdbmOpenCursor(
int mode; /* Mode for opening a table */
Dbbex *pBe = (Dbbex*)pDbbe;
+ if( pBe->inTrans ) writeable = 1;
*ppCursr = 0;
pCursr = sqliteMalloc( sizeof(*pCursr) );
if( pCursr==0 ) return SQLITE_NOMEM;
@@ -224,7 +226,7 @@ static int sqliteGdbmOpenCursor(
}
pFile->writeable = writeable;
pFile->zName = zFile;
- pFile->nRef = 1;
+ pFile->nRef = 1 + pBe->inTrans;
pFile->pPrev = 0;
if( pBe->pOpen ){
pBe->pOpen->pPrev = pFile;
@@ -276,6 +278,29 @@ static void sqliteGdbmDropTable(Dbbe *pBe, const char *zTable){
sqliteFree(zFile);
}
+/*
+** Unlink a file pointer
+*/
+static void sqliteUnlinkFile(Dbbex *pBe, BeFile *pFile){
+ if( pFile->dbf!=NULL ){
+ gdbm_close(pFile->dbf);
+ }
+ if( pFile->pPrev ){
+ pFile->pPrev->pNext = pFile->pNext;
+ }else{
+ pBe->pOpen = pFile->pNext;
+ }
+ if( pFile->pNext ){
+ pFile->pNext->pPrev = pFile->pPrev;
+ }
+ if( pFile->delOnClose ){
+ unlink(pFile->zName);
+ }
+ sqliteFree(pFile->zName);
+ memset(pFile, 0, sizeof(*pFile));
+ sqliteFree(pFile);
+}
+
/*
** Close a cursor previously opened by sqliteGdbmOpenCursor().
**
@@ -295,23 +320,7 @@ static void sqliteGdbmCloseCursor(DbbeCursor *pCursr){
gdbm_sync(pFile->dbf);
}
if( pFile->nRef<=0 ){
- if( pFile->dbf!=NULL ){
- gdbm_close(pFile->dbf);
- }
- if( pFile->pPrev ){
- pFile->pPrev->pNext = pFile->pNext;
- }else{
- pBe->pOpen = pFile->pNext;
- }
- if( pFile->pNext ){
- pFile->pNext->pPrev = pFile->pPrev;
- }
- if( pFile->delOnClose ){
- unlink(pFile->zName);
- }
- sqliteFree(pFile->zName);
- memset(pFile, 0, sizeof(*pFile));
- sqliteFree(pFile);
+ sqliteUnlinkFile(pBe, pFile);
}
if( pCursr->key.dptr ) free(pCursr->key.dptr);
if( pCursr->data.dptr ) free(pCursr->data.dptr);
@@ -493,7 +502,7 @@ static int sqliteGdbmNew(DbbeCursor *pCursr){
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
while( go ){
- iKey = sqliteRandomInteger();
+ iKey = sqliteRandomInteger() & 0x7fffffff;
if( iKey==0 ) continue;
key.dptr = (char*)&iKey;
key.dsize = 4;
@@ -543,6 +552,40 @@ static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){
return rc;
}
+/*
+** Begin a transaction.
+*/
+static int sqliteGdbmBeginTrans(Dbbe *pDbbe){
+ Dbbex *pBe = (Dbbex*)pDbbe;
+ BeFile *pFile;
+ if( pBe->inTrans ) return SQLITE_OK;
+ for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
+ pFile->nRef++;
+ }
+ pBe->inTrans = 1;
+ return SQLITE_OK;
+}
+
+/*
+** End a transaction.
+*/
+static int sqliteGdbmEndTrans(Dbbe *pDbbe){
+ Dbbex *pBe = (Dbbex*)pDbbe;
+ BeFile *pFile, *pNext;
+ if( !pBe->inTrans ) return SQLITE_OK;
+ for(pFile=pBe->pOpen; pFile; pFile=pNext){
+ pNext = pFile->pNext;
+ pFile->nRef--;
+ if( pFile->nRef<=0 ){
+ sqliteUnlinkFile(pBe, pFile);
+ }
+ }
+ pBe->inTrans = 0;
+ return SQLITE_OK;
+}
+
+
+
/*
** This variable contains pointers to all of the access methods
** used to implement the GDBM backend.
@@ -566,6 +609,9 @@ static struct DbbeMethods gdbmMethods = {
/* New */ sqliteGdbmNew,
/* Put */ sqliteGdbmPut,
/* Delete */ sqliteGdbmDelete,
+ /* BeginTrans */ sqliteGdbmBeginTrans,
+ /* Commit */ sqliteGdbmEndTrans,
+ /* Rollback */ sqliteGdbmEndTrans,
};
diff --git a/src/dbbemem.c b/src/dbbemem.c
index 86ddcf5706..f90473e529 100644
--- a/src/dbbemem.c
+++ b/src/dbbemem.c
@@ -30,7 +30,7 @@
** Nothing is ever written to disk using this backend. All information
** is forgotten when the program exits.
**
-** $Id: dbbemem.c,v 1.12 2001/04/03 16:53:22 drh Exp $
+** $Id: dbbemem.c,v 1.13 2001/04/04 11:48:58 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -669,7 +669,7 @@ static int sqliteMemNew(DbbeCursor *pCursr){
int go = 1;
while( go ){
- iKey = sqliteRandomInteger();
+ iKey = sqliteRandomInteger() & 0x7fffffff;
if( iKey==0 ) continue;
key.p = (char*)&iKey;
key.n = 4;
diff --git a/src/expr.c b/src/expr.c
index 64376867e3..f55fa1893c 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -24,7 +24,7 @@
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions.
**
-** $Id: expr.c,v 1.21 2001/01/15 22:51:10 drh Exp $
+** $Id: expr.c,v 1.22 2001/04/04 11:48:58 drh Exp $
*/
#include "sqliteInt.h"
@@ -85,6 +85,16 @@ void sqliteExprResolveInSelect(Parse *pParse, Expr *pExpr){
}
}
+/*
+** Return TRUE if the given string is a row-id column name.
+*/
+static int sqliteIsRowid(const char *z){
+ if( sqliteStrICmp(z, "_ROWID_")==0 ) return 1;
+ if( sqliteStrICmp(z, "ROWID")==0 ) return 1;
+ if( sqliteStrICmp(z, "OID")==0 ) return 1;
+ return 0;
+}
+
/*
** This routine walks an expression tree and resolves references to
** table columns. Nodes of the form ID.ID or ID resolve into an
@@ -117,8 +127,9 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
switch( pExpr->op ){
/* A lone identifier */
case TK_ID: {
- int cnt = 0; /* Number of matches */
- int i; /* Loop counter */
+ int cnt = 0; /* Number of matches */
+ int i; /* Loop counter */
+ int isRowid = 0; /* True if this is the ROWID column */
char *z = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
for(i=0; inId; i++){
int j;
@@ -132,6 +143,11 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
}
}
}
+ if( cnt==0 && sqliteIsRowid(z) ){
+ pExpr->iColumn = -1;
+ pExpr->iTable = pParse->nTab;
+ cnt = 1 + (pTabList->nId>1);
+ }
sqliteFree(z);
if( cnt==0 ){
sqliteSetNString(&pParse->zErrMsg, "no such column: ", -1,
@@ -151,6 +167,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
/* A table name and column name: ID.ID */
case TK_DOT: {
int cnt = 0; /* Number of matches */
+ int cntTab = 0; /* Number of matching tables */
int i; /* Loop counter */
Expr *pLeft, *pRight; /* Left and right subbranches of the expr */
char *zLeft, *zRight; /* Text of an identifier */
@@ -161,6 +178,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
assert( pRight && pRight->op==TK_ID );
zLeft = sqliteStrNDup(pLeft->token.z, pLeft->token.n);
zRight = sqliteStrNDup(pRight->token.z, pRight->token.n);
+ pExpr->iTable = -1;
for(i=0; inId; i++){
int j;
char *zTab;
@@ -172,6 +190,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
zTab = pTab->zName;
}
if( sqliteStrICmp(zTab, zLeft)!=0 ) continue;
+ if( 0==(cntTab++) ) pExpr->iTable = i + pParse->nTab;
for(j=0; jnCol; j++){
if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
cnt++;
@@ -180,6 +199,10 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
}
}
}
+ if( cnt==0 && cntTab==1 && sqliteIsRowid(zRight) ){
+ cnt = 1;
+ pExpr->iColumn = -1;
+ }
sqliteFree(zLeft);
sqliteFree(zRight);
if( cnt==0 ){
@@ -483,8 +506,10 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
case TK_COLUMN: {
if( pParse->useAgg ){
sqliteVdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg, 0, 0);
- }else{
+ }else if( pExpr->iColumn>=0 ){
sqliteVdbeAddOp(v, OP_Field, pExpr->iTable, pExpr->iColumn, 0, 0);
+ }else{
+ sqliteVdbeAddOp(v, OP_Key, pExpr->iTable, 0, 0, 0);
}
break;
}
diff --git a/src/parse.y b/src/parse.y
index 8e413b7dd7..68c30214e6 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -26,7 +26,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.26 2001/01/04 14:20:18 drh Exp $
+** @(#) $Id: parse.y,v 1.27 2001/04/04 11:48:58 drh Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -41,16 +41,15 @@
#include "parse.h"
}
-
-// Input is zero or more commands.
-input ::= cmdlist.
-
// These are extra tokens used by the lexer but never seen by the
// parser. We put them in a rule so that the parser generator will
// add them to the parse.h output file.
//
-input ::= END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
- UMINUS COLUMN AGG_FUNCTION.
+%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
+ COLUMN AGG_FUNCTION.
+
+// Input is zero or more commands.
+input ::= cmdlist.
// A list of commands is zero or more commands
//
@@ -61,10 +60,22 @@ ecmd ::= cmd. {sqliteExec(pParse);}
ecmd ::= .
explain ::= EXPLAIN. {pParse->explain = 1;}
+// Begin and end transactions. Transaction support is sparse.
+// Some backends support only COMMIT and not ROLLBACK. There can
+// be only a single active transaction at a time.
+//
+cmd ::= BEGIN trans_opt. {sqliteBeginTransaction(pParse);}
+trans_opt ::= .
+trans_opt ::= TRANSACTION.
+trans_opt ::= TRANSACTION ids.
+cmd ::= COMMIT trans_opt. {sqliteCommitTransaction(pParse);}
+cmd ::= END trans_opt. {sqliteCommitTransaction(pParse);}
+cmd ::= ROLLBACK trans_opt. {sqliteRollbackTransaction(pParse);}
+
// The first form of a command is a CREATE TABLE statement.
//
cmd ::= create_table create_table_args.
-create_table ::= CREATE(X) TABLE id(Y). {sqliteStartTable(pParse,&X,&Y);}
+create_table ::= CREATE(X) TABLE ids(Y). {sqliteStartTable(pParse,&X,&Y);}
create_table_args ::= LP columnlist conslist_opt RP(X).
{sqliteEndTable(pParse,&X);}
columnlist ::= columnlist COMMA column.
@@ -75,21 +86,39 @@ columnlist ::= column.
// an elaborate typename. Perhaps someday we'll do something with it.
//
column ::= columnid type carglist.
-columnid ::= id(X). {sqliteAddColumn(pParse,&X);}
+columnid ::= ids(X). {sqliteAddColumn(pParse,&X);}
+
+// An IDENTIFIER can be a generic identifier, or one of several
+// keywords. Any non-standard keyword can also be an identifier.
+// We also make DESC and identifier since it comes up so often.
+//
%type id {Token}
-id(A) ::= ID(X). {A = X;}
-id(A) ::= STRING(X). {A = X;}
+id(A) ::= DESC(X). {A = X;}
+id(A) ::= ASC(X). {A = X;}
+id(A) ::= DELIMITERS(X). {A = X;}
+id(A) ::= EXPLAIN(X). {A = X;}
+id(A) ::= VACUUM(X). {A = X;}
+id(A) ::= BEGIN(X). {A = X;}
+id(A) ::= END(X). {A = X;}
+id(A) ::= ID(X). {A = X;}
+
+// And "ids" is an identifer-or-string.
+//
+%type ids {Token}
+ids(A) ::= id(X). {A = X;}
+ids(A) ::= STRING(X). {A = X;}
+
type ::= typename.
type ::= typename LP signed RP.
type ::= typename LP signed COMMA signed RP.
-typename ::= id.
-typename ::= typename id.
+typename ::= ids.
+typename ::= typename ids.
signed ::= INTEGER.
signed ::= PLUS INTEGER.
signed ::= MINUS INTEGER.
carglist ::= carglist carg.
carglist ::= .
-carg ::= CONSTRAINT id ccons.
+carg ::= CONSTRAINT ids ccons.
carg ::= ccons.
carg ::= DEFAULT STRING(X). {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT ID(X). {sqliteAddDefaultValue(pParse,&X,0);}
@@ -116,16 +145,16 @@ conslist_opt ::= COMMA conslist.
conslist ::= conslist COMMA tcons.
conslist ::= conslist tcons.
conslist ::= tcons.
-tcons ::= CONSTRAINT id.
+tcons ::= CONSTRAINT ids.
tcons ::= PRIMARY KEY LP idxlist(X) RP. {sqliteCreateIndex(pParse,0,0,X,0,0);}
tcons ::= UNIQUE LP idlist RP.
tcons ::= CHECK expr.
-idlist ::= idlist COMMA id.
-idlist ::= id.
+idlist ::= idlist COMMA ids.
+idlist ::= ids.
// The next command format is dropping tables.
//
-cmd ::= DROP TABLE id(X). {sqliteDropTable(pParse,&X);}
+cmd ::= DROP TABLE ids(X). {sqliteDropTable(pParse,&X);}
// The select statement
//
@@ -175,7 +204,7 @@ sclp(A) ::= selcollist(X) COMMA. {A = X;}
sclp(A) ::= . {A = 0;}
selcollist(A) ::= STAR. {A = 0;}
selcollist(A) ::= sclp(P) expr(X). {A = sqliteExprListAppend(P,X,0);}
-selcollist(A) ::= sclp(P) expr(X) as id(Y). {A = sqliteExprListAppend(P,X,&Y);}
+selcollist(A) ::= sclp(P) expr(X) as ids(Y). {A = sqliteExprListAppend(P,X,&Y);}
as ::= .
as ::= AS.
@@ -190,10 +219,11 @@ as ::= AS.
from(A) ::= FROM seltablist(X). {A = X;}
stl_prefix(A) ::= seltablist(X) COMMA. {A = X;}
stl_prefix(A) ::= . {A = 0;}
-seltablist(A) ::= stl_prefix(X) id(Y). {A = sqliteIdListAppend(X,&Y);}
-seltablist(A) ::= stl_prefix(X) id(Y) as id(Z).
- {A = sqliteIdListAppend(X,&Y);
- sqliteIdListAddAlias(A,&Z);}
+seltablist(A) ::= stl_prefix(X) ids(Y). {A = sqliteIdListAppend(X,&Y);}
+seltablist(A) ::= stl_prefix(X) ids(Y) as ids(Z). {
+ A = sqliteIdListAppend(X,&Y);
+ sqliteIdListAddAlias(A,&Z);
+}
%type orderby_opt {ExprList*}
%destructor orderby_opt {sqliteExprListDelete($$);}
@@ -231,7 +261,7 @@ having_opt(A) ::= . {A = 0;}
having_opt(A) ::= HAVING expr(X). {A = X;}
-cmd ::= DELETE FROM id(X) where_opt(Y).
+cmd ::= DELETE FROM ids(X) where_opt(Y).
{sqliteDeleteFrom(pParse, &X, Y);}
%type where_opt {Expr*}
@@ -243,16 +273,16 @@ where_opt(A) ::= WHERE expr(X). {A = X;}
%type setlist {ExprList*}
%destructor setlist {sqliteExprListDelete($$);}
-cmd ::= UPDATE id(X) SET setlist(Y) where_opt(Z).
+cmd ::= UPDATE ids(X) SET setlist(Y) where_opt(Z).
{sqliteUpdate(pParse,&X,Y,Z);}
-setlist(A) ::= setlist(Z) COMMA id(X) EQ expr(Y).
+setlist(A) ::= setlist(Z) COMMA ids(X) EQ expr(Y).
{A = sqliteExprListAppend(Z,Y,&X);}
-setlist(A) ::= id(X) EQ expr(Y). {A = sqliteExprListAppend(0,Y,&X);}
+setlist(A) ::= ids(X) EQ expr(Y). {A = sqliteExprListAppend(0,Y,&X);}
-cmd ::= INSERT INTO id(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
+cmd ::= INSERT INTO ids(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
{sqliteInsert(pParse, &X, Y, 0, F);}
-cmd ::= INSERT INTO id(X) inscollist_opt(F) select(S).
+cmd ::= INSERT INTO ids(X) inscollist_opt(F) select(S).
{sqliteInsert(pParse, &X, 0, S, F);}
@@ -283,10 +313,10 @@ item(A) ::= NULL. {A = sqliteExpr(TK_NULL, 0, 0, 0);}
%type inscollist {IdList*}
%destructor inscollist {sqliteIdListDelete($$);}
-inscollist_opt(A) ::= . {A = 0;}
-inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
-inscollist(A) ::= inscollist(X) COMMA id(Y). {A = sqliteIdListAppend(X,&Y);}
-inscollist(A) ::= id(Y). {A = sqliteIdListAppend(0,&Y);}
+inscollist_opt(A) ::= . {A = 0;}
+inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
+inscollist(A) ::= inscollist(X) COMMA ids(Y). {A = sqliteIdListAppend(X,&Y);}
+inscollist(A) ::= ids(Y). {A = sqliteIdListAppend(0,&Y);}
%left OR.
%left AND.
@@ -302,9 +332,9 @@ inscollist(A) ::= id(Y). {A = sqliteIdListAppend(0,&Y);}
%destructor expr {sqliteExprDelete($$);}
expr(A) ::= LP(B) expr(X) RP(E). {A = X; sqliteExprSpan(A,&B,&E);}
-expr(A) ::= ID(X). {A = sqliteExpr(TK_ID, 0, 0, &X);}
expr(A) ::= NULL(X). {A = sqliteExpr(TK_NULL, 0, 0, &X);}
-expr(A) ::= id(X) DOT id(Y). {
+expr(A) ::= id(X). {A = sqliteExpr(TK_ID, 0, 0, &X);}
+expr(A) ::= ids(X) DOT ids(Y). {
Expr *temp1 = sqliteExpr(TK_ID, 0, 0, &X);
Expr *temp2 = sqliteExpr(TK_ID, 0, 0, &Y);
A = sqliteExpr(TK_DOT, temp1, temp2, 0);
@@ -422,7 +452,7 @@ 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).
+cmd ::= CREATE(S) uniqueflag INDEX ids(X) ON ids(Y) LP idxlist(Z) RP(E).
{sqliteCreateIndex(pParse, &X, &Y, Z, &S, &E);}
uniqueflag ::= UNIQUE.
uniqueflag ::= .
@@ -435,14 +465,14 @@ idxlist(A) ::= idxlist(X) COMMA idxitem(Y).
{A = sqliteIdListAppend(X,&Y);}
idxlist(A) ::= idxitem(Y).
{A = sqliteIdListAppend(0,&Y);}
-idxitem(A) ::= id(X). {A = X;}
+idxitem(A) ::= ids(X). {A = X;}
-cmd ::= DROP INDEX id(X). {sqliteDropIndex(pParse, &X);}
+cmd ::= DROP INDEX ids(X). {sqliteDropIndex(pParse, &X);}
-cmd ::= COPY id(X) FROM id(Y) USING DELIMITERS STRING(Z).
+cmd ::= COPY ids(X) FROM ids(Y) USING DELIMITERS STRING(Z).
{sqliteCopy(pParse,&X,&Y,&Z);}
-cmd ::= COPY id(X) FROM id(Y).
+cmd ::= COPY ids(X) FROM ids(Y).
{sqliteCopy(pParse,&X,&Y,0);}
cmd ::= VACUUM. {sqliteVacuum(pParse,0);}
-cmd ::= VACUUM id(X). {sqliteVacuum(pParse,&X);}
+cmd ::= VACUUM ids(X). {sqliteVacuum(pParse,&X);}
diff --git a/src/select.c b/src/select.c
index 2588514c77..c3abbe4e53 100644
--- a/src/select.c
+++ b/src/select.c
@@ -24,7 +24,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements.
**
-** $Id: select.c,v 1.29 2001/02/19 23:23:38 drh Exp $
+** $Id: select.c,v 1.30 2001/04/04 11:48:58 drh Exp $
*/
#include "sqliteInt.h"
@@ -620,7 +620,9 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
**
** SRT_Union Store results as a key in a temporary table iParm
**
-** SRT_Except Remove results form the temporary talbe iParm.
+** SRT_Except Remove results form the temporary table iParm.
+**
+** SRT_Table Store results in temporary table iParm
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 371eafabdd..37f4a6b0b7 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -23,7 +23,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.37 2001/03/20 22:05:00 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.38 2001/04/04 11:48:58 drh Exp $
*/
#include "sqlite.h"
#include "dbbe.h"
@@ -144,6 +144,7 @@ struct sqlite {
#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
#define SQLITE_Initialized 0x00000002 /* True after initialization */
#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */
+#define SQLITE_InTrans 0x00000008 /* True if in a transaction */
/*
** Current file format version
@@ -428,3 +429,6 @@ int sqliteRandomByte(void);
int sqliteRandomInteger(void);
void sqliteRandomName(char*,char*);
char *sqliteDbbeNameToFile(const char*,const char*,const char*);
+void sqliteBeginTransaction(Parse*);
+void sqliteCommitTransaction(Parse*);
+void sqliteRollbackTransaction(Parse*);
diff --git a/src/tokenize.c b/src/tokenize.c
index 3942fc402f..164eadf0b0 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -27,7 +27,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
-** $Id: tokenize.c,v 1.17 2001/02/11 16:56:24 drh Exp $
+** $Id: tokenize.c,v 1.18 2001/04/04 11:48:58 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -53,9 +53,11 @@ static Keyword aKeywordTable[] = {
{ "AND", 0, TK_AND, 0 },
{ "AS", 0, TK_AS, 0 },
{ "ASC", 0, TK_ASC, 0 },
+ { "BEGIN", 0, TK_BEGIN, 0 },
{ "BETWEEN", 0, TK_BETWEEN, 0 },
{ "BY", 0, TK_BY, 0 },
{ "CHECK", 0, TK_CHECK, 0 },
+ { "COMMIT", 0, TK_COMMIT, 0 },
{ "CONSTRAINT", 0, TK_CONSTRAINT, 0 },
{ "COPY", 0, TK_COPY, 0 },
{ "CREATE", 0, TK_CREATE, 0 },
@@ -65,6 +67,7 @@ static Keyword aKeywordTable[] = {
{ "DESC", 0, TK_DESC, 0 },
{ "DISTINCT", 0, TK_DISTINCT, 0 },
{ "DROP", 0, TK_DROP, 0 },
+ { "END", 0, TK_END, 0 },
{ "EXCEPT", 0, TK_EXCEPT, 0 },
{ "EXPLAIN", 0, TK_EXPLAIN, 0 },
{ "FROM", 0, TK_FROM, 0 },
@@ -87,9 +90,11 @@ static Keyword aKeywordTable[] = {
{ "OR", 0, TK_OR, 0 },
{ "ORDER", 0, TK_ORDER, 0 },
{ "PRIMARY", 0, TK_PRIMARY, 0 },
+ { "ROLLBACK", 0, TK_ROLLBACK, 0 },
{ "SELECT", 0, TK_SELECT, 0 },
{ "SET", 0, TK_SET, 0 },
{ "TABLE", 0, TK_TABLE, 0 },
+ { "TRANSACTION", 0, TK_TRANSACTION, 0 },
{ "UNION", 0, TK_UNION, 0 },
{ "UNIQUE", 0, TK_UNIQUE, 0 },
{ "UPDATE", 0, TK_UPDATE, 0 },
diff --git a/src/vdbe.c b/src/vdbe.c
index 1e852f3975..a251610fe0 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -41,7 +41,7 @@
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.53 2001/03/20 22:05:00 drh Exp $
+** $Id: vdbe.c,v 1.54 2001/04/04 11:48:58 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -811,29 +811,29 @@ void sqliteVdbeDelete(Vdbe *p){
** this array, then copy and paste it into this file, if you want.
*/
static char *zOpName[] = { 0,
- "OpenIdx", "OpenTbl", "Close", "Fetch",
- "Fcnt", "New", "Put", "Distinct",
- "Found", "NotFound", "Delete", "Field",
- "KeyAsData", "Key", "FullKey", "Rewind",
- "Next", "Destroy", "Reorganize", "ResetIdx",
- "NextIdx", "PutIdx", "DeleteIdx", "MemLoad",
- "MemStore", "ListOpen", "ListWrite", "ListRewind",
- "ListRead", "ListClose", "SortOpen", "SortPut",
- "SortMakeRec", "SortMakeKey", "Sort", "SortNext",
- "SortKey", "SortCallback", "SortClose", "FileOpen",
- "FileRead", "FileField", "FileClose", "AggReset",
- "AggFocus", "AggIncr", "AggNext", "AggSet",
- "AggGet", "SetInsert", "SetFound", "SetNotFound",
- "SetClear", "MakeRecord", "MakeKey", "Goto",
- "If", "Halt", "ColumnCount", "ColumnName",
- "Callback", "Integer", "String", "Null",
- "Pop", "Dup", "Pull", "Add",
- "AddImm", "Subtract", "Multiply", "Divide",
- "Min", "Max", "Like", "Glob",
- "Eq", "Ne", "Lt", "Le",
- "Gt", "Ge", "IsNull", "NotNull",
- "Negative", "And", "Or", "Not",
- "Concat", "Noop", "Strlen", "Substr",
+ "OpenIdx", "OpenTbl", "Close", "Fetch",
+ "Fcnt", "New", "Put", "Distinct",
+ "Found", "NotFound", "Delete", "Field",
+ "KeyAsData", "Key", "FullKey", "Rewind",
+ "Next", "Destroy", "Reorganize", "ResetIdx",
+ "NextIdx", "PutIdx", "DeleteIdx", "MemLoad",
+ "MemStore", "ListOpen", "ListWrite", "ListRewind",
+ "ListRead", "ListClose", "SortOpen", "SortPut",
+ "SortMakeRec", "SortMakeKey", "Sort", "SortNext",
+ "SortKey", "SortCallback", "SortClose", "FileOpen",
+ "FileRead", "FileField", "FileClose", "AggReset",
+ "AggFocus", "AggIncr", "AggNext", "AggSet",
+ "AggGet", "SetInsert", "SetFound", "SetNotFound",
+ "SetClear", "MakeRecord", "MakeKey", "Goto",
+ "If", "Halt", "ColumnCount", "ColumnName",
+ "Callback", "Integer", "String", "Null",
+ "Pop", "Dup", "Pull", "Add",
+ "AddImm", "Subtract", "Multiply", "Divide",
+ "Min", "Max", "Like", "Glob",
+ "Eq", "Ne", "Lt", "Le",
+ "Gt", "Ge", "IsNull", "NotNull",
+ "Negative", "And", "Or", "Not",
+ "Concat", "Noop", "Strlen", "Substr",
};
/*
diff --git a/src/vdbe.h b/src/vdbe.h
index fef342f9f8..6f5c343a46 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -27,7 +27,7 @@
** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database.
**
-** $Id: vdbe.h,v 1.16 2001/02/19 23:23:39 drh Exp $
+** $Id: vdbe.h,v 1.17 2001/04/04 11:48:58 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -178,7 +178,7 @@ typedef struct VdbeOp VdbeOp;
#define OP_Strlen 91
#define OP_Substr 92
-#define OP_MAX 92
+#define OP_MAX 93
/*
** Prototypes for the VDBE interface. See comments on the implementation
diff --git a/src/where.c b/src/where.c
index aeeb3e904c..fc0221d51d 100644
--- a/src/where.c
+++ b/src/where.c
@@ -25,7 +25,7 @@
** the WHERE clause of SQL statements. Also found here are subroutines
** to generate VDBE code to evaluate expressions.
**
-** $Id: where.c,v 1.12 2001/02/19 23:23:39 drh Exp $
+** $Id: where.c,v 1.13 2001/04/04 11:48:58 drh Exp $
*/
#include "sqliteInt.h"
@@ -160,6 +160,7 @@ WhereInfo *sqliteWhereBegin(
int haveKey; /* True if KEY is on the stack */
int base; /* First available index for OP_Open opcodes */
Index *aIdx[32]; /* Index to use on each nested loop. */
+ int aDirect[32]; /* If TRUE, then index this table using ROWID */
ExprInfo aExpr[50]; /* The WHERE clause is divided into these expressions */
/* Allocate space for aOrder[]. */
@@ -209,18 +210,43 @@ WhereInfo *sqliteWhereBegin(
/* Figure out what index to use (if any) for each nested loop.
** Make aIdx[i] point to the index to use for the i-th nested loop
** where i==0 is the outer loop and i==pTabList->nId-1 is the inner
- ** loop.
+ ** loop. If the expression uses only the ROWID field, then set
+ ** aDirect[i] to 1.
**
** Actually, if there are more than 32 tables in the join, only the
** first 32 tables are candidates for indices.
*/
loopMask = 0;
for(i=0; inId && ia[idx].pTab;
Index *pIdx;
Index *pBestIdx = 0;
+ /* Check to see if there is an expression that uses only the
+ ** ROWID field of this table. If so, set aDirect[i] to 1.
+ ** If not, set aDirect[i] to 0.
+ */
+ aDirect[i] = 0;
+ for(j=0; jpLeft->iColumn<0
+ && (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
+ aDirect[i] = 1;
+ break;
+ }
+ if( aExpr[j].idxRight==idx && aExpr[j].p->pRight->iColumn<0
+ && (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){
+ aDirect[i] = 1;
+ break;
+ }
+ }
+ if( aDirect[i] ){
+ loopMask |= 1<pIndex; pIdx; pIdx=pIdx->pNext){
- int j;
int columnMask = 0;
if( pIdx->nColumn>32 ) continue;
@@ -285,18 +310,58 @@ WhereInfo *sqliteWhereBegin(
for(i=0; inId; i++){
int j, k;
int idx = aOrder[i];
- Index *pIdx = ipLeft->iColumn<0
+ ){
+ sqliteExprCode(pParse, aExpr[k].p->pRight);
+ aExpr[k].p = 0;
+ break;
+ }
+ if( aExpr[k].idxRight==idx
+ && (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
+ && aExpr[k].p->pRight->iColumn<0
+ ){
+ sqliteExprCode(pParse, aExpr[k].p->pLeft);
+ aExpr[k].p = 0;
+ break;
+ }
+ }
+ sqliteVdbeAddOp(v, OP_AddImm, 0, 0, 0, 0);
+ if( i==pTabList->nId-1 && pushKey ){
+ haveKey = 1;
+ }else{
+ sqliteVdbeAddOp(v, OP_Fetch, base+idx, 0, 0, 0);
+ haveKey = 0;
+ }
+ }else if( pIdx==0 ){
+ /* Case 2: There was no usable index. We must do a complete
** scan of the table.
*/
+ cont = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_Next, base+idx, brk, 0, cont);
haveKey = 0;
}else{
- /* Case 2: We do have a usable index in pIdx.
+ /* Case 3: We do have a usable index in pIdx.
*/
+ cont = sqliteVdbeMakeLabel(v);
for(j=0; jnColumn; j++){
for(k=0; k4)
+ }
+ execsql {SELECT a FROM t1 ORDER BY a}
+} {5 6 7 8}
# Do an IN with a constant RHS but where the RHS has many, many
# elements. We need to test that collisions in the hash table
diff --git a/test/index.test b/test/index.test
index 79fe7d9b87..eec1b5746b 100644
--- a/test/index.test
+++ b/test/index.test
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the CREATE INDEX statement.
#
-# $Id: index.test,v 1.8 2000/10/19 14:10:09 drh Exp $
+# $Id: index.test,v 1.9 2001/04/04 11:48:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -334,5 +334,21 @@ do_test index-10.8 {
}
} {0}
+# Automatically create an index when we specify a primary key.
+#
+do_test index-11.1 {
+ execsql {
+ CREATE TABLE t3(
+ a text,
+ b int,
+ c float,
+ PRIMARY KEY(b)
+ );
+ }
+ for {set i 1} {$i<=50} {incr i} {
+ execsql "INSERT INTO t3 VALUES('x${i}x',$i,0.$i)"
+ }
+ execsql {SELECT c, fcnt() FROM t3 WHERE b==10}
+} {0.10 2}
finish_test
diff --git a/test/insert.test b/test/insert.test
index 25988903d6..5eb4732994 100644
--- a/test/insert.test
+++ b/test/insert.test
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the INSERT statement.
#
-# $Id: insert.test,v 1.4 2000/06/07 14:42:27 drh Exp $
+# $Id: insert.test,v 1.5 2001/04/04 11:48:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -100,24 +100,47 @@ do_test insert-1.6c {
# A table to use for testing default values
#
-execsql {
- CREATE TABLE test2(
- f1 int default 111,
- f2 real default -4.32,
- f3 text default hi,
- f4 text default 'abc-123',
- f5 varchar(10)
- )
-}
-
do_test insert-2.1 {
+ execsql {
+ CREATE TABLE test2(
+ f1 int default -111,
+ f2 real default +4.32,
+ f3 int default +222,
+ f4 int default 7.89
+ )
+ }
execsql {SELECT * from test2}
} {}
do_test insert-2.2 {
+ execsql {INSERT INTO test2(f1,f3) VALUES(+10,-10)}
+ execsql {SELECT * FROM test2}
+} {10 4.32 -10 7.89}
+do_test insert-2.3 {
+ execsql {INSERT INTO test2(f2,f4) VALUES(1.23,-3.45)}
+ execsql {SELECT * FROM test2 WHERE f1==-111}
+} {-111 1.23 222 -3.45}
+do_test insert-2.4 {
+ execsql {INSERT INTO test2(f1,f2,f4) VALUES(77,+1.23,3.45)}
+ execsql {SELECT * FROM test2 WHERE f1==77}
+} {77 1.23 222 3.45}
+do_test insert-2.10 {
+ execsql {
+ DROP TABLE test2;
+ CREATE TABLE test2(
+ f1 int default 111,
+ f2 real default -4.32,
+ f3 text default hi,
+ f4 text default 'abc-123',
+ f5 varchar(10)
+ )
+ }
+ execsql {SELECT * from test2}
+} {}
+do_test insert-2.11 {
execsql {INSERT INTO test2(f2,f4) VALUES(-2.22,'hi!')}
execsql {SELECT * FROM test2}
} {111 -2.22 hi hi! {}}
-do_test insert-2.3 {
+do_test insert-2.12 {
execsql {INSERT INTO test2(f1,f5) VALUES(1,'xyzzy')}
execsql {SELECT * FROM test2 ORDER BY f1}
} {1 -4.32 hi abc-123 xyzzy 111 -2.22 hi hi! {}}
diff --git a/test/main.test b/test/main.test
index f236d26b09..354498b340 100644
--- a/test/main.test
+++ b/test/main.test
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is exercising the code in main.c.
#
-# $Id: main.test,v 1.5 2001/03/20 12:55:14 drh Exp $
+# $Id: main.test,v 1.6 2001/04/04 11:48:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -76,6 +76,12 @@ do_test main-1.13 {
db complete {DROP TABLE xyz; -- hi
}
} {1}
+do_test main-1.14 {
+ db complete {SELECT a-b FROM t1; }
+} {1}
+do_test main-1.15 {
+ db complete {SELECT a-b FROM t1 }
+} {0}
# Try to open a database with a corrupt master file.
#
diff --git a/test/rowid.test b/test/rowid.test
new file mode 100644
index 0000000000..a12b31a11e
--- /dev/null
+++ b/test/rowid.test
@@ -0,0 +1,267 @@
+# 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 implements regression tests for SQLite library. The
+# focus of this file is testing the magic ROWID column that is
+# found on all tables.
+#
+# $Id: rowid.test,v 1.1 2001/04/04 11:48:58 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Basic ROWID functionality tests.
+#
+do_test rowid-1.1 {
+ execsql {
+ CREATE TABLE t1(x int, y int);
+ INSERT INTO t1 VALUES(1,2);
+ INSERT INTO t1 VALUES(3,4);
+ SELECT x FROM t1 ORDER BY y;
+ }
+} {1 3}
+do_test rowid-1.2 {
+ set r [execsql {SELECT rowid FROM t1 ORDER BY x}]
+ global x2rowid rowid2x
+ set x2rowid(1) [lindex $r 0]
+ set x2rowid(3) [lindex $r 1]
+ set rowid2x($x2rowid(1)) 1
+ set rowid2x($x2rowid(3)) 3
+ llength $r
+} {2}
+do_test rowid-1.3 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE rowid==$x2rowid(1)"
+ execsql $sql
+} {1}
+do_test rowid-1.4 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE rowid==$x2rowid(3)"
+ execsql $sql
+} {3}
+do_test rowid-1.5 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE oid==$x2rowid(1)"
+ execsql $sql
+} {1}
+do_test rowid-1.6 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE OID==$x2rowid(3)"
+ execsql $sql
+} {3}
+do_test rowid-1.7 {
+ global x2rowid
+ set sql "SELECT x FROM t1 WHERE _rowid_==$x2rowid(1)"
+ execsql $sql
+} {1}
+do_test rowid-1.8 {
+ global x2rowid
+ set v [execsql {SELECT x, oid FROM t1 order by x}]
+ set v2 [list 1 $x2rowid(1) 3 $x2rowid(3)]
+ expr {$v==$v2}
+} {1}
+do_test rowid-1.9 {
+ global x2rowid
+ set v [execsql {SELECT x, RowID FROM t1 order by x}]
+ set v2 [list 1 $x2rowid(1) 3 $x2rowid(3)]
+ expr {$v==$v2}
+} {1}
+do_test rowid-1.9 {
+ global x2rowid
+ set v [execsql {SELECT x, _rowid_ FROM t1 order by x}]
+ set v2 [list 1 $x2rowid(1) 3 $x2rowid(3)]
+ expr {$v==$v2}
+} {1}
+
+# We cannot update or insert the ROWID column
+#
+do_test rowid-2.1 {
+ set v [catch {execsql {INSERT INTO t1(rowid,x,y) VALUES(1234,5,6)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named rowid}}
+do_test rowid-2.2 {
+ set v [catch {execsql {UPDATE t1 SET rowid=12345 WHERE x==1}}]
+ lappend v $msg
+} {1 {table t1 has no column named rowid}}
+do_test rowid-2.3 {
+ set v [catch {execsql {INSERT INTO t1(oid,x,y) VALUES(1234,5,6)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named oid}}
+do_test rowid-2.4 {
+ set v [catch {execsql {UPDATE t1 SET oid=12345 WHERE x==1}}]
+ lappend v $msg
+} {1 {table t1 has no column named oid}}
+do_test rowid-2.5 {
+ set v [catch {execsql {INSERT INTO t1(_rowid_,x,y) VALUES(1234,5,6)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named _rowid_}}
+do_test rowid-2.6 {
+ set v [catch {execsql {UPDATE t1 SET _rowid_=12345 WHERE x==1}}]
+ lappend v $msg
+} {1 {table t1 has no column named _rowid_}}
+
+# But we can use ROWID in the WHERE clause of an UPDATE that does not
+# change the ROWID.
+#
+do_test rowid-2.7 {
+ global x2rowid
+ set sql "UPDATE t1 SET x=2 WHERE OID==$x2rowid(3)"
+ execsql $sql
+ execsql {SELECT x FROM t1 ORDER BY x}
+} {1 2}
+do_test rowid-2.8 {
+ global x2rowid
+ set sql "UPDATE t1 SET x=3 WHERE _rowid_==$x2rowid(3)"
+ execsql $sql
+ execsql {SELECT x FROM t1 ORDER BY x}
+} {1 3}
+
+# We cannot index by ROWID
+#
+do_test rowid-2.9 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(rowid)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named rowid}}
+do_test rowid-2.10 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(_rowid_)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named _rowid_}}
+do_test rowid-2.11 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(oid)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named oid}}
+do_test rowid-2.12 {
+ set v [catch {execsql {CREATE INDEX idxt1 ON t1(x, rowid)}} msg]
+ lappend v $msg
+} {1 {table t1 has no column named rowid}}
+
+# Columns defined in the CREATE statement override the buildin ROWID
+# column names.
+#
+do_test rowid-3.1 {
+ execsql {
+ CREATE TABLE t2(rowid int, x int, y int);
+ INSERT INTO t2 VALUES(1,2,3);
+ INSERT INTO t2 VALUES(4,5,6);
+ INSERT INTO t2 VALUES(7,8,9);
+ SELECT * FROM t2 ORDER BY x;
+ }
+} {1 2 3 4 5 6 7 8 9}
+do_test rowid-3.2 {
+ execsql {SELECT * FROM t2 ORDER BY rowid}
+} {1 2 3 4 5 6 7 8 9}
+do_test rowid-3.3 {
+ execsql {SELECT rowid, x, y FROM t2 ORDER BY rowid}
+} {1 2 3 4 5 6 7 8 9}
+do_test rowid-3.4 {
+ set r1 [execsql {SELECT _rowid_, rowid FROM t2 ORDER BY rowid}]
+ foreach {a b c d e f} $r1 {}
+ set r2 [execsql {SELECT _rowid_, rowid FROM t2 ORDER BY x DESC}]
+ foreach {u v w x y z} $r2 {}
+ expr {$u==$e && $w==$c && $y==$a}
+} {1}
+do_probtest rowid-3.5 {
+ set r1 [execsql {SELECT _rowid_, rowid FROM t2 ORDER BY rowid}]
+ foreach {a b c d e f} $r1 {}
+ expr {$a!=$b && $c!=$d && $e!=$f}
+} {1}
+
+# Let's try some more complex examples, including some joins.
+#
+do_test rowid-4.1 {
+ execsql {
+ DELETE FROM t1;
+ DELETE FROM t2;
+ }
+ for {set i 1} {$i<=50} {incr i} {
+ execsql "INSERT INTO t1(x,y) VALUES($i,[expr {$i*$i}])"
+ }
+ execsql {INSERT INTO t2 SELECT _rowid_, x*y, y*y FROM t1}
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.2 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.2.1 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1.oid==t2.rowid}
+} {256}
+do_test rowid-4.2.2 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.2.3 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t2.rowid==t1.rowid}
+} {256}
+do_test rowid-4.2.4 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t2.rowid==t1.oid AND t1.x==4}
+} {256}
+do_test rowid-4.2.5 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.2.6 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t2.rowid==t1.rowid}
+} {256}
+do_test rowid-4.2.7 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t2.rowid==t1.oid AND t1.x==4}
+} {256}
+do_test rowid-4.3 {
+ execsql {CREATE INDEX idxt1 ON t1(x)}
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.3.1 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.3.2 {
+ execsql {SELECT t2.y FROM t1, t2 WHERE t2.rowid==t1.oid AND 4==t1.x}
+} {256}
+do_test rowid-4.4 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1.rowid==t2.rowid}
+} {256}
+do_test rowid-4.4.1 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t1.x==4 AND t1._rowid_==t2.rowid}
+} {256}
+do_test rowid-4.4.2 {
+ execsql {SELECT t2.y FROM t2, t1 WHERE t2.rowid==t1.oid AND 4==t1.x}
+} {256}
+do_test rowid-4.5 {
+ execsql {CREATE INDEX idxt2 ON t2(y)}
+ execsql {
+ SELECT t1.x, fcnt() FROM t2, t1
+ WHERE t2.y==256 AND t1.rowid==t2.rowid
+ }
+} {4 3}
+do_test rowid-4.5.1 {
+ execsql {
+ SELECT t1.x, fcnt() FROM t2, t1
+ WHERE t1.OID==t2.rowid AND t2.y==81
+ }
+} {3 3}
+do_test rowid-4.6 {
+ execsql {
+ SELECT t1.x FROM t1, t2
+ WHERE t2.y==256 AND t1.rowid==t2.rowid
+ }
+} {4}
+
+do_test rowid-5.1 {
+ execsql {DELETE FROM t1 WHERE _rowid_ IN (SELECT oid FROM t1 WHERE x>8)}
+ execsql {SELECT max(x) FROM t1}
+} {8}
diff --git a/test/select1.test b/test/select1.test
index 5d2e9b2def..f7d2adbf30 100644
--- a/test/select1.test
+++ b/test/select1.test
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the SELECT statement.
#
-# $Id: select1.test,v 1.7 2000/07/29 13:07:00 drh Exp $
+# $Id: select1.test,v 1.8 2001/04/04 11:48:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -262,6 +262,10 @@ do_test select1-6.4 {
set v [catch {execsql2 {SELECT f1+F2 as xyzzy FROM test1 ORDER BY f2}} msg]
lappend v $msg
} {0 {xyzzy 33 xyzzy 77}}
+do_test select1-6.4a {
+ set v [catch {execsql2 {SELECT f1+F2 FROM test1 ORDER BY f2}} msg]
+ lappend v $msg
+} {0 {f1+F2 33 f1+F2 77}}
do_test select1-6.5 {
set v [catch {execsql2 {SELECT test1.f1+F2 FROM test1 ORDER BY f2}} msg]
lappend v $msg
@@ -296,5 +300,19 @@ do_test select1-6.9 {
ORDER BY A.f1, B.f1}} msg]
lappend v $msg
} {0 {A.f1 11 B.f1 11 A.f1 11 B.f1 33 A.f1 33 B.f1 11 A.f1 33 B.f1 33}}
+do_test select1-6.10 {
+ set v [catch {execsql2 {
+ SELECT f1 FROM test1 UNION SELECT f2 FROM test1
+ ORDER BY f2;
+ }} msg]
+ lappend v $msg
+} {0 {f2 11 f2 22 f2 33 f2 44}}
+do_test select1-6.11 {
+ set v [catch {execsql2 {
+ SELECT f1 FROM test1 UNION SELECT f2+100 FROM test1
+ ORDER BY f2+100;
+ }} msg]
+ lappend v $msg
+} {0 {f2+100 11 f2+100 33 f2+100 122 f2+100 144}}
finish_test
diff --git a/test/sort.test b/test/sort.test
index ae1abd9107..bb7d1e8b93 100644
--- a/test/sort.test
+++ b/test/sort.test
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the CREATE TABLE statement.
#
-# $Id: sort.test,v 1.1 2000/06/07 00:12:25 drh Exp $
+# $Id: sort.test,v 1.2 2001/04/04 11:48:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -58,6 +58,12 @@ do_test sort-1.0 {
do_test sort-1.1 {
execsql {SELECT n FROM t1 ORDER BY n}
} {1 2 3 4 5 6 7 8}
+do_test sort-1.1.1 {
+ execsql {SELECT n FROM t1 ORDER BY n ASC}
+} {1 2 3 4 5 6 7 8}
+do_test sort-1.1.1 {
+ execsql {SELECT ALL n FROM t1 ORDER BY n ASC}
+} {1 2 3 4 5 6 7 8}
do_test sort-1.2 {
execsql {SELECT n FROM t1 ORDER BY n DESC}
} {8 7 6 5 4 3 2 1}
@@ -82,9 +88,21 @@ do_test sort-1.7 {
do_test sort-1.8 {
execsql {SELECT n FROM t1 ORDER BY log, flt}
} {1 2 3 5 4 6 7 8}
+do_test sort-1.8.1 {
+ execsql {SELECT n FROM t1 ORDER BY log asc, flt}
+} {1 2 3 5 4 6 7 8}
+do_test sort-1.8.2 {
+ execsql {SELECT n FROM t1 ORDER BY log, flt ASC}
+} {1 2 3 5 4 6 7 8}
+do_test sort-1.8.3 {
+ execsql {SELECT n FROM t1 ORDER BY log ASC, flt asc}
+} {1 2 3 5 4 6 7 8}
do_test sort-1.9 {
execsql {SELECT n FROM t1 ORDER BY log, flt DESC}
} {1 3 2 7 6 4 5 8}
+do_test sort-1.9.1 {
+ execsql {SELECT n FROM t1 ORDER BY log ASC, flt DESC}
+} {1 3 2 7 6 4 5 8}
do_test sort-1.10 {
execsql {SELECT n FROM t1 ORDER BY log DESC, flt}
} {8 5 4 6 7 2 3 1}
@@ -92,4 +110,27 @@ do_test sort-1.11 {
execsql {SELECT n FROM t1 ORDER BY log DESC, flt DESC}
} {8 7 6 4 5 3 2 1}
+# These tests are designed to reach some hard-to-reach places
+# inside the string comparison routines.
+#
+do_test sort-2.1 {
+ execsql {
+ UPDATE t1 SET v='x' || -flt;
+ UPDATE t1 SET v='x-2b' where v=='x-0.123';
+ SELECT v FROM t1 ORDER BY v;
+ }
+} {x-2b x-2.15 x-3.141592653 x-123 x-4221 x0.0013442 x1.6 x11}
+do_test sort-2.2 {
+ execsql {
+ UPDATE t1 SET v='x-2_' where v=='x0.0013442';
+ SELECT v FROM t1 ORDER BY v;
+ }
+} {x-2_ x-2b x-2.15 x-3.141592653 x-123 x-4221 x1.6 x11}
+do_test sort-2.3 {
+ execsql {
+ UPDATE t1 SET v='x ' || (-1.3+0.01*n);
+ SELECT v FROM t1 ORDER BY v;
+ }
+} {{x -1.29} {x -1.28} {x -1.27} {x -1.26} {x -1.25} {x -1.24} {x -1.23} {x -1.22}}
+
finish_test
diff --git a/test/table.test b/test/table.test
index 17d7fc1b13..b3f21c6702 100644
--- a/test/table.test
+++ b/test/table.test
@@ -23,7 +23,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the CREATE TABLE statement.
#
-# $Id: table.test,v 1.7 2000/10/19 14:10:09 drh Exp $
+# $Id: table.test,v 1.8 2001/04/04 11:48:58 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -242,7 +242,7 @@ do_test table-4.1b {
execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name}
} $r
-# Drop the even number tables
+# Drop the even numbered tables
#
set r {}
for {set i 1} {$i<=100} {incr i 2} {
@@ -304,4 +304,30 @@ do_test table-6.1 {
set list [glob -nocomplain testdb/spaces*.tbl]
} {testdb/spaces+in+this+name+.tbl}
+# Try using keywords as table names or column names.
+#
+do_test table-7.1 {
+ set v [catch {execsql {
+ CREATE TABLE weird(
+ desc text,
+ asc text,
+ explain int,
+ vacuum boolean,
+ delimiters varchar(10)
+ )
+ }} msg]
+ lappend v $msg
+} {0 {}}
+do_test table-7.2 {
+ execsql {
+ INSERT INTO weird VALUES('a','b',9,0,'xyz');
+ SELECT * FROM weird;
+ }
+} {a b 9 0 xyz}
+do_test table-7.3 {
+ execsql2 {
+ SELECT * FROM weird;
+ }
+} {desc a asc b explain 9 vacuum 0 delimiters xyz}
+
finish_test
diff --git a/test/tester.tcl b/test/tester.tcl
index 55af445248..9d83d56e55 100644
--- a/test/tester.tcl
+++ b/test/tester.tcl
@@ -23,7 +23,7 @@
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
-# $Id: tester.tcl,v 1.12 2001/03/20 12:55:14 drh Exp $
+# $Id: tester.tcl,v 1.13 2001/04/04 11:48:58 drh Exp $
# Create a test database
#
@@ -186,9 +186,9 @@ proc finish_test {} {
# A procedure to execute SQL
#
-proc execsql {sql} {
+proc execsql {sql {db db}} {
# puts "SQL = $sql"
- return [db eval $sql]
+ return [$db eval $sql]
}
# Another procedure to execute SQL. This one includes the field
diff --git a/test/trans.test b/test/trans.test
new file mode 100644
index 0000000000..3b018ea80a
--- /dev/null
+++ b/test/trans.test
@@ -0,0 +1,191 @@
+# 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 implements regression tests for SQLite library. The
+# focus of this script is database locks.
+#
+# $Id: trans.test,v 1.1 2001/04/04 11:48:58 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if {$dbprefix=="gdbm:" && $::tcl_platform(platform)!="windows"} {
+
+# Create several tables to work with.
+#
+do_test trans-1.0 {
+ execsql {
+ CREATE TABLE one(a int PRIMARY KEY, b text);
+ INSERT INTO one VALUES(1,'one');
+ INSERT INTO one VALUES(2,'two');
+ INSERT INTO one VALUES(3,'three');
+ SELECT b FROM one ORDER BY a;
+ }
+} {one two three}
+do_test trans-1.1 {
+ execsql {
+ CREATE TABLE two(a int PRIMARY KEY, b text);
+ INSERT INTO two VALUES(1,'I');
+ INSERT INTO two VALUES(5,'V');
+ INSERT INTO two VALUES(10,'X');
+ SELECT b FROM two ORDER BY a;
+ }
+} {I V X}
+do_test trans-1.9 {
+ sqlite altdb ${dbprefix}testdb
+ execsql {SELECT b FROM one ORDER BY a} altdb
+} {one two three}
+do_test trans-1.10 {
+ execsql {SELECT b FROM two ORDER BY a} altdb
+} {I V X}
+
+# Basic transactions
+#
+do_test trans-2.1 {
+ set v [catch {execsql {BEGIN}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.2 {
+ set v [catch {execsql {END}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.3 {
+ set v [catch {execsql {BEGIN TRANSACTION}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.4 {
+ set v [catch {execsql {COMMIT TRANSACTION}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.5 {
+ set v [catch {execsql {BEGIN TRANSACTION 'foo'}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.6 {
+ set v [catch {execsql {ROLLBACK TRANSACTION 'foo'}} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-2.10 {
+ execsql {
+ BEGIN;
+ SELECT a FROM one ORDER BY a;
+ SELECT a FROM two ORDER BY a;
+ END;
+ }
+} {1 2 3 1 5 10}
+
+# Check the locking behavior
+#
+do_test trans-3.1 {
+ execsql {
+ BEGIN;
+ SELECT a FROM one ORDER BY a;
+ }
+} {1 2 3}
+do_test trans-3.2 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 5 10}}
+do_test trans-3.3 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {1 {table one is locked}}
+do_test trans-3.4 {
+ set v [catch {execsql {
+ INSERT INTO one VALUES(4,'four');
+ }} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-3.2 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 5 10}}
+do_test trans-3.3 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {1 {table one is locked}}
+do_test trans-3.5 {
+ set v [catch {execsql {
+ INSERT INTO two VALUES(4,'IV');
+ }} msg]
+ lappend v $msg
+} {0 {}}
+do_test trans-3.6 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {1 {table two is locked}}
+do_test trans-3.7 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {1 {table one is locked}}
+do_test trans-3.10 {
+ execsql {END TRANSACTION}
+} {}
+do_test trans-3.11 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test trans-3.12 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } altdb} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+do_test trans-3.13 {
+ set v [catch {execsql {
+ SELECT a FROM two ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 4 5 10}}
+do_test trans-3.14 {
+ set v [catch {execsql {
+ SELECT a FROM one ORDER BY a;
+ } db} msg]
+ lappend v $msg
+} {0 {1 2 3 4}}
+
+do_test trans-99.1 {
+ altdb close
+ execsql {
+ DROP TABLE one;
+ DROP TABLE two;
+ }
+} {}
+
+finish_test
+
+} ;# end if(gdbm and not windows)
diff --git a/tool/opNames.awk b/tool/opNames.awk
index 1150c5aaee..b1f7e3a86b 100644
--- a/tool/opNames.awk
+++ b/tool/opNames.awk
@@ -10,7 +10,7 @@ BEGIN {
/^#define OP_/ {
name = "\"" substr($2,4) "\","
if( n<3 ){
- printf " %-16s", name
+ printf " %-19s", name
n++
} else {
printf " %s\n", name
diff --git a/www/changes.tcl b/www/changes.tcl
index ac8dd670e0..9ca8882b8c 100644
--- a/www/changes.tcl
+++ b/www/changes.tcl
@@ -17,7 +17,14 @@ proc chng {date desc} {
puts "
"
}
-chng {2001 Apr 3 (1.0.28)} {
+chng {2001 Apr 4 (1.0.28)} {
+Added limited support for transactions. At this point, transactions
+ will do table locking on the GDBM backend. There is no support (yet)
+ for rollback or atomic commit.
+Added special column names ROWID, OID, and _ROWID_ that refer to the
+ unique random integer key associated with every row of every table.
+Additional tests added to the regression suite to cover the new ROWID
+ feature and the TCL interface bugs mentioned below.
Changes to the "lemon" parser generator to help it work better when
compiled using MSVC.
Bug fixes in the TCL interface identified by Oleg Oleinick.
diff --git a/www/lang.tcl b/www/lang.tcl
index 7ab318c1d8..56ae8d7661 100644
--- a/www/lang.tcl
+++ b/www/lang.tcl
@@ -1,7 +1,7 @@
#
# Run this Tcl script to generate the sqlite.html file.
#
-set rcsid {$Id: lang.tcl,v 1.6 2001/02/20 13:06:31 drh Exp $}
+set rcsid {$Id: lang.tcl,v 1.7 2001/04/04 11:48:58 drh Exp $}
puts {
@@ -48,6 +48,7 @@ foreach {section} [lsort -index 0 -dictionary {
{COPY copy}
{EXPLAIN explain}
{expression expr}
+ {{BEGIN TRANSACTION} transaction}
}] {
puts "[lindex $section 0]"
}
@@ -99,6 +100,57 @@ proc Example {text} {
puts "$text
"
}
+Section {BEGIN TRANSACTION} createindex
+
+Syntax {sql-statement} {
+BEGIN [TRANSACTION []]
+}
+Syntax {sql-statement} {
+END [TRANSACTION []]
+}
+Syntax {sql-statement} {
+COMMIT [TRANSACTION []]
+}
+Syntax {sql-statement} {
+ROLLBACK [TRANSACTION []]
+}
+
+puts {
+Support for transactions in SQLite is thin. Transactions
+may not be nested. The GDBM backend does not support an atomic
+commit or rollback, but it does support locking. (Note, however,
+that the compilation instructions on this website for using GDBM under
+Windows will disable locking.) The MEM backend has no transaction
+support and silently ignores all requests to begin or end
+transactions. A new backend is currently under
+development for SQLite 2.0 that will support both atomic commits and rollback,
+but that driver is not yet available.
+
+Under GDBM, starting a transaction just locks all
+tables that are either read or written during the course of the
+transaction. The locks are removed when the transaction is ended.
+Thus, transactions can be used to make changes to multiple tables
+with the assurance that other threads or processes will not touch
+the same tables at the same time. For example:
+
+
+SELECT data1, data2, ... FROM table1 WHERE ...;
+... Make a decision to update the table ...
+BEGIN TRANSACTION;
+SELECT data1, data2, ... FROM table1 WHERE ...;
+... Make sure no other process changed the table in between
+the first SELECT and the BEGIN TRANSACTION. ...
+UPDATE table1 SET data1=... WHERE ...;
+END TRANSACTION;
+
+
+In the code above, the table1 table is locked by
+the second SELECT because of the transaction. Thus we know that
+no other process has modified table1 when the UPDATE
+occurs. The END TRANSACTION releases the lock.
+}
+
+
Section COPY copy
Syntax {sql-statement} {
@@ -342,6 +394,19 @@ file globbing syntax for its wildcards. Also, GLOB is case
sensitive, unlike LIKE. Both GLOB and LIKE may be preceded by
the NOT keyword to invert the sense of the test.
+A column name can be any of the names defined in the CREATE TABLE
+statement or one of the following special identifiers: "ROWID",
+"OID", or "_ROWID_".
+These special identifiers all describe the
+unique random integer key (the "row key") associated every every
+row of every table.
+The special identifiers only refer to the row key if the CREATE TABLE
+statement does not define a real column with the same name. Row keys
+act like read-only columns. A row key can be used anywhere a regular
+column can be used, except that you cannot change the value
+of a row key in an UPDATE or INSERT statement.
+"SELECT * ..." does not return the row key.
+
SELECT statements can appear in expressions as either the
right-hand operand of the IN operator or as a scalar quantity.
In both cases, the SELECT should have only a single column in its