aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2008-11-11 18:28:58 +0000
committerdrh <drh@noemail.net>2008-11-11 18:28:58 +0000
commita78c22c43304a9d0ac3c5bb5c03dbc5ed52c51bb (patch)
treeb18aa8691f230e7b52f0db6a2ed57186424a8ade /src
parent5780ebdf791e3c258a7b33cafc486dcb1879b591 (diff)
downloadsqlite-a78c22c43304a9d0ac3c5bb5c03dbc5ed52c51bb.tar.gz
sqlite-a78c22c43304a9d0ac3c5bb5c03dbc5ed52c51bb.zip
Cleanup in flattenSubquery. Add OOM tests for flattenSubquery. Fix issues
with OOM errors causes problems for flattenSubquery. Ticket #3485. (CVS 5882) FossilOrigin-Name: ea5f4baa041aed934600f0f96b84afb92a14bc47
Diffstat (limited to 'src')
-rw-r--r--src/build.c93
-rw-r--r--src/select.c168
-rw-r--r--src/sqliteInt.h3
3 files changed, 191 insertions, 73 deletions
diff --git a/src/build.c b/src/build.c
index d08627cd1..bb058644c 100644
--- a/src/build.c
+++ b/src/build.c
@@ -22,7 +22,7 @@
** COMMIT
** ROLLBACK
**
-** $Id: build.c,v 1.500 2008/11/03 20:55:07 drh Exp $
+** $Id: build.c,v 1.501 2008/11/11 18:28:59 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -2992,10 +2992,80 @@ int sqlite3IdListIndex(IdList *pList, const char *zName){
}
/*
+** Expand the space allocated for the given SrcList object by
+** creating nExtra new slots beginning at iStart. iStart is zero based.
+** New slots are zeroed.
+**
+** For example, suppose a SrcList initially contains two entries: A,B.
+** To append 3 new entries onto the end, do this:
+**
+** sqlite3SrcListEnlarge(db, pSrclist, 3, 2);
+**
+** After the call above it would contain: A, B, nil, nil, nil.
+** If the iStart argument had been 1 instead of 2, then the result
+** would have been: A, nil, nil, nil, B. To prepend the new slots,
+** the iStart value would be 0. The result then would
+** be: nil, nil, nil, A, B.
+**
+** If a memory allocation fails the SrcList is unchanged. The
+** db->mallocFailed flag will be set to true.
+*/
+SrcList *sqlite3SrcListEnlarge(
+ sqlite3 *db, /* Database connection to notify of OOM errors */
+ SrcList *pSrc, /* The SrcList to be enlarged */
+ int nExtra, /* Number of new slots to add to pSrc->a[] */
+ int iStart /* Index in pSrc->a[] of first new slot */
+){
+ int i;
+
+ /* Sanity checking on calling parameters */
+ assert( iStart>=0 );
+ assert( nExtra>=1 );
+ if( pSrc==0 || iStart>pSrc->nSrc ){
+ assert( db->mallocFailed );
+ return pSrc;
+ }
+
+ /* Allocate additional space if needed */
+ if( pSrc->nSrc+nExtra>pSrc->nAlloc ){
+ SrcList *pNew;
+ int nAlloc = pSrc->nSrc+nExtra;
+ pNew = sqlite3DbRealloc(db, pSrc,
+ sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) );
+ if( pNew==0 ){
+ assert( db->mallocFailed );
+ return pSrc;
+ }
+ pSrc = pNew;
+ pSrc->nAlloc = nAlloc;
+ }
+
+ /* Move existing slots that come after the newly inserted slots
+ ** out of the way */
+ for(i=pSrc->nSrc-1; i>=iStart; i--){
+ pSrc->a[i+nExtra] = pSrc->a[i];
+ }
+ pSrc->nSrc += nExtra;
+
+ /* Zero the newly allocated slots */
+ memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra);
+ for(i=iStart; i<iStart+nExtra; i++){
+ pSrc->a[i].iCursor = -1;
+ }
+
+ /* Return a pointer to the enlarged SrcList */
+ return pSrc;
+}
+
+
+/*
** Append a new table name to the given SrcList. Create a new SrcList if
** need be. A new entry is created in the SrcList even if pToken is NULL.
**
-** A new SrcList is returned, or NULL if malloc() fails.
+** A SrcList is returned, or NULL if there is an OOM error. The returned
+** SrcList might be the same as the SrcList that was input or it might be
+** a new one. If an OOM error does occurs, then the prior value of pList
+** that is input to this routine is automatically freed.
**
** If pDatabase is not null, it means that the table has an optional
** database name prefix. Like this: "database.table". The pDatabase
@@ -3028,19 +3098,12 @@ SrcList *sqlite3SrcListAppend(
if( pList==0 ) return 0;
pList->nAlloc = 1;
}
- if( pList->nSrc>=pList->nAlloc ){
- SrcList *pNew;
- pList->nAlloc *= 2;
- pNew = sqlite3DbRealloc(db, pList,
- sizeof(*pList) + (pList->nAlloc-1)*sizeof(pList->a[0]) );
- if( pNew==0 ){
- sqlite3SrcListDelete(db, pList);
- return 0;
- }
- pList = pNew;
+ pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc);
+ if( db->mallocFailed ){
+ sqlite3SrcListDelete(db, pList);
+ return 0;
}
- pItem = &pList->a[pList->nSrc];
- memset(pItem, 0, sizeof(pList->a[0]));
+ pItem = &pList->a[pList->nSrc-1];
if( pDatabase && pDatabase->z==0 ){
pDatabase = 0;
}
@@ -3051,8 +3114,6 @@ SrcList *sqlite3SrcListAppend(
}
pItem->zName = sqlite3NameFromToken(db, pTable);
pItem->zDatabase = sqlite3NameFromToken(db, pDatabase);
- pItem->iCursor = -1;
- pList->nSrc++;
return pList;
}
diff --git a/src/select.c b/src/select.c
index ae9fa153e..a623b8f6b 100644
--- a/src/select.c
+++ b/src/select.c
@@ -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.482 2008/10/31 10:53:23 danielk1977 Exp $
+** $Id: select.c,v 1.483 2008/11/11 18:28:59 drh Exp $
*/
#include "sqliteInt.h"
@@ -2573,7 +2573,9 @@ static int flattenSubquery(
/* Check to see if flattening is permitted. Return 0 if not.
*/
+ assert( p!=0 );
if( p==0 ) return 0;
+ assert( p->pPrior==0 ); /* Unable to flatten compound queries */
pSrc = p->pSrc;
assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc );
pSubitem = &pSrc->a[iFrom];
@@ -2685,92 +2687,146 @@ static int flattenSubquery(
** SELECT <expr-list> FROM (<sub-query>) <where-clause>
**
** followed by any ORDER BY, LIMIT and/or OFFSET clauses. This block
- ** creates N copies of the parent query without any ORDER BY, LIMIT or
+ ** creates N-1 copies of the parent query without any ORDER BY, LIMIT or
** OFFSET clauses and joins them to the left-hand-side of the original
** using UNION ALL operators. In this case N is the number of simple
** select statements in the compound sub-query.
+ **
+ ** Example:
+ **
+ ** SELECT a+1 FROM (
+ ** SELECT x FROM tab
+ ** UNION ALL
+ ** SELECT y FROM tab
+ ** UNION ALL
+ ** SELECT abs(z*2) FROM tab2
+ ** ) WHERE a!=5 ORDER BY 1
+ **
+ ** Transformed into:
+ **
+ ** SELECT x+1 FROM tab WHERE x+1!=5
+ ** UNION ALL
+ ** SELECT y+1 FROM tab WHERE y+1!=5
+ ** UNION ALL
+ ** SELECT abs(z*2)+1 FROM tab2 WHERE abs(z*2)+1!=5
+ ** ORDER BY 1
+ **
+ ** We call this the "compound-subquery flattening".
*/
for(pSub=pSub->pPrior; pSub; pSub=pSub->pPrior){
Select *pNew;
ExprList *pOrderBy = p->pOrderBy;
Expr *pLimit = p->pLimit;
- Expr *pOffset = p->pOffset;
Select *pPrior = p->pPrior;
p->pOrderBy = 0;
p->pSrc = 0;
p->pPrior = 0;
p->pLimit = 0;
pNew = sqlite3SelectDup(db, p);
- pNew->pPrior = pPrior;
- p->pPrior = pNew;
+ p->pLimit = pLimit;
p->pOrderBy = pOrderBy;
- p->op = TK_ALL;
p->pSrc = pSrc;
- p->pLimit = pLimit;
- p->pOffset = pOffset;
+ p->op = TK_ALL;
p->pRightmost = 0;
- pNew->pRightmost = 0;
+ if( pNew==0 ){
+ pNew = pPrior;
+ }else{
+ pNew->pPrior = pPrior;
+ pNew->pRightmost = 0;
+ }
+ p->pPrior = pNew;
+ if( db->mallocFailed ) return 1;
}
/* Begin flattening the iFrom-th entry of the FROM clause
** in the outer query.
*/
pSub = pSub1 = pSubitem->pSelect;
+
+ /* Delete the transient table structure associated with the
+ ** subquery
+ */
+ sqlite3DbFree(db, pSubitem->zDatabase);
+ sqlite3DbFree(db, pSubitem->zName);
+ sqlite3DbFree(db, pSubitem->zAlias);
+ pSubitem->zDatabase = 0;
+ pSubitem->zName = 0;
+ pSubitem->zAlias = 0;
+ pSubitem->pSelect = 0;
+
+ /* Defer deleting the Table object associated with the
+ ** subquery until code generation is
+ ** complete, since there may still exist Expr.pTab entries that
+ ** refer to the subquery even after flattening. Ticket #3346.
+ */
+ if( pSubitem->pTab!=0 ){
+ Table *pTabToDel = pSubitem->pTab;
+ if( pTabToDel->nRef==1 ){
+ pTabToDel->pNextZombie = pParse->pZombieTab;
+ pParse->pZombieTab = pTabToDel;
+ }else{
+ pTabToDel->nRef--;
+ }
+ pSubitem->pTab = 0;
+ }
+
+ /* The following loop runs once for each term in a compound-subquery
+ ** flattening (as described above). If we are doing a different kind
+ ** of flattening - a flattening other than a compound-subquery flattening -
+ ** then this loop only runs once.
+ **
+ ** This loop moves all of the FROM elements of the subquery into the
+ ** the FROM clause of the outer query. Before doing this, remember
+ ** the cursor number for the original outer query FROM element in
+ ** iParent. The iParent cursor will never be used. Subsequent code
+ ** will scan expressions looking for iParent references and replace
+ ** those references with expressions that resolve to the subquery FROM
+ ** elements we are now copying in.
+ */
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
- int nSubSrc = pSubSrc->nSrc;
+ int nSubSrc;
int jointype = 0;
- pSubSrc = pSub->pSrc;
- pSrc = pParent->pSrc;
-
- /* Move all of the FROM elements of the subquery into the
- ** the FROM clause of the outer query. Before doing this, remember
- ** the cursor number for the original outer query FROM element in
- ** iParent. The iParent cursor will never be used. Subsequent code
- ** will scan expressions looking for iParent references and replace
- ** those references with expressions that resolve to the subquery FROM
- ** elements we are now copying in.
- */
+ pSubSrc = pSub->pSrc; /* FROM clause of subquery */
+ nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
+ pSrc = pParent->pSrc; /* FROM clause of the outer query */
+
if( pSrc ){
- Table *pTabToDel;
- pSubitem = &pSrc->a[iFrom];
- nSubSrc = pSubSrc->nSrc;
+ assert( pParent==p ); /* First time through the loop */
jointype = pSubitem->jointype;
- sqlite3DbFree(db, pSubitem->zDatabase);
- sqlite3DbFree(db, pSubitem->zName);
- sqlite3DbFree(db, pSubitem->zAlias);
- pSubitem->zDatabase = 0;
- pSubitem->zName = 0;
- pSubitem->zAlias = 0;
-
- /* If the FROM element is a subquery, defer deleting the Table
- ** object associated with that subquery until code generation is
- ** complete, since there may still exist Expr.pTab entires that
- ** refer to the subquery even after flattening. Ticket #3346.
- */
- if( (pTabToDel = pSubitem->pTab)!=0 ){
- if( pTabToDel->nRef==1 ){
- pTabToDel->pNextZombie = pParse->pZombieTab;
- pParse->pZombieTab = pTabToDel;
- }else{
- pTabToDel->nRef--;
- }
+ }else{
+ assert( pParent!=p ); /* 2nd and subsequent times through the loop */
+ pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
+ if( pSrc==0 ){
+ assert( db->mallocFailed );
+ break;
}
- pSubitem->pTab = 0;
}
- if( nSubSrc!=1 || !pSrc ){
- int extra = nSubSrc - 1;
- for(i=(pSrc?1:0); i<nSubSrc; i++){
- pSrc = sqlite3SrcListAppend(db, pSrc, 0, 0);
- if( pSrc==0 ){
- pParent->pSrc = 0;
- return 1;
- }
- }
- pParent->pSrc = pSrc;
- for(i=pSrc->nSrc-1; i-extra>=iFrom; i--){
- pSrc->a[i] = pSrc->a[i-extra];
+
+ /* The subquery uses a single slot of the FROM clause of the outer
+ ** query. If the subquery has more than one element in its FROM clause,
+ ** then expand the outer query to make space for it to hold all elements
+ ** of the subquery.
+ **
+ ** Example:
+ **
+ ** SELECT * FROM tabA, (SELECT * FROM sub1, sub2), tabB;
+ **
+ ** The outer query has 3 slots in its FROM clause. One slot of the
+ ** outer query (the middle slot) is used by the subquery. The next
+ ** block of code will expand the out query to 4 slots. The middle
+ ** slot is expanded to two slots in order to make space for the
+ ** two elements in the FROM clause of the subquery.
+ */
+ if( nSubSrc>1 ){
+ pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1);
+ if( db->mallocFailed ){
+ break;
}
}
+
+ /* Transfer the FROM clause terms from the subquery into the
+ ** outer query.
+ */
for(i=0; i<nSubSrc; i++){
pSrc->a[i+iFrom] = pSubSrc->a[i];
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index ba13719cb..9af75ec01 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.789 2008/11/10 18:05:36 shane Exp $
+** @(#) $Id: sqliteInt.h,v 1.790 2008/11/11 18:29:00 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -2144,6 +2144,7 @@ void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int,int*,int*,int*);
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
+SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*);
SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
Token*, Select*, Expr*, IdList*);