* 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 ParseCreateTriggerStatement(AParent: TSQLElement; IsAlter: Boolean): TSQLCreateOrAlterStatement;
function ParseSetGeneratorStatement(AParent: TSQLElement) : TSQLSetGeneratorStatement;
function ParseSetISQLStatement(AParent: TSQLElement) : TSQLSetISQLStatement;
function ParseSetTermStatement(AParent: TSQLElement) : TSQLSetTermStatement;
function ParseCreateDatabaseStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateDatabaseStatement;
function ParseCreateShadowStatement(AParent: TSQLElement; IsAlter: Boolean ): TSQLCreateShadowStatement;
@ -158,7 +159,9 @@ Type
Function ParseConnectStatement(AParent : TSQLElement) : TSQLConnectStatement;
Function ParseGrantStatement(AParent: TSQLElement): TSQLGrantStatement;
Function ParseRevokeStatement(AParent: TSQLElement): TSQLGrantStatement;
// Parse single element
Function Parse : TSQLElement;
// Parse script containing 1 or more elements
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
@ -2965,11 +2968,11 @@ begin
Consume(tsqlGenerator) ;
try
Result:=TSQLSetGeneratorStatement(CreateElement(TSQLSetGeneratorStatement,AParent));
expect(tsqlidentifier);
Expect(tsqlidentifier);
Result.ObjectName:=CreateIdentifier(Result,CurrentTokenString);
GetNextToken;
consume(tsqlto);
expect(tsqlIntegerNumber);
Consume(tsqlto);
Expect(tsqlIntegerNumber);
Result.NewValue:=StrToInt(CurrentTokenString);
GetNextToken;
except
@ -2978,6 +2981,37 @@ begin
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
): TSQLSetTermStatement;
var
@ -2987,23 +3021,29 @@ begin
Consume(tsqlTerm) ;
try
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
// change tSQLStatementTerminator as GetNextToken etc need the old one to
// detect the closing terminator
case CurrentToken of
tsqlSemiColon, tsqlStatementTerminator: Result.NewValue:=TokenInfos[CurrentToken];
tsqlSymbolString, tsqlString: Result.NewValue:=CurrentTokenString;
tsqlSemiColon, tsqlStatementTerminator: Result.NewTerminator:=TokenInfos[CurrentToken];
tsqlSymbolString, tsqlString: Result.NewTerminator:=CurrentTokenString;
end;
// Expect the old terminator...
GetNextToken;
// Parser will give tsqlSemicolon rather than tsqlStatementTerminator:
if TokenInfos[tsqlStatementTerminator]=TokenInfos[tsqlSEMICOLON] then
Expect(tsqlSEMICOLON)
begin
Expect(tsqlSEMICOLON);
Result.OldTerminator:=TokenInfos[tsqlSEMICOLON];
end
else
begin
Expect(tsqlStatementTerminator);
Result.OldTerminator:=TokenInfos[tsqlStatementTerminator];
end;
//... and now set the new terminator:
TokenInfos[tsqlStatementTerminator]:=Result.NewValue; //process new terminator value
TokenInfos[tsqlStatementTerminator]:=Result.NewTerminator; //process new terminator value
except
FreeAndNil(Result);
Raise;
@ -3414,20 +3454,7 @@ begin
Case CurrentToken of
tsqlGenerator : Result:=ParseSetGeneratorStatement(AParent); //SET GENERATOR
tsqlTerm : Result:=ParseSetTermStatement(AParent); //SET TERM
tsqlAutoDDL : //SET AUTODDL: ignore these isql commands for now
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;
tsqlAutoDDL : Result:=ParseSetISQLStatement(AParent); //SET AUTODDL
else
// For the time being
UnexpectedToken;

View File

@ -26,7 +26,7 @@ Type
TSQLFormatOption = (sfoDoubleQuotes, // Use double quote character for string literals
sfoBackslashEscape, // Backslash escapes in string literals
sfoSingleQuoteIdentifier, // quote Identifiers using '
sfoDoubleQuoteIdentifier, // quote Identifiers using "
sfoDoubleQuoteIdentifier, // quote Identifiers using " (e.g. as in Firebird)
sfoBackQuoteIdentifier, // quote Identifiers using `
sfoLowercaseKeyword, // Lowercase SQL keywords
sfoOneFieldPerLine, // One field per line in SELECT, Update, Insert
@ -901,13 +901,29 @@ Type
Property NewValue : Integer Read FNewValue Write FNewValue;
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 = Class(TSQLStatement)
private
FNewValue: string;
FOldValue: string;
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;
{ TSQLCreateRoleStatement }
@ -1864,6 +1880,26 @@ begin
Sep:=', ';
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;
begin
Result:=TSQLElement(Items[AIndex]);

View File

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