]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
:-) (CVS 74)
authordrh <drh@noemail.net>
Wed, 7 Jun 2000 23:51:50 +0000 (23:51 +0000)
committerdrh <drh@noemail.net>
Wed, 7 Jun 2000 23:51:50 +0000 (23:51 +0000)
FossilOrigin-Name: 2ffeb8509c469f5a499d56bb109da079fcdff570

12 files changed:
manifest
manifest.uuid
src/build.c
src/delete.c
src/expr.c
src/insert.c
src/main.c
src/parse.y
src/select.c
src/sqliteInt.h
src/update.c
test/in.test

index aeb09b7ec40fd678718bdec097b76fdbf61fcc2a..f1d58fb447077a9988f4991aff5c6ac039c3f645 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,26 +1,26 @@
-C :-)\s(CVS\s73)
-D 2000-06-07T15:39:04
+C :-)\s(CVS\s74)
+D 2000-06-07T23:51:50
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
 F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
 F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
 F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
 F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
-F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20
+F src/build.c 5e3b6bab5604cd99019ea6d58f9166d879476991
 F src/dbbe.c 9b191b16ff01ec5bc0af436558501d07938ba4f0
 F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
-F src/delete.c 8c733bb82a1b84126116d03dcdccf433c0856f5d
-F src/expr.c d350fe393e1753aaa733a5d21f0830a23e547400
-F src/insert.c 93f9e36bf96b57326d8d5709ed44b568b8627aa5
-F src/main.c b4275952180b30e1ca3db266157e2d233c0a24d9
-F src/parse.y 4a2e6b14177c97f61fc3e21d4570f5a9c541c1e1
-F src/select.c 4834ab68a3308871f17fe8e22d903fcdf1b42420
+F src/delete.c c267b93f7ccb5493b677fa18201880267c699aa8
+F src/expr.c baa8a4229b3acf69d908efcd697ab63922009c9f
+F src/insert.c ac4edfff474589c00b2490f206317dc5822122e5
+F src/main.c e3297835b8e38ca726ac73f2c2bdb7cf08103197
+F src/parse.y bb2126c8313c111184b89af8675911dcb57f1dca
+F src/select.c 21d1097e32e0a8430c286309817be33b734eab9d
 F src/shell.c 3f4afc39a36e4824e8aa262623fd03568874799e
 F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
-F src/sqliteInt.h 1ac4ba75fef8249b9bd64a7736d0f584092f24a5
+F src/sqliteInt.h 816c491f9896090dde03804fd3f60346456b99df
 F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
 F src/tokenize.c 900af9479d0feaa76b0225680196aa81afec930a
-F src/update.c 18746f920f989b3d19d96c08263c92584823cd35
+F src/update.c d8d90df714bac99c68446a0c49f3d957ca6fc3c8
 F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
 F src/vdbe.c 0ce44df13c97472686b376cc93cca5f40079878d
 F src/vdbe.h 8f79f57c66ce1030f6371ff067b326d627a52c6d
@@ -30,7 +30,7 @@ F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
 F test/dbbe.test 3978ab21ff2a0531a85618c538d27047d560fc5d
 F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944
 F test/expr.test 7d017f1aa64c981b161408a015424cd90592bc16
-F test/in.test 17cd46a9ca0e5d4a804483e6fb496458494858e6
+F test/in.test 962a605b6a3a619214f84d1950dfc44fcf0d8b8f
 F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
 F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c
 F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
@@ -54,7 +54,7 @@ F www/c_interface.tcl 9ac800854272db5fe439e07b7435b243a5422293
 F www/changes.tcl 04e66b4257589ff78a7e1de93e9dda4725fb03d6
 F www/index.tcl 52e29a4eeda8d59e91af43c61fef177c5f2ffd53
 F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
-P 6f8de336d0c0c5b74fa2ff541ab974ba13638a58
-R e8ba4da21fa9f208a62c1c1e8095f5b6
+P a00b81b0e1878487a6edaaca1b0dff002ad506d8
+R edeb782218e986f032924498f63df3c4
 U drh
