]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Added transaction support (CVS 196)
authordrh <drh@noemail.net>
Wed, 4 Apr 2001 11:48:57 +0000 (11:48 +0000)
committerdrh <drh@noemail.net>
Wed, 4 Apr 2001 11:48:57 +0000 (11:48 +0000)
FossilOrigin-Name: 35a8feed0d10e780c477f7440fbe80637fcf9906

28 files changed:
manifest
manifest.uuid
src/build.c
src/dbbe.h
src/dbbegdbm.c
src/dbbemem.c
src/expr.c
src/parse.y
src/select.c
src/sqliteInt.h
src/tokenize.c
src/vdbe.c
src/vdbe.h
src/where.c
test/expr.test
test/in.test
test/index.test
test/insert.test
test/main.test
test/rowid.test [new file with mode: 0644]
test/select1.test
test/sort.test
test/table.test
test/tester.tcl
test/trans.test [new file with mode: 0644]
tool/opNames.awk
www/changes.tcl
www/lang.tcl

index 5d1dd7e794a25153e9842105bc7e675c6ce2f62e..4bafe2bff4653b8d69f384f5736b8a05731bc7df 100644 (file)
--- 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
index d89cf8b8ebb4469e209a606a3e2a5a2eba34a91d..87d2bca3033927ea1dd170572c25a517c88863c1 100644 (file)
@@ -1 +1 @@
-1f0197d504fa2bde15b287ac6c0102cacdb1e482
\ No newline at end of file
+35a8feed0d10e780c477f7440fbe80637fcf9906
\ No newline at end of file
index a003cacb03154427953f574ed2c00a41825c306c..9c71383bd4616e15bccc904b2173c91866361dd2 100644 (file)
@@ -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;
+  }
+}
index 31c6252f5c506ab5ea4c04f0e6679c0dd127d73a..41442e4a4499bc0b0420ac2fbece37c3a7ec79fc 100644 (file)
@@ -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*);
 };
 
 /*
index 8aba06b32f267bec4573662c44eb6924eda8ead3..c91fd6eb276f61a21f189407216040b9a8e90251 100644 (file)
@@ -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 <gdbm.h>
@@ -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,
 };
 
 
index 86ddcf5706a89e3815f9ad8a23dddabb5511c7ed..f90473e529354b9449503560a97ea5f8db8b38bd 100644 (file)
@@ -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 <sys/stat.h>
@@ -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;
index 64376867e34953955a5e3d1c9cefbe763a39ab24..f55fa1893c6b9b627d516575072386ad4021d40f 100644 (file)
@@ -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; i<pTabList->nId; 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; i<pTabList->nId; 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; j<pTab->nCol; 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;
     }
index 8e413b7dd72df2b714354a7caa88c2119095385c..68c30214e60277cdcc9f8f4bac385042d64a1c45 100644 (file)
@@ -26,7 +26,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.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}
 #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);}
index 2588514c7772180a100155144e7fd74f83865a5d..c3abbe4e53977acb69d9e57c70f80797637e3d78 100644 (file)
@@ -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
index 371eafabdd622954a305d310dd986c00fd5b8c8a..37f4a6b0b7ad4aed3d3352ade71009672a2ae716 100644 (file)
@@ -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*);
index 3942fc402fd0b95200f255d562cd0ee43964cd6e..164eadf0b0267cf4c8869eefa06de423d971227d 100644 (file)
@@ -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 <ctype.h>
@@ -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 },
index 1e852f3975a841d96e0fff7d2f19bf6eec7a607d..a251610fe0264311a34ee92de1fc4f62764f7a3f 100644 (file)
@@ -41,7 +41,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.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 <unistd.h>
@@ -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",
 };
 
 /*
index fef342f9f8ea39c8816f96ddfdf029320a0263fc..6f5c343a46046103047fa7898eb4cb9a0530791a 100644 (file)
@@ -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
index aeeb3e904c6daadd4a49779ad7e8d25faf51cb6c..fc0221d51d52d482b720037ea613a013c1a55f80 100644 (file)
@@ -25,7 +25,7 @@
 ** the WHERE clause of SQL statements.  Also found here are subroutines
 ** to generate VDBE code to evaluate expressions.
 **
-** $Id: where.c,v 1.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; i<pTabList->nId && i<ARRAYSIZE(aIdx); i++){
+    int j;
     int idx = aOrder[i];
     Table *pTab = pTabList->a[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; j<nExpr; j++){
+      if( aExpr[j].idxLeft==idx && aExpr[j].p->pLeft->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<<idx;
+      aIdx[i] = 0;
+      continue;
+    }
+
     /* Do a search for usable indices.  Leave pBestIdx pointing to
     ** the most specific usable index.
     **
@@ -230,7 +256,6 @@ WhereInfo *sqliteWhereBegin(
     ** index.
     */
     for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-      int j;
       int columnMask = 0;
 
       if( pIdx->nColumn>32 ) continue;
