diff --git a/source/helpers.pas b/source/helpers.pas index 34ac5b21..51f99bf1 100644 --- a/source/helpers.pas +++ b/source/helpers.pas @@ -187,7 +187,7 @@ type function MakeInt( Str: String ) : Int64; function MakeFloat( Str: String ): Extended; function CleanupNumber(Str: String): String; - function esc(Text: WideString; ProcessJokerChars: Boolean = false; sql_version: integer = 50000): WideString; + function esc(Text: WideString; ProcessJokerChars: Boolean=false): WideString; function ScanNulChar(Text: WideString): Boolean; function ScanLineBreaks(Text: WideString): TLineBreaks; function RemoveNulChars(Text: WideString): WideString; @@ -1563,109 +1563,12 @@ begin end; -{*** - Attempt to do string replacement faster than StringReplace and WideStringReplace. -} -function escChars(const Text: WideString; EscChar, Char1, Char2, Char3, Char4: WideChar): WideString; -const - // Attempt to match whatever the CPU cache will hold. - block: Cardinal = 65536; -var - bstart, bend, matches, i: Cardinal; - // These could be bumped to uint64 if necessary. - len, respos: Cardinal; - next: WideChar; +function esc(Text: WideString; ProcessJokerChars: Boolean=false): WideString; begin - len := Length(Text); - Result := ''; - bend := 0; - respos := 0; - repeat - bstart := bend + 1; - bend := bstart + block - 1; - if bend > len then bend := len; - matches := 0; - for i := bstart to bend do if - (Text[i] = Char1) or - (Text[i] = Char2) or - (Text[i] = Char3) or - (Text[i] = Char4) - then Inc(matches); - SetLength(Result, bend + 1 - bstart + matches + respos); - for i := bstart to bend do begin - next := Text[i]; - if - (next = Char1) or - (next = Char2) or - (next = Char3) or - (next = Char4) - then begin - Inc(respos); - Result[respos] := EscChar; - // Special values for MySQL escape. - if next = #13 then next := 'r'; - if next = #10 then next := 'n'; - if next = #0 then next := '0'; - end; - Inc(respos); - Result[respos] := next; - end; - until bend = len; + Mainform.Connection.EscapeString(Text, ProcessJokerChars); end; -{*** - Escape all kinds of characters: - - single-backslashes which represent normal parts of the text and not escape-sequences - - characters which MySQL doesn't strictly care about, but which might confuse editors etc. - - single and double quotes in a text string - - joker-chars for LIKE-comparisons - Finally, surround the text by single quotes. - - @param string Text to escape - @param boolean Escape text so it can be used in a LIKE-comparison - @return string -} -function esc(Text: WideString; ProcessJokerChars: Boolean = false; sql_version: integer = 50000): WideString; -var - c1, c2, c3, c4, EscChar: WideChar; -begin - c1 := ''''; - c2 := '\'; - c3 := '%'; - c4 := '_'; - EscChar := '\'; - if (not ProcessJokerChars) or (sql_version = SQL_VERSION_ANSI) then begin - // Do not escape joker-chars which are used in a LIKE-clause - c4 := ''''; - c3 := ''''; - end; - if sql_version = SQL_VERSION_ANSI then begin - c2 := ''''; - EscChar := ''''; - end; - Result := escChars(Text, EscChar, c1, c2, c3, c4); - if sql_version <> SQL_VERSION_ANSI then begin - // Remove characters that SynEdit chokes on, so that - // the SQL file can be non-corruptedly loaded again. - c1 := #13; - c2 := #10; - c3 := #0; - c4 := #0; - // TODO: SynEdit also chokes on WideChar($2028) and possibly WideChar($2029). - Result := escChars(Result, EscChar, c1, c2, c3, c4); - end; - if not ProcessJokerChars then begin - // Add surrounding single quotes only for non-LIKE-values - // because in all cases we're using ProcessLIKEChars we - // need to add leading and/or trailing joker-chars by hand - // without being escaped - Result := WideChar(#39) + Result + WideChar(#39); - end; -end; - - - {*** Detect NUL character in a text. Useful because fx SynEdit cuts of all text after it encounters a NUL. diff --git a/source/mysql_connection.pas b/source/mysql_connection.pas index 6f23db9b..8b01b37b 100644 --- a/source/mysql_connection.pas +++ b/source/mysql_connection.pas @@ -134,7 +134,8 @@ type constructor Create(AOwner: TComponent); override; destructor Destroy; override; function Query(SQL: WideString; DoStoreResult: Boolean=False): PMYSQL_RES; - function EscapeString(Text: WideString; DoQuote: Boolean=True): WideString; + function EscapeString(Text: WideString; ProcessJokerChars: Boolean=False): WideString; + function escChars(const Text: WideString; EscChar, Char1, Char2, Char3, Char4: WideChar): WideString; function QuoteIdent(Identifier: WideString): WideString; function DeQuoteIdent(Identifier: WideString): WideString; function ConvertServerVersion(Version: Integer): String; @@ -561,22 +562,97 @@ end; {** Escapes a string for usage in SQL queries -} -function TMySQLConnection.EscapeString(Text: WideString; DoQuote: Boolean): WideString; -var - BufferLen: Integer; - Buffer: PChar; - NativeText: String; -begin - BufferLen := Length(Text) * 2 + 1; - GetMem(Buffer, BufferLen); - NativeText := UTF8Encode(Text); - BufferLen := mysql_real_escape_string(FHandle, Buffer, PChar(NativeText), Length(Text)); - SetString(Result, Buffer, BufferLen); - FreeMem(Buffer); + - single-backslashes which represent normal parts of the text and not escape-sequences + - characters which MySQL doesn't strictly care about, but which might confuse editors etc. + - single and double quotes in a text string + - joker-chars for LIKE-comparisons + Finally, surround the text by single quotes. - if DoQuote then - Result := '''' + Result + ''''; + @param string Text to escape + @param boolean Escape text so it can be used in a LIKE-comparison + @return string +} +function TMySQLConnection.EscapeString(Text: WideString; ProcessJokerChars: Boolean=false): WideString; +var + c1, c2, c3, c4, EscChar: WideChar; +begin + c1 := ''''; + c2 := '\'; + c3 := '%'; + c4 := '_'; + EscChar := '\'; + if not ProcessJokerChars then begin + // Do not escape joker-chars which are used in a LIKE-clause + c4 := ''''; + c3 := ''''; + end; + Result := escChars(Text, EscChar, c1, c2, c3, c4); + // Remove characters that SynEdit chokes on, so that + // the SQL file can be non-corruptedly loaded again. + c1 := #13; + c2 := #10; + c3 := #0; + c4 := #0; + // TODO: SynEdit also chokes on WideChar($2028) and possibly WideChar($2029). + Result := escChars(Result, EscChar, c1, c2, c3, c4); + if not ProcessJokerChars then begin + // Add surrounding single quotes only for non-LIKE-values + // because in all cases we're using ProcessLIKEChars we + // need to add leading and/or trailing joker-chars by hand + // without being escaped + Result := WideChar(#39) + Result + WideChar(#39); + end; +end; + + +{*** + Attempt to do string replacement faster than StringReplace and WideStringReplace. +} +function TMySQLConnection.escChars(const Text: WideString; EscChar, Char1, Char2, Char3, Char4: WideChar): WideString; +const + // Attempt to match whatever the CPU cache will hold. + block: Cardinal = 65536; +var + bstart, bend, matches, i: Cardinal; + // These could be bumped to uint64 if necessary. + len, respos: Cardinal; + next: WideChar; +begin + len := Length(Text); + Result := ''; + bend := 0; + respos := 0; + repeat + bstart := bend + 1; + bend := bstart + block - 1; + if bend > len then bend := len; + matches := 0; + for i := bstart to bend do if + (Text[i] = Char1) or + (Text[i] = Char2) or + (Text[i] = Char3) or + (Text[i] = Char4) + then Inc(matches); + SetLength(Result, bend + 1 - bstart + matches + respos); + for i := bstart to bend do begin + next := Text[i]; + if + (next = Char1) or + (next = Char2) or + (next = Char3) or + (next = Char4) + then begin + Inc(respos); + Result[respos] := EscChar; + // Special values for MySQL escape. + if next = #13 then next := 'r'; + if next = #10 then next := 'n'; + if next = #0 then next := '0'; + end; + Inc(respos); + Result[respos] := next; + end; + until bend = len; end;