// 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 CDbTableDatabase::CInterface

//
// ctor. Add the initial reference to the database engine
//
CDbTableDatabase::CInterface::CInterface(CDbTableDatabase& aDatabase)
	:iDatabase(aDatabase)
	{
	aDatabase.Open();
	}

//
// dtor. remove our reference from the database engine
//
CDbTableDatabase::CInterface::~CInterface()
	{
	if (Database().Transaction().InTransaction(*this))
		Rollback();			// release the lock if we have it
	Database().Close();
	}

void CDbTableDatabase::CInterface::PrepareDDLL()
	{
	Database().Transaction().DDLPrepareL(*this);
	}

//
// destroy the database (database must be empty)
//
TInt CDbTableDatabase::CInterface::Destroy()
	{
	__ASSERT(Database().Schema().IsEmpty());
	TRAPD(r,Database().DestroyL());
	return r;
	}

//
// Initiate a transaction on the database
//
TInt CDbTableDatabase::CInterface::Begin()
	{
	TRAPD(r,Database().Transaction().BeginL(*this));
	return r;
	}

//
// Commit a transaction on the database
//
TInt CDbTableDatabase::CInterface::Commit()
	{
	TRAPD(r,Database().Transaction().CommitL(*this));
	return r;
	}

//
// Rollback a failed transaction on the database
//
void CDbTableDatabase::CInterface::Rollback()
	{
	Database().Transaction().Rollback(*this);
	}

//
// Report a requested property
//
TInt CDbTableDatabase::CInterface::Property(CDbDatabase::TProperty aProperty)
	{
	if (aProperty==EInTransaction)
		return Database().Transaction().InTransaction(*this) ? 1 : 0;
//
	return Database().Property(aProperty);
	}

void CDbTableDatabase::CInterface::CreateTableL(const TDesC& aName,const CDbColSet& aColSet,const CDbKey* aPrimaryKey)
	{
	PrepareDDLL();
	Database().DoCreateTableL(aName,aColSet,aPrimaryKey);
	}

CDbCursor* CDbTableDatabase::CInterface::PrepareViewL(const TDbQuery& aQuery,const TDbWindow& aWindow,RDbRowSet::TAccess anAccess)
	{
	return Database().PrepareViewL(aQuery,aWindow,anAccess);
	}

CDbCursor* CDbTableDatabase::CInterface::OpenTableL(const TDesC& aName,RDbRowSet::TAccess anAccess)
	{
	return Database().PrepareTableL(aName,anAccess);
	}

CDbIncremental* CDbTableDatabase::CInterface::OpenDropTableL(const TDesC& aTable,TInt& aStep)
	{
	PrepareDDLL();
	return Database().OpenDropTableL(aTable,aStep);
	}

CDbIncremental* CDbTableDatabase::CInterface::OpenAlterTableL(const TDesC& aTable,const CDbColSet& aNewDef,TInt& aStep)
	{
	PrepareDDLL();
	return Database().OpenAlterTableL(aTable,aNewDef,aStep);
	}

CDbIncremental* CDbTableDatabase::CInterface::OpenCreateIndexL(const TDesC& aName,const TDesC& aTable,const CDbKey& aKey,TInt& aStep)
	{
	PrepareDDLL();
	return Database().OpenCreateIndexL(aName,aTable,aKey,aStep);
	}

CDbIncremental* CDbTableDatabase::CInterface::OpenDropIndexL(const TDesC& aName,const TDesC& aTable,TInt& aStep)
	{
	PrepareDDLL();
	return Database().OpenDropIndexL(aName,aTable,aStep);
	}

//
// Create a database utility. Check the corresponding property to see if it is viable
//
CDbIncremental* CDbTableDatabase::CInterface::OpenUtilityL(CDbDatabase::TUtility aType,TInt& aStep)
	{
	if (!Property(TProperty(-aType)))
		{	// nothing to do, or not capable
		aStep=0;
		return 0;
		}
	RDbTransaction& t=Database().Transaction();
	t.UtilityPrepareL(*this);
	if (aType==ERecover)
		Database().Release();		// must not have any tables open during recovery
	return CUtility::NewL(t,aType,aStep);
	}