@@ -285,18 +310,58 @@ WhereInfo *sqliteWhereBegin(
   for(i=0; i<pTabList->nId; i++){
     int j, k;
     int idx = aOrder[i];
-    Index *pIdx = i<ARRAYSIZE(aIdx) ? aIdx[i] : 0;
+    int goDirect;
+    Index *pIdx;
+
+    if( i<ARRAYSIZE(aIdx) ){
+      pIdx = aIdx[i];
+      goDirect = aDirect[i];
+    }else{
+      pIdx = 0;
+      goDirect = 0;
+    }
 
-    cont = sqliteVdbeMakeLabel(v);
-    if( pIdx==0 ){
-      /* Case 1:  There was no usable index.  We must do a complete
+    if( goDirect ){
+      /* Case 1:  We can directly reference a single row using the ROWID field.
+      */
+      cont = brk;
+      for(k=0; k<nExpr; k++){
+        if( aExpr[k].p==0 ) continue;
+        if( aExpr[k].idxLeft==idx 
+           && (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight 
+           && aExpr[k].p->pLeft->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; j<pIdx->nColumn; j++){
         for(k=0; k<nExpr; k++){
           if( aExpr[k].p==0 ) continue;
index f7101b22d6167cb3c22ab3829d91eb3fc4c96879..f75162e2c4931325744f064e08b6a357fa97d360 100644 (file)
@@ -23,7 +23,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing expressions.
 #
-# $Id: expr.test,v 1.9 2000/09/14 01:21:11 drh Exp $
+# $Id: expr.test,v 1.10 2001/04/04 11:48:58 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -76,6 +76,10 @@ test_expr expr-1.34 {i1=1, i2=2} {i1=2 OR i2=2} {1}
 test_expr expr-1.35 {i1=1, i2=2} {i1-i2=-1} {1}
 test_expr expr-1.36 {i1=1, i2=0} {not i1} {0}
 test_expr expr-1.37 {i1=1, i2=NULL} {not i2} {1}
+test_expr expr-1.38 {i1=1} {-i1} {-1}
+test_expr expr-1.39 {i1=1} {+i1} {1}
+test_expr expr-1.40 {i1=1, i2=2} {+(i2+i1)} {3}
+test_expr expr-1.41 {i1=1, i2=2} {-(i2+i1)} {-3}
 
 test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57
 test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11
index bd77c1fef28d58e5beca5bf9a2b0f2ad905a71a8..a4bdd8b9d9c18b964f11b1394e0a25455d0236b2 100644 (file)
@@ -23,7 +23,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the IN and BETWEEN operator.
 #
-# $Id: in.test,v 1.3 2000/06/21 13:59:13 drh Exp $
+# $Id: in.test,v 1.4 2001/04/04 11:48:58 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -145,6 +145,12 @@ do_test in-4.2 {
   }
   execsql {SELECT a FROM t1 ORDER BY a}
 } {1 2 3 4 5 6 7 8}
+do_test in-4.3 {
+  execsql {
+    DELETE FROM t1 WHERE b NOT IN (SELECT b FROM t1 WHERE a>4)
+  }
+  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
index 79fe7d9b879f524fb80d652121e13f636640ef9e..eec1b5746b755b8a26e14b9b9cfcfda8089de0ef 100644 (file)
@@ -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
index 25988903d60a68f733f5f22bd0feee8f6d160829..5eb4732994cb75ea2ae5851b7c005a746eb6e8e8 100644 (file)
@@ -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! {}}
index f236d26b09a5a4b600a4f0be314601c12523bf57..354498b340ffdc0ee4b9807d90bc753bd56309ad 100644 (file)
@@ -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 (file)
index 0000000..a12b31a
--- /dev/null
@@ -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}
index 5d2e9b2defb3da4c6e6059870144b22de214a6b7..f7d2adbf305f93268d3bf2c29af801e7c424c5f4 100644 (file)
@@ -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
index ae1abd91073ebe8e3499c139a9dd6b15611d514b..bb7d1e8b9358b760dfbbcd0ea192d72f8d28a8ff 100644 (file)
@@ -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
index 17d7fc1b13bb5127a3462e60a8303bcfe1026a9d..b3f21c67020b0df008c7689eb79bb5e3ce5bc20f 100644 (file)
@@ -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
index 55af4452482d9d634960123738802825b7bd9034..9d83d56e556f83e9094e2f2d3ef268f09d39ce06 100644 (file)
@@ -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 (file)
index 0000000..3b018ea
--- /dev/null
@@ -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)
index 1150c5aaee06796a272034c027d32e44a3b5f116..b1f7e3a86b8e8ed3ac650ae03dd827d14b1378d7 100644 (file)
@@ -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
index ac8dd670e0d82729f8709054f8c8726a94e04efb..9ca8882b8ce8fb9eb49e9547067013a236460c0e 100644 (file)
@@ -17,7 +17,14 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
-chng {2001 Apr 3 (1.0.28)} {
+chng {2001 Apr 4 (1.0.28)} {
+<li>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.</li>
+<li>Added special column names ROWID, OID, and _ROWID_ that refer to the
+    unique random integer key associated with every row of every table.</li>
+<li>Additional tests added to the regression suite to cover the new ROWID
+    feature and the TCL interface bugs mentioned below.</li>
 <li>Changes to the "lemon" parser generator to help it work better when
     compiled using MSVC.</li>
 <li>Bug fixes in the TCL interface identified by Oleg Oleinick.</li>
index 7ab318c1d82e861f4890c61bfcbe1337004a16a0..56ae8d76610a93793f4d45675d8bcf7887e01171 100644 (file)
@@ -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 {<html>
 <head>
@@ -48,6 +48,7 @@ foreach {section} [lsort -index 0 -dictionary {
   {COPY copy}
   {EXPLAIN explain}
   {expression expr}
+  {{BEGIN TRANSACTION} transaction}
 }] {
   puts "<li><a href=\"#[lindex $section 1]\">[lindex $section 0]</a></li>"
 }
@@ -99,6 +100,57 @@ proc Example {text} {
   puts "<blockquote><pre>$text</pre></blockquote>"
 }
 
+Section {BEGIN TRANSACTION} createindex
+
+Syntax {sql-statement} {
+BEGIN [TRANSACTION [<name>]]
+}
+Syntax {sql-statement} {
+END [TRANSACTION [<name>]]
+}
+Syntax {sql-statement} {
+COMMIT [TRANSACTION [<name>]]
+}
+Syntax {sql-statement} {
+ROLLBACK [TRANSACTION [<name>]]
+}
+
+puts {
+<p>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.</p>
+
+<p>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:</p>
+
+<blockquote>
+<b>SELECT data1, data2, ... FROM table1 WHERE ...;</b><br>
+... Make a decision to update the table ...<br>
+<b>BEGIN TRANSACTION;<br>
+SELECT data1, data2, ... FROM table1 WHERE ...;</b><br>
+... Make sure no other process changed the table in between
+the first SELECT and the BEGIN TRANSACTION. ...<br>
+<b>UPDATE table1 SET data1=... WHERE ...;<br>
+END TRANSACTION;</b>
+</blockquote>
+
+<p>In the code above, the <b>table1</b> table is locked by
+the second SELECT because of the transaction.  Thus we know that
+no other process has modified <b>table1</b> when the UPDATE
+occurs.  The END TRANSACTION releases the lock.</p>
+}
+
+
 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.</p>
 
+<p>A column name can be any of the names defined in the CREATE TABLE
+statement or one of the following special identifiers: "<b>ROWID</b>",
+"<b>OID</b>", or "<b>_ROWID_</b>".
+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.</p>
+
 <p>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