persistentstorage/dbms/utable/UT_QUERY.CPP
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 22 Jan 2010 11:06:30 +0200
changeset 0 08ec8eefde2f
permissions -rw-r--r--
Revision: 201003 Kit: 201003

// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include "UT_STD.H"
#include "D32COMP.H"
#include <d32dbmsconstants.h>

// Class RDbAccessPlan::TPlan

TInt RDbAccessPlan::TPlan::OrderByPlan(const TPlan& aLeft,const TPlan& aRight)
	{
	TUint lpos=aLeft.Type();
	TUint rpos=aRight.Type();
	__ASSERT(lpos!=0 && rpos!=0 ); // should be no table iterators
	return lpos-rpos;
	}

RDbAccessPlan::TPlan::TType RDbAccessPlan::TPlan::Type() const
//
// flag values: 0=A, 1=C, 2=B, 3=D, 8=E/F, 10=G/H, 16=M/N, 18=S/T,	20=I/J, 21=K/L, 22=O/P, 23=Q/R	
// This determines order of plans
//	
	{
	static const TUint KPosition[]={EPlanA,EPlanC,EPlanB,EPlanD,0,0,0,0,EPlanEF,0,EPlanGH,0,0,0,0,0,EPlanMN,
							 0,EPlanST,0,EPlanIJ,EPlanKL,EPlanOP,EPlanQR};
	__ASSERT_ALWAYS(((iFlags&EMask) < (sizeof(KPosition)/sizeof(KPosition[0]))), User::Invariant());						 
	return TType(KPosition[iFlags&EMask]);
	}

// Class RDbAccessPlan::TBounds

RDbAccessPlan::TBounds::TBounds(const TPlan& aPlan)
	: iLowerPred(aPlan.iLower),iUpperPred(aPlan.iUpper),iLower(0),iUpper(0),iInclusion(0)
	{
	SetLowerBounds();
	SetUpperBounds();
	if (aPlan.iIndex->Key()[0].iOrder==TDbKeyCol::EDesc)
		{
		TDbLookupKey* t=iLower;
		iLower=iUpper;
		iUpper=t;
		iInclusion=(iInclusion>>1)|(iInclusion<<1);
		}
	}

void RDbAccessPlan::TBounds::GetLookupKey(const CSqlCompPredicate& aCompPredicate,TDbLookupKey& aLookup)
	{
	const RSqlLiteral& value=aCompPredicate.Value();
	switch (aCompPredicate.ColType())
		{
	default:
		__ASSERT(0);
	case EDbColBit:
	case EDbColInt8:
	case EDbColUint8:
	case EDbColInt16:
	case EDbColUint16:
	case EDbColInt32:
	case EDbColUint32:
	case EDbColInt64:
		aLookup.Add(value.Int64());
		break;
	case EDbColReal32:
	case EDbColReal64:
		aLookup.Add(value.Real64());
		break;
	case EDbColDateTime:
		aLookup.Add(value.Time());
		break;
	case EDbColText8:
	case EDbColLongText8:
		aLookup.Add(value.Text8());
		break;
	case EDbColText16:
	case EDbColLongText16:
		aLookup.Add(value.Text16());
		break;
		}
	}

void RDbAccessPlan::TBounds::SetLowerBounds()
	{
	if (!iLowerPred) // iLower already set to 0
		return;
	GetLookupKey(*iLowerPred,iLowerKey);
	switch (iLowerPred->NodeType())
		{
	default:
		__ASSERT(0);
	case CSqlSearchCondition::ELessEqual:
	case CSqlSearchCondition::ELess:
		iLower=0;
		break;
	case CSqlSearchCondition::EEqual:
	case CSqlSearchCondition::EGreaterEqual:
		iInclusion|=CDbRecordIndex::EIncludeLower;
	case CSqlSearchCondition::EGreater:
		iLower=&iLowerKey;
		break;
		}
	}

void RDbAccessPlan::TBounds::SetUpperBounds()
	{	
	if (!iUpperPred)
		return;
	GetLookupKey(*iUpperPred,iUpperKey);
	switch (iUpperPred->NodeType())
		{
	default:
		__ASSERT(0);
	case CSqlSearchCondition::EGreaterEqual:
	case CSqlSearchCondition::EGreater:
		iUpper=0;
		break;
	case CSqlSearchCondition::EEqual:
	case CSqlSearchCondition::ELessEqual:
		iInclusion|=CDbRecordIndex::EIncludeUpper;
	case CSqlSearchCondition::ELess:
		iUpper=&iUpperKey;
		break;
		}
	}