CDbIncremental* CDbTableDatabase::CInterface::OpenExecuteL(const TDesC& aSql,TDbTextComparison aComparison,TInt& aInit)
	{
	switch (Sql::Type(aSql))
		{
	default:
		__ASSERT(0);
	case Sql::ENone:
		__LEAVE(KErrArgument);
	case Sql::EDDL:
		{
		CDbIncremental* inc=Sql::ParseDDLStatementLC(aSql)->ExecuteL(*this,aComparison,aInit);
		CleanupStack::PopAndDestroy();		// statement
		return inc;
		}
	case Sql::EDML:
		{
		CSqlDMLStatement* statement=Sql::ParseDMLStatementLC(aSql);
		RDbTransaction& t=Database().Transaction();
		t.DMLCheckL();			// ensure we can open a cursor
		t.DMLPrepareL(*this);
		CDbIncremental* inc=statement->ExecuteL(Database(),aComparison,aInit);
		CleanupStack::PopAndDestroy();		// statement
		return inc;
		}
		}
	}

//
// Open a client-side notifier object
//
CDbNotifier* CDbTableDatabase::CInterface::OpenNotifierL()
	{
	return Database().Transaction().NotifierL();
	}

//
// list the tables on the database
//
void CDbTableDatabase::CInterface::TablesL(CDbTableNames& aNames)
	{
	TSglQueIterC<CDbTableDef> iter(Database().SchemaL());
	for (const CDbTableDef* def;(def=iter++)!=0;)
		aNames.AddL(def->Name());
	}

//
// build a column set for the table
//
void CDbTableDatabase::CInterface::ColumnsL(CDbColSet& aColSet,const TDesC& aName)
	{
	TDbCol col;
	const HDbColumnSet& set=Database().SchemaL().FindL(aName).Columns();
	HDbColumnSet::TIteratorC iter=set.Begin();
	const HDbColumnSet::TIteratorC end=set.End();
	do
		{
		iter->AsTDbCol(col);
		aColSet.AddL(col);
		} while (++iter<end);
	}

//
// List the indexes on a table
//
void CDbTableDatabase::CInterface::IndexesL(CDbIndexNames& aNames,const TDesC& aTable)
	{
	TSglQueIterC<CDbTableIndexDef> iter(Database().SchemaL().FindL(aTable).Indexes().AsQue());
	for (const CDbTableIndexDef* def;(def=iter++)!=0;)
		aNames.AddL(def->Name());
	}

//
// build a key for the index
//
void CDbTableDatabase::CInterface::KeysL(CDbKey& aKey,const TDesC& aName,const TDesC& aTable)
	{
	const CDbKey& key=Database().SchemaL().FindL(aTable).Indexes().FindL(aName).Key();
	TInt max=key.Count();
	for (TInt ii=0;ii<max;++ii)
		aKey.AddL(key[ii]);
	if (key.IsUnique())
		aKey.MakeUnique();
	if (key.IsPrimary())
		aKey.MakePrimary();
	aKey.SetComparison(key.Comparison());
	}

// Class CDbTableDatabase::CSource

//
// ctor. Add a reference to the database engine
//
CDbTableDatabase::CSource::CSource(CDbTableDatabase& aDatabase)
	:iDatabase(aDatabase)
	{
	aDatabase.Open();
	}

//
// dtor. remove our reference from the database engine
//
CDbTableDatabase::CSource::~CSource()
	{
	Database().Close();
	}


//SYMBIAN_REMOVE_TRIVIAL_ENCRYPTION version of the method
CDbDatabase* CDbTableDatabase::CSource::AuthenticateL()
	{
	Database().AuthenticateL();
	return Database().InterfaceL();
	}


CDbNotifier* CDbTableDatabase::CSource::OpenNotifierL()
	{
	return Database().Transaction().NotifierL();
	}

// Class CDbTableDatabase

EXPORT_C CDbTableDatabase::CDbTableDatabase()
	{
	iCache.Open();
	iTransaction.Open(*this);
	}

EXPORT_C CDbTableDatabase::~CDbTableDatabase()
	{
	__ASSERT(iRef==0);
	iTransaction.Close();
	iTables.Close();
	iSchema.Close();
	iCache.Close();
	}

void CDbTableDatabase::Close()
	{
	__ASSERT(iRef>0);
	if (--iRef==0)
		delete this;
	}

//
// Construct the implementation interface for the database framework
// On success, the interface takes an owning reference to this
//
EXPORT_C CDbDatabase* CDbTableDatabase::InterfaceL()
	{
	return new(ELeave) CInterface(*this);
	}

//
// Construct the implementation interface for the database framework
// On success, the interface takes an owning reference to this
//
EXPORT_C CDbSource* CDbTableDatabase::SourceL()
	{
	return new(ELeave) CSource(*this);
	}

//
// Load the schema if required
//
EXPORT_C RDbTableSchema& CDbTableDatabase::SchemaL()
	{
	RDbTableSchema& schema=Schema();
	if (!schema.IsLoaded())
		{
		schema.Discard();
		LoadSchemaL();
		schema.Loaded();
		}
	return schema;
	}

//
// Find any tables which are now idle, and cache them
// Caching such a table can result in *any* table being deleted
//  and so the iteration must restart every time an idle table is cached
//
void CDbTableDatabase::CheckIdle()
	{
	while (!iTables.IsEmpty())
		{
		for (TSglQueIter<CDbTable> iter(iTables);;)
			{
			CDbTable* table=iter++;
			if (table==0)		// no more idle tables
				return;
			if (table->IsIdle())
				{	// idle->cache transition can modify the collection
				table->Idle();
				break;
				}
			}
		}
	// If there are no tables left open, then the database can go idle
	Idle();
	}

//
// Does nothing by default
//
EXPORT_C void CDbTableDatabase::Idle()
	{
    }

//
// default implementation
//
EXPORT_C TInt CDbTableDatabase::Property(CDbDatabase::TProperty aProperty)
	{
	if (aProperty<0)
		return 0;		// utilities are not available with table database
	__ASSERT(aProperty!=CDbDatabase::EInTransaction);
	return KErrNotSupported;
	}

//
// default implementation, should never be invoked
//
EXPORT_C CDbTableDatabase::CStepper* CDbTableDatabase::UtilityL(CDbDatabase::TUtility,TInt&)
	{
	__ASSERT(0);
	return 0;
	}

void CDbTableDatabase::FlushL(TDbLockType aLock)
	{
	if (aLock>=EDbWriteLock)
		{
		TSglQueIter<CDbTable> iter(iTables);
		for (CDbTable* table;(table=iter++)!=0;)
			table->FlushL();
		}
	}

void CDbTableDatabase::Abandon(TDbLockType aLock)
	{
	if (aLock>=EDbWriteLock)
		{
		TSglQueIter<CDbTable> iter(iTables);
		for (CDbTable* table;(table=iter++)!=0;)
			table->Abandon();
		}
	if (aLock==EDbSchemaLock)
		{
		Release();			// ensure no-one is using the old schema
		Schema().Discard();
		}
	}

//
// Discard a table if it is using this definition
//
void CDbTableDatabase::Release(const CDbTableDef& aDef)
	{
	TSglQueIter<CDbTable> iter(iTables);
	for (CDbTable* table;(table=iter++)!=0;)
		{
		if (&table->Def()==&aDef)
			{
			table->Release();
			break;
			}
		}
	}

//
// Discard all open tables as the schema has been modified or discarded
//
void CDbTableDatabase::Release()
	{
	TSglQueIter<CDbTable> iter(iTables);
	for (CDbTable* table;(table=iter++)!=0;)
		table->Release();
	}

//
// Remove a table from the open set
//
void CDbTableDatabase::RemoveTable(CDbTable& aTable)
	{
	iTables.Remove(aTable);
	if (!Transaction().IsLocked() && iTables.IsEmpty())
		Idle();
	}

CDbTableSource* CDbTableDatabase::TableSourceL(const TDesC& aTableName)
	{
	CDbTableSource* rec=new(ELeave) CDbTableSource;
	CDbTable* table=iTables.Find(aTableName);
	if (table)
		table->Open();		// a new reference to it
	else
		{
		CleanupStack::PushL(rec);
		table=TableL(SchemaL().FindL(aTableName));
		CleanupStack::Pop();	// table source
		}
	rec->Construct(table);
	iCache.Flush();			// check-point for object cache membership
	return rec;
	}

