diff -r 000000000000 -r 08ec8eefde2f persistentstorage/dbms/utable/UT_TABLE.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/dbms/utable/UT_TABLE.CPP Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,672 @@ +// 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" + +#define UNUSED_VAR(a) a = a + +const TUint KTableExpiry=0x100000; // ~1.0s + +// Class Blob cleanup + +NONSHARABLE_CLASS(CDbBlobCleanup) : public CArrayFixFlat + { +public: + static CDbBlobCleanup* NewLC(CDbBlobSpace& aBlobSpace); + ~CDbBlobCleanup(); +private: + inline CDbBlobCleanup(CDbBlobSpace& aBlobSpace); +private: + CDbBlobSpace& iBlobSpace; + }; + +inline CDbBlobCleanup::CDbBlobCleanup(CDbBlobSpace& aBlobSpace) + : CArrayFixFlat(8),iBlobSpace(aBlobSpace) + {} + +CDbBlobCleanup* CDbBlobCleanup::NewLC(CDbBlobSpace& aBlobSpace) + { + CDbBlobCleanup* self=new(ELeave) CDbBlobCleanup(aBlobSpace); + CleanupStack::PushL(self); + return self; + } + +CDbBlobCleanup::~CDbBlobCleanup() + { + TInt count=Count(); + if (count) + { + const TDbBlobId* blob=&(*this)[0]; + const TDbBlobId* const end=blob+count; + for (;blobType())) + continue; + const TDbColumnC column(aRow,col); + if (column.IsNull()) + continue; + aFuncL(*blobs,CONST_CAST(TDbBlob&,column.Blob()),iter->Type(),aCleanup); + } while (++col,++iterExtendL(); + newId=KDbNullBlobId; + RWriteStream dup(aBlobStore.CreateL(newId,aType)); + dup.PushL(); + dup.WriteL(old,aBlob.Size()); + dup.CommitL(); + CleanupStack::PopAndDestroy(2); // old and dup streams + aBlob.SetId(newId); // row is writable + } + +void CDbTable::DuplicateBlobsL(RDbRow& aRow) +// +// duplicate any blobs +// + { + if (!Def().Columns().HasLongColumns()) + return; + + CDbBlobCleanup* cleaner=CDbBlobCleanup::NewLC(*BlobsL()); + ApplyToBlobsL(aRow,DuplicateBlobL,cleaner); + cleaner->Reset(); + CleanupStack::PopAndDestroy(); + } + +TBool CDbTable::ExistsL(TDbRecordId aRecordId) +// +// Check that aRecordId is good for this table +// + { + __ASSERT(IsActive() && InUse()); + return RecordsL().ExistsL(aRecordId); + } + +void CDbTable::NewRowL(RDbRow& aRow) +// +// Initialise any auto-increment columns in the row +// + { + const HDbColumnSet& columns=Def().Columns(); + if (!columns.HasAutoIncrement()) + return; + + TUint value=RecordsL().AutoIncrementL(); + TDbColNo col=1; + HDbColumnSet::TIteratorC iter=columns.Begin(); + const HDbColumnSet::TIteratorC end=columns.End(); + do + { + if (iter->iAttributes&TDbCol::EAutoIncrement) + { + // auto-increment only for integral types <=32 bits wide + __ASSERT(iter->iType<=EDbColUint32); + TDbColumn column(aRow,col); + column.SetL(TUint32(value)); + } + } while (++col,++iterNext()) + { + TInt size=column->Length(); + if (size==0) + { // check for Null + if (iter->iAttributes&TDbCol::ENotNull) + { + __LEAVE(KErrNotFound); + return; + } + continue; + } + const TUint32* data=(const TUint32*)column->Data(); + switch (iter->iType) + { + case EDbColBit: + if (*data>1) + __LEAVE(KErrOverflow); + break; + case EDbColInt8: + { + TInt val=*data; + if (TInt8(val)!=val) + __LEAVE(KErrOverflow); + } + break; + case EDbColInt16: + { + TInt val=*data; + if (TInt16(val)!=val) + __LEAVE(KErrOverflow); + } + break; + case EDbColUint8: + { + TUint val=*data; + if (TUint8(val)!=val) + __LEAVE(KErrOverflow); + } + break; + case EDbColUint16: + { + TUint val=*data; + if (TUint16(val)!=val) + __LEAVE(KErrOverflow); + } + break; + case EDbColText16: + size>>=1; + case EDbColBinary: + case EDbColText8: + if (iter->iMaxLength==KDbUndefinedLength) + break; + if (size>iter->iMaxLength) + __LEAVE(KErrOverflow); + break; + case EDbColLongBinary: + case EDbColLongText8: + case EDbColLongText16: + if (iter->iMaxLength==KDbUndefinedLength) + break; + size=((TDbBlob*)data)->Size(); + if (size==KDbUndefinedLength) + break; + if (iter->iType==EDbColText16) + size>>=1; + if (size>iter->iMaxLength) + __LEAVE(KErrOverflow); + break; + default: + break; + } + } + for (;iteriAttributes&TDbCol::ENotNull) + { + __LEAVE(KErrNotFound); + return; + } + } + } + +void CDbTable::ReadRowL(RDbRow& aRow,TDbRecordId aRecordId) +// +// Read a record from the table +// + { + CopyToRowL(aRow,RecordsL().ReadL(aRecordId)); + } + +void CDbTable::PrepareAppendL(const RDbTableRow& aRow) +// +// Validate a new record for appending +// + { + EnsureIndexesL(); + ValidateL(aRow); + CDbRecordIndex** end=iIndexesEnd; + for (CDbRecordIndex** pix=iIndexes;pix=end;) + { + update<<=1; + CDbRecordIndex& ix=**pix; + if (ix.IsBroken()) + continue; + switch (ix.FindL(aRecordId,aRow)) + { + case CDbRecordIndex::ENoMatch: // key has changed in index + update|=1; + break; + case CDbRecordIndex::EKeyMatch: // duplicate found + __LEAVE(KErrAlreadyExists); + case CDbRecordIndex::EEntryMatch: // no change in index + break; + } + } + iUpdateMap=update; + } + +void CDbTable::DoReplaceRowL(const RDbRow& aRow,TDbRecordId aRecordId) + { + CopyFromRow(RecordsL().ReplaceL(aRecordId,RecordLength(aRow)),aRow); + ++iGeneration; + } + +void CDbTable::ReplaceRowL(RDbTableRow& aRow,TDbRecordId aRecordId) +// +// Replace a record in the table +// + { + if (Def().Columns().HasLongColumns()) + CheckInliningL(aRow); + TUint32 update=iUpdateMap; + if (update==0) + { + DoReplaceRowL(aRow,aRecordId); + return; + } + RDbTableRow oldRow; // temporary row buffer for old row values + oldRow.Open(this); + oldRow.PushL(); // cleanup buffer if there is trouble + ReadRowL(oldRow,aRecordId); + DoReplaceRowL(aRow,aRecordId); + for (CDbRecordIndex** pix=iIndexes;update;++pix,update>>=1) + { + if (update&1) + { + CDbRecordIndex& index=**pix; + index.DeleteL(aRecordId,oldRow); + __DEBUG(TInt dbgchk=) index.InsertL(aRecordId,aRow); + __ASSERT(dbgchk); + } + } + CleanupStack::PopAndDestroy(); // temp row buffer + } + +LOCAL_C void CheckInlineL(CDbBlobSpace& aBlobStore,TDbBlob& aBlob,TDbColType aType,CDbBlobCleanup*) + { + if (!aBlob.IsInline()) + return; + if (aBlob.Size()>aBlobStore.InlineLimit()) + aBlob.SetId(aBlobStore.CreateL(aType,aBlob.Data(),aBlob.Size())); + } + +void CDbTable::CheckInliningL(RDbRow& aRow) +// +// Ensure that all Blobs are within the current inline limit +// + { + ApplyToBlobsL(aRow,CheckInlineL); + } + +LOCAL_C void DiscardBlobL(CDbBlobSpace& aBlobStore,TDbBlob& aBlob,TDbColType,CDbBlobCleanup*) + { + if (!aBlob.IsInline()) + aBlobStore.DeleteL(aBlob.Id()); + } + +EXPORT_C void CDbTable::DiscardBlobsL(RDbRow& aRow) +// +// Default implemtation xlates the record and then walks the row buffer +// + { + ApplyToBlobsL(aRow,DiscardBlobL); + } + +void CDbTable::DeleteRowL(RDbTableRow& aRow,TDbRecordId aRecordId) +// +// Delete the record from the file and unlock it. +// + { + EnsureIndexesL(); + + if (Def().Columns().HasLongColumns()) + { + // Read data from the stream but do not delete the stream yet. + aRow.ReadL(aRecordId); + } + + CDbRecordIndex** end=iIndexes; + CDbRecordIndex** pix=iIndexesEnd; + if (pix!=end) + aRow.ReadL(aRecordId); + RecordsL().EraseL(aRecordId); + while (--pix>=end) + { + CDbRecordIndex& ix=**pix; + if (!ix.IsBroken()) + ix.DeleteL(aRecordId,aRow); + } + + if (Def().Columns().HasLongColumns()) + { + // Now delete the stream. + DiscardBlobsL(aRow); + } + + ++iGeneration; + } + +EXPORT_C CDbRecordSpace& CDbTable::RecordsL() + { + __ASSERT(IsActive() && InUse()); + CDbRecordSpace* rec=iRecords; + if (rec==NULL) + iRecords=rec=RecordSpaceL(); + if (rec->OpenL()) + __LEAVE(KErrCorrupt); + return *rec; + } + +EXPORT_C CDbBlobSpace* CDbTable::BlobsL() + { + __ASSERT(IsActive() && InUse()); + CDbBlobSpace* blob=iBlobs; + if (blob==NULL) + iBlobs=blob=BlobSpaceL(); + if (blob->OpenL()) + __LEAVE(KErrCorrupt); + return blob; + } + +EXPORT_C CDbRecordIndex& CDbTable::IndexL(const CDbTableIndexDef& aIndex) +// +// Load the index associated with the index definition and ensure it is operational +// + { + __ASSERT(IsActive() && InUse()); + // find the matching slot in the indexes array + CDbRecordIndex** slot=&iIndexes[0]; + for (TSglQueIterC iter(Def().Indexes().AsQue());iter++!=&aIndex;) + ++slot; + __ASSERT(iIndexesEnd==NULL||(slot>=iIndexes&&slotOpenL()) + __LEAVE(KErrCorrupt); + return *index; + } + +void CDbTable::EnsureIndexesL() +// +// Ensure that all indexes are open +// + { + __ASSERT(IsActive() && InUse()); + if (iIndexesEnd==NULL) + { + CDbRecordIndex** pp=iIndexes; + TSglQueIterC iter(Def().Indexes().AsQue()); + for (const CDbTableIndexDef* xDef;(xDef=iter++)!=NULL;++pp) + { + CDbRecordIndex* ix=*pp; + if (ix==NULL) + *pp=ix=RecordIndexL(*xDef); + ix->OpenL(); // ignore broken-ness + } + iIndexesEnd=pp; + } + } + +CDbRecordIter* CDbTable::IteratorL() + { + return RecordsL().IteratorL(); + } + +CDbRecordIter* CDbTable::IteratorL(const CDbTableIndexDef& aIndex,TUint aInclusion,const TDbLookupKey* aLowerBound,const TDbLookupKey* aUpperBound) +// +// create an interator for the index parameter +// + { + return IndexL(aIndex).IteratorL(aInclusion,aLowerBound,aUpperBound); + } + +EXPORT_C TInt CDbTable::IndexSpanL(const CDbTableIndexDef&,TUint,const TDbLookupKey*,const TDbLookupKey*) +// +// Default implementation: no statistics are available +// + { + return EUnavailableSpan; + } + +CDbRecordIter* CDbTable::IteratorL(const TDesC& aIndex) +// +// create an interator for the index named +// + { + return IteratorL(Def().Indexes().FindL(aIndex)); + } + +void CDbTable::ApplyToComponentsL(void (*anOperationL)(CDbRecordBase*)) +// +// Invoke anOperation on all components of the table +// + { + if (iRecords) + anOperationL(iRecords); + if (iBlobs) + anOperationL(iBlobs); + CDbRecordIndex** const ixs=iIndexes; + CDbRecordIndex** pix=iIndexesEnd; + if (pix==NULL) + pix=&iIndexes[KDbTableMaxIndexes]; + while (--pix>=ixs) + if (*pix) + anOperationL(*pix); + } + +// Class CDbtable::TValid + +// this class is used by the cursor to check that it is still operational + +CDbTable::TValid::TValid(CDbTable& aTable) + :iTable(aTable) + { + __ASSERT(aTable.IsActive()); + iRollback.Construct(aTable.Database().Transaction().RollbackGeneration()); + } + +TBool CDbTable::TValid::Reset() + { + TBool b=Table().IsActive(); + if (b) + iRollback.Mark(); + return b; + } + +void CDbTable::TValid::CheckL() const + { + CDbTableDatabase* d=Table().iDatabase; + if (!d) + __LEAVE(KErrDisconnected); + else + d->Transaction().ReadyL(); + if (iRollback.Changed()) + __LEAVE(KErrNotReady); + }