mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-09-13 21:11:23 +02:00
* Fixes Sql parsing problems with spaces lacking between keyword and expression
(like where(id=0) ) Mantis #21965, patch by Ludo, updated by Lacak2. git-svn-id: trunk@21742 -
This commit is contained in:
parent
65e701a198
commit
c4ec774c4c
@ -1010,7 +1010,7 @@ begin
|
||||
end;
|
||||
|
||||
if FWhereStartPos = 0 then
|
||||
SQLstr := SQLstr + ' where (' + Filter + ')'
|
||||
SQLstr := SQLstr + ' where (' + ServerFilter + ')'
|
||||
else if FWhereStopPos > 0 then
|
||||
system.insert(' and ('+ServerFilter+') ',SQLstr,FWhereStopPos+2)
|
||||
else
|
||||
@ -1217,18 +1217,17 @@ end;
|
||||
|
||||
function TCustomSQLQuery.SQLParser(const ASQL : string) : TStatementType;
|
||||
|
||||
type TParsePart = (ppStart,ppWith,ppSelect,ppFrom,ppWhere,ppGroup,ppOrder,ppComment,ppBogus);
|
||||
type TParsePart = (ppStart,ppWith,ppSelect,ppTableName,ppFrom,ppWhere,ppGroup,ppOrder,ppBogus);
|
||||
TPhraseSeparator = (sepNone, sepWhiteSpace, sepComma, sepComment, sepParentheses, sepEnd);
|
||||
|
||||
Var
|
||||
PSQL,CurrentP,
|
||||
PSQL, CurrentP, SavedP,
|
||||
PhraseP, PStatementPart : pchar;
|
||||
S : string;
|
||||
ParsePart : TParsePart;
|
||||
StrLength : Integer;
|
||||
EndOfComment : Boolean;
|
||||
BracketCount : Integer;
|
||||
ConnOptions : TConnOptions;
|
||||
FFromPart : String;
|
||||
Separator : TPhraseSeparator;
|
||||
|
||||
begin
|
||||
PSQL:=Pchar(ASQL);
|
||||
@ -1237,42 +1236,57 @@ begin
|
||||
CurrentP := PSQL-1;
|
||||
PhraseP := PSQL;
|
||||
|
||||
FTableName := '';
|
||||
FUpdateable := False;
|
||||
|
||||
FWhereStartPos := 0;
|
||||
FWhereStopPos := 0;
|
||||
|
||||
ConnOptions := TSQLConnection(DataBase).ConnOptions;
|
||||
FUpdateable := False;
|
||||
|
||||
repeat
|
||||
begin
|
||||
inc(CurrentP);
|
||||
SavedP := CurrentP;
|
||||
|
||||
EndOfComment := SkipComments(CurrentP, sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions);
|
||||
if EndOfcomment then dec(CurrentP);
|
||||
if EndOfComment and (ParsePart = ppStart) then PhraseP := CurrentP;
|
||||
|
||||
// skip everything between bracket, since it could be a sub-select, and
|
||||
// further nothing between brackets could be interesting for the parser.
|
||||
if CurrentP^='(' then
|
||||
begin
|
||||
inc(currentp);
|
||||
BracketCount := 0;
|
||||
while (currentp^ <> #0) and ((currentp^ <> ')') or (BracketCount > 0 )) do
|
||||
case CurrentP^ of
|
||||
' ', #9, #10, #11, #12, #13:
|
||||
Separator := sepWhiteSpace;
|
||||
',':
|
||||
Separator := sepComma;
|
||||
#0, ';':
|
||||
Separator := sepEnd;
|
||||
'(':
|
||||
begin
|
||||
if currentp^ = '(' then inc(bracketcount)
|
||||
else if currentp^ = ')' then dec(bracketcount);
|
||||
inc(currentp);
|
||||
Separator := sepParentheses;
|
||||
// skip everything between brackets, since it could be a sub-select, and
|
||||
// further nothing between brackets could be interesting for the parser.
|
||||
BracketCount := 1;
|
||||
repeat
|
||||
inc(CurrentP);
|
||||
if CurrentP^ = '(' then inc(BracketCount)
|
||||
else if CurrentP^ = ')' then dec(BracketCount);
|
||||
until (CurrentP^ = #0) or (BracketCount = 0);
|
||||
if CurrentP^ <> #0 then inc(CurrentP);
|
||||
end;
|
||||
EndOfComment := True;
|
||||
end;
|
||||
else
|
||||
if SkipComments(CurrentP, sqEscapeSlash in ConnOptions, sqEscapeRepeat in ConnOptions) then
|
||||
Separator := sepComment
|
||||
else
|
||||
Separator := sepNone;
|
||||
end;
|
||||
|
||||
if EndOfComment or (CurrentP^ in [' ',#13,#10,#9,#0,';']) then
|
||||
if (CurrentP > SavedP) and (SavedP > PhraseP) then
|
||||
CurrentP := SavedP; // there is something before comment or left parenthesis
|
||||
|
||||
if Separator <> sepNone then
|
||||
begin
|
||||
if (CurrentP-PhraseP > 0) or (CurrentP^ in [';',#0]) then
|
||||
if ((Separator in [sepWhitespace,sepComment]) and (PhraseP = SavedP)) then
|
||||
PhraseP := CurrentP; // skip comments(but not parentheses) and white spaces
|
||||
|
||||
if (CurrentP-PhraseP > 0) or (Separator = sepEnd) then
|
||||
begin
|
||||
strLength := CurrentP-PhraseP;
|
||||
Setlength(S,strLength);
|
||||
if strLength > 0 then Move(PhraseP^,S[1],(strLength));
|
||||
SetString(s, PhraseP, CurrentP-PhraseP);
|
||||
s := uppercase(s);
|
||||
|
||||
case ParsePart of
|
||||
@ -1284,7 +1298,6 @@ begin
|
||||
else break;
|
||||
end;
|
||||
if not FParseSQL then break;
|
||||
PStatementPart := CurrentP;
|
||||
end;
|
||||
ppWith : begin
|
||||
// WITH [RECURSIVE] CTE_name [ ( column_names ) ] AS ( CTE_query_definition ) [, ...]
|
||||
@ -1299,69 +1312,53 @@ begin
|
||||
end;
|
||||
ppSelect : begin
|
||||
if s = 'FROM' then
|
||||
ParsePart := ppTableName;
|
||||
end;
|
||||
ppTableName:
|
||||
begin
|
||||
// Meta-data requests are never updateable
|
||||
// and select-statements from more then one table
|
||||
// and/or derived tables are also not updateable
|
||||
if (FSchemaType = stNoSchema) and
|
||||
(Separator in [sepWhitespace, sepComment, sepEnd]) then
|
||||
begin
|
||||
ParsePart := ppFrom;
|
||||
PhraseP := CurrentP;
|
||||
PStatementPart := CurrentP;
|
||||
FTableName := s;
|
||||
FUpdateable := True;
|
||||
end;
|
||||
ParsePart := ppFrom;
|
||||
end;
|
||||
ppFrom : begin
|
||||
if (s = 'WHERE') or (s = 'ORDER') or (s = 'GROUP') or (s = 'LIMIT') or (CurrentP^=#0) or (CurrentP^=';') then
|
||||
if (s = 'WHERE') or (s = 'GROUP') or (s = 'ORDER') or (s = 'LIMIT') or (s = 'ROWS') or
|
||||
(Separator = sepEnd) then
|
||||
begin
|
||||
if (s = 'WHERE') then
|
||||
begin
|
||||
ParsePart := ppWhere;
|
||||
StrLength := PhraseP-PStatementPart;
|
||||
end
|
||||
else if (s = 'GROUP') then
|
||||
begin
|
||||
ParsePart := ppGroup;
|
||||
StrLength := PhraseP-PStatementPart;
|
||||
end
|
||||
else if (s = 'ORDER') then
|
||||
begin
|
||||
ParsePart := ppOrder;
|
||||
StrLength := PhraseP-PStatementPart
|
||||
end
|
||||
else if (s = 'LIMIT') then
|
||||
begin
|
||||
ParsePart := ppBogus;
|
||||
StrLength := PhraseP-PStatementPart
|
||||
end
|
||||
else
|
||||
begin
|
||||
ParsePart := ppBogus;
|
||||
StrLength := CurrentP-PStatementPart;
|
||||
end;
|
||||
if Result = stSelect then
|
||||
begin
|
||||
Setlength(FFromPart,StrLength);
|
||||
Move(PStatementPart^,FFromPart[1],(StrLength));
|
||||
FFromPart := trim(FFromPart);
|
||||
case s of
|
||||
'WHERE': ParsePart := ppWhere;
|
||||
'GROUP': ParsePart := ppGroup;
|
||||
'ORDER': ParsePart := ppOrder;
|
||||
else ParsePart := ppBogus;
|
||||
end;
|
||||
|
||||
// Meta-data requests and are never updateable select-statements
|
||||
// from more then one table are not updateable
|
||||
if (FSchemaType=stNoSchema) and
|
||||
(ExtractStrings([',',' '],[],pchar(FFromPart),nil) = 1) then
|
||||
begin
|
||||
FUpdateable := True;
|
||||
FTableName := FFromPart;
|
||||
end;
|
||||
end;
|
||||
|
||||
FWhereStartPos := PStatementPart-PSQL+StrLength+1;
|
||||
FWhereStartPos := PhraseP-PSQL+1;
|
||||
PStatementPart := CurrentP;
|
||||
end
|
||||
else
|
||||
// joined table or user_defined_function (...)
|
||||
if (s = 'JOIN') or (Separator in [sepComma, sepParentheses]) then
|
||||
begin
|
||||
FTableName := '';
|
||||
FUpdateable := False;
|
||||
end;
|
||||
end;
|
||||
ppWhere : begin
|
||||
if (s = 'ORDER') or (s = 'GROUP') or (s = 'LIMIT') or (CurrentP^=#0) or (CurrentP^=';') then
|
||||
if (s = 'GROUP') or (s = 'ORDER') or (s = 'LIMIT') or (s = 'ROWS') or
|
||||
(Separator = sepEnd) then
|
||||
begin
|
||||
ParsePart := ppBogus;
|
||||
FWhereStartPos := PStatementPart-PSQL;
|
||||
if (s = 'ORDER') or (s = 'GROUP') or (s = 'LIMIT') then
|
||||
FWhereStopPos := PhraseP-PSQL+1
|
||||
if (Separator = sepEnd) then
|
||||
FWhereStopPos := CurrentP-PSQL+1
|
||||
else
|
||||
FWhereStopPos := CurrentP-PSQL+1;
|
||||
FWhereStopPos := PhraseP-PSQL+1;
|
||||
end
|
||||
else if (s = 'UNION') then
|
||||
begin
|
||||
@ -1371,6 +1368,8 @@ begin
|
||||
end;
|
||||
end; {case}
|
||||
end;
|
||||
if Separator in [sepComment, sepParentheses] then
|
||||
dec(CurrentP);
|
||||
PhraseP := CurrentP+1;
|
||||
end
|
||||
end;
|
||||
@ -1381,7 +1380,6 @@ procedure TCustomSQLQuery.InternalOpen;
|
||||
|
||||
var tel, fieldc : integer;
|
||||
f : TField;
|
||||
s : string;
|
||||
IndexFields : TStrings;
|
||||
ReadFromFile: Boolean;
|
||||
begin
|
||||
|
@ -38,7 +38,7 @@ type
|
||||
procedure TestInsertLargeStrFields; // bug 9600
|
||||
procedure TestNumericNames; // Bug9661
|
||||
procedure TestApplyUpdFieldnames; // Bug 12275;
|
||||
procedure TestLimitQuery; // bug 15456
|
||||
procedure TestServerFilter; // bug 15456
|
||||
procedure Test11Params;
|
||||
procedure TestRowsAffected; // bug 9758
|
||||
procedure TestLocateNull;
|
||||
@ -1438,7 +1438,13 @@ begin
|
||||
begin
|
||||
SQL.Text:='select TT.NAME from FPDEV left join FPDEV TT on TT.ID=FPDEV.ID';
|
||||
Open;
|
||||
close;
|
||||
AssertFalse(CanModify);
|
||||
Close;
|
||||
|
||||
SQL.Text:='select T1.NAME from FPDEV T1,FPDEV T2 where T1.ID=T2.ID';
|
||||
Open;
|
||||
AssertFalse(CanModify);
|
||||
Close;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@ -1565,25 +1571,57 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestFieldTypes.TestLimitQuery;
|
||||
procedure TTestFieldTypes.TestServerFilter;
|
||||
begin
|
||||
with TSQLDBConnector(DBConnector) do
|
||||
begin
|
||||
with query do
|
||||
begin
|
||||
case sqlDBtype of
|
||||
interbase : SQL.Text:='select first 1 NAME from FPDEV where NAME=''TestName21''';
|
||||
mssql : SQL.Text:='select top 1 NAME from FPDEV where NAME=''TestName21''';
|
||||
else SQL.Text:='select NAME from FPDEV where NAME=''TestName21'' limit 1';
|
||||
end;
|
||||
Open;
|
||||
close;
|
||||
ServerFilter:='ID=21';
|
||||
ServerFiltered:=true;
|
||||
open;
|
||||
close;
|
||||
end;
|
||||
// Tests SQLParser and ServerFilter
|
||||
with TSQLDBConnector(DBConnector).Query do
|
||||
begin
|
||||
ServerFilter:='ID=21';
|
||||
ServerFiltered:=true;
|
||||
|
||||
// tests parsing SELECT without WHERE
|
||||
SQL.Text:='select * from FPDEV';
|
||||
Open;
|
||||
CheckTrue(CanModify, SQL.Text);
|
||||
CheckEquals(1, RecordCount);
|
||||
Close;
|
||||
|
||||
SQL.Text:='select *'#13'from FPDEV'#13'order by 1';
|
||||
Open;
|
||||
CheckTrue(CanModify, SQL.Text);
|
||||
CheckEquals(1, RecordCount);
|
||||
Close;
|
||||
|
||||
// tests parsing SELECT with simple WHERE
|
||||
SQL.Text:='select *'#9'from FPDEV'#9'where NAME<>''''';
|
||||
Open;
|
||||
CheckTrue(CanModify, SQL.Text);
|
||||
CheckEquals(1, RecordCount);
|
||||
Close;
|
||||
|
||||
// tests parsing SELECT with simple WHERE followed by ORDER BY
|
||||
SQL.Text:='select *'#10'from FPDEV'#10'where NAME>'''' order by 1';
|
||||
Open;
|
||||
CheckTrue(CanModify, SQL.Text);
|
||||
CheckEquals(1, RecordCount);
|
||||
Close;
|
||||
|
||||
// tests parsing of WHERE ... LIMIT
|
||||
case sqlDBtype of
|
||||
interbase : SQL.Text:='select first 1 NAME from FPDEV where NAME=''TestName21''';
|
||||
mssql : SQL.Text:='select top 1 NAME from FPDEV where NAME=''TestName21''';
|
||||
else SQL.Text:='select NAME from FPDEV where NAME=''TestName21'' limit 1';
|
||||
end;
|
||||
Open;
|
||||
CheckTrue(CanModify, SQL.Text);
|
||||
Close;
|
||||
|
||||
// tests parsing SELECT with table alias and embedded comments (MySQL requires space after -- )
|
||||
SQL.Text:='/**/select * from/**/FPDEV as fp-- comment'#13'where(NAME>''TestName20'')/**/order by 1';
|
||||
Open;
|
||||
CheckTrue(CanModify, SQL.Text);
|
||||
Close;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestFieldTypes.TestRowsAffected;
|
||||
|
Loading…
Reference in New Issue
Block a user