]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Corrections to transient table handling in compound select statments.
authordrh <drh@noemail.net>
Sun, 29 Aug 2004 16:25:03 +0000 (16:25 +0000)
committerdrh <drh@noemail.net>
Sun, 29 Aug 2004 16:25:03 +0000 (16:25 +0000)
Tickets #826 and #875. (CVS 1912)

FossilOrigin-Name: 9cc765be4611a248cfcce1243fd1bbeaf5202968

manifest
manifest.uuid
src/select.c
test/select7.test [new file with mode: 0644]

index ab7fa379ddc9e80c9e317c527307397c75e83ef9..b6c5ca1d300aecb78e295206e04859c90cd91e77 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Progress\stoward\sfixing\siproblems\swith\scompound\sselects.\s(CVS\s1911)
-D 2004-08-29T01:31:05
+C Corrections\sto\stransient\stable\shandling\sin\scompound\sselect\sstatments.\nTickets\s#826\sand\s#875.\s(CVS\s1912)
+D 2004-08-29T16:25:04
 F Makefile.in 65a7c43fcaf9a710d62f120b11b6e435eeb4a450
 F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -58,7 +58,7 @@ F src/parse.y 581a2ce014b843506805b2470c02b7865ad034d5
 F src/pragma.c a7cea75286fcff6666a5412b04478fcf0ecef5c4
 F src/printf.c 17b28a1eedfe8129b05de981719306c18c3f1327
 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
-F src/select.c 5fa9db32e24f4c0f0ead43cfa59a6cfc3a452a15
+F src/select.c 400b2dcc8e05c0101a65a370f7ebb33c9c85f0b3
 F src/shell.c 42f65424a948f197f389e13bc7aaa3cf24dafd0c
 F src/sqlite.h.in d619f3dd276845c2ff3fbeaed1d037563fc419f0
 F src/sqliteInt.h c7ed161ecc40f9fd0f080fbcc00e34bd7d6735ee
@@ -162,6 +162,7 @@ F test/select3.test 9de435aa84fc406708cd8dc1b1d60e7f27cea685
 F test/select4.test 86e72fc3b07de4fe11439aa419e37db3c49467e2
 F test/select5.test 94db800bbeff2e426c0175e07f7a71d4617853b5
 F test/select6.test 7a4c572ada0c2f969cecacd76f1f5c1533a22bbb
+F test/select7.test c71c822a82c80bbd55558b4b69d35442dfe22ffd
 F test/sort.test 35e9d6bd6930969050606c8feb9c6745469720e3
 F test/subselect.test 50f98723f00e97b1839d36410ee63597ca82d775
 F test/table.test fd9a0f4992230e4ca89cd37ae3191a12750df1d0
@@ -244,7 +245,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
 F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
-P d50c47b4995bd9b58e1293aa6513361cffc6babe
-R e1fb8ddf0dd95f67094014c0c3248f2f
+P 307478593d5d96b79386da222c7742ea2eaa5467
+R dce15d87c754e08c36b4d1c93058acb6
 U drh
-Z 1e45c8abb4ae3be9c92dd0026a2f0205
+Z c4c7da99c7cb81b57cec03f3f74114d2
index bcecb81456f697eed6f1dd3d0639ae07b3faf616..7dafc570628822496a563405cb9521525a957b17 100644 (file)
@@ -1 +1 @@
-307478593d5d96b79386da222c7742ea2eaa5467
\ No newline at end of file
+9cc765be4611a248cfcce1243fd1bbeaf5202968
\ No newline at end of file
index 8cf658b7f1758a0c5aa1a7944298a3f079ac55d9..565fd9038d6c3326b9f677bec1880e4ff9c067da 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.204 2004/08/29 01:31:05 drh Exp $
+** $Id: select.c,v 1.205 2004/08/29 16:25:04 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1280,30 +1280,11 @@ static int openTempIndex(Parse *pParse, Select *p, int iTab, int keyAsData){
 }
 
 /*
-** FIX ME:
-**    +  Omit the ppOpenTemp parameter from multiSelectOpenTempAddr().
-**    +  Attach pOpenList to the right-most term always.
-**    +  Make sure Select.ppOpenTemp is initialized to NULL
+** Add the address "addr" to the set of all OpenTemp opcode addresses
+** that are being accumulated in p->ppOpenTemp.
 */
