diff -r 000000000000 -r 08ec8eefde2f persistentstorage/dbms/usql/UQ_PARSE.CPP --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/dbms/usql/UQ_PARSE.CPP Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,807 @@ +// 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; + }