// Class RDbAccessPlan::CDbCompPredicateList

RDbAccessPlan::CDbCompPredicateList* RDbAccessPlan::CDbCompPredicateList::NewLC(CSqlQuery& aQuery,TDbTextComparison aComparison,const CDbTableDef& aTableDef)
	{
	CDbCompPredicateList* self=new(ELeave) CDbCompPredicateList(aTableDef,aComparison);
	CleanupStack::PushL(self);
	CSqlSearchCondition& sc=aQuery.SearchCondition();
	self->ConstructL(sc);
	return self;
	}

void RDbAccessPlan::CDbCompPredicateList::ConstructL(CSqlSearchCondition& aSearchCondition)
//
// fill the list with valid comp pred's
//
	{
	TUint type=Type(aSearchCondition.NodeType());
	if (type&ECompPred) 
		{
		CSqlCompPredicate* cp=aSearchCondition.CompPredicate();
		const TDesC& colName=cp->ColumnName();
		if (IsIndexed(colName))
			AppendL(cp);
		else
			iRestriction=ETrue;
		}
	else if (type&EAnd)	
		{
		CSqlMultiNode* multiNode=aSearchCondition.MultiNode();	
		for (TInt ii=multiNode->Count();ii--;)				
			{
			CSqlSearchCondition* node=multiNode->SubNode(ii);
			ConstructL(*node);
			}
		}	
	else
		iRestriction=ETrue;
	}

TUint RDbAccessPlan::CDbCompPredicateList::Type(CSqlSearchCondition::TType aType) const
//
// converts CSqlSearchCondition::TType into flag
//
	{
	switch (aType)
		{
	case CSqlSearchCondition::EAnd:
		return EAnd;
	case CSqlSearchCondition::ELess:
		return ELess;
	case CSqlSearchCondition::ELessEqual:
		return ELessEqual;
	case CSqlSearchCondition::EEqual:
		return EEqual;
	case CSqlSearchCondition::EGreaterEqual:
		return EGreaterEqual;
	case CSqlSearchCondition::EGreater:
		return EGreater;
	default:
		return ENone;
		}
	}

TBool RDbAccessPlan::CDbCompPredicateList::IsIndexed(const TDesC& aColumnName)
//
// Checks if aColumnName is indexed. If its a text column the comparison method should be the same
//
	{
	const CDbTableIndexDef* key=iTableDef.Key(aColumnName);
	if (!key)
		return EFalse;
	const TDbColumnDef* colDef=NULL;
	TRAPD(errCode, colDef=iTableDef.Columns().ColumnL(aColumnName));
	if(errCode != KErrNone)
		return EFalse;
	if (colDef->Type()>EDbColDateTime && key->Key().Comparison()!=iComparison)
		return EFalse;
	return ETrue;
	}

CSqlCompPredicate* RDbAccessPlan::CDbCompPredicateList::CompPredicate(TDbColNo aColNo,TUint aType)
//
// Returns first CompPredicate found in the list with required type & col no, and removes from list
//
	{
	for (TInt ii=Count();ii--;)
		{
		CSqlCompPredicate* cp=At(ii);
		TUint type=Type(cp->NodeType());
		TDbColNo colNo=cp->ColNo();
		if (!type&aType || aColNo!=colNo)
			continue;
		Delete(ii);
		return cp;
		}
	return 0;
	}

// class Validate

void Validate::NameL(const TDesC& aName)
	{
	if (aName.Length()<=KDbMaxName)
		{
		TLex lex(aName);
		if (!lex.Eos() && lex.Get().IsAlpha())
			{
			TChar c;
			do
				{
				if (lex.Eos())
					return;
				c=lex.Get();
				} while (c.IsAlphaDigit()||c=='_');
			}
		}
	__LEAVE(KErrBadName);
	}

