mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-15 18:29:09 +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 ParseExprLevel5(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
||||||
function ParseExprLevel6(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
function ParseExprLevel6(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
||||||
function ParseExprPrimitive(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
function ParseExprPrimitive(AParent: TSQLElement; EO : TExpressionOptions): TSQLExpression;
|
||||||
|
function ParseCaseExpression(AParent: TSQLElement): TSQLCaseExpression;
|
||||||
function ParseInoperand(AParent: TSQLElement): TSQLExpression;
|
function ParseInoperand(AParent: TSQLElement): TSQLExpression;
|
||||||
// Lists, primitives
|
// Lists, primitives
|
||||||
function ParseIdentifierList(AParent: TSQLElement; AList: TSQLelementList): integer;
|
function ParseIdentifierList(AParent: TSQLElement; AList: TSQLelementList): integer;
|
||||||
@ -1331,6 +1332,34 @@ begin
|
|||||||
end;
|
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);
|
procedure TSQLParser.ParseIntoList(AParent : TSQLElement; List : TSQLElementList);
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@ -2733,6 +2762,7 @@ begin
|
|||||||
TSQLCastExpression(Result).NewType:=ParseTypeDefinition(Result,[ptfCast]);
|
TSQLCastExpression(Result).NewType:=ParseTypeDefinition(Result,[ptfCast]);
|
||||||
Consume(tsqlBraceClose);
|
Consume(tsqlBraceClose);
|
||||||
end;
|
end;
|
||||||
|
tsqlCase: Result:=ParseCaseExpression(AParent);
|
||||||
tsqlExtract:
|
tsqlExtract:
|
||||||
begin
|
begin
|
||||||
GetNextToken;
|
GetNextToken;
|
||||||
|
@ -51,7 +51,7 @@ type
|
|||||||
{ Note: if adding before tsqlALL or after tsqlWHEN please update FirstKeyword/LastKeyword }
|
{ 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,
|
tsqlALL, tsqlAND, tsqlANY, tsqlASC, tsqlASCENDING, tsqlAVG, tsqlALTER, tsqlAdd, tsqlActive, tsqlAction, tsqlAs,tsqlAt, tsqlAuto, tsqlAfter,tsqlAdmin,
|
||||||
tsqlBETWEEN, tsqlBinary, tsqlBY, tsqlBLOB, tsqlBegin, tsqlBefore,
|
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,
|
tsqlDESC, tsqlDESCENDING, tsqlDISTINCT, tsqlDEFAULT, tsqlDELETE, tsqlDO, tsqlDouble, tsqlDECLARE, tsqlDROP, tsqlDomain, tsqlDecimal, tsqlDate,tsqlDatabase,
|
||||||
tsqlESCAPE, tsqlEXISTS, tsqlELSE, tsqlException, tsqlExternal, tsqlExecute, tsqlEnd,tsqlExit,tsqlEntrypoint,tsqlExtract,
|
tsqlESCAPE, tsqlEXISTS, tsqlELSE, tsqlException, tsqlExternal, tsqlExecute, tsqlEnd,tsqlExit,tsqlEntrypoint,tsqlExtract,
|
||||||
tsqlFIRST, tsqlFROM, tsqlFULL, tsqlFOREIGN, tsqlFOR, tsqlFUNCTION, tsqlFLOAT, tsqlFile,tsqlFreeIt,
|
tsqlFIRST, tsqlFROM, tsqlFULL, tsqlFOREIGN, tsqlFOR, tsqlFUNCTION, tsqlFLOAT, tsqlFile,tsqlFreeIt,
|
||||||
@ -97,7 +97,7 @@ const
|
|||||||
// Identifiers last:
|
// Identifiers last:
|
||||||
'ALL', 'AND', 'ANY', 'ASC', 'ASCENDING', 'AVG', 'ALTER', 'ADD','ACTIVE','ACTION', 'AS', 'AT', 'AUTO', 'AFTER', 'ADMIN',
|
'ALL', 'AND', 'ANY', 'ASC', 'ASCENDING', 'AVG', 'ALTER', 'ADD','ACTIVE','ACTION', 'AS', 'AT', 'AUTO', 'AFTER', 'ADMIN',
|
||||||
'BETWEEN', 'BINARY', 'BY', 'BLOB','BEGIN', 'BEFORE',
|
'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',
|
'DESC', 'DESCENDING', 'DISTINCT', 'DEFAULT', 'DELETE', 'DO', 'DOUBLE', 'DECLARE', 'DROP', 'DOMAIN', 'DECIMAL', 'DATE','DATABASE',
|
||||||
'ESCAPE', 'EXISTS', 'ELSE', 'EXCEPTION', 'EXTERNAL','EXECUTE', 'END','EXIT','ENTRY_POINT','EXTRACT',
|
'ESCAPE', 'EXISTS', 'ELSE', 'EXCEPTION', 'EXTERNAL','EXECUTE', 'END','EXIT','ENTRY_POINT','EXTRACT',
|
||||||
'FIRST', 'FROM', 'FULL','FOREIGN', 'FOR', 'FUNCTION', 'FLOAT','FILE', 'FREE_IT',
|
'FIRST', 'FROM', 'FULL','FOREIGN', 'FOR', 'FUNCTION', 'FLOAT','FILE', 'FREE_IT',
|
||||||
|
@ -1411,6 +1411,38 @@ Type
|
|||||||
Property FalseBranch : TSQLStatement Read FFalseBranch Write FFalseBranch;
|
Property FalseBranch : TSQLStatement Read FFalseBranch Write FFalseBranch;
|
||||||
end;
|
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 }
|
||||||
|
|
||||||
TSQLForStatement = Class(TSQLStatement)
|
TSQLForStatement = Class(TSQLStatement)
|
||||||
@ -1937,6 +1969,63 @@ begin
|
|||||||
Sep:=', ';
|
Sep:=', ';
|
||||||
end;
|
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 }
|
{ TSQLSelectLimit }
|
||||||
|
|
||||||
constructor TSQLSelectLimit.Create;
|
constructor TSQLSelectLimit.Create;
|
||||||
|
@ -69,6 +69,7 @@ type
|
|||||||
Procedure TestSimpleSelect;
|
Procedure TestSimpleSelect;
|
||||||
Procedure TestAnyExpression;
|
Procedure TestAnyExpression;
|
||||||
procedure TestAllExpression;
|
procedure TestAllExpression;
|
||||||
|
procedure TestCaseExpression;
|
||||||
procedure TestExistsExpression;
|
procedure TestExistsExpression;
|
||||||
procedure TestSomeExpression;
|
procedure TestSomeExpression;
|
||||||
procedure TestSingularExpression;
|
procedure TestSingularExpression;
|
||||||
@ -816,7 +817,7 @@ begin
|
|||||||
AssertSQL(U,'constraint C unique (A , B)',[sfoLowercaseKeyWord]);
|
AssertSQL(U,'constraint C unique (A , B)',[sfoLowercaseKeyWord]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TTestGenerateSQL.TestTableprimaryKeyConstraintDef;
|
procedure TTestGenerateSQL.TestTablePrimaryKeyConstraintDef;
|
||||||
|
|
||||||
Var
|
Var
|
||||||
U : TSQLTablePrimaryKeyConstraintDef;
|
U : TSQLTablePrimaryKeyConstraintDef;
|
||||||
@ -1872,6 +1873,35 @@ begin
|
|||||||
AssertSQL(B,'BEGIN'+sLineBreak+' BEGIN'+sLineBreak+' EXIT;'+sLineBreak+' END'+sLineBreak+'END');
|
AssertSQL(B,'BEGIN'+sLineBreak+' BEGIN'+sLineBreak+' EXIT;'+sLineBreak+' END'+sLineBreak+'END');
|
||||||
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;
|
procedure TTestGenerateSQL.TestAssignment;
|
||||||
|
|
||||||
var
|
var
|
||||||
|
@ -230,6 +230,7 @@ type
|
|||||||
procedure TestAnd;
|
procedure TestAnd;
|
||||||
procedure TestOr;
|
procedure TestOr;
|
||||||
procedure TestNotOr;
|
procedure TestNotOr;
|
||||||
|
procedure TestCase;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TTestDomainParser }
|
{ TTestDomainParser }
|
||||||
@ -2203,6 +2204,34 @@ begin
|
|||||||
AssertLiteralExpr('Right is integer',T.Right,TSQLIntegerLiteral);
|
AssertLiteralExpr('Right is integer',T.Right,TSQLIntegerLiteral);
|
||||||
end;
|
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;
|
procedure TTestCheckParser.TestNotBetween;
|
||||||
|
|
||||||
Var
|
Var
|
||||||
|
Loading…
Reference in New Issue
Block a user