* fcl-db: sql parser:

- fix parsing scripts containing SET AUTODDL and SET TERM statements
- SET TERM/SET AUTODDL will be output as SQL comments when regenerating SQL so thethe commands work with e.g. sqldb

git-svn-id: trunk@27923 -
This commit is contained in:
reiniero 2014-06-10 10:01:22 +00:00
parent 1b11541c90
commit 81548f0e9c
3 changed files with 110 additions and 29 deletions

View File

@ -101,6 +101,7 @@ Type
function ParseCreateViewStatement(AParent: TSQLElement; IsAlter: Boolean): TSQLCreateOrAlterStatement; function ParseCreateViewStatement(AParent: TSQLElement; IsAlter: Boolean): TSQLCreateOrAlterStatement;
function ParseCreateTriggerStatement(AParent: TSQLElement; IsAlter: Boolean): TSQLCreateOrAlterStatement; function ParseCreateTriggerStatement(AParent: TSQLElement; IsAlter: Boolean): TSQLCreateOrAlterStatement;
function ParseSetGeneratorStatement(AParent: TSQLElement) : TSQLSetGeneratorStatement; function ParseSetGeneratorStatement(AParent: TSQLElement) : TSQLSetGeneratorStatement;
function ParseSetISQLStatement(AParent: TSQLElement) : TSQLSetISQLStatement;
function ParseSetTermStatement(AParent: TSQLElement) : TSQLSetTermStatement; function ParseSetTermStatement(AParent: TSQLElement) : TSQLSetTermStatement;
function ParseCreateDatabaseStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateDatabaseStatement; function ParseCreateDatabaseStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateDatabaseStatement;
function ParseCreateShadowStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateShadowStatement; function ParseCreateShadowStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateShadowStatement;
@ -158,7 +159,9 @@ Type
Function ParseConnectStatement(AParent : TSQLElement) : TSQLConnectStatement; Function ParseConnectStatement(AParent : TSQLElement) : TSQLConnectStatement;
Function ParseGrantStatement(AParent: TSQLElement): TSQLGrantStatement; Function ParseGrantStatement(AParent: TSQLElement): TSQLGrantStatement;
Function ParseRevokeStatement(AParent: TSQLElement): TSQLGrantStatement; Function ParseRevokeStatement(AParent: TSQLElement): TSQLGrantStatement;
// Parse single element
Function Parse : TSQLElement; Function Parse : TSQLElement;
// Parse script containing 1 or more elements
Function ParseScript(AllowPartial : Boolean = False) : TSQLElementList; Function ParseScript(AllowPartial : Boolean = False) : TSQLElementList;
// Gets statement terminator (as e.g. used in SET TERM) so statements like // Gets statement terminator (as e.g. used in SET TERM) so statements like
// EXECUTE BLOCK or CREATE PROCEDURE that contain semicolons can be parsed // EXECUTE BLOCK or CREATE PROCEDURE that contain semicolons can be parsed
@ -2965,11 +2968,11 @@ begin
Consume(tsqlGenerator) ; Consume(tsqlGenerator) ;
try try
Result:=TSQLSetGeneratorStatement(CreateElement(TSQLSetGeneratorStatement,AParent)); Result:=TSQLSetGeneratorStatement(CreateElement(TSQLSetGeneratorStatement,AParent));
expect(tsqlidentifier); Expect(tsqlidentifier);
Result.ObjectName:=CreateIdentifier(Result,CurrentTokenString); Result.ObjectName:=CreateIdentifier(Result,CurrentTokenString);
GetNextToken; GetNextToken;
consume(tsqlto); Consume(tsqlto);
expect(tsqlIntegerNumber); Expect(tsqlIntegerNumber);
Result.NewValue:=StrToInt(CurrentTokenString); Result.NewValue:=StrToInt(CurrentTokenString);
GetNextToken; GetNextToken;
except except
@ -2978,6 +2981,37 @@ begin
end; end;
end; end;
function TSQLParser.ParseSetISQLStatement(AParent: TSQLElement
): TSQLSetISQLStatement;
begin
// On entry, we're on the first argument e.g. AUTODDL in SET AUTODDL
// for now, only support AutoDDL
//SET AUTODDL: ignore these isql commands for now
case CurrentToken of
tsqlAutoDDL:
begin
Result:=TSQLSetISQLStatement(CreateElement(TSQLSetISQLStatement,AParent));
// SET AUTODDL ON, SET AUTODDL OFF; optional arguments
if (PeekNextToken in [tsqlOn, tsqlOff]) then
begin
GetNextToken;
if CurrentToken=tsqlOFF then
Result.Arguments:='AUTODDL OFF'
else
Result.Arguments:='AUTODDL ON';
Consume([tsqlOn,tsqlOff]);
end
else
begin
Result.Arguments:='AUTODDL ON';
Consume(tsqlAutoDDL);
end;
end
else
UnexpectedToken;
end;
end;
function TSQLParser.ParseSetTermStatement(AParent: TSQLElement function TSQLParser.ParseSetTermStatement(AParent: TSQLElement
): TSQLSetTermStatement; ): TSQLSetTermStatement;
var var
@ -2987,23 +3021,29 @@ begin
Consume(tsqlTerm) ; Consume(tsqlTerm) ;
try try
Result:=TSQLSetTermStatement(CreateElement(TSQLSetTermStatement,AParent)); Result:=TSQLSetTermStatement(CreateElement(TSQLSetTermStatement,AParent));
expect([tsqlSemiColon,tsqlStatementTerminator,tsqlSymbolString,tsqlString]); Expect([tsqlSemiColon,tsqlStatementTerminator,tsqlSymbolString,tsqlString]);
// Already set the expression's new value to the new terminator, but do not // Already set the expression's new value to the new terminator, but do not
// change tSQLStatementTerminator as GetNextToken etc need the old one to // change tSQLStatementTerminator as GetNextToken etc need the old one to
// detect the closing terminator // detect the closing terminator
case CurrentToken of case CurrentToken of
tsqlSemiColon, tsqlStatementTerminator: Result.NewValue:=TokenInfos[CurrentToken]; tsqlSemiColon, tsqlStatementTerminator: Result.NewTerminator:=TokenInfos[CurrentToken];
tsqlSymbolString, tsqlString: Result.NewValue:=CurrentTokenString; tsqlSymbolString, tsqlString: Result.NewTerminator:=CurrentTokenString;
end; end;
// Expect the old terminator... // Expect the old terminator...
GetNextToken; GetNextToken;
// Parser will give tsqlSemicolon rather than tsqlStatementTerminator: // Parser will give tsqlSemicolon rather than tsqlStatementTerminator:
if TokenInfos[tsqlStatementTerminator]=TokenInfos[tsqlSEMICOLON] then if TokenInfos[tsqlStatementTerminator]=TokenInfos[tsqlSEMICOLON] then
Expect(tsqlSEMICOLON) begin
Expect(tsqlSEMICOLON);
Result.OldTerminator:=TokenInfos[tsqlSEMICOLON];
end
else else
begin
Expect(tsqlStatementTerminator); Expect(tsqlStatementTerminator);
Result.OldTerminator:=TokenInfos[tsqlStatementTerminator];
end;
//... and now set the new terminator: //... and now set the new terminator:
TokenInfos[tsqlStatementTerminator]:=Result.NewValue; //process new terminator value TokenInfos[tsqlStatementTerminator]:=Result.NewTerminator; //process new terminator value
except except
FreeAndNil(Result); FreeAndNil(Result);
Raise; Raise;
@ -3414,20 +3454,7 @@ begin
Case CurrentToken of Case CurrentToken of
tsqlGenerator : Result:=ParseSetGeneratorStatement(AParent); //SET GENERATOR tsqlGenerator : Result:=ParseSetGeneratorStatement(AParent); //SET GENERATOR
tsqlTerm : Result:=ParseSetTermStatement(AParent); //SET TERM tsqlTerm : Result:=ParseSetTermStatement(AParent); //SET TERM
tsqlAutoDDL : //SET AUTODDL: ignore these isql commands for now tsqlAutoDDL : Result:=ParseSetISQLStatement(AParent); //SET AUTODDL
begin
// SET AUTODDL ON, SET AUTODDL OFF; optional arguments
if (PeekNextToken in [tsqlOn, tsqlOff]) then
begin
GetNextToken;
Consume([tsqlOn,tsqlOff]);
end
else
begin
Consume(tsqlAutoDDL);
end;
Result:=nil; //ignore
end;
else else
// For the time being // For the time being
UnexpectedToken; UnexpectedToken;