void Validate::UniqueNameL(TDesC const** aNames,TInt aCount,const TDesC& aName)
//
// Ensure that aName is not a duplicate of any of aNames, and add
// the new name to the collection. Binary search is used for speed
//
	{
	TInt left=0;
	TInt right=aCount;
	while (left<right)
		{
		TInt mid=(left+right)>>1;
		TInt c=aNames[mid]->CompareF(aName);
		if (c<0)
			left=mid+1;
		else if (c>0)
			right=mid;
		else
			__LEAVE(KErrArgument);
		}
	Mem::Move(aNames+left+1,aNames+left,(aCount-left)*sizeof(TDesC const*));
	aNames[left]=&aName;
	}

void Validate::ColSetL(const CDbColSet& aColSet)
	{
	TDbColSetIter iter(aColSet);
	if (!iter)
		__LEAVE(KErrArgument);
	TDesC const** names=(TDesC const**)User::AllocLC(aColSet.Count()*sizeof(TDesC const*));
	do
		{
		const TDbCol& col=*iter;
		NameL(col.iName);
		UniqueNameL(names,iter.Col()-1,col.iName);
		TInt type=TInt(col.iType);
		if (type<TInt(EDbColBit)||type>TInt(EDbColLongBinary))
			__LEAVE(KErrNotSupported);
		else if (col.iMaxLength<1&&col.iMaxLength!=KDbUndefinedLength)
			__LEAVE(KErrArgument);
		else if (col.iAttributes&~(TDbCol::ENotNull|TDbCol::EAutoIncrement))
			__LEAVE(KErrNotSupported);				// unknown attributes
		else if (type>EDbColUint32 && col.iAttributes&TDbCol::EAutoIncrement)
			__LEAVE(KErrArgument);					// auto increment on non-integral type
		} while (++iter);
	CleanupStack::PopAndDestroy();
	}

void Validate::KeyL(const CDbKey& aKey,const HDbColumnSet& aColumns)
//
// Check that the key is valid for the table
//
	{
	TInt max=aKey.Count()-1;
	if (max<0)
		__LEAVE(KErrArgument);
	for (TInt ii=0;ii<=max;++ii)
		{
		const TDbKeyCol& kCol=aKey[ii];
		HDbColumnSet::TIteratorC col=aColumns.ColumnL(kCol.iName);
		if (col==NULL)
			__LEAVE(KErrNotFound);
		TInt len=kCol.iLength;
		if (len!=KDbUndefinedLength)
			{
			if (col->iType<=EDbColDateTime)
				__LEAVE(KErrArgument);
			TInt cLen=col->iMaxLength;
			if (ii<max)
				{
				if (len!=cLen)
					__LEAVE(KErrNotSupported);
				continue;
				}
			if (len<=0)
				__LEAVE(KErrArgument);
			else if (cLen!=KDbUndefinedLength && cLen<len)
				__LEAVE(KErrArgument);
			}
		}
	}

// Class RDbAccessPlan

TUint RDbAccessPlan::FindMatchL(const CDbTableIndexDef* aIndex)
//
// Checks if index best matches the order specified
//
	{
	CDbKey& order=iQuery->SortSpecification();
	TInt count=order.Count();
    const TDbColumnDef* columnDef = iTable->Def().Columns().ColumnL(order[count-1].iName);
    __ASSERT(columnDef != NULL);
    TInt columnLength = columnDef->iMaxLength;
	const CDbKey& key=aIndex->Key();
	TUint ret=0;
	if (TextKeyL(order) && order.Comparison()!=key.Comparison())
		return ret;
	TInt kCount=key.Count();
	for (TInt ii=0,rev=0;;)
		{
		const TDbKeyCol& kcol=key[ii];
		const TDbKeyCol& ocol=order[ii];
		if (kcol.iName.CompareF(ocol.iName)!=0)
			break;
		TInt revcol=kcol.iOrder^ocol.iOrder;
		if (ii==0)
			rev=revcol;
		else if (rev!=revcol)
			break;
		if (++ii==count)	// end of order key	
			{
			ret|=EMatch;
			if (kcol.iLength!=columnLength)
				ret|=ETruncated;
			if (rev)
				ret|=EReverse;
			return ret;
			}
		if (ii==kCount)		// end of index key
			{
			ret|=EMatch;
			if (key.IsUnique())
				{			// will provide the right order by, use it
				if (rev)
					ret|=EReverse;
			return ret;
				}
			break;
			}
		}
	return ret;
	}

