--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/dbms/pcdbms/utable/UT_QUERY.CPP Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,820 @@
+// 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 "U32STD_DBMS.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
+//
+ {
+ 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};
+ 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();
+ TInt columnLength=iTable->Def().Columns().ColumnL(order[count-1].iName)->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)
+ {
+ switch (cols.ColumnL(anOrder[ii].iName)->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;
+ 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());
+ }