//
// Prepare the data pipeline
//
CDbCursor* CDbTableDatabase::PrepareViewL(const TDbQuery& aQuery,const TDbWindow& aWindow,RDbRowSet::TAccess aAccess)
	{
	Transaction().DMLCheckL();	// ensure we can open a cursor
	CSqlQuery *query=Sql::ParseQueryLC(aQuery.Query());
	RDbAccessPlan plan(query,aQuery.Comparison());
	plan.BuildLC(*this,aAccess,aWindow);
	CDbTableCursor* cursor=CDbTableCursor::NewL(plan,aAccess);
	CleanupStack::Pop();			// the plan
	CleanupStack::PopAndDestroy();	// the query
	return cursor;
	}

//
// Prepare the data pipeline
//
CDbCursor* CDbTableDatabase::PrepareTableL(const TDesC& aTable,RDbRowSet::TAccess aAccess)
	{
	Transaction().DMLCheckL();	// ensure we can open a cursor
	RDbAccessPlan plan;
	plan.BuildLC(*this,aTable,aAccess);
	CDbTableCursor* cursor=CDbTableCursor::NewL(plan,aAccess);
	CleanupStack::Pop();			// the plan
	return cursor;
	}

void CDbTableDatabase::DoCreateTableL(const TDesC& aName,const CDbColSet& aColSet,const CDbKey* aPrimaryKey)
	{
	if (SchemaL().Find(aName))
		__LEAVE(KErrAlreadyExists);
// validate the colset set passed in
	Validate::NameL(aName);
	Validate::ColSetL(aColSet);
	Transaction().DDLBeginLC();
	iSchema.Add(CreateTableL(aName,aColSet,aPrimaryKey));
	Transaction().DDLCommitL();
	CleanupStack::Pop();		// rollback not required
	}

//
// Create index definition and prepare to build it
//
CDbTableDatabase::CIncremental* CDbTableDatabase::OpenCreateIndexL(const TDesC& aName,const TDesC& aTable,const CDbKey& aKey,TInt& aStep)
	{
	CDbTableDef& tDef=SchemaL().FindL(aTable);
	RDbIndexes& indexes=tDef.Indexes();
	if (indexes.Find(aName)!=NULL)
		__LEAVE(KErrAlreadyExists);
	if (indexes.Count()==KDbTableMaxIndexes)
		__LEAVE(KErrNotSupported);
	Validate::NameL(aName);
	Validate::KeyL(aKey,tDef.Columns());
	if (aKey.IsPrimary())
		__LEAVE(KErrArgument);	// cannot create a primary index

	CCreateIndex* builder=CCreateIndex::NewLC(Transaction());
	CDbTableIndexDef* xDef=CreateIndexL(tDef,aName,aKey);
	tDef.Indexes().Add(xDef);
	Release(tDef);				// release any table using the old schema
	aStep=builder->ConstructL(tDef,*xDef);
	CleanupStack::Pop();	// builder
	return builder;
	}

//
// Remove index from schema first, then remove index data
//
CDbTableDatabase::CIncremental* CDbTableDatabase::OpenDropIndexL(const TDesC& aName,const TDesC& aTable,TInt& aStep)
	{
	CDbTableDef& tDef=SchemaL().FindL(aTable);
	CDbTableIndexDef& xDef=tDef.Indexes().FindL(aName);
//
	Release(tDef);				// release any table using the old schema
	CDropIndex* drop=CDropIndex::NewL(Transaction(),tDef,&xDef,aStep);
	tDef.Indexes().Remove(&xDef);
	return drop;
	}

CDbTableDatabase::CIncremental* CDbTableDatabase::OpenDropTableL(const TDesC& aTable,TInt& aStep)
	{
	CDbTableDef& tDef=SchemaL().FindL(aTable);
//
	Release(tDef);				// release the tables using the old schema
	CDropTable* drop=CDropTable::NewL(Transaction(),&tDef,aStep);
	iSchema.Remove(&tDef);
	return drop;
	}

//
// Create an incremental table altering object
//
CDbTableDatabase::CIncremental* CDbTableDatabase::OpenAlterTableL(const TDesC& aTable,const CDbColSet& aNewDef,TInt& aStep)
	{
	CDbTableDef& tDef=SchemaL().FindL(aTable);
	Validate::ColSetL(aNewDef);
	Release(tDef);				// release the tables using the old schema
	return CAlterTable::NewL(Transaction(),tDef,aNewDef,aStep);
	}

//
// Reserved for future development
//
EXPORT_C void CDbTableDatabase::Reserved_1()
	{
    }

//
// Reserved for future development
//
EXPORT_C void CDbTableDatabase::Reserved_2()
	{
    }