TBool RDbAccessPlan::TextKeyL(const CDbKey& anOrder)
//
// return whether any of the keys are text columns
//
	{
	const HDbColumnSet& cols=iTable->Def().Columns();
	TInt count=anOrder.Count();
	for (TInt ii=0;ii<count;++ii)
		{
        const TDbColumnDef* columnDef = cols.ColumnL(anOrder[ii].iName);
        __ASSERT(columnDef != NULL);

        switch (columnDef->Type())
			{
		case EDbColText8:
		case EDbColText16:
		case EDbColLongText8:
		case EDbColLongText16:
			return ETrue;
		default:
			break;
			}
		}
	return EFalse;
	}

CDbTableSource* RDbAccessPlan::TableLC(CDbTableDatabase& aDatabase,const TDesC& aTable)
	{
	__ASSERT(!iSource);
	CDbTableSource* source=aDatabase.TableSourceL(aTable);
	iSource=source;
	iTable=&source->Table();
	CleanupStack::PushL(TCleanupItem(Cleanup,this));
	return source;
	}

void RDbAccessPlan::Insert(CDbDataStage* aStage)
	{
	__ASSERT(iSource);
	aStage->SetSource(iSource);
	iSource=aStage;
	}

void RDbAccessPlan::Cleanup(TAny* aPtr)
	{
	RDbAccessPlan& self=*STATIC_CAST(RDbAccessPlan*,aPtr);
	self.iPlans.Close();
	delete self.iSource;
	}

CDbRecordIter* RDbAccessPlan::IteratorL(const TPlan& aPlan)
//
// Returns the right iterator
//
	{
	if (aPlan.iFlags&TPlan::EIndex)
		return iTable->IteratorL(*aPlan.iIndex);
	if (aPlan.iFlags&TPlan::EBounded)
		return BoundedIteratorL(aPlan);
	return iTable->IteratorL();
	}

CDbRecordIter* RDbAccessPlan::BoundedIteratorL(const TPlan& aPlan)
	{
	TBounds bounds(aPlan);
	CDbRecordIter* iter=iTable->IteratorL(*aPlan.iIndex,bounds.iInclusion,bounds.iLower,bounds.iUpper);
	if (aPlan.iLower)
		iQuery->RemovePredicate(aPlan.iLower);
	if (aPlan.iUpper && aPlan.iLower!=aPlan.iUpper)
		iQuery->RemovePredicate(aPlan.iUpper);
	return iter;
	}

void RDbAccessPlan::RestrictionL()
	{
	CDbRestrictStage* restriction=new(ELeave) CDbRestrictStage(iComparison);
	Insert(restriction);
	CSqlSearchCondition* searchcondition=iQuery->AdoptSearchCondition();
	restriction->SetRestriction(searchcondition);
	}

void RDbAccessPlan::OrderByL(const RDbTableRow& aRowBuf)
	{
	CDbOrderByStage* ordering=new(ELeave) CDbOrderByStage(aRowBuf);
	Insert(ordering);
	ordering->ConstructL(iQuery->SortSpecification());
	}

void RDbAccessPlan::ProjectionL()
	{
	CDbProjectStage* projection=new(ELeave) CDbProjectStage;
	Insert(projection);
	projection->ConstructL(iQuery->ColumnList(),iTable->Def().Columns());
	}

void RDbAccessPlan::WindowL(const TPlan& aPlan,const TDbWindow& aWindow)
	{
	if (aPlan.iFlags&TPlan::EWindow)
		Insert(new(ELeave) CDbWindowStage(KDbUnlimitedWindow));
	else if (aWindow.Size()!=aWindow.ENone && aWindow.Size()!=aWindow.EUnlimited)
		Insert(new(ELeave) CDbWindowStage(aWindow));
	}

TBool RDbAccessPlan::IsIndexIteratorL(TPlan& aPlan,const CDbTableIndexDef* aIndex)
//
// If an index iterator can be used, sets aPlan, else returns EFalse
//
	{
	if (!iQuery->HasSortSpecification())
		return EFalse;
	TUint ret=FindMatchL(aIndex);
	if ((ret&EMatch)==0)
		return EFalse;
	// can use index iterator
	aPlan.iFlags&=~TPlan::EOrder;
	if (ret&EReverse)
		aPlan.iFlags|=TPlan::EReverse;
	aPlan.iFlags|=TPlan::EIndex;
	aPlan.iIndex=aIndex;
	return ETrue;
	}

