+ fcl-db: sql parser:

- add support for Firebird SET TERM statements (changing SQL statement terminator)
- fix sigsegv (e.g. running TTestCheckParser.TestNotBetween)

git-svn-id: trunk@27907 -
This commit is contained in:
reiniero 2014-06-09 07:54:13 +00:00
parent e699c6948f
commit 75169f7a2b
3 changed files with 230 additions and 71 deletions

View File

@ -59,8 +59,10 @@ Type
procedure Expect(aToken: TSQLToken);
// Verify that current token is one of the expected tokens; raise error if not
procedure Expect(aTokens: TSQLTokens);
// Expects aToken as current token and eats it
// Expects aToken as current token and eats it by calling GetNextToken
procedure Consume(aToken: TSQLToken);
// Expects aTokens tokens and eats the token by calling GetNextToken
procedure Consume(aTokens: TSQLTokens);
procedure Error(Msg : String);
procedure Error(Fmt : String; Args : Array of const);
// Expression support
@ -99,6 +101,7 @@ Type
function ParseCreateViewStatement(AParent: TSQLElement; IsAlter: Boolean): TSQLCreateOrAlterStatement;
function ParseCreateTriggerStatement(AParent: TSQLElement; IsAlter: Boolean): TSQLCreateOrAlterStatement;
function ParseSetGeneratorStatement(AParent: TSQLElement) : TSQLSetGeneratorStatement;
function ParseSetTermStatement(AParent: TSQLElement) : TSQLSetTermStatement;
function ParseCreateDatabaseStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateDatabaseStatement;
function ParseCreateShadowStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateShadowStatement;
function ParseAlterDatabaseStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLAlterDatabaseStatement;
@ -146,6 +149,7 @@ Type
Function ParseUpdateStatement(AParent : TSQLElement) : TSQLUpdateStatement;
Function ParseInsertStatement(AParent : TSQLElement) : TSQLInsertStatement;
Function ParseDeleteStatement(AParent : TSQLElement) : TSQLDeleteStatement;
// Parses both create and alter statements
Function ParseCreateStatement(AParent : TSQLElement; IsAlter : Boolean = False) : TSQLCreateOrAlterStatement;
Function ParseDropStatement(AParent : TSQLElement) : TSQLDropStatement;
Function ParseRollbackStatement(AParent : TSQLElement) : TSQLRollbackStatement;
@ -156,6 +160,12 @@ Type
Function ParseRevokeStatement(AParent: TSQLElement): TSQLGrantStatement;
Function Parse : TSQLElement;
Function ParseScript(AllowPartial : Boolean = False) : TSQLElementList;
// Gets statement terminator (as e.g. used in SET TERM) so statements like
// EXECUTE BLOCK or CREATE PROCEDURE that contain semicolons can be parsed
function GetStatementTerminator: string;
// Sets statement terminator (as e.g. used in SET TERM) so statements like
// EXECUTE BLOCK or CREATE PROCEDURE that contain semicolons can be parsed
procedure SetStatementTerminator(AValue: string);
// Auxiliary stuff
Function CurrentToken : TSQLToken;
Function CurrentTokenString : String;
@ -253,6 +263,12 @@ begin
GetNextToken;
end;
procedure TSQLParser.Consume(aTokens: TSQLTokens);
begin
Expect(aTokens);
GetNextToken;
end;
function TSQLParser.CurSource: String;
begin
Result:=FScanner.CurFilename;
@ -304,7 +320,8 @@ begin
Result.SourcePos:=CurPos;
end;
Function TSQLParser.ParseTableRef(AParent : TSQLSelectStatement) : TSQLTableReference;
function TSQLParser.ParseTableRef(AParent: TSQLSelectStatement
): TSQLTableReference;
Var
T : TSQLSimpleTablereference;
J : TSQLJoinTableReference;
@ -362,7 +379,8 @@ begin
until Not (CurrentToken in [tsqlInner,tsqlJoin,tsqlOuter,tsqlLeft,tsqlRight]);
end;
Procedure TSQLParser.ParseFromClause(AParent : TSQLSelectStatement; AList : TSQLElementList);
procedure TSQLParser.ParseFromClause(AParent: TSQLSelectStatement;
AList: TSQLElementList);
Var
T : TSQLTableReference;
@ -380,7 +398,8 @@ begin
until Done;
end;
Procedure TSQLParser.ParseSelectFieldList(AParent : TSQLSelectStatement; AList : TSQLElementList; Singleton : Boolean);
procedure TSQLParser.ParseSelectFieldList(AParent: TSQLSelectStatement;
AList: TSQLElementList; Singleton: Boolean);
Var
F : TSQLSelectField;
B : Boolean;
@ -429,7 +448,8 @@ begin
until (CurrentToken=tsqlFROM);
end;
Procedure TSQLParser.ParseGroupBy(AParent : TSQLSelectStatement; AList : TSQLElementList);
procedure TSQLParser.ParseGroupBy(AParent: TSQLSelectStatement;
AList: TSQLElementList);
Var
N : TSQLStringType;
@ -455,7 +475,8 @@ begin
until (CurrentToken<>tsqlComma);
end;
Function TSQLParser.ParseForUpdate(AParent : TSQLSelectStatement) : TSQLElementList;
function TSQLParser.ParseForUpdate(AParent: TSQLSelectStatement
): TSQLElementList;
begin
// On entry we're on the FOR token.
@ -474,7 +495,8 @@ begin
end;
end;
Procedure TSQLParser.ParseOrderBy(AParent : TSQLSelectStatement; AList : TSQLElementList);
procedure TSQLParser.ParseOrderBy(AParent: TSQLSelectStatement;
AList: TSQLElementList);
Var
O : TSQLOrderByElement;
@ -525,7 +547,7 @@ begin
until (CurrentToken<>tsqlComma);
end;
Function TSQLParser.ParseSelectPlan(AParent : TSQLElement) : TSQLSelectPlan;
function TSQLParser.ParseSelectPlan(AParent: TSQLElement): TSQLSelectPlan;
Var
E : TSQLSelectPlanExpr;
@ -789,7 +811,7 @@ begin
end;
end;
Function TSQLParser.ParseTableFieldDef(AParent : TSQLElement) : TSQLTableFieldDef;
function TSQLParser.ParseTableFieldDef(AParent: TSQLElement): TSQLTableFieldDef;
begin
// on entry, we're on the field name
Result:=TSQLTableFieldDef(CreateElement(TSQLTableFieldDef,AParent));
@ -1163,7 +1185,8 @@ begin
end;
end;
Procedure TSQLParser.ParseProcedureParamList(AParent: TSQLElement; AList : TSQLElementList);
procedure TSQLParser.ParseProcedureParamList(AParent: TSQLElement;
AList: TSQLElementList);
Var
P : TSQLProcedureParamDef;
@ -1187,7 +1210,8 @@ begin
Consume(tsqlBraceClose);
end;
Procedure TSQLParser.ParseCreateProcedureVariableList(AParent: TSQLElement; AList : TSQLElementList);
procedure TSQLParser.ParseCreateProcedureVariableList(AParent: TSQLElement;
AList: TSQLElementList);
Var
P : TSQLProcedureParamDef;
@ -1212,7 +1236,7 @@ begin
Until (CurrentToken<>tsqlDeclare);
end;
Function TSQLParser.ParseIfStatement(AParent : TSQLElement) : TSQLIFStatement;
function TSQLParser.ParseIfStatement(AParent: TSQLElement): TSQLIFStatement;
Var
Pt : TSQLToken;
@ -1261,7 +1285,7 @@ begin
Until (CurrentToken<>tsqlComma);
end;
Function TSQLParser.ParseForStatement(AParent : TSQLElement) : TSQLForStatement;
function TSQLParser.ParseForStatement(AParent: TSQLElement): TSQLForStatement;
begin
// On Entry, we're on the FOR token
@ -1280,7 +1304,8 @@ begin
end;
end;
Function TSQLParser.ParseExceptionStatement(AParent : TSQLElement) : TSQLExceptionStatement;
function TSQLParser.ParseExceptionStatement(AParent: TSQLElement
): TSQLExceptionStatement;
begin
// On Entry, we're on the EXCEPTION token
@ -1296,7 +1321,8 @@ begin
end;
end;
Function TSQLParser.ParseAssignStatement(AParent : TSQLElement) : TSQLAssignStatement;
function TSQLParser.ParseAssignStatement(AParent: TSQLElement
): TSQLAssignStatement;
Var
N : TSQLStringType;
@ -1324,7 +1350,8 @@ begin
end;
end;
Function TSQLParser.ParsePostEventStatement(AParent : TSQLElement) : TSQLPostEventStatement;
function TSQLParser.ParsePostEventStatement(AParent: TSQLElement
): TSQLPostEventStatement;
begin
// On Entry, we're on the POST_EVENT token
@ -1344,7 +1371,8 @@ begin
end;
end;
Function TSQLParser.ParseWhileStatement(AParent : TSQLElement) : TSQLWhileStatement;
function TSQLParser.ParseWhileStatement(AParent: TSQLElement
): TSQLWhileStatement;
begin
// On entry, we're on the WHILE Token
@ -1362,7 +1390,7 @@ begin
end;
end;
Function TSQLParser.ParseWhenStatement(AParent : TSQLElement) : TSQLWhenStatement;
function TSQLParser.ParseWhenStatement(AParent: TSQLElement): TSQLWhenStatement;
Var
E : TSQLWhenException;
@ -1420,7 +1448,8 @@ begin
end;
end;
Function TSQLParser.ParseProcedureStatement(AParent : TSQLElement) : TSQLStatement;
function TSQLParser.ParseProcedureStatement(AParent: TSQLElement
): TSQLStatement;
begin
Result:=Nil;
@ -1455,7 +1484,8 @@ begin
end;
end;
Procedure TSQLParser.ParseStatementBlock(AParent: TSQLElement; Statements : TSQLElementList);
procedure TSQLParser.ParseStatementBlock(AParent: TSQLElement;
Statements: TSQLElementList);
Var
S: TSQLStatement;
@ -1467,7 +1497,7 @@ begin
S:=ParseProcedureStatement(AParent);
Statements.Add(S);
if not (PreviousToken=tsqlEnd) then
Consume(tsqlSemicolon);
Consume([tsqlSemicolon,tsqlStatementTerminator]);
end;
Consume(tsqlEnd);
end;
@ -1538,7 +1568,8 @@ begin
GetNextToken; // Comma;
end;
Procedure TSQLParser.ParseCharTypeDefinition(Out DT : TSQLDataType; Out Len : Integer; Out ACharset : TSQLStringType);
procedure TSQLParser.ParseCharTypeDefinition(out DT: TSQLDataType; out
Len: Integer; out ACharset: TSQLStringType);
begin
Len:=0;
@ -1592,7 +1623,8 @@ begin
end;
end;
Procedure TSQLParser.ParseBlobDefinition(Var ASegmentSize,ABlobType : Integer; Var ACharset : TSQLStringType);
procedure TSQLParser.ParseBlobDefinition(var ASegmentSize, ABlobType: Integer;
var ACharset: TSQLStringType);
begin
// On entry, we are on the blob token.
@ -1647,7 +1679,8 @@ begin
end;
end;
Function TSQLParser.ParseForeignKeyDefinition(AParent : TSQLElement) : TSQLForeignKeyDefinition;
function TSQLParser.ParseForeignKeyDefinition(AParent: TSQLElement
): TSQLForeignKeyDefinition;
// On entry, we're on ON Return true if On delete
Function ParseForeignKeyAction (Out Res : TForeignKeyAction) : Boolean;
@ -1723,7 +1756,8 @@ begin
end;
end;
Function TSQLParser.ParseFieldConstraint(AParent : TSQLElement) : TSQLFieldConstraint;
function TSQLParser.ParseFieldConstraint(AParent: TSQLElement
): TSQLFieldConstraint;
Var
N : TSQLStringType;
@ -1800,7 +1834,8 @@ begin
end;
end;
Function TSQLParser.ParseTypeDefinition(AParent : TSQLElement; Flags : TParseTypeFlags) : TSQLTypeDefinition;
function TSQLParser.ParseTypeDefinition(AParent: TSQLElement;
Flags: TParseTypeFlags): TSQLTypeDefinition;
Var
TN : String;
@ -2047,7 +2082,8 @@ begin
Error('Unexpected end of command');
end;
Function TSQLParser.ParseExprLevel1(AParent : TSQLElement; EO : TExpressionOptions) : TSQLExpression;
function TSQLParser.ParseExprLevel1(AParent: TSQLElement; EO: TExpressionOptions
): TSQLExpression;
var
tt: TSQLToken;
@ -2098,7 +2134,7 @@ begin
end;
end;
Function TSQLParser.ParseInoperand(AParent : TSQLElement) : TSQLExpression;
function TSQLParser.ParseInoperand(AParent: TSQLElement): TSQLExpression;
Var
S : TSQLSelectExpression;
@ -2133,7 +2169,8 @@ begin
end;
end;
Function TSQLParser.ParseExprLevel2(AParent : TSQLElement; EO : TExpressionOptions) : TSQLExpression;
function TSQLParser.ParseExprLevel2(AParent: TSQLElement; EO: TExpressionOptions
): TSQLExpression;
var
tt: TSQLToken;
Right : TSQLExpression;
@ -2141,7 +2178,7 @@ var
T : TSQLTernaryExpression;
O : TSQLBinaryOperation;
U : TSQLUnaryExpression;
I,bw,doin : Boolean;
Inverted,bw,doin : Boolean;
begin
{$ifdef debugexpr} Writeln('Level 2 ',TokenInfos[CurrentToken],': ',CurrentTokenString);{$endif debugexpr}
@ -2150,11 +2187,11 @@ begin
if (CurrentToken in sqlComparisons) then
begin
tt:=CurrentToken;
I:=CurrentToken=tsqlnot;
Inverted:=CurrentToken=tsqlnot;
CheckEOF;
GetNextToken;
CheckEOF;
if I then
if Inverted then
begin
tt:=CurrentToken;
if Not (tt in sqlInvertableComparisons) then
@ -2174,12 +2211,13 @@ begin
GetNextToken;
if not (tt=tsqlis) then
UnexpectedToken;
I:=true;
Inverted:=true;
end;
end;
bw:=False;
doin:=false;
B:=nil; //needed for test later
Case tt of
tsqlLT : O:=boLT;
tsqlLE : O:=boLE;
@ -2237,8 +2275,8 @@ begin
B.Right:=Right;
end;
end;
If I then
if B.Operation=boIs then
If Inverted then
if (Assigned(B)) and (B.Operation=boIs) then
B.Operation:=boIsNot
else
begin
@ -2254,7 +2292,8 @@ begin
end;
end;
Function TSQLParser.ParseExprLevel3(AParent : TSQLElement; EO : TExpressionOptions) : TSQLExpression;
function TSQLParser.ParseExprLevel3(AParent: TSQLElement; EO: TExpressionOptions
): TSQLExpression;
Function NegativeNumber : Boolean; inline;
@ -2302,7 +2341,8 @@ begin
end;
end;
Function TSQLParser.ParseExprLevel4(AParent : TSQLElement; EO : TExpressionOptions) : TSQLExpression;
function TSQLParser.ParseExprLevel4(AParent: TSQLElement; EO: TExpressionOptions
): TSQLExpression;
var
tt : TSQLToken;
@ -2333,7 +2373,8 @@ begin
end;
end;
Function TSQLParser.ParseExprLevel5(AParent : TSQLElement; EO : TExpressionOptions) : TSQLExpression;
function TSQLParser.ParseExprLevel5(AParent: TSQLElement; EO: TExpressionOptions
): TSQLExpression;
Var
tt : tsqltoken;
@ -2365,7 +2406,8 @@ begin
end;
end;
Function TSQLParser.ParseExprLevel6(AParent : TSQLElement ; EO : TExpressionOptions ) : TSQLExpression;
function TSQLParser.ParseExprLevel6(AParent: TSQLElement; EO: TExpressionOptions
): TSQLExpression;
@ -2399,7 +2441,8 @@ begin
Result:=ParseExprPrimitive(AParent,EO);
end;
Function TSQLParser.ParseIdentifierList(AParent : TSQLElement; AList : TSQLelementList) : integer;
function TSQLParser.ParseIdentifierList(AParent: TSQLElement;
AList: TSQLelementList): integer;
Var
Done : Boolean;
@ -2420,7 +2463,8 @@ begin
GetNextToken;
end;
Function TSQLParser.ParseValueList(AParent : TSQLElement; EO : TExpressionOptions) : TSQLElementList;
function TSQLParser.ParseValueList(AParent: TSQLElement; EO: TExpressionOptions
): TSQLElementList;
Var
E : TSQLExpression;
@ -2467,15 +2511,16 @@ begin
Error(SErrUnexpectedTokenOf,[CurrentTokenString,S]);
end;
function TSQLParser.CreateIdentifier(AParent : TSQLElement; Const AName: TSQLStringType
): TSQLIdentifierName;
function TSQLParser.CreateIdentifier(AParent: TSQLElement;
const AName: TSQLStringType): TSQLIdentifierName;
begin
Result:=TSQLIdentifierName(CreateElement(TSQLIdentifierName,AParent));
Result.Name:=AName;
end;
Function TSQLParser.ParseExprAggregate(AParent : TSQLElement; EO : TExpressionOptions) : TSQLAggregateFunctionExpression;
function TSQLParser.ParseExprAggregate(AParent: TSQLElement;
EO: TExpressionOptions): TSQLAggregateFunctionExpression;
begin
Result:=TSQLAggregateFunctionExpression(CreateElement(TSQLAggregateFunctionExpression,AParent));
try
@ -2514,7 +2559,8 @@ begin
end;
end;
Function TSQLParser.ParseExprPrimitive(AParent : TSQLElement; EO : TExpressionOptions) : TSQLExpression;
function TSQLParser.ParseExprPrimitive(AParent: TSQLElement;
EO: TExpressionOptions): TSQLExpression;
Var
L : TSQLElementList;
@ -2900,6 +2946,38 @@ begin
end;
end;
function TSQLParser.ParseSetTermStatement(AParent: TSQLElement
): TSQLSetTermStatement;
var
ExistingStatTerm: string;
begin
// On entry, we're on the 'TERM' token
Consume(tsqlTerm) ;
try
Result:=TSQLSetTermStatement(CreateElement(TSQLSetTermStatement,AParent));
expect([tsqlSemiColon,tsqlStatementTerminator,tsqlSymbolLiteral,tsqlString]);
// Already set the expression's new value to the new terminator, but do not
// change tSQLStatementTerminator as GetNextToken etc need the old one to
// detect the closing terminator
case CurrentToken of
tsqlSemiColon, tsqlStatementTerminator: Result.NewValue:=TokenInfos[CurrentToken];
tsqlSymbolLiteral, tsqlString: Result.NewValue:=CurrentTokenString;
end;
// Expect the old terminator...
GetNextToken;
// Parser will give tsqlSemicolon rather than tsqlStatementTerminator:
if TokenInfos[tsqlStatementTerminator]=TokenInfos[tsqlSEMICOLON] then
Expect(tsqlSEMICOLON)
else
Expect(tsqlStatementTerminator);
//... and now set the new terminator:
TokenInfos[tsqlStatementTerminator]:=Result.NewValue; //process new terminator value
except
FreeAndNil(Result);
Raise;
end;
end;
function TSQLParser.ParseSecondaryFile(AParent: TSQLElement) : TSQLDatabaseFileInfo;
Var
@ -3163,7 +3241,7 @@ begin
end;
function TSQLParser.ParseRollbackStatement(AParent: TSQLElement
): TSQLRollBackStatement;
): TSQLRollbackStatement;
begin
@ -3302,7 +3380,8 @@ begin
// On Entry, we're on the SET statement
Consume(tsqlSet);
Case CurrentToken of
tsqlGenerator : Result:=ParseSetGeneratorStatement(AParent)
tsqlGenerator : Result:=ParseSetGeneratorStatement(AParent);
tsqlTerm : Result:=ParseSetTermStatement(AParent);
else
// For the time being
UnexpectedToken;
@ -3375,7 +3454,8 @@ begin
inherited Destroy;
end;
Function TSQLParser.ParseDeclareFunctionStatement(AParent : TSQLElement) : TSQLDeclareExternalFunctionStatement;
function TSQLParser.ParseDeclareFunctionStatement(AParent: TSQLElement
): TSQLDeclareExternalFunctionStatement;
begin
// On entry, we're on the EXTERNAL token
@ -3410,7 +3490,7 @@ begin
end;
end;
Function TSQLParser.ParseDeclareStatement(AParent : TSQLElement) : TSQLStatement;
function TSQLParser.ParseDeclareStatement(AParent: TSQLElement): TSQLStatement;
begin
@ -3818,7 +3898,7 @@ begin
else
UnexpectedToken;
end;
if Not (CurrentToken in [tsqlEOF,tsqlSemicolon]) then
if Not (CurrentToken in [tsqlEOF,tsqlSemicolon,tsqlStatementTerminator]) then
begin
FreeAndNil(Result);
if (CurrentToken=tsqlBraceClose) then
@ -3850,6 +3930,16 @@ begin
end;
end;
function TSQLParser.GetStatementTerminator: string;
begin
result:=TokenInfos[tsqlStatementTerminator];
end;
procedure TSQLParser.SetStatementTerminator(AValue: string);
begin
TokenInfos[tsqlStatementTerminator]:=AValue;;
end;
function TSQLParser.CurrentToken: TSQLToken;
begin
Result:=FCurrent;
@ -3863,13 +3953,14 @@ end;
function TSQLParser.GetNextToken: TSQLToken;
begin
FPrevious:=FCurrent;
// Set if not already peeked; otherwise fetch and look
If (FPeekToken<>tsqlUnknown) then
begin
FCurrent:=FPeekToken;
FCurrentString:=FPeekTokenString;
FPeekToken:=tsqlUnknown;
FPeekTokenString:='';
end
begin
FCurrent:=FPeekToken;
FCurrentString:=FPeekTokenString;
FPeekToken:=tsqlUnknown;
FPeekTokenString:='';
end
else
begin
FCurrent:=FScanner.FetchToken;

