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