mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-11-04 09:02:22 +01: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