View File

@ -37,10 +37,16 @@ type
TSQLToken = (tsqlUnknown,
// Specials
tsqlEOF,tsqlWhiteSpace,tsqlString,tsqlIdentifier,tsqlIntegerNumber,tsqlFloatNumber,tsqlComment,
tsqlEOF,tsqlWhiteSpace,
tsqlString {string literal},
tsqlIdentifier {a table etc name},
tsqlSymbolLiteral {a literal containing symbols/punctuation marks},
tsqlIntegerNumber,tsqlFloatNumber,tsqlComment,
tsqlStatementTerminator {statement separator, usually semicolon but may be changed by code. For now, limited to semicolon and symbol literals not already defined like tsqlCOMMA},
tsqlBraceOpen,tsqlBraceClose,tsqlSquareBraceOpen,tsqlSquareBraceClose,
tsqlPlaceHolder,tsqlCOMMA,tsqlCOLON,tsqlDOT,tsqlSEMICOLON,tsqlGT,tsqlLT,
tsqlPLUS,tsqlMINUS,tsqlMUL,tsqlDIV,tsqlConcatenate,
tsqlPlaceHolder {question mark},
tsqlCOMMA,tsqlCOLON,tsqlDOT,tsqlSEMICOLON,
tsqlGT,tsqlLT,tsqlPLUS,tsqlMINUS,tsqlMUL,tsqlDIV,tsqlConcatenate,
tsqlEQ,tsqlGE,tsqlLE,tsqlNE,
{ Reserved words/keywords start here. They must be last }
{ Note: if adding before tsqlALL or after tsqlWHEN please update FirstKeyword/LastKeyword }
@ -62,7 +68,7 @@ type
tsqlPrecision, tsqlPRIMARY, tsqlProcedure, tsqlPosition, tsqlPlan, tsqlPassword, tsqlPage,tsqlPages,tsqlPageSize,tsqlPostEvent,tsqlPrivileges,tsqlPublic,
tsqlRIGHT, tsqlROLE, tsqlReferences, tsqlRollBack, tsqlRelease, tsqlretain, tsqlReturningValues,tsqlReturns, tsqlrevoke,
tsqlSELECT, tsqlSET, tsqlSINGULAR, tsqlSOME, tsqlSTARTING, tsqlSUM, tsqlSKIP,tsqlSUBTYPE,tsqlSize,tsqlSegment, tsqlSORT, tsqlSnapShot,tsqlSchema,tsqlShadow,tsqlSuspend,tsqlSQLCode,tsqlSmallint,
tSQLTABLE, tsqlText, tsqlTrigger, tsqlTime, tsqlTimeStamp, tsqlType, tsqlTo, tsqlTransaction, tsqlThen,
tSQLTABLE, tsqlText, tsqlTerm, tsqlTrigger, tsqlTime, tsqlTimeStamp, tsqlType, tsqlTo, tsqlTransaction, tsqlThen,
tsqlUNION, tsqlUPDATE, tsqlUPPER, tsqlUNIQUE, tsqlUSER,
tsqlValue, tsqlVALUES, tsqlVARIABLE, tsqlVIEW, tsqlVARCHAR,TSQLVARYING,
tsqlWHERE, tsqlWITH, tsqlWHILE, tsqlWork, tsqlWhen
@ -79,7 +85,12 @@ const
// Strings that represent tokens in TSQLToken
TokenInfos: array[TSQLToken] of string = ('unknown',
// Specials
'EOF','whitespace','String', 'identifier','integer number','float number', 'comment',
'EOF','whitespace',
'String',
'identifier',
'symbol literal',
'integer number','float number', 'comment',
';' {value may be changed at run time to any symbol or set of symbols},
'(',')', '[',']',
'?',',',':','.',';','>','<',
'+','-','*','/','||',
@ -103,7 +114,7 @@ const
'PRECISION', 'PRIMARY', 'PROCEDURE','POSITION','PLAN', 'PASSWORD','PAGE','PAGES','PAGE_SIZE','POST_EVENT','PRIVILEGES','PUBLIC',
'RIGHT', 'ROLE', 'REFERENCES', 'ROLLBACK','RELEASE', 'RETAIN', 'RETURNING_VALUES', 'RETURNS','REVOKE',
'SELECT', 'SET', 'SINGULAR', 'SOME', 'STARTING', 'SUM', 'SKIP','SUB_TYPE', 'SIZE', 'SEGMENT', 'SORT', 'SNAPSHOT','SCHEMA','SHADOW','SUSPEND','SQLCODE','SMALLINT',
'TABLE', 'TEXT', 'TRIGGER', 'TIME', 'TIMESTAMP', 'TYPE', 'TO', 'TRANSACTION', 'THEN',
'TABLE', 'TEXT', 'TERM', 'TRIGGER', 'TIME', 'TIMESTAMP', 'TYPE', 'TO', 'TRANSACTION', 'THEN',
'UNION', 'UPDATE', 'UPPER', 'UNIQUE', 'USER',
'VALUE','VALUES','VARIABLE', 'VIEW','VARCHAR','VARYING',
'WHERE', 'WITH', 'WHILE','WORK','WHEN'
@ -149,6 +160,7 @@ Type
soReturnWhiteSpace,
soBackslashEscapes,
soNoDoubleDelimIsChar,
soNoSetTerm {if set, do not allow changing terminator with SET TERM commands as used in Firebird},
soDoubleQuoteStringLiteral, // Default: single quote is string literal
soSingleQuoteIdentifier, // Default: double quote is identifier. Ignored if soDoubleQuoteStringLiteral is not specified
soBackQuoteIdentifier // Default: double quote is identifier
@ -172,12 +184,18 @@ Type
FKeyWords : TFPHashList;
FExclude : TStringList;
function CommentDiv: TSQLToken;
// Used to parse out an identifier/name and store it in the list of identifiers
function DoIdentifier : TSQLToken;
// Used to parse out a literal containing symbols
// Also looks for tsqlStatementTerminator
function DoSymbolLiteral : TSQLToken;
function DoMultiLineComment: TSQLToken;
function DoNumericLiteral: TSQLToken;
function DoSingleLineComment: TSQLToken;
function DoStringLiteral: TSQLToken;
function DoWhiteSpace: TSQLToken;
// Reads a new line into TokenStr and returns true
// If no new lines, returns false
function FetchLine: Boolean;
function GetCurColumn: Integer;
function GetExcludeKeywords: TStrings;
@ -211,11 +229,12 @@ Type
implementation
Var
// Keeps track of identifiers used
IdentifierTokens : array[FirstKeyword..LastKeyWord] of TSQLToken;
IdentifierTokensOK : Boolean;
Resourcestring
SErrUNknownToken = 'Unknown token: %s';
SErrUnknownToken = 'Unknown token: %s';
Procedure BuildIdentifierTokens;
@ -316,10 +335,10 @@ begin
Inc(TokenStr);
if TokenStr[0] = #0 then
if not FetchLine then
begin
FCurToken := Result;
exit;
end;
begin
FCurToken := Result;
exit;
end;
until not (TokenStr[0] in [#9, ' ']);
end;
@ -454,7 +473,7 @@ begin
If FKeywords.Count>0 then
FKeywords.Clear;
for I:=FirstKeyword to LastKeyword do
if (not Assigned(FExclude)) or (FExclude.INdexOf(TokenInfos[I])=-1) then
if (not Assigned(FExclude)) or (FExclude.IndexOf(TokenInfos[I])=-1) then
FKeywords.Add(TokenInfos[I],@IdentifierTokens[i]);
end;
@ -616,7 +635,7 @@ function TSQLScanner.DoIdentifier : TSQLToken;
Var
TokenStart:PChar;
Len : Integer;
I : TSQLToken;
{I : TSQLToken;}
S : ShortString;
P : ^TSQLToken;
@ -652,6 +671,43 @@ begin
end;}
end;
function TSQLScanner.DoSymbolLiteral : TSQLToken;
Var
Len : Integer;
P : ^TSQLToken;
TokenStart : PChar;
begin
Result:=tsqlUnknown;
// Get "word" finalized by end of string, space/tab.
TokenStart:=TokenStr;
repeat
Inc(TokenStr);
until (TokenStr[0] in [#0, #9, #10, #13, ' ']);
Len:=(TokenStr-TokenStart);
if Len > 0 then
begin
result:=tsqlSymbolLiteral;
SetLength(FCurTokenString,Len);
Move(TokenStart^,FCurTokenString[1],Len);
// Check if it is the statement terminator
if FCurTokenString=TokenInfos[tsqlStatementTerminator] then
begin
exit(tsqlStatementTerminator);
end;
// Check if this is a keyword or identifier/literal
// Probably not (due to naming rules) but it doesn't hurt
If FKeyWords.Count=0 then
BuildKeyWords;
P:=FKeyWords.Find(FCurTokenString); //case-sensitive search
If (P<>Nil) then
Result:=P^; //keyword found, just in case
end;
end;
function TSQLScanner.FetchToken: TSQLToken;
begin
@ -819,7 +875,7 @@ begin
begin
Inc(TokenStr);
if TokenStr[0] = '=' then
begin
begin
Inc(TokenStr);
Result:=tsqlGE;
end
@ -830,7 +886,9 @@ begin
'A'..'Z':
Result:=DoIdentifier;
else
Error(SErrUnknownToken,[TokenStr[0]]);
// Symbol of some sort
Result:=DoSymbolLiteral;
//Error(SErrUnknownToken,[TokenStr[0]]);
end; // Case
Until (Not (Result in [tsqlComment,tsqlWhitespace])) or
((Result=tsqlComment) and (soReturnComments in options)) or

View File

@ -889,6 +889,7 @@ Type
public
Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
end;
{ TSQLSetGeneratorStatement }
TSQLSetGeneratorStatement = Class(TSQLCreateOrAlterGenerator)
@ -898,6 +899,15 @@ Type
Property NewValue : Integer Read FNewValue Write FNewValue;
end;
{ TSQLSetTermStatement }
TSQLSetTermStatement = Class(TSQLStatement)
private
FNewValue: string;
Public
Property NewValue : string Read FNewValue Write FNewValue;
end;
{ TSQLCreateRoleStatement }
TSQLCreateRoleStatement = Class(TSQLCreateOrAlterStatement)