-
-/*
-** Add the address "addr" to the set of all opcode addresses contained
-** in the pOpenTemp list for the whole compound select.  If no pOpenTemp
-** list has been created yet, then create a new one and make *ppOpenTemp
-** point to it.  If the pOpenTemp list already exists, leave *ppOpenTemp
-** unchanged and just add the new address to the existing list.
-*/
-static int multiSelectOpenTempAddr(Select *p, int addr, IdList **ppOpenTemp){
-  IdList *pList;
-  if( !p->ppOpenTemp ){
-    /* Create a new list */
-    *ppOpenTemp = sqlite3IdListAppend(0, 0);
-    p->ppOpenTemp = ppOpenTemp;
-  }else{
-    /* Add a new element onto the end of the existing list */
-    *p->ppOpenTemp = sqlite3IdListAppend(*p->ppOpenTemp, 0);
-  }
-  pList = *p->ppOpenTemp;
+static int multiSelectOpenTempAddr(Select *p, int addr){
+  IdList *pList = *p->ppOpenTemp = sqlite3IdListAppend(*p->ppOpenTemp, 0);
   if( pList==0 ){
     return SQLITE_NOMEM;
   }
@@ -1373,6 +1354,9 @@ static int multiSelect(
   Select *pPrior;       /* Another SELECT immediately to our left */
   Vdbe *v;              /* Generate code to this VDBE */
   IdList *pOpenTemp = 0;/* OP_OpenTemp opcodes that need a KeyInfo */
+  int aAddr[5];         /* Addresses of SetNumColumns operators */
+  int nAddr = 0;        /* Number used */
+  int nCol;             /* Number of columns in the result set */
 
   /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs.  Only
   ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
@@ -1403,7 +1387,11 @@ static int multiSelect(
     goto multi_select_end;
   }
 
-  /* PART OF FIX:
+  /* If *p this is the right-most select statement, then initialize
+  ** p->ppOpenTemp to point to pOpenTemp.  If *p is not the right most
+  ** statement then p->ppOpenTemp will have already been initialized
+  ** by a prior call to this same procedure.  Pass along the pOpenTemp
+  ** pointer to pPrior, the next statement to our left.
   */
   if( p->ppOpenTemp==0 ){
     p->ppOpenTemp = &pOpenTemp;
@@ -1415,15 +1403,8 @@ static int multiSelect(
   if( eDest==SRT_TempTable ){
     assert( p->pEList );
     sqlite3VdbeAddOp(v, OP_OpenTemp, iParm, 0);
-
-    /* FIX ME:
-    ** p->pEList->nExpr might contain a "*" and so might not be the 
-    ** correct number.  Go ahead and code the SetNumColumns instruction
-    ** here, but also record its address.   Change the P2 value of the
-    ** instruction to the number of columns after sqlite3Select() has
-    ** been called to code the subquery and has modified pEList->nExpr
-    ** to be the correct value. */
-    sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, p->pEList->nExpr);
+    assert( nAddr==0 );
+    aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, 0);
     eDest = SRT_Table;
   }
 
@@ -1434,7 +1415,6 @@ static int multiSelect(
       if( p->pOrderBy==0 ){
         pPrior->nLimit = p->nLimit;
         pPrior->nOffset = p->nOffset;
-        /* pPrior->ppOpenTemp = p->ppOpenTemp; // FIX */
         rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff);
         if( rc ){
           goto multi_select_end;
@@ -1480,25 +1460,23 @@ static int multiSelect(
         }
         addr = sqlite3VdbeAddOp(v, OP_OpenTemp, unionTab, 0);
         if( p->op!=TK_ALL ){
-          rc = multiSelectOpenTempAddr(p, addr, &pOpenTemp);
+          rc = multiSelectOpenTempAddr(p, addr);
           if( rc!=SQLITE_OK ){
             goto multi_select_end;
           }
           sqlite3VdbeAddOp(v, OP_KeyAsData, unionTab, 1);
         }
+       assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
+        aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, unionTab, 0);
         assert( p->pEList );
       }
 
       /* Code the SELECT statements to our left
       */
-      /* pPrior->ppOpenTemp = p->ppOpenTemp; // FIX */
       rc = sqlite3Select(pParse, pPrior, priorOp, unionTab, 0, 0, 0, aff);
       if( rc ){
         goto multi_select_end;
       }
-      if( p->op==TK_ALL ){
-        sqlite3VdbeAddOp(v, OP_SetNumColumns, unionTab, pPrior->pEList->nExpr);
-      }
 
       /* Code the current SELECT statement
       */
@@ -1570,16 +1548,17 @@ static int multiSelect(
       }
 
       addr = sqlite3VdbeAddOp(v, OP_OpenTemp, tab1, 0);
-      rc = multiSelectOpenTempAddr(p, addr, &pOpenTemp);
+      rc = multiSelectOpenTempAddr(p, addr);
       if( rc!=SQLITE_OK ){
         goto multi_select_end;
       }
       sqlite3VdbeAddOp(v, OP_KeyAsData, tab1, 1);
+      assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
+      aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab1, 0);
       assert( p->pEList );
 
       /* Code the SELECTs to our left into temporary table "tab1".
       */
-      /* pPrior->ppOpenTemp = p->ppOpenTemp; // FIX */
       rc = sqlite3Select(pParse, pPrior, SRT_Union, tab1, 0, 0, 0, aff);
       if( rc ){
         goto multi_select_end;
@@ -1588,11 +1567,13 @@ static int multiSelect(
       /* Code the current SELECT into temporary table "tab2"
       */
       addr = sqlite3VdbeAddOp(v, OP_OpenTemp, tab2, 0);
-      rc = multiSelectOpenTempAddr(p, addr, &pOpenTemp);
+      rc = multiSelectOpenTempAddr(p, addr);
       if( rc!=SQLITE_OK ){
         goto multi_select_end;
       }
       sqlite3VdbeAddOp(v, OP_KeyAsData, tab2, 1);
+      assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
+      aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab2, 0);
       p->pPrior = 0;
       nLimit = p->nLimit;
       p->nLimit = -1;
@@ -1634,6 +1615,10 @@ static int multiSelect(
       break;
     }
   }
+
+  /* Make sure all SELECTs in the statement have the same number of elements
+  ** in their result sets.
+  */
   assert( p->pEList && pPrior->pEList );
   if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
     sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
@@ -1642,16 +1627,29 @@ static int multiSelect(
     goto multi_select_end;
   }
 
+  /* Set the number of columns in temporary tables
+  */
+  nCol = p->pEList->nExpr;
+  while( nAddr>0 ){
+    nAddr--;
+    sqlite3VdbeChangeP2(v, aAddr[nAddr], nCol);
+  }
+
   /* Compute collating sequences used by either the ORDER BY clause or
   ** by any temporary tables needed to implement the compound select.
   ** Attach the KeyInfo structure to all temporary tables.  Invoke the
   ** ORDER BY processing if there is an ORDER BY clause.
+  **
+  ** This section is run by the right-most SELECT statement only.
+  ** SELECT statements to the left always skip this part.  The right-most
+  ** SELECT might also skip this part if it has no ORDER BY clause and
+  ** no temp tables are required.
   */
   if( p->pOrderBy || (pOpenTemp && pOpenTemp->nId>0) ){
-    int nCol = p->pEList->nExpr;  /* Number of columns in the result set */
     int i;                        /* Loop counter */
     KeyInfo *pKeyInfo;            /* Collating sequence for the result set */
 
+    assert( p->ppOpenTemp == &pOpenTemp );
     pKeyInfo = sqliteMalloc(sizeof(*pKeyInfo)+nCol*sizeof(CollSeq*));
     if( !pKeyInfo ){
       rc = SQLITE_NOMEM;
@@ -2369,21 +2367,6 @@ int sqlite3Select(
     generateColumnNames(pParse, pTabList, pEList);
   }
 
-  /* If the destination is SRT_Union, then set the number of columns in
-  ** the records that will be inserted into the temporary table. The caller
-  ** couldn't do this, in case the select statement is of the form 
-  ** "SELECT * FROM ....". 
-  **
-  ** We need to do this before we start inserting records into the 
-  ** temporary table (which has had OP_KeyAsData executed on it), because
-  ** it is required by the key comparison function. So do it now, even
-  ** though this means that OP_SetNumColumns may be executed on the same
-  ** cursor more than once.
-  */
-  if( eDest==SRT_Union ){
-    sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, pEList->nExpr);
-  }
-
   /* Generate code for all sub-queries in the FROM clause
   */
   for(i=0; i<pTabList->nSrc; i++){
diff --git a/test/select7.test b/test/select7.test
new file mode 100644 (file)
index 0000000..ae91b1b
--- /dev/null
@@ -0,0 +1,50 @@
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this file is testing compute SELECT statements and nested
+# views.
+#
+# $Id: select7.test,v 1.1 2004/08/29 16:25:04 drh Exp $
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# A 3-way INTERSECT.  Ticket #875
+do_test select7-1.1 {
+  execsql {
+    create temp table t1(x);
+    insert into t1 values('amx');
+    insert into t1 values('anx');
+    insert into t1 values('amy');
+    insert into t1 values('bmy');
+    select * from t1 where x like 'a__'
+      intersect select * from t1 where x like '_m_'
+      intersect select * from t1 where x like '__x';
+  }
+} {amx}
+
+
+# Nested views do not handle * properly.  Ticket #826.
+#
+do_test select7-2.1 {
+  execsql {
+    CREATE TABLE x(id integer primary key, a TEXT NULL);
+    INSERT INTO x (a) VALUES ('first');
+    CREATE TABLE tempx(id integer primary key, a TEXT NULL);
+    INSERT INTO tempx (a) VALUES ('t-first');
+    CREATE VIEW tv1 AS SELECT x.id, tx.id FROM x JOIN tempx tx ON tx.id=x.id;
+    CREATE VIEW tv1b AS SELECT x.id, tx.id FROM x JOIN tempx tx on tx.id=x.id;
+    CREATE VIEW tv2 AS SELECT * FROM tv1 UNION SELECT * FROM tv1b;
+    SELECT * FROM tv2;
+  }
+} {1 1}
+
+
+finish_test