mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-22 13:31:20 +02:00
sql parser: support CASE expression
git-svn-id: trunk@46428 -
This commit is contained in:
parent
887de9f27c
commit
0fb6419edd
@ -77,6 +77,7 @@ Type
|
||||
function ParseExprLevel5(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
||||
function ParseExprLevel6(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
||||
function ParseExprPrimitive(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
||||
function ParseCaseExpression(AParent: TSQLElement): TSQLCaseExpression;
|
||||
function ParseInoperand(AParent: TSQLElement): TSQLExpression;
|
||||
// Lists, primitives
|
||||
function ParseIdentifierList(AParent: TSQLElement; AList: TSQLelementList): integer;
|
||||
@ -1331,6 +1332,34 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
function TSQLParser.ParseCaseExpression(AParent: TSQLElement): TSQLCaseExpression;
|
||||
var
|
||||
Branch: TSQLCaseExpressionBranch;
|
||||
begin
|
||||
Consume(tsqlCASE);
|
||||
Result:=TSQLCaseExpression(CreateElement(TSQLCaseExpression,AParent));
|
||||
try
|
||||
while CurrentToken=tsqlWhen do
|
||||
begin
|
||||
GetNextToken;
|
||||
Branch := TSQLCaseExpressionBranch.Create;
|
||||
Branch.Condition:=ParseExprLevel1(AParent,[eoIF]);
|
||||
Consume(tsqlThen);
|
||||
Branch.Expression:=ParseExprLevel1(AParent,[eoIF]);
|
||||
Result.AddBranch(Branch);
|
||||
end;
|
||||
if CurrentToken=tsqlELSE then
|
||||
begin
|
||||
GetNextToken;
|
||||
Result.ElseBranch:=ParseExprLevel1(AParent,[eoIF]);
|
||||
end;
|
||||
Consume(tsqlEnd);
|
||||
except
|
||||
FreeAndNil(Result);
|
||||
Raise;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSQLParser.ParseIntoList(AParent : TSQLElement; List : TSQLElementList);
|
||||
|
||||
begin
|
||||
@ -2733,6 +2762,7 @@ begin
|
||||
TSQLCastExpression(Result).NewType:=ParseTypeDefinition(Result,[ptfCast]);
|
||||
Consume(tsqlBraceClose);
|
||||
end;
|
||||
tsqlCase: Result:=ParseCaseExpression(AParent);
|
||||
tsqlExtract:
|
||||
begin
|
||||
GetNextToken;
|
||||
|
@ -51,7 +51,7 @@ type
|
||||
{ Note: if adding before tsqlALL or after tsqlWHEN please update FirstKeyword/LastKeyword }
|
||||
tsqlALL, tsqlAND, tsqlANY, tsqlASC, tsqlASCENDING, tsqlAVG, tsqlALTER, tsqlAdd, tsqlActive, tsqlAction, tsqlAs,tsqlAt, tsqlAuto, tsqlAfter,tsqlAdmin,
|
||||
tsqlBETWEEN, tsqlBinary, tsqlBY, tsqlBLOB, tsqlBegin, tsqlBefore,
|
||||
tsqlCOLLATE, tsqlCONTAINING, tsqlCOUNT, tsqlCREATE, tsqlCOLUMN, tsqlCONSTRAINT, tsqlChar,tsqlCHARACTER, tsqlCHECK, tsqlComputed,tsqlCASCADE, tsqlCast, tsqlCommit,tsqlConnect,tsqlCache,tsqlConditional,tsqlCString,
|
||||
tsqlCASE, tsqlCOLLATE, tsqlCONTAINING, tsqlCOUNT, tsqlCREATE, tsqlCOLUMN, tsqlCONSTRAINT, tsqlChar,tsqlCHARACTER, tsqlCHECK, tsqlComputed,tsqlCASCADE, tsqlCast, tsqlCommit,tsqlConnect,tsqlCache,tsqlConditional,tsqlCString,
|
||||
tsqlDESC, tsqlDESCENDING, tsqlDISTINCT, tsqlDEFAULT, tsqlDELETE, tsqlDO, tsqlDouble, tsqlDECLARE, tsqlDROP, tsqlDomain, tsqlDecimal, tsqlDate,tsqlDatabase,
|
||||
tsqlESCAPE, tsqlEXISTS, tsqlELSE, tsqlException, tsqlExternal, tsqlExecute, tsqlEnd,tsqlExit,tsqlEntrypoint,tsqlExtract,
|
||||
tsqlFIRST, tsqlFROM, tsqlFULL, tsqlFOREIGN, tsqlFOR, tsqlFUNCTION, tsqlFLOAT, tsqlFile,tsqlFreeIt,
|
||||
@ -97,7 +97,7 @@ const
|
||||
// Identifiers last:
|
||||
'ALL', 'AND', 'ANY', 'ASC', 'ASCENDING', 'AVG', 'ALTER', 'ADD','ACTIVE','ACTION', 'AS', 'AT', 'AUTO', 'AFTER', 'ADMIN',
|
||||
'BETWEEN', 'BINARY', 'BY', 'BLOB','BEGIN', 'BEFORE',
|
||||
'COLLATE', 'CONTAINING', 'COUNT', 'CREATE', 'COLUMN', 'CONSTRAINT', 'CHAR','CHARACTER','CHECK', 'COMPUTED','CASCADE','CAST', 'COMMIT', 'CONNECT', 'CACHE','CONDITIONAL', 'CSTRING',
|
||||
'CASE', 'COLLATE', 'CONTAINING', 'COUNT', 'CREATE', 'COLUMN', 'CONSTRAINT', 'CHAR','CHARACTER','CHECK', 'COMPUTED','CASCADE','CAST', 'COMMIT', 'CONNECT', 'CACHE','CONDITIONAL', 'CSTRING',
|
||||
'DESC', 'DESCENDING', 'DISTINCT', 'DEFAULT', 'DELETE', 'DO', 'DOUBLE', 'DECLARE', 'DROP', 'DOMAIN', 'DECIMAL', 'DATE','DATABASE',
|
||||
'ESCAPE', 'EXISTS', 'ELSE', 'EXCEPTION', 'EXTERNAL','EXECUTE', 'END','EXIT','ENTRY_POINT','EXTRACT',
|
||||
'FIRST', 'FROM', 'FULL','FOREIGN', 'FOR', 'FUNCTION', 'FLOAT','FILE', 'FREE_IT',
|
||||
|
@ -1411,6 +1411,38 @@ Type
|
||||
Property FalseBranch : TSQLStatement Read FFalseBranch Write FFalseBranch;
|
||||
end;
|
||||
|
||||
{ TSQLCaseExpressionBranch }
|
||||
|
||||
TSQLCaseExpressionBranch = Class
|
||||
private
|
||||
FCondition: TSQLExpression;
|
||||
FExpression: TSQLExpression;
|
||||
public
|
||||
destructor Destroy; override;
|
||||
public
|
||||
property Condition: TSQLExpression read FCondition write FCondition;
|
||||
property Expression: TSQLExpression read FExpression write FExpression;
|
||||
end;
|
||||
|
||||
{ TSQLCaseExpression }
|
||||
|
||||
TSQLCaseExpression = Class(TSQLExpression)
|
||||
private
|
||||
FBranches: array of TSQLCaseExpressionBranch;
|
||||
FElseBranch: TSQLExpression;
|
||||
function GetBranch(Index: Integer): TSQLCaseExpressionBranch;
|
||||
function GetBranchCount: Integer;
|
||||
Public
|
||||
Destructor Destroy; override;
|
||||
Function GetAsSQL(Options : TSQLFormatOptions; AIndent : Integer = 0): TSQLStringType; override;
|
||||
|
||||
Property BranchCount: Integer Read GetBranchCount;
|
||||
Procedure AddBranch(ABranch: TSQLCaseExpressionBranch);
|
||||
Procedure ClearBranches;
|
||||
Property Branches[Index: Integer] : TSQLCaseExpressionBranch Read GetBranch;
|
||||
Property ElseBranch : TSQLExpression Read FElseBranch Write FElseBranch;
|
||||
end;
|
||||
|
||||
{ TSQLForStatement }
|
||||
|
||||
TSQLForStatement = Class(TSQLStatement)
|
||||
@ -1937,6 +1969,63 @@ begin
|
||||
Sep:=', ';
|
||||
end;
|
||||
|
||||
{ TSQLCaseExpressionBranch }
|
||||
|
||||
destructor TSQLCaseExpressionBranch.Destroy;
|
||||
begin
|
||||
FreeAndNil(FCondition);
|
||||
FreeAndNil(FExpression);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{ TSQLCaseExpression }
|
||||
|
||||
procedure TSQLCaseExpression.AddBranch(ABranch: TSQLCaseExpressionBranch);
|
||||
begin
|
||||
SetLength(FBranches, Length(FBranches)+1);
|
||||
FBranches[High(FBranches)] := ABranch;
|
||||
end;
|
||||
|
||||
procedure TSQLCaseExpression.ClearBranches;
|
||||
var
|
||||
B: TSQLCaseExpressionBranch;
|
||||
begin
|
||||
for B in FBranches do
|
||||
B.Free;
|
||||
FBranches:=nil;
|
||||
end;
|
||||
|
||||
destructor TSQLCaseExpression.Destroy;
|
||||
begin
|
||||
ClearBranches;
|
||||
FreeAndNil(FElseBranch);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TSQLCaseExpression.GetAsSQL(Options: TSQLFormatOptions; AIndent: Integer): TSQLStringType;
|
||||
var
|
||||
B: TSQLCaseExpressionBranch;
|
||||
begin
|
||||
Result:=SQLKeyWord('CASE',Options)+' ';
|
||||
for B in FBranches do
|
||||
Result:=Result+
|
||||
SQLKeyWord('WHEN ',Options)+B.Condition.GetAsSQL(Options, AIndent)+' '+
|
||||
SQLKeyWord('THEN ',Options)+B.Expression.GetAsSQL(Options, AIndent)+' ';
|
||||
If Assigned(FElseBranch) then
|
||||
Result:=Result+SQLKeyWord('ELSE ',Options)+ElseBranch.GetAsSQL(Options,AIndent)+' ';
|
||||
Result:=Result+SQLKeyWord('END',Options);
|
||||
end;
|
||||
|
||||
function TSQLCaseExpression.GetBranch(Index: Integer): TSQLCaseExpressionBranch;
|
||||
begin
|
||||
Result := FBranches[Index];
|
||||
end;
|
||||
|
||||
function TSQLCaseExpression.GetBranchCount: Integer;
|
||||
begin
|
||||
Result := Length(FBranches);
|
||||
end;
|
||||
|
||||
{ TSQLSelectLimit }
|
||||
|
||||
constructor TSQLSelectLimit.Create;
|
||||
|
@ -69,6 +69,7 @@ type
|
||||
Procedure TestSimpleSelect;
|
||||
Procedure TestAnyExpression;
|
||||
procedure TestAllExpression;
|
||||
procedure TestCaseExpression;
|
||||
procedure TestExistsExpression;
|
||||
procedure TestSomeExpression;
|
||||
procedure TestSingularExpression;
|
||||
@ -816,7 +817,7 @@ begin
|
||||
AssertSQL(U,'constraint C unique (A , B)',[sfoLowercaseKeyWord]);
|
||||
end;
|
||||
|
||||
procedure TTestGenerateSQL.TestTableprimaryKeyConstraintDef;
|
||||
procedure TTestGenerateSQL.TestTablePrimaryKeyConstraintDef;
|
||||
|
||||
Var
|
||||
U : TSQLTablePrimaryKeyConstraintDef;
|
||||
@ -1872,6 +1873,35 @@ begin
|
||||
AssertSQL(B,'BEGIN'+sLineBreak+' BEGIN'+sLineBreak+' EXIT;'+sLineBreak+' END'+sLineBreak+'END');
|
||||
end;
|
||||
|
||||
procedure TTestGenerateSQL.TestCaseExpression;
|
||||
|
||||
Var
|
||||
E : TSQLCaseExpression;
|
||||
B : TSQLCaseExpressionBranch;
|
||||
C : TSQLBinaryExpression;
|
||||
|
||||
begin
|
||||
E:=TSQLCaseExpression.Create(Nil);
|
||||
|
||||
B:=TSQLCaseExpressionBranch.Create;
|
||||
C:=CreateBinaryExpression(CreateIdentifierExpression('A'),CreateIdentifierExpression('B'));
|
||||
C.Operation:=boEQ;
|
||||
B.Condition:=C;
|
||||
B.Expression:=CreateLiteralExpression(CreateLiteral(1));
|
||||
E.AddBranch(B);
|
||||
|
||||
B:=TSQLCaseExpressionBranch.Create;
|
||||
C:=CreateBinaryExpression(CreateIdentifierExpression('A'),CreateIdentifierExpression('B'));
|
||||
C.Operation:=boGT;
|
||||
B.Condition:=C;
|
||||
B.Expression:=CreateLiteralExpression(CreateLiteral(2));
|
||||
E.AddBranch(B);
|
||||
|
||||
E.ElseBranch:=CreateLiteralExpression(CreateLiteral(3));
|
||||
FTofree:=E;
|
||||
AssertSQL(E,'CASE WHEN A = B THEN 1 WHEN A > B THEN 2 ELSE 3 END');
|
||||
end;
|
||||
|
||||
procedure TTestGenerateSQL.TestAssignment;
|
||||
|
||||
var
|
||||
|
@ -230,6 +230,7 @@ type
|
||||
procedure TestAnd;
|
||||
procedure TestOr;
|
||||
procedure TestNotOr;
|
||||
procedure TestCase;
|
||||
end;
|
||||
|
||||
{ TTestDomainParser }
|
||||
@ -2203,6 +2204,34 @@ begin
|
||||
AssertLiteralExpr('Right is integer',T.Right,TSQLIntegerLiteral);
|
||||
end;
|
||||
|
||||
procedure TTestCheckParser.TestCase;
|
||||
|
||||
Var
|
||||
T : TSQLCaseExpression;
|
||||
B : TSQLBinaryExpression;
|
||||
R : TSQLIdentifierName;
|
||||
|
||||
begin
|
||||
T:=TSQLCaseExpression(TestCheck('CASE WHEN A=1 THEN "a" WHEN B=2 THEN "b" ELSE "c" END',TSQLCaseExpression));
|
||||
AssertEquals('Branch count = 2',2,T.BranchCount);
|
||||
AssertNotNull('Else branch exists',T.ElseBranch);
|
||||
|
||||
B:=(T.Branches[0].Condition as TSQLBinaryExpression);
|
||||
R:=(T.Branches[0].Expression as TSQLIdentifierExpression).Identifier;
|
||||
AssertEquals('First WHEN Identifier is A', 'A', (B.Left as TSQLIdentifierExpression).Identifier.Name);
|
||||
AssertEquals('First WHEN Number is 1', 1, ((B.Right as TSQLLiteralExpression).Literal as TSQLIntegerLiteral).Value);
|
||||
AssertEquals('First THEN result is "a"', 'a', R.Name);
|
||||
|
||||
B:=(T.Branches[1].Condition as TSQLBinaryExpression);
|
||||
R:=(T.Branches[1].Expression as TSQLIdentifierExpression).Identifier;
|
||||
AssertEquals('Second WHEN Identifier is B', 'B', (B.Left as TSQLIdentifierExpression).Identifier.Name);
|
||||
AssertEquals('Second WHEN Number is 2', 2, ((B.Right as TSQLLiteralExpression).Literal as TSQLIntegerLiteral).Value);
|
||||
AssertEquals('Second THEN result is "b"', 'b', R.Name);
|
||||
|
||||
R:=(T.ElseBranch as TSQLIdentifierExpression).Identifier;
|
||||
AssertEquals('ELSE result is "c"', 'c', R.Name);
|
||||
end;
|
||||
|
||||
procedure TTestCheckParser.TestNotBetween;
|
||||
|
||||
Var
|
||||
|
Loading…
Reference in New Issue
Block a user