TBool RDbAccessPlan::IsBoundedIteratorL(TPlan& aPlan,const CDbTableIndexDef* aIndex)
//
// If a bounded iterator can be used, sets aPlan, else returns EFalse
//
	{
	if (!iQuery->HasSearchCondition())
		return EFalse;
	TDbColNo keyColNo=iTable->Def().Columns().ColNoL(aIndex->Key()[0].iName);
	CDbCompPredicateList* list=CDbCompPredicateList::NewLC(*iQuery,iComparison,iTable->Def());
	CSqlCompPredicate* cp=list->CompPredicate(keyColNo,CDbCompPredicateList::EEqual); 
	if (cp==0 && (cp=list->CompPredicate(keyColNo))==0)								   
		{
		CleanupStack::PopAndDestroy(); // list
		return EFalse; 
		}
	// boundaries
	TUint type=list->Type(cp->NodeType());
	aPlan.iLower=type&(CDbCompPredicateList::ELess|CDbCompPredicateList::ELessEqual)?0:cp;
	aPlan.iUpper=type&(CDbCompPredicateList::EGreater|CDbCompPredicateList::EGreaterEqual)?0:cp;
	// find other boundary, if present
	if (list->Count()!=0 && cp->NodeType()!=CSqlSearchCondition::EEqual)	
		{
		TUint nextType=type&(CDbCompPredicateList::ELess|CDbCompPredicateList::ELessEqual) ? 
			CDbCompPredicateList::EGreater|CDbCompPredicateList::EGreaterEqual : 
			CDbCompPredicateList::ELess|CDbCompPredicateList::ELessEqual;
		CSqlCompPredicate* cp2=list->CompPredicate(keyColNo,nextType);
		if (cp2)
			{
			if (nextType&(CDbCompPredicateList::ELess|CDbCompPredicateList::ELessEqual))
				aPlan.iUpper=cp2;
			else
				aPlan.iLower=cp2;
			}
		}	
	// check which order the index is in and reverse if descending
	const TDbKeyCol& key=aIndex->Key()[0];
	if ((key.iOrder==TDbKeyCol::EDesc && !aPlan.iUpper) || (key.iOrder==TDbKeyCol::EAsc && !aPlan.iLower))
		aPlan.iFlags|=TPlan::EReverse;				// optimise the bounding for forwards iteration
	if (!list->IsRestriction() && list->Count()==0)
		aPlan.iFlags&=~TPlan::ERestrict;
	CleanupStack::PopAndDestroy();			// list
	aPlan.iFlags|=TPlan::EBounded;
	aPlan.iIndex=aIndex;
	return ETrue;
	}

void RDbAccessPlan::EvaluatePlansL()
//
// try to find a bounded or index iterator
//
	{
	TPlan plan;
	// initialise flags
	if (iQuery->HasSearchCondition())
		plan.iFlags|=TPlan::ERestrict;
	if (iQuery->HasSortSpecification())
		plan.iFlags|=TPlan::EOrder;
	// find the right type of iterator
	TSglQueIterC<CDbTableIndexDef> indexes(iTable->Def().Indexes().AsQue());
	for (const CDbTableIndexDef* index;(index=indexes++)!=0;)
		{ // only on first column
		TPlan newPlan=plan;
		TBool foundIter=IsBoundedIteratorL(newPlan,index);
		if (!foundIter)
			foundIter=IsIndexIteratorL(newPlan,index);
		else
			EvaluateReorderStage(newPlan,index);
		if (foundIter)
			{
			EvaluateWindowStage(newPlan);
			__LEAVE_IF_ERROR(iPlans.Append(newPlan));
			}
		}
	}

