persistentstorage/dbms/tdbms/t_dbscript.cpp
changeset 0 08ec8eefde2f
child 55 44f437012c90
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/dbms/tdbms/t_dbscript.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,1493 @@
+// 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 <d32dbms.h>
+#include <f32file.h>
+#include <e32test.h>
+#include <e32math.h>
+#include <d32dbmsconstants.h>
+
+LOCAL_D RTest TheTest(_L("t_dbscript"));
+LOCAL_D CTrapCleanup* TheTrapCleanup;
+LOCAL_D RDbNamedDatabase TheDatabase;
+LOCAL_D RFs TheFs;
+LOCAL_D RDbView TheView;
+//
+const TPtrC KTestDatabase=_L("c:\\dbms-tst\\t_script.db");
+
+const TPtrC KRomScriptFile=_L("z:\\test\\t_script.txt");
+
+const TPtrC KOutputFile=_L("c:\\dbms-tst\\t_script.log");
+
+const TInt KTestCleanupStack=0x20;
+const TPtrC KDDLKeywords[]={_L("CREATE"),_L("DROP"),_L("ALTER")};
+const TPtrC KDMLKeywords[]={_L("INSERT"),_L("DELETE"),_L("UPDATE")};
+const TPtrC KQueryKeywords[]={_L("SELECT")};
+const TPtrC KScriptKeywords[]={_L("PRINT"),_L("ROWS"),_L("COMPARE"),_L("ERROR"),_L("!"),
+							   _L("POPULATE"),_L("RESULTS"),_L("BUILD"),_L("QUERY"),
+							   _L("NORMAL"),_L("FOLDED"),_L("COLLATED"),_L("START"),_L("STOP"),
+							   _L("LOAD"),_L("ECHO"),_L("WINDOW"),_L("ACCESS")};
+const TPtrC KRowIdColName=_L("Rw");
+enum TKeyword {EPrint,ERows,ECompare,EError,EComment,EPopulate,EResults,EBuild,EQuery,ENormal,
+               EFolded,ECollated,EStart,EStop,ELoad,EEcho,EWindow,EAccess,ENumKeywords,EUnknown};
+//
+typedef TBuf<256> TScriptLine;
+typedef TBuf<256> TScriptToken;
+//
+#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
+
+class TScript;
+class TTimer
+	{
+public:
+	inline TTimer(TScript& aScript);
+	void Start(const TDesC& aDes);
+	void Stop(const TDesC& aDes);
+private:
+	TUint iTicks;
+	TScript& iScript;
+	};
+
+class TResults
+	{
+public:
+	inline TResults();
+public:
+	RDbView& iView;
+	TInt iError;
+	TInt iRows;
+	TInt iLineNo;
+	TScriptLine iLine;
+	};
+
+class TSqlStatement
+	{
+private:
+	enum TSqlType {EDML,EDDL,EQuery,EUnknown};
+public:
+	inline TSqlStatement(const TDesC& aSql,TResults& aResults);
+	TInt Execute(TDbTextComparison aTextComparison=EDbCompareNormal,
+		RDbRowSet::TAccess aAccess=RDbRowSet::EReadOnly,TBool aWindow=EFalse) const;
+private:
+	void ExecuteSql(TDbTextComparison aTextComparison) const;
+	void ExecuteQuery(RDbRowSet::TAccess aAccess,TBool aWindow) const;
+	TSqlType SqlType() const;
+private:
+	const TDesC& iSql;
+	TResults& iResults;
+	};
+
+class TScript
+	{
+public:
+	TScript();
+	~TScript()
+		{
+		iFile.Close();
+		}
+	TInt ReadNextStatement();
+	void GetNextTokenFromStatement(TDes& aToken);
+	void GetNextTokenFromLine(TDes& aToken);
+	TPtrC Statement();
+	TInt IntValue();
+	void WriteLine(const TDesC& aLine);
+	void WriteError(const TDesC& aLine);
+	void WriteSqlError(const TResults& aResults,const TDesC& aLine);
+	void WriteComment(const TDesC& aLine);
+	TKeyword Keyword(const TDesC& aKeyword) const;
+	void ConsumeLine();
+	void ConsumeStatement();
+	inline TInt LineNo() const;
+private:
+	TInt ReadNextLine();
+	TInt AppendNextLine();
+	TBool IsStatement();
+private:
+	TFileText iInput;
+	TFileText iOutput;
+	TScriptLine iBuf;
+	TLex iStatement;
+	TLex iLine;
+	TInt iLineNo;
+	RFile iFile;
+	};
+
+class TScriptEngine
+	{
+public:
+	inline TScriptEngine();
+	void RunL();
+private:
+	void ExecuteL();
+	TInt ExecuteScriptL();
+	TInt ExecuteSql(const TDesC& aSql);
+	TInt ExecuteSql();
+	// keyword operations
+	void DoPrintL();
+	void DoComment();
+	void DoError();
+	void DoEcho();
+	void DoWindow();
+	void DoAccess();
+	void DoRows();
+	void DoCompareL();
+	void DoPopulate();
+	void DoResultsL();
+	void DoBuildTable();
+	void DoQuery();
+	void DoTextComparison(TKeyword aKeyword);
+	void DoStartTimer();
+	void DoStopTimer();
+	void DoLoadDb();
+	//
+	void PrintL(RDbRowSet& aRowSet);
+	void CompareL(RDbRowSet& aRowSet);
+	void CompareValues(RDbRowSet& aRowSet,TDbColNo ColNo,TDbColType aType,const TDesC& aToken);
+	void FatalError(const TDesC& aLine);
+	void FatalError();
+	void FatalSqlError(const TDesC& aLine);
+	void TestForNoError();
+private:
+	TScript iScript;
+	TResults iResults;
+	TDbTextComparison iTextComparison;
+	TTimer iTimer;
+	TBool iEcho;
+	TBool iWindow;
+	RDbRowSet::TAccess iAccess;
+
+	};
+
+//
+// class TTimer
+//
+
+inline TTimer::TTimer(TScript& aScript)
+	: iScript(aScript)
+	{}
+
+void TTimer::Start(const TDesC& aDes)
+	{
+	TScriptLine line;
+	line.Format(_L("%S: "),&aDes);
+	iScript.WriteLine(line);
+	iTicks=User::TickCount();
+	}
+
+void TTimer::Stop(const TDesC& aDes)
+	{
+	TScriptLine line;
+	line.Format(_L("%S: "),&aDes);
+#ifdef __EPOC32__
+#define TICK_TIME 15625
+#else
+#define	TICK_TIME 100000
+#endif
+	TInt microSec=(User::TickCount()-iTicks)*TICK_TIME;
+	TUint sec=microSec/1000000;
+	TUint centi=(microSec/10000)-sec*100;
+	line.AppendFormat(_L("%u.%02us\n"),sec,centi);
+	iScript.WriteLine(line);
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+LOCAL_C void DeleteDataFile(const TDesC& aFullName)
+    {
+    RFs fsSession;
+    TInt err = fsSession.Connect();
+    if(err == KErrNone)
+        {
+        TEntry entry;
+        if(fsSession.Entry(aFullName, entry) == KErrNone)
+            {
+            RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
+            err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
+            if(err != KErrNone)
+                {
+                RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
+                }
+            err = fsSession.Delete(aFullName);
+            if(err != KErrNone)
+                {
+                RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
+                }
+            }
+        fsSession.Close();
+        }
+    else
+        {
+        RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
+        }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+//Tests macros and functions.
+//If (!aValue) then the test will be panicked, the test data files will be deleted.
+static void Check(TInt aValue, TInt aLine)
+    {
+    if(!aValue)
+        {
+        RDebug::Print(_L("*** Expression evaluated to false\r\n"));
+        DeleteDataFile(KTestDatabase);
+        DeleteDataFile(KOutputFile);
+        TheTest(EFalse, aLine);
+        }
+    }
+//If (aValue != aExpected) then the test will be panicked, the test data files will be deleted.
+static void Check(TInt aValue, TInt aExpected, TInt aLine)
+    {
+    if(aValue != aExpected)
+        {
+        RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue);
+        DeleteDataFile(KTestDatabase);
+        DeleteDataFile(KOutputFile);
+        TheTest(EFalse, aLine);
+        }
+    }
+//Use these to test conditions.
+#define TEST(arg) Check((arg), __LINE__)
+#define TEST2(aValue, aExpected) Check(aValue, aExpected, __LINE__)
+
+///////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////
+
+//
+// class TResults
+//
+
+inline TResults::TResults()
+	: iView(TheView),iError(KErrNone)
+	{}
+
+
+//
+// class TSqlStatement
+//
+
+inline TSqlStatement::TSqlStatement(const TDesC& aSql,TResults& aResults)
+	: iSql(aSql),iResults(aResults)
+	{}
+
+//
+// executes DML or DDL
+//
+void TSqlStatement::ExecuteSql(TDbTextComparison aTextComparison) const
+	{
+	TInt r=TheDatabase.Execute(iSql,aTextComparison);
+	if (r<0)
+		iResults.iError=r;
+	else
+		{
+		iResults.iError=KErrNone;
+		iResults.iRows=r;
+		}
+	}
+
+void TSqlStatement::ExecuteQuery(RDbRowSet::TAccess aAccess,TBool aWindow) const
+	{
+	iResults.iView.Close();			// discard any previous queries
+	TInt& err=iResults.iError;
+	if (aWindow)
+		err=iResults.iView.Prepare(TheDatabase,iSql,KDbUnlimitedWindow,aAccess);
+	else
+		err=iResults.iView.Prepare(TheDatabase,iSql,aAccess);
+	if (err==KErrNone)
+		err=iResults.iView.EvaluateAll();
+	}
+
+//
+// determines the type of sql statement by matching keywords
+//
+TSqlStatement::TSqlType TSqlStatement::SqlType() const
+	{
+	for (TUint i=0;i<ARRAY_SIZE(KDDLKeywords);++i)
+		{
+		if (iSql.FindF(KDDLKeywords[i])==0)
+			return EDDL;
+		}
+	for (TUint ii=0;ii<ARRAY_SIZE(KDMLKeywords);++ii)
+		{
+		if (iSql.FindF(KDMLKeywords[ii])==0)
+			return EDML;
+		}
+	for (TUint j=0;j<ARRAY_SIZE(KQueryKeywords);++j)
+		{
+		if (iSql.FindF(KQueryKeywords[j])==0)
+			return EQuery;
+		}
+	return EUnknown;
+	}
+
+//
+// executes the sql statement
+//
+TInt TSqlStatement::Execute(TDbTextComparison aTextComparison,RDbRowSet::TAccess aAccess,TBool aWindow) const
+	{
+	TInt r=KErrNone;
+	switch (SqlType())
+		{
+	case EDDL:
+	case EDML:
+		ExecuteSql(aTextComparison);
+		break;
+	case EQuery:
+		ExecuteQuery(aAccess,aWindow);
+		break;
+	case EUnknown:
+	default:
+		r=KErrNotFound;
+		break;
+		}
+	return r;
+	}
+
+
+//
+// class TScript
+//
+
+TScript::TScript()
+	: iLineNo(0)
+	{
+	TheTest.Printf(_L("---TScript::TScript(), Open the script file \"%S\"\r\n"), &KRomScriptFile);
+	RFile file;
+	TInt r=file.Open(TheFs, KRomScriptFile, EFileRead);
+	TEST2(r, KErrNone);
+	iInput.Set(file);
+    TheTest.Printf(_L("---TScript::TScript(), Create the file \"%S\"\r\n"), &KOutputFile);
+	TEST2(file.Replace(TheFs, KOutputFile, EFileWrite), KErrNone);
+	iOutput.Set(file);
+	iFile = file;
+	}
+
+inline TInt TScript::LineNo() const
+	{return iLineNo;}
+
+//
+//  checks for keywords which possibly conform to the usual statement format (ie end in ';')
+//
+TBool TScript::IsStatement()
+	{
+	if (iLine.Remainder().Length()==0)	// null statement
+		return ETrue;
+	TPtrC line=iLine.Remainder();
+	TKeyword keyword=Keyword(line);
+	switch (keyword)
+		{
+	case EPrint:
+	case ERows:
+	case EBuild:
+	case EError:
+	case EQuery:
+	case ENormal:
+	case EFolded:
+	case ECollated:
+	case EStart:
+	case EStop:
+	case ELoad:
+	case EEcho:
+	case EWindow:
+	case EAccess:
+	case EUnknown:						// could be sql
+		return ETrue;
+	case EComment:
+	case EPopulate:
+	case ECompare:
+	case EResults:
+		iStatement=line;				// not a statement, so make it the whole line
+		return EFalse;
+	default:
+		TEST(0);
+		return EFalse;
+		}
+	}
+
+//
+// reads the next non-blank statement or line
+//
+TInt TScript::ReadNextStatement()
+	{
+	TInt r=ReadNextLine();
+	if (r!=KErrNone || !IsStatement())
+		return r;
+	TChar c=0;
+	while (c!=';')
+		{
+		c=iLine.Get();
+		if (!c)							// nothing left to read
+			{
+			r=AppendNextLine();
+			if (r!=KErrNone)
+				return r;
+			}
+		}
+	iLine.UnGet();						// the semi-colon
+	iStatement=iLine.MarkedToken();
+	iLine.Get();						// the semi-colon
+	iLine.SkipSpaceAndMark();
+	return KErrNone;
+	}
+
+TPtrC TScript::Statement()
+	{
+	return iStatement.Remainder();
+	}
+
+void TScript::ConsumeLine()
+	{
+	iLine=TPtrC();
+	iStatement=TPtrC();
+	}
+
+void TScript::ConsumeStatement()
+	{
+	iLine=iLine.Remainder();
+	iStatement=TPtrC();
+	}
+
+//
+// reads the next non-blank line into iLine
+//
+TInt TScript::ReadNextLine()
+	{
+	while (iLine.Remainder().Length()==0)
+		{
+		TInt r=iInput.Read(iBuf);
+		if (r!=KErrNone)
+			return r;
+		if (iBuf.Length()>0 && iBuf[0]==0xfeff)	// unicode stream marker
+			iBuf.Delete(0,1);
+		iLineNo++;
+		iLine=iBuf;
+		iLine.SkipSpaceAndMark();
+		}
+	return KErrNone;
+	}
+
+//
+// adds next line from file to iLine
+//
+TInt TScript::AppendNextLine()
+	{
+	TScriptLine line;
+	do {
+		TInt r=iInput.Read(line);
+		if (r!=KErrNone)
+			return r;
+		iLineNo++;
+		} while (line.Length()==0);
+	iBuf=iLine.MarkedToken();
+	iBuf.Append(line);
+	iLine=iBuf;
+	return KErrNone;
+	}
+
+void TScript::WriteError(const TDesC& aLine)
+	{
+	TScriptLine line;
+	line.Format(_L("Error at line %d: %S\n"),iLineNo,&aLine);
+	WriteLine(line);
+	TheTest.Printf(line);
+	}
+
+void TScript::WriteSqlError(const TResults& aResults,const TDesC& aLine)
+	{
+	TScriptLine line;
+	line.Format(_L("Error at line %d: %S :-\n"),aResults.iLineNo,&aLine);
+	WriteLine(line);
+	TheTest.Printf(line);
+	line.Format(_L("\t%S\n"),&aResults.iLine);
+	WriteLine(line);
+	TheTest.Printf(line);
+	}
+
+void TScript::WriteLine(const TDesC& aLine)
+	{
+	TScriptLine l=aLine;
+	l.Append('\r');
+	iOutput.Write(l);
+	}
+
+void TScript::WriteComment(const TDesC& aLine)
+	{
+	TScriptLine line;
+	line.Format(_L("\n%S"),&aLine);
+	WriteLine(line);
+	}
+
+//
+// returns the integer n from the ' = n ' which must follow in the statement
+//
+TInt TScript::IntValue()
+	{
+	TScriptToken keyword;
+	GetNextTokenFromStatement(keyword);
+	if (keyword.Compare(_L("="))!=0)
+		WriteError(_L("expected '=' missing"));
+	iStatement.SkipSpaceAndMark();
+	TInt num=0;
+	TInt err=iStatement.Val(num);
+	if (err!=KErrNone)
+		WriteError(_L("expected number missing"));
+	return num;
+	}
+
+TKeyword TScript::Keyword(const TDesC& aKeyword) const
+	{
+	for (TInt ii=0; ii<ENumKeywords; ++ii)
+		{
+		if (aKeyword.FindF(KScriptKeywords[ii])==0)
+			return TKeyword(ii);
+		}
+	return EUnknown;
+	}
+
+//
+// gets the next token from iStatement
+//
+void TScript::GetNextTokenFromStatement(TDes& aToken)
+	{
+	iStatement.SkipSpaceAndMark();
+	TUint c;
+	do
+		{
+		c=iStatement.Get();
+		if (c=='=' || c=='!')
+			break;
+		} while (iStatement.Peek().IsAlphaDigit());
+	aToken=iStatement.MarkedToken();
+	iStatement.SkipSpaceAndMark();
+	}
+
+//
+// gets the next token from iLine
+//
+void TScript::GetNextTokenFromLine(TDes& aToken)
+	{
+	iLine.SkipSpaceAndMark();
+	TUint c=0;
+	TChar cc=c;
+	TBool literal=EFalse;
+	do
+		{
+		c=iLine.Get();
+		if (!c)
+			{
+			AppendNextLine();
+			iLine.SkipSpaceAndMark();
+			c=iLine.Get();
+			}
+		if (c=='\'' || c=='#')
+			literal=!literal;
+		if ((c==',' || c=='(' || c==')' || c=='{' || c=='}' || c=='!' ) && !literal)
+			break;
+		cc=iLine.Peek();
+		} while (cc.IsAlphaDigit() || literal || TUint(cc)=='.' || TUint(cc)=='+' || TUint(cc)=='-');
+	aToken=iLine.MarkedToken();
+	if (TUint(cc)==';')					// ignore semi-colons - they're optional
+		iLine.Get();
+	iLine.SkipSpaceAndMark();
+	if (c=='!')
+		{
+		iStatement=iLine.Remainder();
+		TPtrC comment=iStatement.Remainder();
+		WriteComment(comment);
+		iLine=TPtrC();
+		GetNextTokenFromLine(aToken);
+		}
+	}
+
+
+//
+// class TScriptEngine
+//
+
+TScriptEngine::TScriptEngine()
+	: iTextComparison(EDbCompareNormal),iTimer(iScript),iEcho(EFalse),iWindow(EFalse),iAccess(RDbRowSet::EReadOnly)
+	{}
+
+
+//
+// runs the script file
+//
+void TScriptEngine::RunL()
+	{
+	while (iScript.ReadNextStatement()!=KErrEof)
+		ExecuteL();
+	TestForNoError();
+	}
+
+void TScriptEngine::TestForNoError()
+	{
+	if (iResults.iError!=KErrNone)
+		{
+		TScriptLine line;
+		line.Format(_L("unexpected error %d"),iResults.iError);
+		FatalSqlError(line);
+		}
+	}
+
+void TScriptEngine::ExecuteL()
+	{
+	if (ExecuteScriptL()!=KErrNone && ExecuteSql()!=KErrNone)
+		FatalSqlError(_L("syntax error"));
+	}
+
+TInt TScriptEngine::ExecuteScriptL()
+	{
+	TKeyword keyword=iScript.Keyword(iScript.Statement());
+	if (keyword!=EError)
+		TestForNoError();
+	switch (keyword)
+		{
+	case EPrint:
+		DoPrintL();
+		break;
+	case ERows:
+		DoRows();
+		break;
+	case ELoad:
+		DoLoadDb();
+		break;
+	case EEcho:
+		DoEcho();
+		break;
+	case EWindow:
+		DoWindow();
+		break;
+	case EAccess:
+		DoAccess();
+		break;
+	case ECompare:
+		DoCompareL();
+		break;
+	case EError:
+		DoError();
+		break;
+	case EPopulate:
+		DoPopulate();
+		break;
+	case EComment:
+		DoComment();
+		break;
+	case EResults:
+		DoResultsL();
+		break;
+	case EBuild:
+		DoBuildTable();
+		break;
+	case EQuery:
+		DoQuery();
+		break;
+	case ENormal:
+	case EFolded:
+	case ECollated:
+		DoTextComparison(keyword);
+		break;
+	case EStart:
+		DoStartTimer();
+		break;
+	case EStop:
+		DoStopTimer();
+		break;
+	case EUnknown:
+	default:
+		return KErrNotFound;
+		}
+	return KErrNone;
+	}
+
+TInt TScriptEngine::ExecuteSql()
+	{
+	return ExecuteSql(iScript.Statement());
+	}
+
+TInt TScriptEngine::ExecuteSql(const TDesC& aSql)
+	{
+	if (iEcho)
+		{
+		TScriptLine line(_L("\nSQL:\t"));
+		line.Append(aSql);
+		iScript.WriteLine(line);
+		}
+	iResults.iLineNo=iScript.LineNo();
+	iResults.iLine=aSql;
+	TSqlStatement statement(aSql,iResults);
+	return statement.Execute(iTextComparison,iAccess,iWindow);
+	}
+
+void TScriptEngine::DoStartTimer()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("START")), 0);
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("TIMER")), 0);
+	//
+	iTimer.Start(_L("Timer started"));
+	}
+
+void TScriptEngine::DoStopTimer()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("STOP")), 0);
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("TIMER")), 0);
+	//
+	iTimer.Stop(_L("Timer stopped"));
+	}
+
+void TScriptEngine::DoTextComparison(TKeyword aKeyword)
+	{
+	TPtrC line;
+	switch (aKeyword)
+		{
+	case ENormal:
+		iTextComparison=EDbCompareNormal;
+		line.Set(_L("[Normal text comparison]"));
+		break;
+	case EFolded:
+		iTextComparison=EDbCompareFolded;
+		line.Set(_L("[Folded text comparison]"));
+		break;
+	case ECollated:
+		iTextComparison=EDbCompareCollated;
+		line.Set(_L("[Collated text comparison]"));
+		break;
+	default:
+		TEST(0);
+		}
+	iScript.WriteLine(line);
+	}
+
+void TScriptEngine::DoComment()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("!")), 0);
+	//
+	TPtrC comment=iScript.Statement();
+	iScript.WriteComment(comment);
+	iScript.ConsumeLine();
+	}
+
+void TScriptEngine::DoError()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("ERROR")), 0);
+	//
+	TScriptLine line;
+	if (iScript.Statement().Length()==0)
+		{
+		if (iResults.iError>=0)
+			FatalSqlError(_L("no error when one was expected"));
+		line=_L("\t\tERROR OK");
+		}
+	else
+		{
+		TInt err=iScript.IntValue();
+		if (iResults.iError!=err)
+			{
+			line.Format(_L("expected error %d, actual error %d"),err,iResults.iError);
+			FatalSqlError(line);
+			}
+		line.Format(_L("\t\tERROR=%D OK"),err);
+		}
+	iResults.iError=0;
+	iScript.WriteLine(line);
+	}
+
+void TScriptEngine::DoRows()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("ROWS")), 0);
+	//
+	TScriptLine line;
+	TInt rows=iScript.IntValue();
+	if (iResults.iRows!=rows)
+		{
+		line.Format(_L("expected rows %d, actual rows %d"),rows,iResults.iRows);
+		FatalSqlError(line);
+		}
+	line.Format(_L("\t\tROWS=%D OK"),rows);
+	iScript.WriteLine(line);
+	}
+
+void TScriptEngine::DoLoadDb()
+	{
+	// test its right function
+	TScriptToken token;
+	iScript.GetNextTokenFromStatement(token);
+	TEST2(token.CompareF(_L("LOAD")), 0);
+	//
+	TFileName database(iScript.Statement());
+	TheDatabase.Close();
+	TScriptLine line;
+	line.Format(_L("Opening database: %S"),&database);
+	iScript.WriteLine(line);
+	TEST2(TheDatabase.Open(TheFs,database), KErrNone);
+	}
+
+void TScriptEngine::DoEcho()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("ECHO")), 0);
+	//
+	iScript.GetNextTokenFromStatement(keyword);
+	if (keyword.CompareF(_L("OFF"))==0)
+		{
+		iEcho=EFalse;
+		iScript.WriteLine(_L("Echo is off"));
+		}
+	else if (keyword.CompareF(_L("ON"))==0)
+		{
+		iEcho=ETrue;
+		iScript.WriteLine(_L("Echo is on"));
+		}
+	else
+		FatalError(_L("Expected ON|OFF to follow ECHO statement"));
+	}
+
+void TScriptEngine::DoWindow()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("WINDOW")), 0);
+	//
+	iScript.GetNextTokenFromStatement(keyword);
+	if (keyword.CompareF(_L("OFF"))==0)
+		{
+		iWindow=EFalse;
+		iScript.WriteLine(_L("Window is off"));
+		}
+	else if (keyword.CompareF(_L("ON"))==0)
+		{
+		iWindow=ETrue;
+		iScript.WriteLine(_L("Window is on"));
+		}
+	else
+		FatalError(_L("Expected ON|OFF to follow WINDOW statement"));
+	}
+
+void TScriptEngine::DoAccess()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("ACCESS")), 0);
+	//
+	iScript.GetNextTokenFromStatement(keyword);
+	if (keyword.CompareF(_L("UPDATE"))==0)
+		{
+		iAccess=RDbRowSet::EUpdatable;
+		iScript.WriteLine(_L("Access is updateable"));
+		}
+	else if (keyword.CompareF(_L("READ"))==0)
+		{
+		iAccess=RDbRowSet::EReadOnly;
+		iScript.WriteLine(_L("Access is read only"));
+		}
+	else if (keyword.CompareF(_L("INSERT"))==0)
+		{
+		iAccess=RDbRowSet::EInsertOnly;
+		iScript.WriteLine(_L("Access is insert only"));
+		}
+	else
+		FatalError(_L("Expected UPDATE|INSERT|READ to follow ACCESS statement"));
+	}
+
+void TScriptEngine::DoResultsL()
+	{
+	// test its right function
+	TScriptToken token;
+	iScript.GetNextTokenFromLine(token);
+	TEST2(token.CompareF(_L("RESULTS")), 0);
+	//
+	iScript.GetNextTokenFromLine(token);
+	if (token.Compare(_L("{"))!=0)
+		FatalError(_L("missing '{'"));
+	iScript.GetNextTokenFromLine(token);					// first value
+	TLex value;
+	RDbRowSet& rowset=iResults.iView;
+	CDbColSet* colset=rowset.ColSetL();
+	CleanupStack::PushL(colset);
+	TDbColNo colno=colset->ColNo(KRowIdColName);
+	CArrayFixFlat<TInt>* rowIdScript=new CArrayFixFlat<TInt>(4);
+	CleanupStack::PushL(rowIdScript);
+	CArrayFixFlat<TInt>* rowIdView=new CArrayFixFlat<TInt>(4);
+	CleanupStack::PushL(rowIdView);
+	rowset.BeginningL();
+	while (rowset.NextL())
+		{
+		rowset.GetL();
+		TUint rIdScript;
+		value=token;
+		if (value.Val(rIdScript)!=KErrNone)
+			{
+			TScriptLine line;
+			line.Format(_L("Unable to extract row id from \"%S\""),&token);
+			FatalError(line);
+			}
+		TUint rIdView=rowset.ColUint(colno);
+		rowIdScript->AppendL(rIdScript);
+		rowIdView->AppendL(rIdView);
+		iScript.GetNextTokenFromLine(token);
+		if (token.Compare(_L(","))==0 || token.Compare(_L("}"))==0)
+			{
+			if (rowIdScript->Count())
+				{
+				TKeyArrayFix key(0,ECmpTInt);
+				rowIdScript->Sort(key);
+				rowIdView->Sort(key);
+				for (TInt ii=0;ii<rowIdScript->Count();++ii)
+					{
+					TInt expectedId=(*rowIdScript)[ii];
+					TInt actualId=(*rowIdView)[ii];
+					if (actualId!=expectedId)
+						{
+						TScriptLine line;
+						line.Format(_L("expected row id %d, actual row id %d"),actualId,expectedId);
+						FatalError(line);
+						}
+					}
+				rowIdScript->Reset();
+				rowIdView->Reset();
+				}
+			if (token.Compare(_L(","))==0)
+				iScript.GetNextTokenFromLine(token);
+			}
+		}
+	if (token.Compare(_L("}"))!=0)
+		FatalError(_L("too many results expected"));
+	CleanupStack::PopAndDestroy(3);	// arrays + colset
+	iScript.ConsumeStatement();
+	}
+
+//
+// same as Sql create statement, but adds a counter
+//
+void TScriptEngine::DoBuildTable()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("BUILD")), 0);
+	//
+	TScriptLine sql(_L("CREATE "));
+	sql.Append(iScript.Statement());
+	TInt pos=sql.Find(_L("("));
+	sql.Insert(++pos,_L("Rw COUNTER,"));
+	iScript.ConsumeStatement();
+	ExecuteSql(sql);
+	}
+
+//
+// same as Sql select statement, but makes sure counter is included
+//
+void TScriptEngine::DoQuery()
+	{
+	// test its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("QUERY")), 0);
+	//
+	TScriptLine sql(iScript.Statement());
+	if (sql.Find(_L("*"))!=0)
+		{
+		sql.Insert(0,_L(","));
+		sql.Insert(0,KRowIdColName);
+		}
+	sql.Insert(0,_L("SELECT "));
+	iScript.ConsumeStatement();
+	ExecuteSql(sql);
+	}
+
+void TScriptEngine::FatalError(const TDesC& aLine)
+	{
+	iScript.WriteError(aLine);
+	TEST(0);
+	}
+
+void TScriptEngine::FatalError()
+	{
+	FatalError(_L("wrong expected value"));
+	}
+
+void TScriptEngine::FatalSqlError(const TDesC& aLine)
+	{
+	iScript.WriteSqlError(iResults,aLine);
+	TEST(0);
+	}
+
+void TScriptEngine::DoPopulate()
+	{
+	// check its right function
+	TScriptToken token;
+	iScript.GetNextTokenFromLine(token);
+	TEST2(token.CompareF(_L("POPULATE")), 0);
+	//
+	TScriptLine sqlbase=_L("INSERT INTO ");
+	iScript.GetNextTokenFromLine(token);	// table name
+	sqlbase.AppendFormat(_L("%S "),&token);
+	iScript.GetNextTokenFromLine(token);
+	if (token.Compare(_L("("))==0)			// optional column names present?
+		{
+		for (;;)
+			{
+			sqlbase.AppendFormat(token);
+			if (token.Compare(_L(")"))==0)
+				break;
+			iScript.GetNextTokenFromLine(token);
+			}
+		iScript.GetNextTokenFromLine(token);
+		}
+	if (token.Compare(_L("{"))!=0)
+		FatalError(_L("missing '{'"));
+	sqlbase.AppendFormat(_L(" VALUES ("));
+	iScript.GetNextTokenFromLine(token);	// first value
+	TheDatabase.Begin();					// all in same transaction
+	for (;;)
+		{
+		if (token.Compare(_L("}"))==0)
+			break;
+		TScriptLine sql=sqlbase;
+		for (;;)
+			{
+			sql.Append(token);
+			iScript.GetNextTokenFromLine(token);
+			if (token.Compare(_L(","))==0)
+				{
+				sql.Append(token);			// comma
+				iScript.GetNextTokenFromLine(token);
+				}
+			else
+				break;
+			}
+		sql.AppendFormat(_L(")"));
+		ExecuteSql(sql);
+		TestForNoError();
+		}
+	TheDatabase.Commit();
+	iScript.ConsumeStatement();
+	}
+
+void TScriptEngine::DoPrintL()
+	{
+	// check its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromStatement(keyword);
+	TEST2(keyword.CompareF(_L("PRINT")), 0);
+	//
+	iScript.GetNextTokenFromStatement(keyword);
+	if (keyword.CompareF(_L("VIEW"))==0)
+		PrintL(iResults.iView);
+	else if (keyword.CompareF(_L("TABLE"))==0)
+		{
+		iScript.GetNextTokenFromStatement(keyword);	// name of table
+		RDbTable table;
+		TInt err=table.Open(TheDatabase,keyword,table.EReadOnly);
+		if (err!=KErrNone)
+			FatalError(_L("unable to open table"));
+		PrintL(table);
+		table.Close();
+		}
+	else
+		FatalError(_L("expected VIEW or TABLE keyword not present"));
+	}
+
+void TScriptEngine::DoCompareL()
+	{
+	// check its right function
+	TScriptToken keyword;
+	iScript.GetNextTokenFromLine(keyword);
+	TEST2(keyword.CompareF(_L("COMPARE")), 0);
+	//
+	iScript.GetNextTokenFromLine(keyword);
+	if (keyword.CompareF(_L("VIEW"))==0)
+		CompareL(iResults.iView);
+	else if (keyword.CompareF(_L("TABLE"))==0)
+		{
+		iScript.GetNextTokenFromLine(keyword);	// name of table
+		RDbTable table;
+		TInt err=table.Open(TheDatabase,keyword,table.EReadOnly);
+		if (err!=KErrNone)
+			FatalError(_L("unable to open table"));
+		CompareL(table);
+		table.Close();
+		}
+	else
+		FatalError(_L("expected VIEW or TABLE keyword not present"));
+	}
+
+void TScriptEngine::CompareL(RDbRowSet& aRowSet)
+	{
+	TScriptToken token;
+	iScript.GetNextTokenFromLine(token);
+	TBool rowIdMode=EFalse;
+	CArrayFixFlat<TInt>* rowIdToTest=new CArrayFixFlat<TInt>(4);
+	CleanupStack::PushL(rowIdToTest);
+	if (token.Compare(_L("("))==0)				// optional row ids present?
+		{
+		rowIdMode=ETrue;
+		iScript.GetNextTokenFromLine(token);	// first value
+		TLex value;
+		TInt rowId;
+		for (;;)
+			{
+			value=token;
+			TEST2(value.Val(rowId), KErrNone);
+			rowIdToTest->AppendL(rowId);		// add row id to array
+			iScript.GetNextTokenFromLine(token);
+			if (token.Compare(_L(")"))==0)
+				break;
+			if (token.Compare(_L(","))==0)
+				iScript.GetNextTokenFromLine(token);
+			}
+		iScript.GetNextTokenFromLine(token);
+		}
+	if (token.Compare(_L("{"))!=0)
+		FatalError(_L("missing '{'"));
+	TInt columns=aRowSet.ColCount();
+	CDbColSet* colset=aRowSet.ColSetL();
+	aRowSet.BeginningL();
+	while (aRowSet.NextL())
+		{
+		aRowSet.GetL();
+		if (rowIdMode)
+			{
+			TInt currentId=aRowSet.ColUint(colset->ColNo(KRowIdColName));
+			TBool toTest=EFalse;
+			for (TInt jj=0; jj<rowIdToTest->Count(); ++jj)
+				{
+				if (currentId==(*rowIdToTest)[jj])
+					toTest=ETrue;
+				}
+			if (!toTest)
+				continue;
+			}
+		for (TInt ii=1;ii<=columns;++ii)
+			{
+			if (rowIdMode && ii==colset->ColNo(KRowIdColName))	// ignore row id column
+				continue;
+			const TDbCol& col=(*colset)[ii];
+			iScript.GetNextTokenFromLine(token);				// value
+			if (token.Compare(_L(","))==0)
+				iScript.GetNextTokenFromLine(token);			// ignore comma
+			if (aRowSet.IsColNull(ii))
+				{
+				if (token.CompareF(_L("NULL"))!=0)
+					FatalError(_L("NULL expected"));
+				continue;
+				}
+			CompareValues(aRowSet,ii,col.iType,token);
+			}
+		}
+	delete colset;
+	CleanupStack::PopAndDestroy();				// rowIdToTest
+	iScript.GetNextTokenFromLine(token);		// look for closing '}'
+	if (token.Compare(_L("}"))!=0)
+		FatalError(_L("missing '}'"));
+	iScript.ConsumeStatement();
+	}
+
+//
+// compares the value from a rowset aRowset, colimn number aColNo and of type aType, with the value
+// contained in the descriptor aToken
+//
+void TScriptEngine::CompareValues(RDbRowSet& aRowSet,TDbColNo ColNo,TDbColType aType,const TDesC& aToken)
+	{
+	TLex value=aToken;
+	switch (aType)
+		{
+	case EDbColInt32:
+		{
+		TInt num;
+		TEST2(value.Val(num), KErrNone);
+		if (num!=aRowSet.ColInt(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColInt8:
+		{
+		TInt8 num8;
+		TEST2(value.Val(num8), KErrNone);
+		if (num8!=aRowSet.ColInt8(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColInt16:
+		{
+		TInt16 num16;
+		TEST2(value.Val(num16), KErrNone);
+		if (num16!=aRowSet.ColInt16(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColInt64:
+		{
+		TInt64 num64;
+		TEST2(value.Val(num64), KErrNone);
+		if (num64!=aRowSet.ColInt64(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColUint8:
+		{
+		TUint8 numu8;
+		TEST2(value.Val(numu8,EDecimal), KErrNone);
+		if (numu8!=aRowSet.ColUint8(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColUint16:
+		{
+		TUint16 numu16;
+		TEST2(value.Val(numu16,EDecimal), KErrNone);
+		if (numu16!=aRowSet.ColUint16(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColUint32:
+		{
+		TUint32 numu32;
+		TEST2(value.Val(numu32,EDecimal), KErrNone);
+		if (numu32!=aRowSet.ColUint32(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColReal32:
+		{
+		TReal32 numr32;
+		TEST2(value.Val(numr32), KErrNone);
+		if (numr32!=aRowSet.ColReal32(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColReal64:
+		{
+		TReal64 numr64;
+		TEST2(value.Val(numr64), KErrNone);
+		if (numr64!=aRowSet.ColReal64(ColNo))
+			FatalError();
+		break;
+		}
+	case EDbColText8:
+	case EDbColText16:
+		{
+		TPtrC text=aToken.Mid(1,aToken.Length()-2);			// skip quotes
+		if (text.CompareF(aRowSet.ColDes(ColNo))!=0)
+			FatalError();
+		break;
+		}
+	case EDbColDateTime:
+		{
+		TScriptLine time=aToken.Mid(1,aToken.Length()-2);	// skip hashes
+		TTime t1;
+		t1.Parse(time);
+		TTime t2(aRowSet.ColTime(ColNo).DateTime());
+		if (t1!=t2)
+			FatalError();
+		break;
+		}
+	default:
+		break;
+		}
+	}
+
+void TScriptEngine::PrintL(RDbRowSet& aRowSet)
+	{
+	iScript.WriteLine(TPtrC());
+	TInt columns=aRowSet.ColCount();
+	CDbColSet* colset=aRowSet.ColSetL();
+	TScriptLine line;
+	for (TInt i=1;i<=columns;++i)
+		{
+		const TDbCol& col=(*colset)[i];
+		line.AppendFormat(_L("%S\t"),&col.iName);
+		}
+	iScript.WriteLine(line);
+	aRowSet.BeginningL();
+	while (aRowSet.NextL())
+		{
+		line=TPtrC();
+		for (TInt ii=1;ii<=columns;++ii)
+			{
+			const TDbCol& col=(*colset)[ii];
+			aRowSet.GetL();
+			if (aRowSet.IsColNull(ii))
+				{
+				line.AppendFormat(_L("NULL\t"));
+				continue;
+				}
+			switch (col.iType)
+				{
+			case EDbColInt32:
+				line.AppendFormat(_L("%d\t"),aRowSet.ColInt(ii));
+				break;
+			case EDbColInt8:
+				line.AppendFormat(_L("%d\t"),aRowSet.ColInt8(ii));
+				break;
+			case EDbColInt16:
+				line.AppendFormat(_L("%d\t"),aRowSet.ColInt16(ii));
+				break;
+			case EDbColInt64:
+				line.AppendFormat(_L("%ld\t"),aRowSet.ColInt64(ii));
+				break;
+			case EDbColUint8:
+				line.AppendFormat(_L("%u\t"),aRowSet.ColUint8(ii));
+				break;
+			case EDbColUint16:
+				line.AppendFormat(_L("%u\t"),aRowSet.ColUint16(ii));
+				break;
+			case EDbColUint32:
+				line.AppendFormat(_L("%u\t"),aRowSet.ColUint(ii));
+				break;
+			case EDbColReal32:
+				line.AppendFormat(_L("%f\t"),aRowSet.ColReal32(ii));
+				break;
+			case EDbColReal64:
+				line.AppendFormat(_L("%f\t"),aRowSet.ColReal64(ii));
+				break;
+			case EDbColText:
+				line.Append(aRowSet.ColDes(ii));
+				line.Append('\t');
+				break;
+			case EDbColDateTime:
+				{
+				TDateTime time(aRowSet.ColTime(ii).DateTime());
+				line.AppendFormat(_L("%d:%d:%d %d/%d/%d"),time.Hour(),time.Minute(),time.Second(),time.Day(),time.Month(),time.Year());
+				}
+				break;
+			case EDbColLongText:
+				{
+				RDbColReadStream blob;
+				blob.OpenLC(aRowSet,ii);
+				TScriptLine text;
+				blob.ReadL(text,aRowSet.ColLength(ii));
+				CleanupStack::PopAndDestroy();
+				line.AppendFormat(_L("%S\t"),&text);
+				}
+			default:
+				break;
+				}
+			}
+		iScript.WriteLine(line);
+		}
+	iScript.WriteLine(TPtrC());
+	delete colset;
+	}
+
+
+//
+// Create the database
+//
+LOCAL_C void CreateDatabase()
+	{
+	TEST2(TheDatabase.Replace(TheFs,KTestDatabase), KErrNone);
+	}
+
+//
+// Close the database
+//
+LOCAL_C void CloseDatabase()
+	{
+	TheDatabase.Close();
+	}
+
+//
+// Prepare the test directory.
+//
+LOCAL_C void SetupTestDirectory()
+    {
+	TInt err=TheFs.Connect();
+	TEST2(err, KErrNone);
+//
+	err=TheFs.MkDir(KTestDatabase);
+	TEST(err==KErrNone || err==KErrAlreadyExists);
+	}
+
+//
+// Initialise the cleanup stack.
+//
+LOCAL_C void SetupCleanup()
+    {
+	TheTrapCleanup=CTrapCleanup::New();
+	TEST(TheTrapCleanup!=NULL);
+	TRAPD(err,\
+		{\
+		for (TInt i=KTestCleanupStack;i>0;i--)\
+			CleanupStack::PushL((TAny*)0);\
+		CleanupStack::Pop(KTestCleanupStack);\
+		});
+	TEST2(err, KErrNone);
+	}
+
+/**
+@SYMTestCaseID          SYSLIB-DBMS-CT-0632
+@SYMTestCaseDesc        Executes the script files
+@SYMTestPriority        Medium
+@SYMTestActions         Start the script engine
+@SYMTestExpectedResults Test must not fail
+@SYMREQ                 REQ0000
+*/
+LOCAL_C void RunScriptL()
+	{
+	TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-DBMS-CT-0632 Running script "));
+	CreateDatabase();
+	TScriptEngine script;
+	script.RunL();
+	CloseDatabase();
+	TheView.Close();
+	}
+
+//
+// entry point
+//
+GLDEF_C TInt E32Main()
+    {
+	TheTest.Title();
+	SetupTestDirectory();
+	SetupCleanup();
+	__UHEAP_MARK;
+//
+	TRAPD(err,RunScriptL());
+	TEST2(err, KErrNone);
+
+	//deletion of data files must be done before call to end - DEF047652
+	::DeleteDataFile(KTestDatabase);
+	::DeleteDataFile(KOutputFile);//Comment this line if you want to keep "t_script.log" file.
+	TheTest.End();
+//
+	__UHEAP_MARKEND;
+	delete TheTrapCleanup;
+
+	TheFs.Close();
+	TheTest.Close();
+
+	return 0;
+    }