-Z 9c51fcb8cc6b0390f306d27b43fea58b
+Z e7171fdd9bf45a1d225b747155ebb197
index 4f4f06c0c4b5ded2c43784b03b501b0214215a12..adde17c19dbae953fac9d68f0d4856c5d791689e 100644 (file)
@@ -1 +1 @@
-a00b81b0e1878487a6edaaca1b0dff002ad506d8
\ No newline at end of file
+2ffeb8509c469f5a499d56bb109da079fcdff570
\ No newline at end of file
index d88e57cd4549b5b46369c1dd7616772bb6ee4127..ad6113e75b77d06522aed1a92bac200486336b35 100644 (file)
@@ -33,7 +33,7 @@
 **     COPY
 **     VACUUM
 **
-** $Id: build.c,v 1.15 2000/06/05 18:54:46 drh Exp $
+** $Id: build.c,v 1.16 2000/06/07 23:51:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -60,6 +60,7 @@ void sqliteExec(Parse *pParse){
     }
     sqliteVdbeDelete(pParse->pVdbe);
     pParse->pVdbe = 0;
+    pParse->colNamesSet = 0;
   }
 }
 
@@ -330,11 +331,9 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
       { OP_Close,       0, 0, 0},
     };
     int n, base;
-    Vdbe *v = pParse->pVdbe;
+    Vdbe *v;
 
-    if( v==0 ){
-      v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-    }
+    v = sqliteGetVdbe(pParse);
     if( v==0 ) return;
     n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1;
     base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable);
@@ -379,10 +378,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){
   }
 
   /* Generate code to remove the table and its reference in sys_master */
