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 "US_STD.H"
sl@0: #include "U32STD_DBMS.H"
sl@0: #include <d32dbmsconstants.h>
sl@0: 
sl@0: // class TRecordSize
sl@0: 
sl@0: TUint8 const TRecordSize::FieldSizes[]=
sl@0: 	{
sl@0: 	0,					// EDbColBit
sl@0: 	sizeof(TInt8),		// EDbColInt8
sl@0: 	sizeof(TUint8),		// EDbColUint8
sl@0: 	sizeof(TInt16),		// EDbColInt16
sl@0: 	sizeof(TUint16),	// EDbColUint16
sl@0: 	sizeof(TInt32),		// EDbColInt32
sl@0: 	sizeof(TUint32),	// EDbColUint32
sl@0: 	sizeof(TInt64),		// EDbColInt64
sl@0: 	sizeof(TReal32),	// EDbColReal32
sl@0: 	sizeof(TReal64),	// EDbColReal64
sl@0: 	sizeof(TTime)		// EDbColDateTime
sl@0: 	};
sl@0: 
sl@0: TBool TRecordSize::Set(const HDbColumnSet& aColumns)
sl@0: //
sl@0: // Calculate stats for the record size and shape from the definition
sl@0: //
sl@0: 	{
sl@0: 	TInt fix=0,null=0,var=0,blob=0;
sl@0: 	HDbColumnSet::TIteratorC const end=aColumns.End();
sl@0: 	HDbColumnSet::TIteratorC col=aColumns.Begin();
sl@0: 	do
sl@0: 		{
sl@0: 		TBool notnull=col->iAttributes&TDbCol::ENotNull;
sl@0: 		if (notnull==0)
sl@0: 			++fix;
sl@0: 		TDbColType type=col->Type();
sl@0: 		__ASSERT(type>=EDbColBit&&type<=EDbColLongBinary);
sl@0: 		if (type==EDbColBit)
sl@0: 			++fix;
sl@0: 		else if (type<=EDbColDateTime)
sl@0: 			{
sl@0: 			TInt bits=FixedFieldSize(type)<<3;
sl@0: 			if (notnull)
sl@0: 				fix+=bits;
sl@0: 			else
sl@0: 				null+=bits;
sl@0: 			}
sl@0: 		else if (type<=EDbColBinary)
sl@0: 			{
sl@0: 			TInt size=col->iMaxLength;
sl@0: 			if (type==EDbColText16)
sl@0: 				size<<=1;
sl@0: 			var+=8+(size<<3);
sl@0: 			}
sl@0: 		else
sl@0: 			++blob;
sl@0: 		} while(++col<end);
sl@0: //
sl@0: // assuming Blobs take at least 16 bytes + 1 bit
sl@0: 	TInt max=(fix+null+var+blob*(1+(KMinInlineLimit<<3))+7)>>3;
sl@0: 	if (max>KDbStoreMaxRecordLength)
sl@0: 		return ETrue;
sl@0: //
sl@0: // Assuming a Maximally full record, how much excess space is available for Blobs?
sl@0: //
sl@0: 	iInlineLimit=KDbMaxInlineBlobSize;
sl@0: 	if (blob)
sl@0: 		{	// use the spare space for extra inlining
sl@0: 		TInt spare=(KDbStoreMaxRecordLength-max);
sl@0: 		TInt inl=spare/blob+KMinInlineLimit-1;
sl@0: 		if (inl<KDbMaxInlineBlobSize)
sl@0: 			iInlineLimit=inl;
sl@0: 		}
sl@0: //
sl@0: // Calculate the average cluster size for a column set
sl@0: // This assumes that the nullable columns are present 50%, Variable average 1/16
sl@0: // Blobs achieve 16 bytes, or inline limit (if smaller)
sl@0: //
sl@0: 	TInt average=(fix+(null>>1)+(var>>4)+blob*(1+(16<<3))+7)>>3;
sl@0: 	TInt clustering=KClusterLimit/average;
sl@0: 	if (clustering==0)
sl@0: 		clustering=1;
sl@0: 	else if (clustering>KMaxClustering)
sl@0: 		clustering=KMaxClustering;
sl@0: 	iClustering=clustering;
sl@0: 	return EFalse;
sl@0: 	}
sl@0: 
sl@0: void TRecordSize::CheckSizeL(const HDbColumnSet& aColumns)
sl@0: //
sl@0: // Check that the columns definition is a valid size
sl@0: //
sl@0: 	{
sl@0: 	TRecordSize size;
sl@0: 	if (size.Set(aColumns))
sl@0: 		__LEAVE(KErrTooBig);
sl@0: 	}
sl@0: 
sl@0: TInt TRecordSize::InlineLimit(const HDbColumnSet& aColumns)
sl@0: //
sl@0: // Evaluate the inline limit for the column set. It is assumed to be valid
sl@0: //
sl@0: 	{
sl@0: 	TRecordSize size;
sl@0: 	__DEBUG(TBool chk =)size.Set(aColumns);
sl@0: 	__ASSERT(!chk);
sl@0: 	return size.InlineLimit();
sl@0: 	}
sl@0: 
sl@0: // Streaming column definitions
sl@0: 
sl@0: LOCAL_C void ExternalizeL(const TDbColumnDef& aCol,RWriteStream& aStream)
sl@0: 	{
sl@0: 	aStream<<*aCol.iName;
sl@0: 	aStream.WriteUint8L(aCol.iType);
sl@0: 	aStream.WriteUint8L(aCol.iAttributes);
sl@0: 	switch (aCol.iType)
sl@0: 		{
sl@0: 	case EDbColBinary:
sl@0: 	case EDbColText8:
sl@0: 	case EDbColText16:
sl@0: 		aStream.WriteUint8L(aCol.iMaxLength);
sl@0: 		break;
sl@0: 	default:
sl@0: 		break;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: LOCAL_C void InternalizeL(TDbColumnDef& aCol,RReadStream& aStream)
sl@0: 	{
sl@0: 	aCol.iName=HBufC::NewL(aStream,KDbMaxColName);
sl@0: 	TDbColType type=TDbColType(aStream.ReadUint8L());
sl@0: 	aCol.iType=TUint8(type);
sl@0: 	aCol.iAttributes=aStream.ReadUint8L();
sl@0: 	if (type>EDbColLongBinary || (aCol.iAttributes&~(TDbCol::ENotNull|TDbCol::EAutoIncrement))!=0)
sl@0: 		__LEAVE(KErrCorrupt);
sl@0: 	if (type>=EDbColText8 && type<=EDbColBinary)
sl@0: 		{
sl@0: 		aCol.iMaxLength=aStream.ReadUint8L();
sl@0: 		if (aCol.iMaxLength==0)
sl@0: 			__LEAVE(KErrCorrupt);
sl@0: 		}
sl@0: 	else
sl@0: 		aCol.iMaxLength=KDbUndefinedLength;
sl@0: 	}
sl@0: 
sl@0: inline RWriteStream& operator<<(RWriteStream& aStream,const TDbColumnDef& aCol)
sl@0: 	{ExternalizeL(aCol,aStream);return aStream;}
sl@0: inline RReadStream& operator>>(RReadStream& aStream,TDbColumnDef& aCol)
sl@0: 	{InternalizeL(aCol,aStream);return aStream;}
sl@0: 
sl@0: // Streaming key columns
sl@0: 
sl@0: LOCAL_C void ExternalizeL(const TDbKeyCol& aKeyCol,RWriteStream& aStream)
sl@0: 	{
sl@0: 	aStream<<aKeyCol.iName;
sl@0: 	aStream.WriteUint8L(aKeyCol.iLength!=KDbUndefinedLength ? aKeyCol.iLength : 0);
sl@0: 	aStream.WriteUint8L(aKeyCol.iOrder);
sl@0: 	}
sl@0: 
sl@0: LOCAL_C void InternalizeL(TDbKeyCol& aKeyCol,RReadStream& aStream)
sl@0: 	{
sl@0: 	TPtr des=aKeyCol.iName.Des();
sl@0: 	aStream>>des;
sl@0: 	TUint len=aStream.ReadUint8L();
sl@0: 	aKeyCol.iLength=len!=0 ? TInt(len) : KDbUndefinedLength;
sl@0: 	aKeyCol.iOrder=TDbKeyCol::TOrder(aStream.ReadUint8L());
sl@0: 	if (aKeyCol.iOrder>TDbKeyCol::EDesc)
sl@0: 		__LEAVE(KErrCorrupt);
sl@0: 	}
sl@0: 
sl@0: inline RWriteStream& operator<<(RWriteStream& aStream,const TDbKeyCol& aCol)
sl@0: 	{ExternalizeL(aCol,aStream);return aStream;}
sl@0: inline RReadStream& operator>>(RReadStream& aStream,TDbKeyCol& aCol)
sl@0: 	{InternalizeL(aCol,aStream);return aStream;}
sl@0: 
sl@0: 
sl@0: // Class CDbStoreIndexDef
sl@0: 
sl@0: CDbStoreIndexDef::CDbStoreIndexDef()
sl@0: 	{}
sl@0: 
sl@0: CDbStoreIndexDef* CDbStoreIndexDef::NewLC(const TDesC& aName)
sl@0: 	{
sl@0: 	CDbStoreIndexDef* self=new(ELeave) CDbStoreIndexDef;
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL(aName);
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: CDbStoreIndexDef* CDbStoreIndexDef::NewLC(const TDesC& aName,const CDbKey& aKey,const HDbColumnSet& aColumns)
sl@0: 	{
sl@0: 	CDbStoreIndexDef* self=NewLC(aName);
sl@0: 	CDbKey& key=self->Key();
sl@0: 	TInt max=aKey.Count();
sl@0: 	for (TInt ii=0;ii<max;++ii)
sl@0: 		{
sl@0: 		TDbKeyCol kCol=aKey[ii];
sl@0: 		const TDbColumnDef& col=*aColumns.ColumnL(kCol.iName);
sl@0: 		switch (col.iType)
sl@0: 			{
sl@0: 		default:
sl@0: 			break;
sl@0: 		case EDbColText8:
sl@0: 		case EDbColText16:
sl@0: 			if (kCol.iLength==KDbUndefinedLength)
sl@0: 				kCol.iLength=col.iMaxLength;
sl@0: 			break;
sl@0: 		case EDbColLongText8:
sl@0: 		case EDbColLongText16:
sl@0: 			if (kCol.iLength==KDbUndefinedLength)
sl@0: 				__LEAVE(KErrArgument);
sl@0: 			break;
sl@0: 		case EDbColBinary:
sl@0: 		case EDbColLongBinary:
sl@0: 			__LEAVE(KErrArgument);
sl@0: 			break;
sl@0: 			}
sl@0: 		key.AddL(kCol);
sl@0: 		}
sl@0: 	if (aKey.IsUnique())
sl@0: 		key.MakeUnique();
sl@0: 	if (aKey.IsPrimary())
sl@0: 		key.MakePrimary();
sl@0: 	key.SetComparison(aKey.Comparison());
sl@0: 	CheckSizeL(key,aColumns);
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: CDbStoreIndexDef* CDbStoreIndexDef::NewL(RReadStream& aStream)
sl@0: //
sl@0: // Construct an index definition from persistent storage
sl@0: //
sl@0: 	{
sl@0: 	TDbName name;
sl@0: 	aStream>>name;
sl@0: 	CDbStoreIndexDef* self=NewLC(name);
sl@0: 	CDbKey& key=self->Key();
sl@0: 	TDbTextComparison comp=TDbTextComparison(aStream.ReadUint8L());
sl@0: 	if (comp>EDbCompareCollated)
sl@0: 		__LEAVE(KErrCorrupt);
sl@0: 	key.SetComparison(comp);
sl@0: 	if (aStream.ReadUint8L())
sl@0: 		key.MakeUnique();
sl@0: 	TCardinality count;
sl@0: 	aStream>>count;
sl@0: 	for (TInt ii=count;ii>0;--ii)
sl@0: 		{
sl@0: 		TDbKeyCol keycol;
sl@0: 		aStream>>keycol;
sl@0: 		key.AddL(keycol);
sl@0: 		}
sl@0: 	aStream>>self->iTokenId;
sl@0: 	CleanupStack::Pop();
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: void CDbStoreIndexDef::ExternalizeL(RWriteStream& aStream) const
sl@0: 	{
sl@0: 	aStream<<Name();
sl@0: 	const CDbKey& key=Key();
sl@0: 	aStream.WriteUint8L(TUint8(key.Comparison()));
sl@0: 	aStream.WriteUint8L(key.IsUnique());
sl@0: 	TInt max=key.Count();
sl@0: 	aStream<<TCardinality(max);
sl@0: 	for (TInt ii=0;ii<max;++ii)
sl@0: 		aStream<<key[ii];
sl@0: 	aStream<<iTokenId;
sl@0: 	}
sl@0: 
sl@0: TInt CDbStoreIndexDef::KeySize(const TDbKeyCol& aKeyCol,const TDbColumnDef& aColumn)
sl@0: 	{
sl@0: 	LOCAL_D const TUint8 KFixedSize[]=
sl@0: 		{
sl@0: 		sizeof(TUint32),
sl@0: 		sizeof(TInt32),
sl@0: 		sizeof(TUint32),
sl@0: 		sizeof(TInt32),
sl@0: 		sizeof(TUint32),
sl@0: 		sizeof(TInt32),
sl@0: 		sizeof(TUint32),
sl@0: 		sizeof(TInt64),
sl@0: 		sizeof(TReal32),
sl@0: 		sizeof(TReal64),
sl@0: 		sizeof(TTime)
sl@0: 		};
sl@0: 	TDbColType t=aColumn.Type();
sl@0: 	if (TUint(t)<sizeof(KFixedSize)/sizeof(KFixedSize[0]))
sl@0: 		return KFixedSize[t];
sl@0: 	switch (t)
sl@0: 		{
sl@0: 	default:
sl@0: 		__ASSERT(0);
sl@0: 	case EDbColText8:
sl@0: 	case EDbColLongText8:
sl@0: 		return aKeyCol.iLength;
sl@0: 	case EDbColText16:
sl@0: 	case EDbColLongText16:
sl@0: 		return aKeyCol.iLength<<1;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: void CDbStoreIndexDef::CheckSizeL(const CDbKey& aKey,const HDbColumnSet& aColSet)
sl@0: //
sl@0: // Check the size of the key for the index definition
sl@0: //
sl@0: 	{
sl@0: 	TInt len=aKey.IsUnique()?0:sizeof(TDbRecordId);
sl@0: 	for (TInt ii=aKey.Count();--ii>=0;)
sl@0: 		{
sl@0: 		const TDbKeyCol& keyCol=aKey[ii];
sl@0: 		len+=Align4(KeySize(keyCol,*aColSet.ColumnL(keyCol.iName)));
sl@0: 		}
sl@0: 	if (len>KMaxIndexKeySize)
sl@0: 		__LEAVE(KErrTooBig);
sl@0: 	}
sl@0: 
sl@0: 
sl@0: // Class CDbStoreDef
sl@0: 
sl@0: LOCAL_C void SetColumnL(TDbColumnDef& aDef,const TDbCol& aCol,TUint aFlag=0)
sl@0: 	{
sl@0: 	aDef.SetL(aCol);
sl@0: 	if (aDef.iAttributes&TDbCol::EAutoIncrement)
sl@0: 		aDef.iAttributes|=TDbCol::ENotNull;	// auto-increment => not-null
sl@0: 	aDef.iFlags=TUint8(aFlag);
sl@0: 	TDbColType type=aCol.iType;
sl@0: 	if (type>=EDbColText8 && type<=EDbColBinary)
sl@0: 		{
sl@0: 		if (aCol.iMaxLength==KDbUndefinedLength)
sl@0: 			aDef.iMaxLength=KDbStoreMaxColumnLength;
sl@0: 		else if (aCol.iMaxLength>KDbStoreMaxColumnLength)
sl@0: 			__LEAVE(KErrNotSupported);
sl@0: 		}
sl@0: 	else
sl@0: 		aDef.iMaxLength=KDbUndefinedLength;
sl@0: 	}
sl@0: 
sl@0: LOCAL_C HDbColumnSet::TIterator CheckColumnsL(HDbColumnSet::TIterator anIter,const CDbColSet& aColSet,TInt aNotNull,TUint aFlag=0)
sl@0: //
sl@0: // Check the columns from aColset into anIter, according to ENotNull attribute
sl@0: // Validate as we go
sl@0: //
sl@0: 	{
sl@0: 	for (TDbColSetIter iter(aColSet);iter;++iter)
sl@0: 		{
sl@0: 		TInt att=iter->iAttributes;
sl@0: 		if (att&TDbCol::EAutoIncrement)
sl@0: 			att|=TDbCol::ENotNull;		// auto-increment => not-null
sl@0: 		if ((att&TDbCol::ENotNull)==aNotNull)
sl@0: 			{
sl@0: 			SetColumnL(*anIter,*iter,aFlag);
sl@0: 			++anIter;
sl@0: 			}
sl@0: 		}
sl@0: 	return anIter;
sl@0: 	}
sl@0: 
sl@0: CDbStoreDef::CDbStoreDef()
sl@0: 	{}
sl@0: 
sl@0: CDbStoreDef* CDbStoreDef::NewLC(const TDesC& aName,TInt aColumnCount)
sl@0: 	{
sl@0: 	CDbStoreDef* self=new(ELeave) CDbStoreDef;
sl@0: 	CleanupStack::PushL(self);
sl@0: 	self->ConstructL(aName,aColumnCount);
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: CDbStoreDef* CDbStoreDef::NewLC(const TDesC& aName,const CDbColSet& aColSet)
sl@0: //
sl@0: // Construct a table definition from the column set supplied
sl@0: //
sl@0: 	{
sl@0: 	CDbStoreDef* self=NewLC(aName,aColSet.Count());
sl@0: 	HDbColumnSet& columns=self->Columns();
sl@0: 	HDbColumnSet::TIterator def=CheckColumnsL(columns.Begin(),aColSet,TDbCol::ENotNull);
sl@0: 	def=CheckColumnsL(def,aColSet,0);
sl@0: 	__ASSERT(def==columns.End());
sl@0: 	TRecordSize::CheckSizeL(columns);
sl@0: 	self->Changed();
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: CDbStoreDef* CDbStoreDef::NewL(RReadStream& aStream)
sl@0: //
sl@0: // Construct a table definition from persistent storage
sl@0: //
sl@0: 	{
sl@0: 	TDbName name;
sl@0: 	aStream>>name;
sl@0: 	TCardinality count;
sl@0: 	aStream>>count;
sl@0: 	CDbStoreDef* self=NewLC(name,count);
sl@0: 	HDbColumnSet& columns=self->Columns();
sl@0: 	HDbColumnSet::TIterator iter=columns.Begin();
sl@0: 	const HDbColumnSet::TIteratorC end=columns.End();
sl@0: 	do
sl@0: 		{
sl@0: 		aStream>>*iter;
sl@0: 		} while (++iter<end);
sl@0: 	aStream>>count;
sl@0: 	TInt cluster=count;
sl@0: 	if (cluster==0 || cluster>KMaxClustering)
sl@0: 		__LEAVE(KErrCorrupt);
sl@0: 	aStream>>self->iTokenId;
sl@0: 	RDbIndexes& indexes=self->Indexes();
sl@0: 	aStream>>count;
sl@0: 	for (TInt ii=count;ii>0;--ii)
sl@0: 		indexes.Add(CDbStoreIndexDef::NewL(aStream));
sl@0: 	self->Changed();
sl@0: 	CleanupStack::Pop();
sl@0: 	return self;
sl@0: 	}
sl@0: 
sl@0: void CDbStoreDef::Changed()
sl@0: //
sl@0: // The definition has changed, following creation or alteration of the table
sl@0: // Recalculate cached data for the definition.
sl@0: //
sl@0: 	{
sl@0: 	CDbTableDef::Changed();
sl@0: 	__DEBUG(TBool dbg =)iInfo.Set(Columns());
sl@0: 	__ASSERT(!dbg);
sl@0: 	}
sl@0: 
sl@0: void CDbStoreDef::ExternalizeL(RWriteStream& aStream) const
sl@0: 	{
sl@0: 	aStream<<Name();
sl@0: 	const HDbColumnSet& columns=Columns();
sl@0: 	aStream<<TCardinality(columns.Count());
sl@0: 	HDbColumnSet::TIteratorC iter=columns.Begin();
sl@0: 	const HDbColumnSet::TIteratorC end=columns.End();
sl@0: 	do
sl@0: 		{
sl@0: 		aStream<<*iter;
sl@0: 		} while (++iter<end);
sl@0: 	aStream<<TCardinality(Clustering());	// old stuff, not needed
sl@0: 	aStream<<iTokenId;
sl@0: 	aStream<<TCardinality(Indexes().Count());
sl@0: 	TSglQueIterC<CDbStoreIndexDef> ixIter(Indexes().AsQue());
sl@0: 	for (const CDbStoreIndexDef* def;(def=ixIter++)!=0;)
sl@0: 		aStream<<*def;
sl@0: 	}
sl@0: 
sl@0: void CDbStoreDef::AlteredColumnSetL(HDbColumnSet& aSet,const CDbColSet& aChange,const CDbColSet& aAdd)
sl@0: //
sl@0: // Generate an altered column set
sl@0: // We can hijack the non-user attribs of the column sets for marking changes
sl@0: //
sl@0: 	{
sl@0: 	// add not-null columns to the front
sl@0: 	HDbColumnSet::TIterator newCol=CheckColumnsL(aSet.Begin(),aAdd,TDbCol::ENotNull,TDbColumnDef::EAdded);
sl@0: 	// copy current set, minus deleted ones, apply text column length changes
sl@0: 	TDbColSetIter change(aChange);
sl@0: 	HDbColumnSet::TIterator col=Columns().Begin();
sl@0: 	HDbColumnSet::TIteratorC const end=Columns().End();
sl@0: 	do
sl@0: 		{
sl@0: 		TUint flag=col->iFlags;
sl@0: 		if (flag&TDbColumnDef::EDropped)
sl@0: 			continue;
sl@0: 		if (flag&(TDbColumnDef::EChangedType|TDbColumnDef::EChangedLen))
sl@0: 			{
sl@0: 			// check allowed changes
sl@0: 			SetColumnL(*newCol,*change);
sl@0: 			++change;
sl@0: 			if (flag&TDbColumnDef::EChangedType)
sl@0: 				{	// validate type changes (only text->longtext etc)
sl@0: 				if (!TDbCol::IsLong(newCol->Type()) || newCol->iType-col->iType!=3)
sl@0: 					__LEAVE(KErrNotSupported);
sl@0: 				}
sl@0: 			else
sl@0: 				{
sl@0: 				col->iFlags=TUint8(flag&~TDbColumnDef::EChangedLen);	// no real changes req'd
sl@0: 				if (newCol->iMaxLength<col->iMaxLength)
sl@0: 					__LEAVE(KErrNotSupported);					// can only extend columns
sl@0: 				}
sl@0: 			}
sl@0: 		else
sl@0: 			newCol->SetL(*col);
sl@0: 		++newCol;
sl@0: 		} while (++col<end);
sl@0: 	// add nullable columns to the end
sl@0: 	newCol=CheckColumnsL(newCol,aAdd,0,TDbColumnDef::EAdded);
sl@0: 	__ASSERT(newCol==aSet.End());
sl@0: 	TRecordSize::CheckSizeL(aSet);
sl@0: 	}
sl@0: