mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-06 18:24:26 +08:00
Move string escaping methods from helpers to connection unit, replace code in TMySQLConnection.EscapeString(). So we don't use two different methods for the same task. Also, EscapeString() converted WideStrings into Strings using Utf8Encode, which was then displayed as broken text in SQL log.
This commit is contained in:
@ -187,7 +187,7 @@ type
|
|||||||
function MakeInt( Str: String ) : Int64;
|
function MakeInt( Str: String ) : Int64;
|
||||||
function MakeFloat( Str: String ): Extended;
|
function MakeFloat( Str: String ): Extended;
|
||||||
function CleanupNumber(Str: String): String;
|
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 ScanNulChar(Text: WideString): Boolean;
|
||||||
function ScanLineBreaks(Text: WideString): TLineBreaks;
|
function ScanLineBreaks(Text: WideString): TLineBreaks;
|
||||||
function RemoveNulChars(Text: WideString): WideString;
|
function RemoveNulChars(Text: WideString): WideString;
|
||||||
@ -1563,109 +1563,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{***
|
function esc(Text: WideString; ProcessJokerChars: Boolean=false): WideString;
|
||||||
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;
|
|
||||||
begin
|
begin
|
||||||
len := Length(Text);
|
Mainform.Connection.EscapeString(Text, ProcessJokerChars);
|
||||||
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;
|
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.
|
Detect NUL character in a text.
|
||||||
Useful because fx SynEdit cuts of all text after it encounters a NUL.
|
Useful because fx SynEdit cuts of all text after it encounters a NUL.
|
||||||
|
@ -134,7 +134,8 @@ type
|
|||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function Query(SQL: WideString; DoStoreResult: Boolean=False): PMYSQL_RES;
|
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 QuoteIdent(Identifier: WideString): WideString;
|
||||||
function DeQuoteIdent(Identifier: WideString): WideString;
|
function DeQuoteIdent(Identifier: WideString): WideString;
|
||||||
function ConvertServerVersion(Version: Integer): String;
|
function ConvertServerVersion(Version: Integer): String;
|
||||||
@ -561,22 +562,97 @@ end;
|
|||||||
|
|
||||||
{**
|
{**
|
||||||
Escapes a string for usage in SQL queries
|
Escapes a string for usage in SQL queries
|
||||||
}
|
- single-backslashes which represent normal parts of the text and not escape-sequences
|
||||||
function TMySQLConnection.EscapeString(Text: WideString; DoQuote: Boolean): WideString;
|
- characters which MySQL doesn't strictly care about, but which might confuse editors etc.
|
||||||
var
|
- single and double quotes in a text string
|
||||||
BufferLen: Integer;
|
- joker-chars for LIKE-comparisons
|
||||||
Buffer: PChar;
|
Finally, surround the text by single quotes.
|
||||||
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);
|
|
||||||
|
|
||||||
if DoQuote then
|
@param string Text to escape
|
||||||
Result := '''' + Result + '''';
|
@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;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user