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: // SQL parser tokeniser
sl@0: // 
sl@0: //
sl@0: 
sl@0: #include "UQ_STD.H"
sl@0: 
sl@0: // optimised tables for ASCII character set
sl@0: 
sl@0: const TUint8 KAlpha=0x1;
sl@0: const TUint8 KDigitOr_=0x2;
sl@0: 
sl@0: const TUint8 KCharAttrib[]=
sl@0: 	{
sl@0: 	KDigitOr_,KDigitOr_,KDigitOr_,KDigitOr_,KDigitOr_,KDigitOr_,KDigitOr_,KDigitOr_,
sl@0: 	KDigitOr_,KDigitOr_,0,0,0,0,0,0,
sl@0: 	0,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,
sl@0: 	KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,
sl@0: 	KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,
sl@0: 	KAlpha,KAlpha,KAlpha,0,0,0,0,KDigitOr_,
sl@0: 	0,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,
sl@0: 	KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,
sl@0: 	KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,KAlpha,
sl@0: 	KAlpha,KAlpha,KAlpha
sl@0: 	};
sl@0: 
sl@0: #define ISALPHA(aChar) (TUint(aChar-'0')<=TUint('z'-'0') && KCharAttrib[aChar-'0']&KAlpha)
sl@0: #define ISALPHA_DIGIT_OR_(aChar) (TUint(aChar-'0')<=TUint('z'-'0') && KCharAttrib[aChar-'0']&(KAlpha|KDigitOr_))
sl@0: #define LCASE(aChar) (aChar|0x20)
sl@0: 
sl@0: // The keywords
sl@0: // These are always stored as ASCII because DBMS has its own 
sl@0: 
sl@0: const TInt KMaxKeywordLength=15;
sl@0: 
sl@0: static const TText8 KSqlKeywords[][KMaxKeywordLength+1]=
sl@0: 	{
sl@0: #define KEYWORD(s) #s
sl@0: #include "UQ_KEYWD.H"
sl@0: #undef KEYWORD
sl@0: 	};
sl@0: const TInt KSqlKeywordCount=sizeof(KSqlKeywords)/sizeof(*KSqlKeywords);
sl@0: 
sl@0: #if defined(_ASSERTIONS)
sl@0: TInt CheckKeywords()
sl@0: //
sl@0: // ensure that the keyword table is in alphabetical order
sl@0: //
sl@0: 	{
sl@0: 	for (TInt ii=1;ii<KSqlKeywordCount;++ii)
sl@0: 		__ASSERT(TPtrC8(KSqlKeywords[ii-1])<TPtrC8(KSqlKeywords[ii]));
sl@0: 	return 1;
sl@0: 	}
sl@0: #endif
sl@0: 
sl@0: // class TSqlLexer
sl@0: 
sl@0: TInt TSqlLexer::CompareKeyword(TInt aKeyword,const RSqlLiteral& aIdentifier)
sl@0: //
sl@0: // Check if the identifer in aIdentifier is a keyword
sl@0: // uses a case-insensitive match, not folding
sl@0: //
sl@0: 	{
sl@0: 	__ASSERT(TUint(aKeyword)<TUint(KSqlKeywordCount));
sl@0: //
sl@0: 	const TText* ptr=aIdentifier.Ptr();
sl@0: 	const TText* end=aIdentifier.End();
sl@0: 	const TText8* pk=&KSqlKeywords[aKeyword][0];
sl@0: 	for (;;)
sl@0: 		{
sl@0: 		TUint ck=*pk++;
sl@0: 		if (ptr==end)
sl@0: 			return ck;
sl@0: 		if (!ck)
sl@0: 			return -1;
sl@0: 		TInt d=ck-LCASE(TUint(*ptr++));
sl@0: 		if (d)
sl@0: 			return d;
sl@0: 		}
sl@0: 	}
sl@0: 
sl@0: TSqlKeyword TSqlLexer::Keyword(const TSqlToken& aToken)
sl@0: //
sl@0: // non-member function: Return the keyword value
sl@0: //
sl@0: 	{
sl@0: 	if (aToken==ESqlIdentifier)
sl@0: 		{
sl@0: 		TInt r=KSqlKeywordCount;
sl@0: 		TInt l=0;
sl@0: 		while (r>l)
sl@0: 			{
sl@0: 			TInt m=(l+r)>>1;
sl@0: 			TInt k=CompareKeyword(m,aToken.Literal());
sl@0: 			if (k>0)
sl@0: 				r=m;
sl@0: 			else if (k<0)
sl@0: 				l=m+1;
sl@0: 			else
sl@0: 				return TSqlKeyword(m);		// keyword
sl@0: 			}
sl@0: 		}
sl@0: 	// identifier
sl@0: 	return ESqlNotKeyword;
sl@0: 	}
sl@0: 
sl@0: TSqlLexer::TSqlLexer(const TDesC& aSql)
sl@0: 	: iNext(aSql.Ptr()),iEnd(iNext+aSql.Length())
sl@0: 	{
sl@0: 	__ASSERT(CheckKeywords());
sl@0: 	}
sl@0: 
sl@0: TSqlTokenType TSqlLexer::GetIdentifier(TSqlToken& aToken)
sl@0: //
sl@0: // Get a keyword or identifier. Do not resolve a keyword at this stage
sl@0: //
sl@0: 	{
sl@0: 	const TText* end=iEnd;
sl@0: 	const TText* next=iNext-1;
sl@0: 	while (++next<end)
sl@0: 		{
sl@0: 		TUint ch=*next;
sl@0: 		if (ISALPHA_DIGIT_OR_(ch))
sl@0: 			continue;
sl@0: 		if (!TChar(ch).IsAlphaDigit())
sl@0: 			break;
sl@0: 		}
sl@0: 	aToken.iLiteral.SetText(iNext-1,next);
sl@0: 	iNext=next;
sl@0: 	return ESqlIdentifier;
sl@0: 	}
sl@0: 
sl@0: TInt TSqlLexer::GetInteger(TInt64 &aVal)
sl@0: //
sl@0: // A rather more optimised version of TLex::Val(TInt64&)
sl@0: // initially accumulate the value in a TUint32, and only switch to 64-bit arithmetic
sl@0: // if the value overflows. Always return a 64-bit value
sl@0: //
sl@0: 	{
sl@0: 	const TUint KPreMultiplyLimit32=429496728;	//(KMaxTUint-9)/10
sl@0: 	const TUint KPreMultiplyLimit64=214748364;	//(KMaxTInt+1)/10
sl@0: //
sl@0: 	const TText* ptr=iNext;
sl@0: 	const TText* const end=iEnd;
sl@0: 	__ASSERT(ptr<end);
sl@0: 	TUint sign=0;
sl@0: 	TUint c=*ptr;
sl@0: 	if (c=='-')
sl@0: 		{
sl@0: 		sign=1;
sl@0: 		if (++ptr==end)
sl@0: 			return KErrGeneral;
sl@0: 		c=*ptr;
sl@0: 		}
sl@0: 	else if (c=='+')
sl@0: 		{
sl@0: 		if (++ptr==end)
sl@0: 			return KErrGeneral;
sl@0: 		c=*ptr;
sl@0: 		}
sl@0: 	c-='0';
sl@0: 	if (c>=10u)
sl@0: 		return KErrGeneral;		// no digits at all
sl@0: 	TUint val32=c;
sl@0: 	while (++ptr<end)
sl@0: 		{
sl@0: 		c=*ptr-'0';
sl@0: 		if (c>=10u)
sl@0: 			break;
sl@0: 		if (val32>KPreMultiplyLimit32)
sl@0: 			goto overflow64;	// will not fit into 32 bit arithmetic
sl@0: 		val32*=10;
sl@0: 		val32+=c;
sl@0: 		}
sl@0: // we have result, just set the sign and finish
sl@0: 	aVal=val32;
sl@0: 	goto checksign;
sl@0: //
sl@0: // continue the accumulation with a 64-bit integer
sl@0: overflow64:
sl@0: 	aVal=val32;
sl@0: 	for (;;)
sl@0: 		{
sl@0: 		I64MUL10(aVal);
sl@0: 		aVal+=c;
sl@0: 		if (++ptr==end)
sl@0: 			break;
sl@0: 		c=*ptr-'0';
sl@0: 		if (c>=10u)
sl@0: 			break;
sl@0: 		if (I64HIGH(aVal)>KPreMultiplyLimit64)
sl@0: 			return KErrOverflow;	// the value is certain to overflow
sl@0: 		}
sl@0: 	if (I64HIGH(aVal)&0x80000000u)
sl@0: 		{	// greater than the "half way mark"
sl@0: 		if (!sign)
sl@0: 			return KErrOverflow;
sl@0: 		if (I64LOW(aVal)!=0)
sl@0: 			return KErrOverflow;
sl@0: 		}
sl@0: checksign:
sl@0: 	iNext=ptr;
sl@0: 	if (sign)
sl@0: 		aVal=-aVal;
sl@0: 	return KErrNone;
sl@0: 	}
sl@0: 
sl@0: TSqlTokenType TSqlLexer::GetNumber(TSqlToken& aToken)
sl@0: 	{
sl@0: 	const TText* mark=--iNext;			// rewind past initial character
sl@0: 	// attempt to parse a integer
sl@0: 	TInt r=GetInteger(aToken.iLiteral.SetInt());
sl@0: 	if (r==KErrNone)
sl@0: 		{
sl@0: 		if (iNext<iEnd)
sl@0: 			{
sl@0: 			TUint c=*iNext;
sl@0: 			if (c!='.' && c!='e' && c!='E')
sl@0: 				return ESqlLiteralInt;		// it is an integer
sl@0: 			}
sl@0: 		else
sl@0: 			return ESqlLiteralInt;		// it is an integer
sl@0: 		}
sl@0: 	TLex lex(TPtrC(mark,iEnd-mark));
sl@0: 	r=lex.Val(aToken.iLiteral.SetReal(),TChar('.'));
sl@0: 	if (r!=KErrNone)
sl@0: 		return SqlError(r);
sl@0: 	iNext=mark+lex.Offset();
sl@0: 	return ESqlLiteralReal;
sl@0: 	}
sl@0: 
sl@0: TSqlTokenType TSqlLexer::GetString(TSqlToken& aToken)
sl@0: 	{
sl@0: 	const TText* next=iNext;
sl@0: 	const TText* end=iEnd;
sl@0: 	for (;;)
sl@0: 		{
sl@0: 		if (next==end)
sl@0: 			return SqlError();
sl@0: 		TUint c=*next++;
sl@0: 		if (c=='\'')
sl@0: 			{
sl@0: 			if (next==end)
sl@0: 				break;
sl@0: 			if (*next!='\'')
sl@0: 				break;
sl@0: 			next++;
sl@0: 			}
sl@0: 		}
sl@0: 	aToken.iLiteral.SetText(iNext,next-1);
sl@0: 	iNext=next;
sl@0: 	return ESqlLiteralText;
sl@0: 	}
sl@0: 
sl@0: TSqlTokenType TSqlLexer::GetDate(TSqlToken& aToken)
sl@0: 	{
sl@0: 	const TText* end=iEnd;
sl@0: 	const TText* next=iNext;
sl@0: 	do
sl@0: 		{
sl@0: 		if (next==end)
sl@0: 			return SqlError();
sl@0: 		} while (*next++!='#');
sl@0: 	TInt r=aToken.iLiteral.SetTime().Parse(TPtrC(iNext,(next-1)-iNext));
sl@0: 	if (r<0)
sl@0: 		return SqlError(r);
sl@0: 	iNext=next;
sl@0: 	return ESqlLiteralTime;
sl@0: 	}
sl@0: 
sl@0: TSqlTokenType TSqlLexer::GetBlob(TSqlToken& aToken)
sl@0: 	{
sl@0: 	const TText* end=iEnd;
sl@0: 	const TText* next=iNext;
sl@0: 	// Blob literalisation format X'<hex-data>'
sl@0: 	// first char must be single quote - '
sl@0: 	if ( *next != '\'' ) {
sl@0: 		return SqlError(KErrArgument);
sl@0: 	}
sl@0: 	const TText* start = ++next;
sl@0: 	do
sl@0: 		{
sl@0: 		if (next==end)
sl@0: 			return SqlError();
sl@0: 		} while (*next++!='\'');
sl@0: 	const TText* blobend = next-1;
sl@0: 
sl@0: 	aToken.iLiteral.SetBlob(start,blobend);
sl@0: 	iNext=next;
sl@0: 	return ESqlLiteralBlob;
sl@0: 	}
sl@0: 
sl@0: TSqlTokenType TSqlLexer::GetNextToken(TSqlToken& aToken)
sl@0: 	{
sl@0: 	const TText* ptr=iNext;
sl@0: 	const TText* const end=iEnd;
sl@0: 	for (;;)
sl@0: 		{
sl@0: 		if (ptr==end)
sl@0: 			return ESqlEos;
sl@0: 		TUint ch=*ptr++;
sl@0: 		iNext=ptr;
sl@0: 		switch (ch)
sl@0: 			{
sl@0: 		case ' ':			// a "normal" space
sl@0: 			continue;
sl@0: 		case '0': case '1': case '2': case '3': case '4':	// literal number
sl@0: 		case '5': case '6': case '7': case '8': case '9':
sl@0: 		case '+': case '-': case '.':
sl@0: 			return GetNumber(aToken);
sl@0: 		case '\'':
sl@0: 			return GetString(aToken);
sl@0: 		case '#':
sl@0: 			return GetDate(aToken);
sl@0: 		case 'X':
sl@0: 			return GetBlob(aToken);
sl@0: 		case '*':
sl@0: 			return ESqlAsterisk;
sl@0: 		case ',':
sl@0: 			return ESqlComma;
sl@0: 		case '(':
sl@0: 			return ESqlLeftBracket;
sl@0: 		case ')':
sl@0: 			return ESqlRightBracket;
sl@0: 		case '=':
sl@0: 			return ESqlEqual;
sl@0: 		case '<':
sl@0: 			{
sl@0: 			ch=*ptr++;				
sl@0: 			if (ch=='=')
sl@0: 				{
sl@0: 				iNext=ptr;
sl@0: 				return ESqlLessEqual;
sl@0: 				}
sl@0: 			if (ch=='>')
sl@0: 				{
sl@0: 				iNext=ptr;
sl@0: 				return ESqlNotEqual;
sl@0: 				}
sl@0: 			return ESqlLess;
sl@0: 			}
sl@0: 		case '>':
sl@0: 			{
sl@0: 			ch=*ptr++;			
sl@0: 			if (ch=='=')
sl@0: 				{
sl@0: 				iNext=ptr;
sl@0: 				return ESqlGreaterEqual;
sl@0: 				}
sl@0: 			return ESqlGreater;
sl@0: 			}
sl@0: 		default:
sl@0: 			break;
sl@0: 			}
sl@0: 		if (ISALPHA(ch))
sl@0: 			return GetIdentifier(aToken);		// keyword or identifier
sl@0: 		TChar cc(ch);
sl@0: 		if (cc.IsAlpha())
sl@0: 			return GetIdentifier(aToken);		// keyword or identifier
sl@0: 		if (!cc.IsSpace())
sl@0: 			return SqlError();
sl@0: 		}
sl@0: 	}
sl@0: 	
sl@0: const TText* TSqlLexer::Next() const
sl@0: 	{
sl@0: 	return iNext;
sl@0: 	}
sl@0: void TSqlLexer::Set(const TText* aNext) 
sl@0: 	{
sl@0: 	iNext = aNext ;
sl@0: 	}