-  v = pParse->pVdbe;
-  if( v==0 ){
-    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  }
+  v = sqliteGetVdbe(pParse);
   if( v ){
     static VdbeOp dropTable[] = {
       { OP_Open,       0, 1,        MASTER_NAME },
@@ -576,9 +572,7 @@ void sqliteCreateIndex(
     int lbl1, lbl2;
     int i;
 
-    if( v==0 ){
-      v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-    }
+    v = sqliteGetVdbe(pParse);
     if( v==0 ) goto exit_create_index;
     sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
     sqliteVdbeAddOp(v, OP_Open, 1, 1, pIndex->zName, 0);
@@ -637,7 +631,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
   }
 
   /* Generate code to remove the index and from the master table */
-  v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  v = sqliteGetVdbe(pParse);
   if( v ){
     static VdbeOp dropIndex[] = {
       { OP_Open,       0, 1,       MASTER_NAME},
@@ -808,7 +802,7 @@ void sqliteCopy(
     pParse->nErr++;
     goto copy_cleanup;
   }
-  v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  v = sqliteGetVdbe(pParse);
   if( v ){
     addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0);
     sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
@@ -872,7 +866,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
     pParse->nErr++;
     goto vacuum_cleanup;
   }
-  v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
+  v = sqliteGetVdbe(pParse);
   if( v==0 ) goto vacuum_cleanup;
   if( zName ){
     sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0);
index 3f640430645de1c77759711b69d8ef86519dc3b5..493d3990ce6983cf7d529241a90c46aeac5dfdbd 100644 (file)
@@ -24,7 +24,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle DELETE FROM statements.
 **
-** $Id: delete.c,v 1.3 2000/06/06 13:54:15 drh Exp $
+** $Id: delete.c,v 1.4 2000/06/07 23:51:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -82,10 +82,7 @@ void sqliteDeleteFrom(
 
   /* Begin generating code.
   */
-  v = pParse->pVdbe;
-  if( v==0 ){
-    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  }
+  v = sqliteGetVdbe(pParse);
   if( v==0 ) goto delete_from_cleanup;
 
   /* Begin the database scan
index 2870a484d9bd6c998501adfca280dd36a977bbce..d5e4f747c5da7ac079543118bb563962057c72ef 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** This file contains C code routines used for processing expressions
 **
-** $Id: expr.c,v 1.10 2000/06/06 17:27:05 drh Exp $
+** $Id: expr.c,v 1.11 2000/06/07 23:51:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -206,10 +206,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
     }
 
     case TK_IN: {
-      Vdbe *v = pParse->pVdbe;
-      if( v==0 ){
-        v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-      }
+      Vdbe *v = sqliteGetVdbe(pParse);
       if( v==0 ) return 1;
       if( sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){
         return 1;
@@ -766,7 +763,7 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
 ** Do a deep comparison of two expression trees.  Return TRUE (non-zero)
 ** if they are identical and return FALSE if they differ in any way.
 */
-static int exprDeepCompare(Expr *pA, Expr *pB){
+int sqliteExprCompare(Expr *pA, Expr *pB){
   int i;
   if( pA==0 ){
     return pB==0;
@@ -774,13 +771,13 @@ static int exprDeepCompare(Expr *pA, Expr *pB){
     return 0;
   }
   if( pA->op!=pB->op ) return 0;
-  if( !exprDeepCompare(pA->pLeft, pB->pLeft) ) return 0;
-  if( !exprDeepCompare(pA->pRight, pB->pRight) ) return 0;
+  if( !sqliteExprCompare(pA->pLeft, pB->pLeft) ) return 0;
+  if( !sqliteExprCompare(pA->pRight, pB->pRight) ) return 0;
   if( pA->pList ){
     if( pB->pList==0 ) return 0;
     if( pA->pList->nExpr!=pB->pList->nExpr ) return 0;
     for(i=0; i<pA->pList->nExpr; i++){
-      if( !exprDeepCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
+      if( !sqliteExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
         return 0;
       }
     }
@@ -868,7 +865,7 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
       aAgg = pParse->aAgg;
       for(i=0; i<pParse->nAgg; i++){
         if( !aAgg[i].isAgg ) continue;
-        if( exprDeepCompare(aAgg[i].pExpr, pExpr) ){
+        if( sqliteExprCompare(aAgg[i].pExpr, pExpr) ){
           break;
         }
       }
index 204c6facd3cad64139427d16b16ef56b3694ffec..2d838d48f5a5b57d36d8b96b1fae021d4817de8f 100644 (file)
@@ -24,7 +24,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements.
 **
-** $Id: insert.c,v 1.8 2000/06/07 15:11:27 drh Exp $
+** $Id: insert.c,v 1.9 2000/06/07 23:51:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -67,10 +67,7 @@ void sqliteInsert(
     pParse->nErr++;
     goto insert_cleanup;
   }
-  v = pParse->pVdbe;
-  if( v==0 ){
-    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  }
+  v = sqliteGetVdbe(pParse);
   if( v==0 ) goto insert_cleanup;
   if( pSelect ){
     int rc;
index 8a388a01f267564e91e36cd918e61588ce54b801..3c81beca33052932a72d5ee3c4f0d3e82295a473 100644 (file)
@@ -26,7 +26,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.11 2000/06/07 15:11:27 drh Exp $
+** $Id: main.c,v 1.12 2000/06/07 23:51:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -129,6 +129,10 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
   ** the program.  The delete the virtual machine.
   */
   vdbe = sqliteVdbeCreate(db->pBe);
+  if( vdbe==0 ){
+    sqliteSetString(pzErrMsg, "out of memory",0); 
+    return 1;
+  }
   sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
   rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg);
   sqliteVdbeDelete(vdbe);
index 049a631734d3ca67e10f07713bbddc0f14f66971..864bce4cff549f259e904248db1cc0ca59ac3e5c 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.15 2000/06/07 14:42:27 drh Exp $
+** @(#) $Id: parse.y,v 1.16 2000/06/07 23:51:50 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -211,11 +211,11 @@ orderby_opt(A) ::= .                          {A = 0;}
 orderby_opt(A) ::= ORDER BY sortlist(X).      {A = X;}
 sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
   A = sqliteExprListAppend(X,Y,0);
-  A->a[A->nExpr-1].idx = Z;       /* 0 for ascending order, 1 for decending */
+  A->a[A->nExpr-1].sortOrder = Z;   /* 0 for ascending order, 1 for decending */
 }
 sortlist(A) ::= sortitem(Y) sortorder(Z). {
   A = sqliteExprListAppend(0,Y,0);
-  A->a[0].idx = Z;
+  A->a[0].sortOrder = Z;
 }
 sortitem(A) ::= expr(X).   {A = X;}
 
index 1992612e4b0df4fb8f4a52b2177cffe99156b04d..d524b1a17e08dad3d935212543b0662159df1a2b 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.15 2000/06/07 14:42:27 drh Exp $
+** $Id: select.c,v 1.16 2000/06/07 23:51:50 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -135,11 +135,11 @@ static int selectInnerLoop(
   */
   if( pOrderBy ){
     char *zSortOrder;
-    sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
+    sqliteVdbeAddOp(v, OP_SortMakeRec, nField, 0, 0, 0);
     zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
     if( zSortOrder==0 ) return 1;
     for(i=0; i<pOrderBy->nExpr; i++){
-      zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
+      zSortOrder[i] = pOrderBy->a[i].sortOrder ? '-' : '+';
       sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
     }
     zSortOrder[pOrderBy->nExpr] = 0;
@@ -179,7 +179,7 @@ static int selectInnerLoop(
   ** item into the set table with bogus data.
   */
   if( eDest==SRT_Set ){
-    assert( pEList->nExpr==1 );
+    assert( nField==1 );
     sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
     sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
   }else 
@@ -190,7 +190,7 @@ static int selectInnerLoop(
   ** of the scan loop.
   */
   if( eDest==SRT_Mem ){
-    assert( pEList->nExpr==1 );
+    assert( nField==1 );
     sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
     sqliteVdbeAddOp(v, OP_Goto, 0, iBreak, 0, 0);
   }else
@@ -203,22 +203,39 @@ static int selectInnerLoop(
   return 0;
 }
 
+/*
+** If the inner loop was generated using a non-null pOrderBy argument,
+** then the results were placed in a sorter.  After the loop is terminated
+** we need to run the sorter and output the results.  The following
+** routine generates the code needed to do that.
+*/
+static void generateSortTail(Vdbe *v, int nField){
+  int end = sqliteVdbeMakeLabel(v);
+  int addr;
+  sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
+  addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
+  sqliteVdbeAddOp(v, OP_SortCallback, nField, 0, 0, 0);
+  sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
+  sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end);
+}
+
 /*
 ** Generate code that will tell the VDBE how many columns there
 ** are in the result and the name for each column.  This information
 ** is used to provide "argc" and "azCol[]" values in the callback.
 */
-static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){
+static 
+void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
+  Vdbe *v = pParse->pVdbe;
   int i;
+  if( pParse->colNamesSet ) return;
+  pParse->colNamesSet = 1;
   sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
   for(i=0; i<pEList->nExpr; i++){
     Expr *p;
     if( pEList->a[i].zName ){
       char *zName = pEList->a[i].zName;
-      int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
-      if( zName[0]=='\'' || zName[0]=='"' ){
-        sqliteVdbeDequoteP3(v, addr);
-      }
+      sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
       continue;
     }
     p = pEList->a[i].pExpr;
@@ -247,69 +264,260 @@ static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){
 }
 
 /*
-** This routine is called to process a query that is really the union
-** or intersection of two or more separate queries.
+** Name of the connection operator, used for error messages.
 */
-static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
-  int rc;
-  Select *pPrior;
-  Vdbe *v;
-  int i;
+static const char *selectOpName(int id){
+  char *z;
+  switch( id ){
+    case TK_ALL:       z = "UNION ALL";   break;
+    case TK_INTERSECT: z = "INTERSECT";   break;
+    case TK_EXCEPT:    z = "EXCEPT";      break;
+    default:           z = "UNION";       break;
+  }
+  return z;
+}
 
-  /* Make sure we have a valid query engine.  If not, create a new one.
+/*
+** For the given SELECT statement, do two things.
+**
+**    (1)  Fill in the pTab fields of the IdList that defines the set
+**         of tables we are scanning.
+**
+**    (2)  If the columns to be extracted variable (pEList) is NULL
+**         (meaning that a "*" was used in the SQL statement) then
+**         create a fake pEList containing the names of all columns
+**         of all tables.
+**
+** Return 0 on success.  If there are problems, leave an error message
+** in pParse and return non-zero.
+*/
+static int fillInColumnList(Parse *pParse, Select *p){
+  int i, j;
+  IdList *pTabList = p->pSrc;
+  ExprList *pEList = p->pEList;
+
+  /* Look up every table in the table list.
   */
-  v = pParse->pVdbe;
+  for(i=0; i<pTabList->nId; i++){
+    if( pTabList->a[i].pTab ){
+      /* This routine has run before!  No need to continue */
+      return 0;
+    }
+    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
+    if( pTabList->a[i].pTab==0 ){
+      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
+         pTabList->a[i].zName, 0);
+      pParse->nErr++;
+      return 1;
+    }
+  }
+
+  /* If the list of columns to retrieve is "*" then replace it with
+  ** a list of all columns from all tables.
+  */
+  if( pEList==0 ){
+    for(i=0; i<pTabList->nId; i++){
+      Table *pTab = pTabList->a[i].pTab;
+      for(j=0; j<pTab->nCol; j++){
+        Expr *pExpr = sqliteExpr(TK_DOT, 0, 0, 0);
+        pExpr->pLeft = sqliteExpr(TK_ID, 0, 0, 0);
+        pExpr->pLeft->token.z = pTab->zName;
+        pExpr->pLeft->token.n = strlen(pTab->zName);
+        pExpr->pRight = sqliteExpr(TK_ID, 0, 0, 0);
+        pExpr->pRight->token.z = pTab->aCol[j].zName;
+        pExpr->pRight->token.n = strlen(pTab->aCol[j].zName);
+        pEList = sqliteExprListAppend(pEList, pExpr, 0);
+      }
+    }
+    p->pEList = pEList;
+  }
+  return 0;
+}
+
+/*
+** This routine associates entries in an ORDER BY expression list with
+** columns in a result.  For each ORDER BY expression, the opcode of
+** the top-level node is changed to TK_FIELD and the iField value of
+** the top-level node is filled in with column number and the iTable
+** value of the top-level node is filled with iTable parameter.
+**
+** If there are prior SELECT clauses, they are processed first.  A match
+** in an earlier SELECT takes precedence over a later SELECT.
+**
+** Any entry that does not match is flagged as an error.  The number
+** of errors is returned.
+*/
+static int matchOrderbyToColumn(
+  Parse *pParse,          /* A place to leave error messages */
+  Select *pSelect,        /* Match to result columns of this SELECT */
+  ExprList *pOrderBy,     /* The ORDER BY values to match against columns */
+  int iTable,             /* Insert this this value in iTable */
+  int mustComplete        /* If TRUE all ORDER BYs must match */
+){
+  int nErr = 0;
+  int i, j;
+  ExprList *pEList;
+
+  assert( pSelect && pOrderBy );
+  if( mustComplete ){
+    for(i=0; i<pOrderBy->nExpr; i++){ pOrderBy->a[i].done = 0; }
+  }
+  if( fillInColumnList(pParse, pSelect) ){
+    return 1;
+  }
+  if( pSelect->pPrior ){
+    matchOrderbyToColumn(pParse, pSelect->pPrior, pOrderBy, iTable, 0);
+  }
+  pEList = pSelect->pEList;
+  for(i=0; i<pOrderBy->nExpr; i++){
+    Expr *pE = pOrderBy->a[i].pExpr;
+    if( pOrderBy->a[i].done ) continue;
+    for(j=0; j<pEList->nExpr; j++){
+      int match = 0;
+      if( pEList->a[i].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){
+        char *zName = pEList->a[i].zName;
+        char *zLabel = 0;
+        sqliteSetString(&zLabel, pE->token.z, pE->token.n, 0);
+        sqliteDequote(zLabel);
+        if( sqliteStrICmp(zName, zLabel)==0 ){ 
+          match = 1; 
+        }
+      }
+      if( match==0 && sqliteExprCompare(pE, pEList->a[i].pExpr) ){
+        match = 1;
+      }
+      if( match ){
+        pE->op = TK_FIELD;
+        pE->iField = j;
+        pE->iTable = iTable;
+        pOrderBy->a[i].done = 1;
+        break;
+      }
+    }
+    if( mustComplete ){
+      char zBuf[30];
+      sprintf(zBuf,"%d",i+1);
+      sqliteSetString(&pParse->zErrMsg, "ORDER BY term number ", zBuf, 
+        " does not match any result column", 0);
+      pParse->nErr++;
+      nErr++;
+      break;
+    }
+  }
+  return nErr;  
+}
+
+/*
+** Get a VDBE for the given parser context.  Create a new one if necessary.
+** If an error occurs, return NULL and leave a message in pParse.
+*/
+Vdbe *sqliteGetVdbe(Parse *pParse){
+  Vdbe *v = pParse->pVdbe;
   if( v==0 ){
     v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
   }
   if( v==0 ){
     sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
     pParse->nErr++;
-    return 1;
   }
+  return v;
+}
+    
 
+/*
+** This routine is called to process a query that is really the union
+** or intersection of two or more separate queries.
+*/
+static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
+  int rc;
+  Select *pPrior;
+  Vdbe *v;
+
+  /* Make sure there is no ORDER BY clause on prior SELECTs.  Only the 
+  ** last SELECT in the series may have an ORDER BY.
+  */
   assert( p->pPrior!=0 );
   pPrior = p->pPrior;
+  if( pPrior->pOrderBy ){
+    sqliteSetString(&pParse->zErrMsg,"ORDER BY clause should come after ",
+      selectOpName(p->op), " not before", 0);
+    pParse->nErr++;
+    return 1;
+  }
+
+  /* Make sure we have a valid query engine.  If not, create a new one.
+  */
+  v = sqliteGetVdbe(pParse);
+  if( v==0 ) return 1;
+
+  /* Process the UNION or INTERSECTION
+  */
   switch( p->op ){
-    case TK_ALL: {
-      rc = sqliteSelect(pParse, pPrior, eDest, iParm);
-      if( rc ) return rc;
-      p->pPrior = 0;
-      rc = sqliteSelect(pParse, p, eDest, iParm);
-      p->pPrior = pPrior;
-      break;
-    }
+    case TK_ALL:
     case TK_EXCEPT:
     case TK_UNION: {
-      int unionTab;
-      int op;
-
-      if( eDest==SRT_Union ){
+      int unionTab;    /* Cursor number of the temporary table holding result */
+      int op;          /* One of the SRT_ operations to apply to self */
+      int priorOp;     /* The SRT_ operation to apply to prior selects */
+
+      priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union;
+      if( eDest==priorOp ){
+        /* We can reuse a temporary table generated by a SELECT to our
+        ** right.  This also means we are not the right-most select and so
+        ** we cannot have an ORDER BY clause
+        */
         unionTab = iParm;
+        assert( p->pOrderBy==0 );
       }else{
-        unionTab = pParse->nTab++;          
+        /* We will need to create our own temporary table to hold the
+        ** intermediate results.
+        */
+        unionTab = pParse->nTab++;
+        if( p->pOrderBy 
+        && matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){
+          return 1;
+        }
         sqliteVdbeAddOp(v, OP_Open, unionTab, 1, 0, 0);
-        sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0);
+        if( p->op!=TK_ALL ){
+          sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0);
+        }
       }
-      rc = sqliteSelect(pParse, pPrior, SRT_Union, unionTab);
+
+      /* Code the SELECT statements to our left
+      */
+      rc = sqliteSelect(pParse, pPrior, priorOp, unionTab);
       if( rc ) return rc;
-      op = p->op==TK_EXCEPT ? SRT_Except : SRT_Union;
+
+      /* Code the current SELECT statement
+      */
+      switch( p->op ){
+         case TK_EXCEPT:  op = SRT_Except;   break;
+         case TK_UNION:   op = SRT_Union;    break;
+         case TK_ALL:     op = SRT_Table;    break;
+      }
       p->pPrior = 0;
       rc = sqliteSelect(pParse, p, op, unionTab);
       p->pPrior = pPrior;
       if( rc ) return rc;
-      if( eDest!=SRT_Union ){
+
+      /* Convert the data in the temporary table into whatever form
+      ** it is that we currently need.
+      */      
+      if( eDest!=priorOp ){
         int iCont, iBreak;
         assert( p->pEList );
-        generateColumnNames(v, 0, p->pEList);
+        generateColumnNames(pParse, 0, p->pEList);
         iBreak = sqliteVdbeMakeLabel(v);
         iCont = sqliteVdbeAddOp(v, OP_Next, unionTab, iBreak, 0, 0);
         rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr,
-                             0, -1, eDest, iParm, 
+                             p->pOrderBy, -1, eDest, iParm, 
                              iCont, iBreak);
         if( rc ) return 1;
         sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
         sqliteVdbeAddOp(v, OP_Close, unionTab, 0, 0, iBreak);
+        if( p->pOrderBy ){
+          generateSortTail(v, p->pEList->nExpr);
+        }
       }
       break;
     }
@@ -317,38 +525,58 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
       int tab1, tab2;
       int iCont, iBreak;
 
+      /* INTERSECT is different from the others since it requires
+      ** two temporary tables.  Hence it has its own case.  Begine
+      ** by allocating the tables we will need.
+      */
       tab1 = pParse->nTab++;
       tab2 = pParse->nTab++;
+      if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){
+        return 1;
+      }
       sqliteVdbeAddOp(v, OP_Open, tab1, 1, 0, 0);
       sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0);
+
+      /* Code the SELECTs to our left into temporary table "tab1".
+      */
       rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1);
       if( rc ) return rc;
+
+      /* Code the current SELECT into temporary table "tab2"
+      */
       sqliteVdbeAddOp(v, OP_Open, tab2, 1, 0, 0);
       sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0);
       p->pPrior = 0;
       rc = sqliteSelect(pParse, p, SRT_Union, tab2);
       p->pPrior = pPrior;
       if( rc ) return rc;
+
+      /* Generate code to take the intersection of the two temporary
+      ** tables.
+      */
       assert( p->pEList );
-      generateColumnNames(v, 0, p->pEList);
+      generateColumnNames(pParse, 0, p->pEList);
       iBreak = sqliteVdbeMakeLabel(v);
       iCont = sqliteVdbeAddOp(v, OP_Next, tab1, iBreak, 0, 0);
       sqliteVdbeAddOp(v, OP_Key, tab1, 0, 0, 0);
       sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont, 0, 0);
       rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr,
-                             0, -1, eDest, iParm, 
+                             p->pOrderBy, -1, eDest, iParm, 
                              iCont, iBreak);
       if( rc ) return 1;
       sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
       sqliteVdbeAddOp(v, OP_Close, tab2, 0, 0, iBreak);
       sqliteVdbeAddOp(v, OP_Close, tab1, 0, 0, 0);
+      if( p->pOrderBy ){
+        generateSortTail(v, p->pEList->nExpr);
+      }
       break;
     }
   }
   assert( p->pEList && pPrior->pEList );
   if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
-    sqliteSetString(&pParse->zErrMsg, "SELECTs have different numbers "
-       "of columns and therefore cannot be joined", 0);
+    sqliteSetString(&pParse->zErrMsg, "SELECTs to the left and right of ",
+      selectOpName(p->op), " do not have the same number of result columns", 0);
     pParse->nErr++;
     return 1;
   }
@@ -386,7 +614,7 @@ int sqliteSelect(
   int eDest,             /* One of: SRT_Callback Mem Set Union Except */
   int iParm              /* Save result in this memory location, if >=0 */
 ){
-  int i, j;
+  int i;
   WhereInfo *pWInfo;
   Vdbe *v;
   int isAgg = 0;         /* True for select lists like "count(*)" */
@@ -407,7 +635,6 @@ int sqliteSelect(
 
   /* Make local copies of the parameters for this query.
   */
-  pEList = p->pEList;
   pTabList = p->pSrc;
   pWhere = p->pWhere;
   pOrderBy = p->pOrderBy;
@@ -422,17 +649,14 @@ int sqliteSelect(
   if( pParse->nErr>0 ) return 0;
   sqliteParseInfoReset(pParse);
 
-  /* Look up every table in the table list.
+  /* Look up every table in the table list and create an appropriate
+  ** columnlist in pEList if there isn't one already.  (The parser leaves
+  ** a NULL in the pEList field if the SQL said "SELECT * FROM ...")
   */
-  for(i=0; i<pTabList->nId; i++){
-    pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
-    if( pTabList->a[i].pTab==0 ){
-      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
-         pTabList->a[i].zName, 0);
-      pParse->nErr++;
-      return 1;
-    }
+  if( fillInColumnList(pParse, p) ){
+    return 1;
   }
+  pEList = p->pEList;
 
   /* Allocate a temporary table to use for the DISTINCT set, if
   ** necessary.  This must be done early to allocate the cursor before
@@ -444,21 +668,6 @@ int sqliteSelect(
     distinct = -1;
   }
 
-  /* If the list of fields to retrieve is "*" then replace it with
-  ** a list of all fields from all tables.
-  */
-  if( pEList==0 ){
-    for(i=0; i<pTabList->nId; i++){
-      Table *pTab = pTabList->a[i].pTab;
-      for(j=0; j<pTab->nCol; j++){
-        Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
-        pExpr->iTable = i + pParse->nTab;
-        pExpr->iField = j;
-        p->pEList = pEList = sqliteExprListAppend(pEList, pExpr, 0);
-      }
-    }
-  }
-
   /* If writing to memory or generating a set
   ** only a single column may be output.
   */
@@ -587,7 +796,7 @@ int sqliteSelect(
   ** step is skipped if the output is going to a table or a memory cell.
   */
   if( eDest==SRT_Callback ){
-    generateColumnNames(v, pTabList, pEList);
+    generateColumnNames(pParse, pTabList, pEList);
   }
 
   /* Reset the aggregator
@@ -707,13 +916,7 @@ int sqliteSelect(
   ** and send them to the callback one by one.
   */
   if( pOrderBy ){
-    int end = sqliteVdbeMakeLabel(v);
-    int addr;
-    sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
-    addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
-    sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
-    sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
-    sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end);
+    generateSortTail(v, pEList->nExpr);
   }
   return 0;
 }
index d2b1347f9061a04a05bfc7563d0194097699dba8..a4530ce79b7d2da2f7ad235b9d846dc06e52a280 100644 (file)
@@ -23,7 +23,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.21 2000/06/07 15:24:40 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.22 2000/06/07 23:51:51 drh Exp $
 */
 #include "sqlite.h"
 #include "dbbe.h"
@@ -182,8 +182,9 @@ struct ExprList {
   struct {
     Expr *pExpr;           /* The list of expressions */
     char *zName;           /* Token associated with this expression */
-    int idx;               /* ... */
-    int isAgg;             /* True if this is an aggregate like count(*) */
+    char sortOrder;        /* 1 for DESC or 0 for ASC */
+    char isAgg;            /* True if this is an aggregate like count(*) */
+    char done;             /* A flag to indicate when processing is finished */
   } *a;                  /* One entry for each expression */
 };
 
@@ -279,6 +280,7 @@ struct Parse {
   Token sLastToken;    /* The last token parsed */
   Table *pNewTable;    /* A table being constructed by CREATE TABLE */
   Vdbe *pVdbe;         /* An engine for executing database bytecode */
+  int colNamesSet;     /* TRUE after OP_ColumnCount has been issued to pVdbe */
   int explain;         /* True if the EXPLAIN flag is found on the query */
   int initFlag;        /* True if reparsing CREATE TABLEs */
   int nErr;            /* Number of errors seen */
@@ -349,8 +351,10 @@ int sqliteGlobCompare(const char*,const char*);
 int sqliteLikeCompare(const unsigned char*,const unsigned char*);
 char *sqliteTableNameFromToken(Token*);
 int sqliteExprCheck(Parse*, Expr*, int, int*);
+int sqliteExprCompare(Expr*, Expr*);
 int sqliteFuncId(Token*);
 int sqliteExprResolveIds(Parse*, IdList*, Expr*);
 void sqliteExprResolveInSelect(Parse*, Expr*);
 int sqliteExprAnalyzeAggregates(Parse*, Expr*);
-void sqlitePArseInfoReset(Parse*);
+void sqliteParseInfoReset(Parse*);
+Vdbe *sqliteGetVdbe(Parse*);
index ca114254188cc9a77bc54533a659d2be81d2c228..2b5c444d61cdaba372a96ff7df57defcbbac1072 100644 (file)
@@ -24,7 +24,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.4 2000/06/06 13:54:16 drh Exp $
+** $Id: update.c,v 1.5 2000/06/07 23:51:51 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -104,7 +104,7 @@ void sqliteUpdate(
     }
     for(j=0; j<pTab->nCol; j++){
       if( strcmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
-        pChanges->a[i].idx = j;
+        /* pChanges->a[i].idx = j; */
         aXRef[j] = i;
         break;
       }
@@ -138,10 +138,7 @@ void sqliteUpdate(
 
   /* Begin generating code.
   */
-  v = pParse->pVdbe;
-  if( v==0 ){
-    v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
-  }
+  v = sqliteGetVdbe(pParse);
   if( v==0 ) goto update_cleanup;
 
   /* Begin the database scan
index c30879ef5018e80e50596495a7d6bdac25e88c04..01276b79a786174e93d0dc11099abf598cb6443b 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.1 2000/06/06 13:54:16 drh Exp $
+# $Id: in.test,v 1.2 2000/06/07 23:51:51 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -146,5 +146,20 @@ do_test in-4.2 {
   execsql {SELECT a FROM t1 ORDER BY a}
 } {1 2 3 4 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
+# are resolved properly.
+#
+do_test in-5.1 {
+  execsql {
+    INSERT INTO t1 VALUES('hello', 'world');
+    SELECT * FROM t1
+    WHERE a IN (
+       'Do','an','IN','with','a','constant','RHS','but','where','the',
+       'has','many','elements','We','need','to','test','that',
+       'collisions','hash','table','are','resolved','properly',
+       'This','in-set','contains','thirty','one','entries','hello');
+  }
+} {hello world}
 
 finish_test