View File

@ -26,7 +26,7 @@ Type
TSQLFormatOption = (sfoDoubleQuotes, // Use double quote character for string literals TSQLFormatOption = (sfoDoubleQuotes, // Use double quote character for string literals
sfoBackslashEscape, // Backslash escapes in string literals sfoBackslashEscape, // Backslash escapes in string literals
sfoSingleQuoteIdentifier, // quote Identifiers using ' sfoSingleQuoteIdentifier, // quote Identifiers using '
sfoDoubleQuoteIdentifier, // quote Identifiers using " sfoDoubleQuoteIdentifier, // quote Identifiers using " (e.g. as in Firebird)
sfoBackQuoteIdentifier, // quote Identifiers using ` sfoBackQuoteIdentifier, // quote Identifiers using `
sfoLowercaseKeyword, // Lowercase SQL keywords sfoLowercaseKeyword, // Lowercase SQL keywords
sfoOneFieldPerLine, // One field per line in SELECT, Update, Insert sfoOneFieldPerLine, // One field per line in SELECT, Update, Insert
@ -901,13 +901,29 @@ Type
Property NewValue : Integer Read FNewValue Write FNewValue; Property NewValue : Integer Read FNewValue Write FNewValue;
end; end;
{ TSQLSetISQLStatement }
// SET statements as used by the isql Firebird command line utility
TSQLSetISQLStatement = Class(TSQLStatement)
private
FArgument: string;
Public
Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
// The test of the SET statement excluding the SET command
Property Arguments : string Read FArgument Write FArgument;
end;
{ TSQLSetTermStatement } { TSQLSetTermStatement }
TSQLSetTermStatement = Class(TSQLStatement) TSQLSetTermStatement = Class(TSQLStatement)
private private
FNewValue: string; FNewValue: string;
FOldValue: string;
Public Public
Property NewValue : string Read FNewValue Write FNewValue; Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
// The first, new terminator in the SET TERM statement
Property NewTerminator : string Read FNewValue Write FNewValue;
// The second, old terminator in the SET TERM statement
Property OldTerminator : string Read FOldValue Write FOldValue;
end; end;
{ TSQLCreateRoleStatement } { TSQLCreateRoleStatement }
@ -1864,6 +1880,26 @@ begin
Sep:=', '; Sep:=', ';
end; end;
{ TSQLSetISQLStatement }
function TSQLSetISQLStatement.GetAsSQL(Options: TSQLFormatOptions;
AIndent: Integer): TSQLStringType;
begin
// Note: we generate this as a comment as this is ISQL-specific and will generate
// errors when passed as SQL to servers
Result:='-- SET '+Arguments;
end;
{ TSQLSetTermStatement }
function TSQLSetTermStatement.GetAsSQL(Options: TSQLFormatOptions;
AIndent: Integer): TSQLStringType;
begin
// Note: we generate this as a comment as this is ISQL-specific and will generate
// errors when passed as SQL to servers
Result:='-- SET TERM '+NewTerminator+' '+OldTerminator;
end;
function TSQLElementList.GetE(AIndex : Integer): TSQLElement; function TSQLElementList.GetE(AIndex : Integer): TSQLElement;
begin begin
Result:=TSQLElement(Items[AIndex]); Result:=TSQLElement(Items[AIndex]);

View File

@ -872,21 +872,39 @@ uses typinfo;
{ TTestSetParser } { TTestSetParser }
procedure TTestSetParser.TestSetAutoDDL; procedure TTestSetParser.TestSetAutoDDL;
Const
Desired='-- SET AUTODDL ON';
Var
I: TSQLSetISQLStatement;
begin begin
CreateParser('SET AUTODDL;'); CreateParser('SET AUTODDL;');
AssertNull('SET AUTODDL should be ignored and give nil result',Parser.Parse); FToFree:=Parser.Parse;
I:=TSQLSetISQLStatement(CheckClass(FToFree,TSQLSetISQLStatement));
AssertEquals('GetAsSQL',I.GetAsSQL([]),Desired);
end; end;
procedure TTestSetParser.TestSetAutoDDLOn; procedure TTestSetParser.TestSetAutoDDLOn;
Const
Desired='-- SET AUTODDL ON';
Var
I: TSQLSetISQLStatement;
begin begin
CreateParser('SET AUTODDL ON;'); CreateParser('SET AUTODDL ON;');
AssertNull('SET AUTODDL should be ignored and give nil result',Parser.Parse); FToFree:=Parser.Parse;
I:=TSQLSetISQLStatement(CheckClass(FToFree,TSQLSetISQLStatement));
AssertEquals('GetAsSQL',I.GetAsSQL([]),Desired);
end; end;
procedure TTestSetParser.TestSetAutoDDLOff; procedure TTestSetParser.TestSetAutoDDLOff;
Const
Desired='-- SET AUTODDL OFF';
Var
I: TSQLSetISQLStatement;
begin begin
CreateParser('SET AUTODDL OFF;'); CreateParser('SET AUTODDL OFF;');
AssertNull('SET AUTODDL should be ignored and give nil result',Parser.Parse); FToFree:=Parser.Parse;
I:=TSQLSetISQLStatement(CheckClass(FToFree,TSQLSetISQLStatement));
AssertEquals('GetAsSQL',I.GetAsSQL([]),Desired);
end; end;
procedure TTestSetParser.TestSetAutoDDLCreateProcedure; procedure TTestSetParser.TestSetAutoDDLCreateProcedure;
@ -913,7 +931,7 @@ begin
CreateParser('SET TERM ^ ;'); CreateParser('SET TERM ^ ;');
FToFree:=Parser.Parse; FToFree:=Parser.Parse;
S:=TSQLSetTermStatement(CheckClass(FToFree,TSQLSetTermStatement)); S:=TSQLSetTermStatement(CheckClass(FToFree,TSQLSetTermStatement));
AssertEquals('New value','^',S.NewValue); AssertEquals('New terminator','^',S.NewTerminator);
AssertEquals('Closing semicolon',tsqlSEMICOLON,Parser.CurrentToken); AssertEquals('Closing semicolon',tsqlSEMICOLON,Parser.CurrentToken);
Parser.GetNextToken; Parser.GetNextToken;
AssertEquals('End of stream reached',tsqlEOF,Parser.CurrentToken); AssertEquals('End of stream reached',tsqlEOF,Parser.CurrentToken);
@ -929,7 +947,7 @@ begin
AssertEquals('Closing statement terminator should match ^','^',Parser.GetStatementTerminator); AssertEquals('Closing statement terminator should match ^','^',Parser.GetStatementTerminator);
FToFree:=Parser.Parse; FToFree:=Parser.Parse;
S:=TSQLSetTermStatement(CheckClass(FToFree,TSQLSetTermStatement)); S:=TSQLSetTermStatement(CheckClass(FToFree,TSQLSetTermStatement));
AssertEquals('New value',';',S.NewValue); AssertEquals('New terminator',';',S.NewTerminator);
AssertEquals('Closing terminator',tsqlStatementTerminator,Parser.CurrentToken); AssertEquals('Closing terminator',tsqlStatementTerminator,Parser.CurrentToken);
AssertEquals('Closing ^','^',Parser.CurrentTokenString); AssertEquals('Closing ^','^',Parser.CurrentTokenString);
Parser.GetNextToken; Parser.GetNextToken;