--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/dbms/pcdbms/utable/UT_CURS.CPP Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,629 @@
+// 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"
+
+// Class CDbTableCursor::HColumns
+
+class CDbTableCursor::HColumns
+ {
+public:
+ static HColumns* NewL(const CDbDataSource* aSource);
+ inline TInt Count() const
+ {return iCount;}
+ inline TDbColType Type(TDbColNo aCol) const
+ {__DEBUG(Check(aCol));return TDbColType(iType[aCol-1]);}
+ void Check(TDbColNo aCol) const;
+private:
+ TInt iCount;
+ TUint8 iType[1];
+ };
+
+CDbTableCursor::HColumns* CDbTableCursor::HColumns::NewL(const CDbDataSource* aSource)
+ {
+ TInt count=aSource->ColumnCount();
+ HColumns* self=(HColumns*)User::AllocL(_FOFF(HColumns,iType[count]));
+ self->iCount=count;
+ TUint8* pp=&self->iType[0];
+ for (TDbColNo ii=1;ii<=count;++ii,++pp)
+ *pp=aSource->ColumnDef(ii).iType;
+ return self;
+ }
+
+void CDbTableCursor::HColumns::Check(TDbColNo aColNo) const
+ {
+ __ASSERT_ALWAYS(TUint(aColNo-1)<TUint(iCount),Panic(EDbInvalidColumn));
+ }
+
+// Class CDbTableCursor::CConstraint
+
+NONSHARABLE_CLASS(CDbTableCursor::CConstraint) : public CDbRowConstraint
+ {
+public:
+ CConstraint(CDbTableCursor& aCursor,CSqlSearchCondition* aSearchCondition,TDbTextComparison aComparison);
+ ~CConstraint();
+//
+ inline TBool Check(CDbTableCursor& aCursor) const;
+ inline TBool MatchL() const;
+private:
+ CDbTableCursor& iCursor;
+ CSqlSearchCondition* iSearchCondition;
+ const TTextOps& iTextOps;
+ };
+
+CDbTableCursor::CConstraint::CConstraint(CDbTableCursor& aCursor,CSqlSearchCondition* aSearchCondition,TDbTextComparison aComparison)
+ : iCursor(aCursor), iSearchCondition(aSearchCondition), iTextOps(TTextOps::Ops(aComparison))
+ {}
+
+CDbTableCursor::CConstraint::~CConstraint()
+ {
+ delete iSearchCondition;
+ }
+
+inline TBool CDbTableCursor::CConstraint::Check(CDbTableCursor& aCursor) const
+ {return &iCursor==&aCursor;}
+inline TBool CDbTableCursor::CConstraint::MatchL() const
+ {return iSearchCondition->EvaluateL(iTextOps);}
+
+// Class CDbTableCursor
+
+inline void CDbTableCursor::CheckStateL() const
+ {iValid.CheckL();}
+inline RDbTransaction& CDbTableCursor::Transaction()
+ {__ASSERT(iValid);return iValid.Transaction();}
+inline TBool CDbTableCursor::InUpdate() const
+ {return iFlags&(EUpdating|EInserting);}
+
+CDbTableCursor::CDbTableCursor(RDbAccessPlan& aPlan,RDbRowSet::TAccess aAccess)
+ : iState(ERowBeginning),iValid(aPlan.Table()),iDataSource(aPlan.Adopt())
+ {
+ switch (aAccess)
+ {
+ default:
+ __ASSERT(0);
+ case RDbRowSet::EUpdatable:
+ iFlags=EUpdatable|EReadable;
+ break;
+ case RDbRowSet::EReadOnly:
+ iFlags=EReadable;
+ break;
+ case RDbRowSet::EInsertOnly:
+ iFlags=EUpdatable;
+ break;
+ }
+ }
+
+CDbTableCursor::~CDbTableCursor()
+ {
+ Cancel();
+ delete iDataSource;
+ delete iColumns;
+ }
+
+CDbTableCursor* CDbTableCursor::NewL(RDbAccessPlan& aPlan,RDbRowSet::TAccess aAccess)
+ {
+ CDbTableCursor* self=new(ELeave) CDbTableCursor(aPlan,aAccess);
+ CleanupStack::PushL(self);
+ self->iColumns=HColumns::NewL(self->iDataSource);
+ self->Reset();
+ CleanupStack::Pop();
+ return self;
+ }
+
+TDbColType CDbTableCursor::Type(TDbColNo aCol) const
+ {
+ iColumns->Check(aCol);
+ return iColumns->Type(aCol);
+ }
+
+void CDbTableCursor::Reset()
+//
+// Reset the cursor for re-evaluation
+//
+ {
+ AssertNotInUpdate();
+ if (iValid.Reset())
+ {
+ iDataSource->Reset();
+ iState=ERowBeginning;
+ }
+ }
+
+TBool CDbTableCursor::EvaluateL()
+//
+// Do a unit of evaluation work
+//
+ {
+ AssertNotInUpdate();
+ CheckStateL();
+ TInt work=256;
+ TBool atRow=EFalse;
+ TBool more=iDataSource->EvaluateL(work,iRecord,atRow);
+ if (atRow)
+ { // evaluation results in a record appearing under the cursor
+ switch (iState)
+ {
+ case ERowEnd:
+ case ERowBeginning:
+ iState=ERowOK;
+ break;
+ case ERowDeletedAtEnd:
+ iState=ERowDeletedAtNext;
+ break;
+ default:
+ break;
+ }
+ }
+ return more?1:0;
+ }
+
+//void CDbTableCursor::Evaluate(TRequestStatus& aStatus)
+////
+//// Asynchronous evaluation: invoke synchronous version
+////
+// {
+// TRequestStatus* pStatus=&aStatus;
+// User::RequestComplete(pStatus,CDbCursor::Evaluate());
+// }
+
+TBool CDbTableCursor::Unevaluated()
+//
+// Report if there is evaluation to be done
+//
+ {
+ return iValid ? iDataSource->Unevaluated() : EFalse;
+ }
+
+TInt CDbTableCursor::CountL(RDbRowSet::TAccuracy aAccuracy)
+ {
+ AssertNotInUpdate();
+ CheckReadL();
+ TInt count=iDataSource->CountL();
+ return (count==KDbUndefinedCount && aAccuracy==RDbRowSet::EEnsure)
+ ? CDbCursor::CountL(aAccuracy)
+ : count;
+ }
+
+TBool CDbTableCursor::AtBeginning()
+ {
+ return iState==ERowBeginning;
+ }
+
+TBool CDbTableCursor::AtEnd()
+ {
+ return iState==ERowEnd;
+ }
+
+TBool CDbTableCursor::AtRow()
+ {
+ return (iState==ERowOK||(iFlags&EInserting));
+ }
+
+TBool CDbTableCursor::GotoL(RDbRowSet::TPosition aPosition)
+//
+// Move the cursor in the requested direction
+// return whether we are at a row or not
+//
+ {
+ AssertNotInUpdate();
+ CheckReadL();
+ iFlags&=~ERead;
+ switch (aPosition)
+ {
+ default:
+ __ASSERT(0);
+ case RDbRowSet::EFirst:
+ case RDbRowSet::ELast:
+ break;
+ case RDbRowSet::ENext:
+ switch (iState)
+ {
+ default:
+ __ASSERT(0);
+ case ERowInLimbo: // in between previous and next, must evaluate
+ case ERowOK:
+ break;
+ case ERowBeginning: // goto first record
+ aPosition=RDbRowSet::EFirst;
+ break;
+ case ERowEnd:
+ case ERowInvalid:
+ Panic(EDbInvalidRow);
+ break;
+ case ERowDeletedAtNext: // already have the id
+ if (iDataSource->GotoL(iRecord))
+ { // and the record is still there
+ iState=ERowOK;
+ return ETrue;
+ }
+ break;
+ case ERowDeletedAtEnd: // straight to end
+ iState=ERowEnd;
+ return EFalse;
+ }
+ break;
+ case RDbRowSet::EPrevious:
+ switch (iState)
+ {
+ default:
+ __ASSERT(0);
+ case ERowOK:
+ case ERowDeletedAtNext: // goto previous will do what we want
+ case ERowInLimbo: // in between previous and next, must evaluate
+ break;
+ case ERowEnd: // goto last row
+ case ERowDeletedAtEnd: // previous is last row
+ aPosition=RDbRowSet::ELast;
+ break;
+ case ERowBeginning:
+ case ERowInvalid:
+ Panic(EDbInvalidRow);
+ break;
+ }
+ break;
+ case RDbRowSet::EBeginning:
+ iState=ERowBeginning;
+ return EFalse;
+ case RDbRowSet::EEnd:
+ iState=ERowEnd;
+ return EFalse;
+ }
+ iState=ERowInvalid;
+ switch (iDataSource->GotoL(TDbPosition(aPosition),iRecord))
+ {
+ default:
+ __ASSERT(0);
+ case CDbDataSource::ESynchFailure:
+ __LEAVE(KErrNotReady);
+ case CDbDataSource::ESuccess:
+ iState=ERowOK;
+ return ETrue;
+ case CDbDataSource::ENoRow:
+ iState=TUint8(aPosition<RDbRowSet::EPrevious ? ERowEnd : ERowBeginning);
+ return EFalse;
+ }
+ }
+
+void CDbTableCursor::Bookmark(TDbBookmark::TMark& aMark)
+//
+// Create a bookmark for the current cursor state
+// Can bookmark ANY position.
+//
+ {
+ AssertNotInUpdate();
+ aMark.iMark[0]=iState;
+ aMark.iMark[1]=iRecord.Value();
+ }
+
+void CDbTableCursor::GotoL(const TDbBookmark::TMark& aMark)
+//
+// Reestablish the cursor state from a bookmark (if possible)
+//
+ {
+ AssertNotInUpdate();
+ CheckStateL();
+ iState=ERowInvalid;
+ iRecord=aMark.iMark[1];
+ TState state=TState(aMark.iMark[0]);
+ switch (state)
+ {
+ default:
+ Panic(EDbInvalidBookmark);
+ case ERowBeginning:
+ case ERowEnd:
+ case ERowDeletedAtEnd:
+ case ERowInLimbo:
+ case ERowInvalid:
+ break;
+ case ERowDeletedAtNext:
+ case ERowOK:
+ if (!iDataSource->GotoL(iRecord))
+ __LEAVE(KErrNotFound);
+ break;
+ }
+ iState=TUint8(state);
+ }
+
+void CDbTableCursor::GetL()
+//
+// read the current row into the row buffer for access
+//
+ {
+ AssertValidRow();
+ CheckStateL();
+ iFlags&=~ERead;
+ iDataSource->ReadRowL(iRecord);
+ iFlags|=ERead;
+ }
+
+void CDbTableCursor::InsertL(TInsert aClearRow)
+//
+// Insert a new row. If aCLearRow==aCopy, then copy the current row
+//
+ {
+ AssertNotInUpdate();
+ CheckUpdateL();
+ Transaction().DMLPrepareL(*this);
+ if (aClearRow==ECopy)
+ {
+ AssertValidRow();
+ iFlags&=~ERead; // in case of failure in NewRowL
+ iDataSource->NewRowL(iRecord);
+ }
+ else
+ iDataSource->NewRowL(KDbNullRecordId);
+ iFlags|=EInserting|ERead;
+ Transaction().DMLBegin();
+ }
+
+void CDbTableCursor::UpdateL()
+ {
+ CheckUpdateL();
+ Transaction().DMLPrepareL(*this);
+ GetL();
+ iFlags|=EUpdating;
+ Transaction().DMLBegin();
+ }
+
+void CDbTableCursor::Cancel()
+ {
+ AssertNoStreams();
+ if (InUpdate())
+ {
+ RDbTransaction& t=Transaction();
+ if (iFlags&EDirty)
+ t.DMLTouch(); // we've mucked about with BLOBs, so force true roll-back
+ t.DMLRollback();
+ if (iFlags&EUpdating)
+ iDataSource->ReadRowL(KDbNullRecordId); // row buffer contains NULL row (cannot fail)
+ iFlags&=(EUpdatable|EReadable);
+ }
+ }
+
+void CDbTableCursor::PutL()
+ {
+ AssertInUpdate();
+ CheckStateL();
+ CDbDataSource::TWrite mode=iFlags&EUpdating ? CDbDataSource::EReplace : CDbDataSource::EAppend;
+ iDataSource->PrepareToWriteRowL(mode);
+ RDbTransaction& t=Transaction();
+ t.DMLTouch();
+ iFlags&=~EDirty;
+ iRecord=iDataSource->WriteRowL(mode,iFlags&EReadable ? CDbDataSource::ESynch : CDbDataSource::ENoSynch);
+ t.DMLCommitL();
+ if ((iFlags&(EInserting|EReadable))==(EInserting|EReadable))
+ iState=ERowOK;
+ iFlags&=(EUpdatable|EReadable|ERead);
+ }
+
+void CDbTableCursor::DeleteL()
+ {
+ AssertValidRow();
+ CheckUpdateL();
+ RDbTransaction& t=Transaction();
+ t.DMLPrepareL(*this);
+ t.DMLBeginLC();
+ CDbDataSource::TDelete del=iDataSource->DeleteRowL(iRecord);
+ t.DMLCommitL();
+ CleanupStack::Pop(); // rollback not required
+ iState=TUint8(del+ERowDeletedAtNext);
+ }
+
+TInt CDbTableCursor::ColumnCount()
+ {
+ return iColumns->Count();
+ }
+
+void CDbTableCursor::ColumnDef(TDbCol& aCol,TDbColNo aColNo)
+ {
+ iColumns->Check(aColNo);
+ if (iValid)
+ iDataSource->ColumnDef(aColNo).AsTDbCol(aCol);
+ }
+
+TDbColType CDbTableCursor::ColumnType(TDbColNo aCol)
+ {
+ return Type(aCol);
+ }
+
+void CDbTableCursor::ReplaceBlobL(TDbColumn& aCol)
+ {
+ CheckStateL();
+ const TDbBlob& blob=TDbColumnC(aCol).Blob();
+ if (!blob.IsInline())
+ {
+ iFlags|=EDirty;
+ BlobsL().DeleteL(blob.Id());
+ }
+ }
+
+void CDbTableCursor::AddBlobSource()
+//
+// Increment the source count and take a read-lock on the database
+//
+ {
+ AddSource();
+ Transaction().ReadBegin(*this);
+ }
+
+void CDbTableCursor::ReleaseBlobSource()
+//
+// Decrement the source count and release the read-lock on the database
+//
+ {
+ ReleaseSource();
+ Transaction().ReadRelease(*this);
+ }
+
+MStreamBuf* CDbTableCursor::ColumnSourceL(TDbColNo aCol)
+ {
+ TDbColumnC col(ColumnC(aCol));
+ if ((iFlags&EWriteBuf) || iReadBuf==EMaxReadBuf)
+ __LEAVE(KErrInUse); // only allow 1-write or 255-read streambufs
+ TDbColType type=iColumns->Type(aCol);
+ if (!TDbCol::IsLong(type))
+ return HMemBuf::NewL(*this,col.PtrC8());
+ // blobs
+ const TDbBlob& blob=col.Blob();
+ if (blob.IsInline())
+ return HMemBuf::NewL(*this,blob.PtrC8());
+ // if small enough, pull the blob data through immediately and avoid locking the database
+ if (blob.Size()<=HHeapBuf::EMaxBlobBuffer)
+ return HHeapBuf::NewL(*this,blob,type);
+ //
+ CheckStateL();
+ Transaction().ReadPrepareL(*this);
+ HReadBuf* buf=HReadBuf::NewLC(*this);
+ buf->Set(BlobsL().ReadL(blob.Id(),type));
+ CleanupStack::Pop();
+ return buf;
+ }
+
+MStreamBuf* CDbTableCursor::ColumnSinkL(TDbColNo aCol)
+ {
+ TDbColType type=Type(aCol);
+ __ASSERT_ALWAYS(TDbCol::IsLong(type),Panic(EDbWrongType));
+ TDbColumn col=Column(aCol);
+ ReplaceBlobL(col);
+ iFlags|=EDirty;
+ return HWriteBuf::NewL(*this,col,type);
+ }
+
+void CDbTableCursor::SetNullL(TDbColNo aCol)
+//
+// Make the column Null
+//
+ {
+ TDbColumn col=Column(aCol);
+ if (TDbCol::IsLong(Type(aCol)))
+ ReplaceBlobL(col);
+ col.SetNull();
+ }
+
+TInt CDbTableCursor::ColumnSize(TDbColNo aCol)
+ {
+ TDbColumnC col(ColumnC(aCol));
+ return TDbCol::IsLong(Type(aCol)) ? col.Blob().Size() : col.Size();
+ }
+
+RDbRow* CDbTableCursor::RowBuffer()
+//
+// Invoked by the server for whole-row access where possible
+//
+ {
+ __ASSERT(iFlags&ERead);
+ return iDataSource->RowBuffer();
+ }
+
+TDbColumnC CDbTableCursor::ColumnC(TDbColNo aCol)
+//
+// check row is valid for extraction
+//
+ {
+ __ASSERT_ALWAYS(iFlags&ERead,Panic(EDbRowNotRead));
+ return iDataSource->Column(aCol);
+ }
+
+TDbColumn CDbTableCursor::Column(TDbColNo aCol)
+//
+// check row is valid for writing
+//
+ {
+ AssertInUpdate();
+ return iDataSource->Column(aCol);
+ }
+
+void CDbTableCursor::SetIndexL(const TDesC* anIndex)
+ {
+ AssertNotInUpdate();
+ CheckReadL();
+ iDataSource->SetIndexL(anIndex);
+ iState=ERowBeginning;
+ }
+
+TBool CDbTableCursor::SeekL(const TDbLookupKey& aKey,RDbTable::TComparison aComparison)
+ {
+ AssertNotInUpdate();
+ CheckReadL();
+ iFlags&=~ERead;
+ iState=ERowInvalid;
+ TBool atrow=iDataSource->SeekL(aKey,aComparison,iRecord);
+ if (atrow)
+ iState=ERowOK;
+ return atrow;
+ }
+
+CDbRowConstraint* CDbTableCursor::OpenConstraintL(const TDbQuery& aCriteria)
+//
+// Construct a constraint for this rowset
+//
+ {
+ CSqlSearchCondition* sc=iDataSource->ParseConstraintLC(aCriteria.Query());
+ CDbRowConstraint* constraint=new(ELeave) CConstraint(*this,sc,aCriteria.Comparison());
+ CleanupStack::Pop();
+ return constraint;
+ }
+
+TBool CDbTableCursor::MatchL(CDbRowConstraint& aConstraint)
+ {
+ CConstraint& c=STATIC_CAST(CConstraint&,aConstraint);
+ __ASSERT_ALWAYS(c.Check(*this),Panic(EDbRowSetConstraintMismatch));
+ GetL();
+ return c.MatchL();
+ }
+
+void CDbTableCursor::CheckReadL() const
+//
+// Ensure we are a readable cursor
+//
+ {
+ CheckStateL();
+ if ((iFlags&EReadable)==0)
+ __LEAVE(KErrWrite);
+ }
+
+void CDbTableCursor::CheckUpdateL() const
+//
+// Ensure we are a writable cursor
+//
+ {
+ CheckStateL();
+ if ((iFlags&EUpdatable)==0)
+ __LEAVE(KErrWrite);
+ }
+
+void CDbTableCursor::AssertNoStreams() const
+ {
+ __ASSERT_ALWAYS((iFlags&EWriteBuf)==0 && iReadBuf==0,Panic(EDbStreamOpen));
+ }
+
+void CDbTableCursor::AssertNotInUpdate() const
+ {
+ __ASSERT_ALWAYS(!InUpdate(),Panic(EDbInUpdate));
+ AssertNoStreams();
+ }
+
+void CDbTableCursor::AssertInUpdate() const
+ {
+ __ASSERT_ALWAYS(InUpdate(),Panic(EDbNotInUpdate));
+ AssertNoStreams();
+ }
+
+void CDbTableCursor::AssertValidRow() const
+ {
+ AssertNotInUpdate();
+ __ASSERT_ALWAYS(iState==ERowOK||(iFlags&EInserting),Panic(EDbInvalidRow));
+ }
+