void RDbAccessPlan::ChoosePlanL(TPlan& aPlan)
	{
	ReducePlans();
	if (iPlans.Count()==0)
		{
		CreateTableIteratorPlan(aPlan);
		return;
		}
	TPlan::TType type=iPlans[0].Type();
	if (!iQuery->HasSearchCondition())						
		{													
		__ASSERT(type==TPlan::EPlanEF);
		GetSmallestKeySize(aPlan,TPlan::EPlanEF);
		}
	else if (!iQuery->HasSortSpecification())					
		{													
		if (type==TPlan::EPlanIJ)								
			{	
			GetSmallestKeySize(aPlan,TPlan::EPlanIJ);
			TInt r=IndexSpanL(aPlan);
			if (r!=CDbTable::EUnavailableSpan && !iWindow && r>60)
				CreateTableIteratorPlan(aPlan);
			}
		else if (type==TPlan::EPlanOP)					
			{
			TInt r=GetTightestRestrictionL(aPlan,TPlan::EPlanOP);
			if (r==CDbTable::EUnavailableSpan)	// no index stats available
				aPlan=iPlans[0];				// use first O/P as a guess
			else if ((!iWindow && r>55) || (iWindow && r>60))
				CreateTableIteratorPlan(aPlan);
			}
		else
			__ASSERT(0);
		}
	else if (type==TPlan::EPlanMN) 
		{
		GetSmallestKeySize(aPlan,TPlan::EPlanMN);
		if (iAccess==RDbRowSet::EUpdatable || iWindow)
			aPlan.iFlags|=TPlan::EWindow;
		}
	else if (type==TPlan::EPlanKL) 
		{	
		GetSmallestKeySize(aPlan,TPlan::EPlanKL);
		TInt r=IndexSpanL(aPlan);
		if (r!=CDbTable::EUnavailableSpan && r>60)
			{	// don't use K plan
			if (r<75 || GetSmallestKeySize(aPlan,TPlan::EPlanGH)==KErrNotFound)
				CreateTableIteratorPlan(aPlan);	
			}
		}
	else if (type==TPlan::EPlanQR) 
		{
		TInt r=GetTightestRestrictionL(aPlan,TPlan::EPlanQR);
		if (r==CDbTable::EUnavailableSpan)			// no index stats available
			aPlan=iPlans[0];						// use first Q/R as a guess
		else if (r>60)
			CreateTableIteratorPlan(aPlan);	
		}
	else 
		{
		__ASSERT(type==TPlan::EPlanGH);
		// don't use this plan without further data, resort to default
		CreateTableIteratorPlan(aPlan);	
		}
	}

void RDbAccessPlan::EvaluateWindowStage(TPlan& aPlan)
	{
	if (!iWindow)
		return;
	TUint f=aPlan.iFlags|TPlan::EWindow;
	if (f&TPlan::EReorder)
		f&=~TPlan::EWindow;
	if (f&TPlan::ERestrict)
		f|=TPlan::EWindow; 
	if (f&TPlan::EOrder)						// order-by stage includes window
		f&=~TPlan::EWindow;
	aPlan.iFlags=f;
	}

TInt RDbAccessPlan::GetTightestRestrictionL(TPlan& aPlan,TPlan::TType aType)
//
// aPlan is set to the smallest restricted plan with aType
//
	{
	TInt span=KMaxTInt;
	TPlan plan;
	for (TInt ii=iPlans.Count();ii--;)
		{
		if (iPlans[ii].Type()!=aType)
			continue;
		TInt t=IndexSpanL(iPlans[ii]);
		if (t==CDbTable::EUnavailableSpan)
			continue;
		__ASSERT(t == span ?  (iPlans[ii].iIndex != NULL && plan.iIndex != NULL) : ETrue);
		//coverity[uninit_use_in_call]
		if (t<span || (t==span && iPlans[ii].iIndex->Key().Count()<plan.iIndex->Key().Count()))
			{
			span=t;
			plan=iPlans[ii];
			}
		}
	aPlan=plan;
	return span!=KMaxTInt?span:CDbTable::EUnavailableSpan;
	}

TInt RDbAccessPlan::GetSmallestKeySize(TPlan& aPlan,TPlan::TType aType)
//
// aPlan is set to the plan with smallest index with aType
//
	{
	TInt cols=KMaxTInt;
	TPlan plan;
	for (TInt ii=iPlans.Count();ii--;)
		{
		if (iPlans[ii].Type()!=aType)
			continue;
		__ASSERT(iPlans[ii].iIndex);
		TInt count=iPlans[ii].iIndex->Key().Count();
		if (count<cols)
			{
			cols=count;
			plan=iPlans[ii];
			}
		}
	aPlan=plan;
	return cols!=KMaxTInt?cols:KErrNotFound;
	}

