// 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:
// SQL parser
//
//
#include "UQ_STD.H"
// class TSqlParser
const TInt KSqlError = -1;
TSqlParser::TSqlParser(const TDesC& aSql)
: iSql(aSql)
{
NextToken(); // parse the first token
}
TSqlTokenType TSqlParser::NextToken()
{
return iSql.NextToken(iToken);
}
CSqlSearchCondition* TSqlParser::SqlError()
{
if (!Error())
iToken.SetError(KErrArgument);
return 0;
}
TSqlTokenType TSqlParser::SqlErrorL()
//
// Report a SQL syntax error
//
{
return TSqlTokenType(__LEAVE_IF_ERROR(KErrArgument));
}
TSqlTokenType TSqlParser::Parse(TSqlKeyword aKeyword)
//
// look for requested keyword, skip to the next token if found
// return the next token if found, else ESqlNoToken (==0)
//
{
return TSqlLexer::IsKeyword(aKeyword,iToken) ? NextToken() : ESqlNoToken;
}
TSqlTokenType TSqlParser::ParseL(TSqlTokenType aToken)
//
// parse the desired token
//
{
return iToken!=aToken ? SqlErrorL() : NextToken();
}
TSqlTokenType TSqlParser::ParseL(TSqlKeyword aKeyword)
//
// parse the desired keyword
//
{
TSqlTokenType t=Parse(aKeyword);
return t==ESqlNoToken ? SqlErrorL() : t;
}
TSqlKeyword TSqlParser::Keyword()
//
// parse a keyword
//
{
TSqlKeyword k=TSqlLexer::Keyword(iToken);
if (k!=ESqlNotKeyword)
NextToken();
return k;
}
TSqlTokenType TSqlParser::RightBracketL()
//
// parse a right bracket, and fail if not found
//
{
return ParseL(ESqlRightBracket);
}
void TSqlParser::EndL()
//
// Check that the SQL has been fully parsed
//
{
if (iToken!=ESqlEos)
SqlErrorL();
}
TSqlTokenType TSqlParser::IdentifierL(TPtrC& aIdentifier)
//
// parse an identifer, fail if not found
//
{
if (iToken!=ESqlIdentifier)
return SqlErrorL();
const TText* p=iToken.Literal().Ptr();
TInt len=iToken.Literal().End()-p;
aIdentifier.Set(p,len);
return NextToken();
}
TPtrC TSqlParser::IdentifierL()
//
// parse an identifer, fail if not found
//
{
if (iToken!=ESqlIdentifier)
SqlErrorL();
return iToken.Literal().DesC();
}
TSqlTokenType TSqlParser::ColumnNameL(RSqlColumnList& aList)
//
// Parse a column-identifier
//
{
__LEAVE_IF_ERROR(aList.Append(IdentifierL()));
return NextToken();
}
TSqlTokenType TSqlParser::ColumnListL(RSqlColumnList& aList)
//
// Parse a column-identifier-comma-list
//
{
for (;;)
{
TSqlTokenType t=ColumnNameL(aList);
if (t!=ESqlComma)
return t;
NextToken();
}
}
TSqlTokenType TSqlParser::AddColumnSpecL(TDbCol& aDef)
//
// Parse an add-column-spec
//
{
aDef.iAttributes=0;
aDef.iName=IdentifierL();
NextToken();
//
TDbColType type;
switch (Keyword())
{
case ESqlKeyword_bit:
type=EDbColBit;
break;
case ESqlKeyword_tinyint:
type=EDbColInt8;
break;
case ESqlKeyword_smallint:
type=EDbColInt16;
break;
case ESqlKeyword_integer:
type=EDbColInt32;
break;
case ESqlKeyword_counter:
aDef.iAttributes=aDef.EAutoIncrement;
type=EDbColUint32;
break;
case ESqlKeyword_bigint:
type=EDbColInt64;
break;
case ESqlKeyword_real:
type=EDbColReal32;
break;
case ESqlKeyword_double:
//Return value is intentionaly not checked. Parse() checks for
//an optional SQL keyword and calls internal checks.
//coverity[check_return]
//coverity[unchecked_value]
Parse(ESqlKeyword_precision);
// fall through
case ESqlKeyword_float:
type=EDbColReal64;
break;
case ESqlKeyword_date:
case ESqlKeyword_time:
case ESqlKeyword_timestamp:
type=EDbColDateTime;
break;
case ESqlKeyword_char:
case ESqlKeyword_varchar:
type=EDbColText;
break;
case ESqlKeyword_binary:
case ESqlKeyword_varbinary:
type=EDbColBinary;
break;
case ESqlKeyword_unsigned: // unsigned tinyint, smallint or integer
switch (Keyword())
{
case ESqlKeyword_tinyint:
type=EDbColUint8;
break;
case ESqlKeyword_smallint:
type=EDbColUint16;
break;
case ESqlKeyword_integer:
type=EDbColUint32;
break;
default:
return SqlErrorL();
};
break;
case ESqlKeyword_long: // varchar or varbinary
switch (Keyword())
{
case ESqlKeyword_varchar:
type=EDbColLongText;
break;
case ESqlKeyword_varbinary:
type=EDbColLongBinary;
break;
default:
return SqlErrorL();
};
break;
default:
return SqlErrorL();
}
aDef.iType=type;
//
// get any optional length
aDef.iMaxLength=KDbUndefinedLength;
TSqlTokenType t=iToken.Type();
switch (type)
{
case EDbColText:
case EDbColBinary:
if (t==ESqlLeftBracket)
{
if (NextToken()==ESqlLiteralInt)
{
iToken.Literal().ToInt32L();
aDef.iMaxLength=iToken.Literal().Int32();
NextToken();
t=RightBracketL();
}
else
return SqlErrorL();
}
break;
default:
break;
}
return t;
}
CSqlSearchCondition* TSqlParser::SearchCondition(TInt aNot)
//
// Parse a search-condition
//
{
CSqlSearchCondition* left=BooleanTerm(aNot);
if (left==0 || Parse(ESqlKeyword_or)==ESqlNoToken)
return left;
return CSqlMultiNode::New(aNot ? CSqlMultiNode::EAnd : CSqlMultiNode::EOr,left,SearchCondition(aNot));
}
CSqlSearchCondition* TSqlParser::BooleanTerm(TInt aNot)
//
// Parse a boolean-term
//
{
CSqlSearchCondition* left=BooleanFactor(aNot);
if (left==0 || Parse(ESqlKeyword_and)==ESqlNoToken)
return left;
return CSqlMultiNode::New(aNot ? CSqlMultiNode::EOr : CSqlMultiNode::EAnd,left,BooleanTerm(aNot));
}
CSqlSearchCondition* TSqlParser::BooleanFactor(TInt aNot)
//
// Parse a boolean-factor
//
{
while (Parse(ESqlKeyword_not))
aNot=~aNot;
return BooleanPrimary(aNot);
}
CSqlSearchCondition* TSqlParser::BooleanPrimary(TInt aNot)
//
// Parse a boolean-factor
//
{
// brackets only allowed in this element, so this ordering is valid
if (iToken!=ESqlLeftBracket)
return Predicate(aNot);
// bracketed search condition
NextToken();
CSqlSearchCondition* node=SearchCondition(aNot);
if (!node)
return node;
if (iToken==ESqlRightBracket)
{
NextToken();
return node;
}
delete node;
return SqlError();
}
CSqlSearchCondition* TSqlParser::Predicate(TInt aNot)
//
// Parse predicate
// On null return, error will already be set
//
{
if (iToken!=ESqlIdentifier) // column name
return SqlError();
TPtrC column(iToken.Literal().DesC());
TSqlTokenType t=NextToken();
if (t==ESqlIdentifier)
{ // like-predicate or null-predicate
switch (Keyword())
{
case ESqlKeyword_is: // IS [NOT] NULL
if (Parse(ESqlKeyword_not))
aNot=~aNot;
if (Parse(ESqlKeyword_null)==ESqlNoToken)
return SqlError();
return new CSqlNullPredicate(aNot ? CSqlNullPredicate::EIsNotNull : CSqlNullPredicate::EIsNull,column);
case ESqlKeyword_not: // NOT LIKE
if (Parse(ESqlKeyword_like)==ESqlNoToken)
return SqlError();
aNot=~aNot;
// drop through to Like
case ESqlKeyword_like: // LIKE
{
if (iToken!=ESqlLiteralText)
return SqlError();
//Following code is for the implementation of limited-ESCAPE-clause
const TText *next = iSql.Next(); //remember thecurrent position in the SQL statement
RSqlLiteral pattern = iToken.Literal(); //this is the LIKE pattern, remember it, because the next keyword might be ESCAPE
NextToken(); // Searching for ESCAPE keyword
if (Keyword() == ESqlKeyword_escape)
{
if (pattern.DesC().Length() <= 0 || pattern.DesC().Length() > (KMaxSegmentLength + 2 ))
return SqlError();
TPtrC escapeChar(iToken.Literal().DesC());
if (escapeChar.Length() <= 0)
return SqlError();
TChar escchar = escapeChar[0];
TText newPattern[KMaxSegmentLength + 2]; // '*' can come as first and last char
TInt count = PatternFilter(pattern.DesC(),escchar, newPattern);//remove the escape characters from the pattern and store it in "newPattern" variable
if (count <=0 )
return SqlError();
iToken.Literal().SetText(newPattern,(newPattern + count));
// copy the text to RSqlLiteral as "newPattern" is stack based variable and will go out of scope
if (iToken.Literal().CopyText() != KErrNone)
{
return SqlError();
}
CSqlSearchCondition* node = new CSqlLikePredicate(aNot ? CSqlLikePredicate::ENotLike : CSqlLikePredicate::ELike,column,iToken.Literal());
//cleanup iToken.Literal(), because if there is another LIKE predicate, the allocated by CopyText() buffer will
//be shared between two RSqlLiteral instances and RSqlLiteral::Close() call will crash.
//RSqlLiteral::RSqlLiteral() will set the "iBuffer" data member to NULL.
iToken.Literal() = RSqlLiteral();
if (node)
{
NextToken();
node->iIsEscape = ETrue;
}
return node;
}
else
{
//Set to the previous node
iSql.Set(next);
CSqlSearchCondition* node = new CSqlLikePredicate(aNot ? CSqlLikePredicate::ENotLike : CSqlLikePredicate::ELike,column,pattern);
if(node)
NextToken();
return node;
}
}
default:
return SqlError();
}
}
// Comparison predicate...
CSqlSearchCondition::TType op;
switch (t)
{
case ESqlGreaterEqual:
aNot=~aNot;
// drop through to less
case ESqlLess:
op=aNot ? CSqlSearchCondition::EGreaterEqual : CSqlSearchCondition::ELess;
break;
case ESqlGreater:
aNot=~aNot;
// drop through to less equal
case ESqlLessEqual:
op=aNot ? CSqlSearchCondition::EGreater: CSqlSearchCondition::ELessEqual;
break;
case ESqlNotEqual:
aNot=~aNot;
// drop through to equal
case ESqlEqual:
op=aNot ? CSqlSearchCondition::ENotEqual : CSqlSearchCondition::EEqual;
break;
default:
return SqlError();
}
t=NextToken();
if (t!=ESqlLiteralInt && t!=ESqlLiteralReal && t!=ESqlLiteralTime && t!=ESqlLiteralText)
return SqlError();
CSqlSearchCondition* node=new CSqlCompPredicate(op,column,iToken.Literal());
if (node)
NextToken();
return node;
}
CSqlSearchCondition* TSqlParser::SearchConditionL()
//
// Parse a search-condition
//
{
CSqlSearchCondition* sc=SearchCondition(0);
if (!sc)
{
__LEAVE_IF_ERROR(Error()); // syntax error
User::LeaveNoMemory(); // otherwise a OOM error
}
return sc;
}
void TSqlParser::SortSpecificationL(CDbKey& aKey)
//
// Parse a sort-specification
//
{
for (;;)
{
TDbKeyCol col(IdentifierL());
NextToken();
if (Parse(ESqlKeyword_desc))
col.iOrder=col.EDesc;
else
{
//Return value is intentionaly not checked. Parse() checks for
//an optional SQL keyword and calls internal checks.
//coverity[check_return]
//coverity[unchecked_value]
Parse(ESqlKeyword_asc);
col.iOrder=col.EAsc;
}
aKey.AddL(col);
if (iToken!=ESqlComma)
break;
NextToken();
}
}
CSqlQuery* TSqlParser::QueryLC()
//
// Generate a CSqlQuery
//
{
CSqlQuery* query=new(ELeave) CSqlQuery;
CleanupStack::PushL(query);
if (ParseL(ESqlKeyword_select)==ESqlAsterisk)
NextToken();
else
ColumnListL(query->iColumns);
ParseL(ESqlKeyword_from);
IdentifierL(query->iTable);
if (Parse(ESqlKeyword_where))
query->iSearchCondition=SearchConditionL();
if (Parse(ESqlKeyword_order))
{
ParseL(ESqlKeyword_by);
SortSpecificationL(query->SortSpecificationL());
}
EndL();
return query;
}
CSqlSearchCondition* TSqlParser::SearchConditionLC()
//
// Parse a standalone search-condition
//
{
CSqlSearchCondition* sc=SearchConditionL();
CleanupStack::PushL(sc);
EndL();
return sc;
}
CSqlDDLStatement* TSqlParser::CreateTableLC()
//
// Parse a CREATE TABLE statement
//
{
CSqlCreateTableStatement* statement=new(ELeave) CSqlCreateTableStatement;
CleanupStack::PushL(statement);
IdentifierL(statement->iName);
ParseL(ESqlLeftBracket);
TDbCol def;
for (;;)
{
AddColumnSpecL(def);
if (Parse(ESqlKeyword_not))
{
ParseL(ESqlKeyword_null);
def.iAttributes|=TDbCol::ENotNull;
}
statement->iColumns.AddL(def);
if (iToken!=ESqlComma)
break;
NextToken();
}
RightBracketL();
return statement;
}
CSqlDDLStatement* TSqlParser::DropTableLC()
//
// Parse a DROP TABLE statement
//
{
CSqlDropTableStatement* statement=new(ELeave) CSqlDropTableStatement;
CleanupStack::PushL(statement);
IdentifierL(statement->iName);
return statement;
}
CSqlDDLStatement* TSqlParser::AlterTableLC()
//
// Parse a CREATE TABLE statement
//
{
CSqlAlterTableStatement* statement=new(ELeave) CSqlAlterTableStatement;
CleanupStack::PushL(statement);
IdentifierL(statement->iName);
TSqlTokenType t=Parse(ESqlKeyword_add);
if (t!=ESqlNoToken)
{
TDbCol def;
if (t==ESqlLeftBracket)
{
NextToken();
for (;;)
{
t=AddColumnSpecL(def);
statement->iAdd.AddL(def);
if (t!=ESqlComma)
break;
NextToken();
}
RightBracketL();
}
else
{
AddColumnSpecL(def);
statement->iAdd.AddL(def);
}
}
t=Parse(ESqlKeyword_drop);
if (t!=ESqlNoToken)
{
if (t!=ESqlLeftBracket)
ColumnNameL(statement->iDrop);
else
{
NextToken();
ColumnListL(statement->iDrop);
RightBracketL();
}
}
return statement;
}
CSqlDDLStatement* TSqlParser::CreateIndexLC(TBool aUnique)
//
// Parse a CREATE INDEX statement
//
{
CSqlCreateIndexStatement* statement=new(ELeave) CSqlCreateIndexStatement;
CleanupStack::PushL(statement);
if (aUnique)
statement->iKey.MakeUnique();
IdentifierL(statement->iName);
ParseL(ESqlKeyword_on);
IdentifierL(statement->iTable);
ParseL(ESqlLeftBracket);
SortSpecificationL(statement->iKey);
RightBracketL();
return statement;
}
CSqlDDLStatement* TSqlParser::DropIndexLC()
//
// Parse a DROP INDEX statement
//
{
CSqlDropIndexStatement* statement=new(ELeave) CSqlDropIndexStatement;
CleanupStack::PushL(statement);
IdentifierL(statement->iName);
ParseL(ESqlKeyword_from);
IdentifierL(statement->iTable);
return statement;
}
CSqlDDLStatement* TSqlParser::DDLStatementLC()
{
CSqlDDLStatement* statement;
if (Parse(ESqlKeyword_create))
{
if (Parse(ESqlKeyword_table))
statement=CreateTableLC();
else
{
TSqlTokenType unique=Parse(ESqlKeyword_unique);
ParseL(ESqlKeyword_index);
statement=CreateIndexLC(unique);
}
}
else if (Parse(ESqlKeyword_drop))
{
if (Parse(ESqlKeyword_table))
statement=DropTableLC();
else
{
ParseL(ESqlKeyword_index);
statement=DropIndexLC();
}
}
else
{
ParseL(ESqlKeyword_alter);
ParseL(ESqlKeyword_table);
statement=AlterTableLC();
}
EndL();
return statement;
}
TSqlTokenType TSqlParser::ColumnValueL(CSqlValues& aValues)
//
// parse a column-value and add to aValues
//
{
switch (iToken.Type())
{
case ESqlLiteralInt:
case ESqlLiteralReal:
case ESqlLiteralTime:
case ESqlLiteralText:
aValues.AddL(iToken.Literal());
return NextToken();
case ESqlIdentifier: // NULL
{
TSqlTokenType t=ParseL(ESqlKeyword_null);
aValues.AddL(RSqlLiteral()); // default c'ted RSqlLiteral is null
return t;
}
default: // SQL error
return SqlErrorL();
}
}
CSqlDMLStatement* TSqlParser::InsertStatementLC()
//
// parse an insert-statement
//
{
ParseL(ESqlKeyword_into);
CSqlInsertStatement* statement=CSqlInsertStatement::NewLC();
if (IdentifierL(statement->iQuery.iTable)==ESqlLeftBracket)
{
NextToken();
ColumnListL(statement->iQuery.iColumns);
RightBracketL();
}
ParseL(ESqlKeyword_values);
ParseL(ESqlLeftBracket);
CSqlValues& values=statement->ValuesL();
while (ColumnValueL(values)==ESqlComma)
NextToken();
RightBracketL();
return statement;
}
CSqlDMLStatement* TSqlParser::UpdateStatementLC()
//
// parse an update-statement
//
{
CSqlModifyStatement* statement=CSqlModifyStatement::NewLC();
IdentifierL(statement->iQuery.iTable);
ParseL(ESqlKeyword_set);
CSqlValues& values=statement->ValuesL();
for (;;)
{
ColumnNameL(statement->iQuery.iColumns);
ParseL(ESqlEqual);
if (ColumnValueL(values)!=ESqlComma)
break;
NextToken();
}
if (Parse(ESqlKeyword_where))
statement->iQuery.iSearchCondition=SearchConditionL();
return statement;
}
CSqlDMLStatement* TSqlParser::DeleteStatementLC()
//
// parse a delete-statement
//
{
ParseL(ESqlKeyword_from);
CSqlModifyStatement* statement=CSqlModifyStatement::NewLC();
IdentifierL(statement->iQuery.iTable);
if (Parse(ESqlKeyword_where))
statement->iQuery.iSearchCondition=SearchConditionL();
return statement;
}
CSqlDMLStatement* TSqlParser::DMLStatementLC()
{
CSqlDMLStatement* statement;
if (Parse(ESqlKeyword_insert))
statement=InsertStatementLC();
else if (Parse(ESqlKeyword_update))
statement=UpdateStatementLC();
else
{
ParseL(ESqlKeyword_delete);
statement=DeleteStatementLC();
}
EndL();
return statement;
}
Sql::TStatementType TSqlParser::Type()
{
TSqlKeyword k=TSqlLexer::Keyword(iToken);
if (k==ESqlKeyword_create || k==ESqlKeyword_alter || k==ESqlKeyword_drop)
return Sql::EDDL;
if (k==ESqlKeyword_insert || k==ESqlKeyword_update || k==ESqlKeyword_delete)
return Sql::EDML;
return Sql::ENone;
}
TInt TSqlParser::PatternFilter(const TDesC& aPattern,const TChar aEscape,TText *aNewPatternBuffer )
{
TInt length = aPattern.Length();
TInt count =0;
// Ensure that the pattern begins and ends with an '*'
if ((length < 2) || (aPattern[0] != KMatchAny || aPattern[length-1] != KMatchAny))
{
return KSqlError;
}
for (TInt i = 1 ; i< length -1 ;++i)
{
if (aPattern[i]== (TUint)aEscape)
{
if ((aPattern[i + 1] == KMatchAny) || (aPattern[i + 1] == KMatchOne) || (aPattern[i + 1] == (TUint)aEscape))
{
i++;
aNewPatternBuffer[count++] = aPattern[i];
}
else
{
return KSqlError;
}
}
else
{
if ((aPattern[i] == KMatchAny || aPattern[i] == KMatchOne) )
{
return KSqlError;
}
else
{
aNewPatternBuffer[count++] = aPattern[i];
}
}
}
return count;
}