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: }