TInt RDbAccessPlan::IndexSpanL(const TPlan& aPlan)
	{
	__ASSERT(aPlan.iIndex);
	TBounds bounds(aPlan);
	return iTable->IndexSpanL(*aPlan.iIndex,bounds.iInclusion,bounds.iLower,bounds.iUpper);
	}

void RDbAccessPlan::ReducePlans()
	{
	for (TInt ii=iPlans.Count();--ii>=0;)
		{
		switch (iPlans[ii].Type())
			{
		default:
			continue;
		case TPlan::EPlanGH:									
			if (iWindow)
				iPlans.Remove(ii);								// remove Gw/Hw 
			break;
		case TPlan::EPlanST:
			iPlans[ii].iFlags|=(TPlan::EReorder|TPlan::EOrder);	// convert S/T to Q/R
			EvaluateWindowStage(iPlans[ii]);
			break;
			};
		}
// explicit conversion required as GCC can't find the TLinearOrder instantiation
	iPlans.Sort(TLinearOrder<TPlan>(TPlan::OrderByPlan));
	}

void RDbAccessPlan::CreateTableIteratorPlan(TPlan& aPlan)
	{
	aPlan.iFlags&=~(TPlan::EIndex|TPlan::EBounded);
	if (iQuery->HasSearchCondition())
		aPlan.iFlags=TPlan::ERestrict;
	if (iQuery->HasSortSpecification())
		aPlan.iFlags|=TPlan::EOrder;
	EvaluateWindowStage(aPlan);
	}

void RDbAccessPlan::EvaluateReorderStage(TPlan& aPlan,const CDbTableIndexDef* aIndex)
//
// for a bounded iter with an order by - can the index be used?
//
	{
	aPlan.iFlags|=TPlan::EReorder;
	if (!iQuery->HasSortSpecification())
		return;
	TUint ret=0;
	TRAPD(errCode, ret=FindMatchL(aIndex));
	if (errCode==KErrNone && ret&EMatch)
		{// do we really need a reorder stage?
		aPlan.iFlags&=~TPlan::EOrder;			// don't need to order it
		aPlan.iFlags&=~TPlan::EReorder;			// don't need a reorder stage 
		if (ret&EReverse)			
			aPlan.iFlags|=TPlan::EReverse;
		else
			aPlan.iFlags&=~TPlan::EReverse;
		}
	}

void RDbAccessPlan::PrepareQueryL(CDbTableSource* aSource)
	{
	if (iQuery->HasSearchCondition())
		{ 
		CSqlSearchCondition& sc=iQuery->SearchCondition();
		sc.BindL(aSource->Row());
		}
	if (iQuery->HasSortSpecification())
		{
		CDbKey& order=iQuery->SortSpecification();
		order.SetComparison(iComparison);
		Validate::KeyL(order,iTable->Def().Columns());
		}
	}
	

void RDbAccessPlan::BuildLC(CDbTableDatabase& aDatabase,RDbRowSet::TAccess aAccess,const TDbWindow& aWindow)
//
// Prepare the data pipeline
//
	{
	__ASSERT(iQuery);
	CDbTableSource* source=TableLC(aDatabase,iQuery->Table());
	//
	if (aAccess!=RDbRowSet::EInsertOnly)
		{	// insert only views do not use Iterators, Restrictions or Windows
		iWindow=aWindow.Size()==aWindow.EUnlimited;
		iAccess=aAccess;
		PrepareQueryL(source);
		EvaluatePlansL();
		TPlan plan;
		ChoosePlanL(plan);
		source->SetIterator(IteratorL(plan));
		if (plan.iFlags&TPlan::EReorder)
			Insert(new(ELeave) CDbReorderWindowStage); 
		if (plan.iFlags&TPlan::EReverse)
			source->ReverseIteratorL();
		if (plan.iFlags&TPlan::ERestrict)
			RestrictionL();
		if (plan.iFlags&TPlan::EOrder)	
			OrderByL(source->Row());
		WindowL(plan,aWindow);
		iPlans.Reset();
		}
	if (iQuery->HasColumnList())
		ProjectionL();
	}

void RDbAccessPlan::BuildLC(CDbTableDatabase& aDatabase,const TDesC& aTable,RDbRowSet::TAccess aAccess)
	{
	CDbTableSource* source=TableLC(aDatabase,aTable);
	if (aAccess!=RDbRowSet::EInsertOnly)
		source->SetIterator(iTable->IteratorL());
	}