Prefer local formatting over raw numbers in grids:

* This time, also support formatted integers, not only floats.
* Do not quote both types in grid updates/deletes/inserts.
* Do no internal conversion from string to float and back to string (FloatToStr did that) to avoid silent data corruption. Just use strings in all places.
* Replace FloatStr() function with UnformatNumber(), which is also able to handle integers
Should fix issue #1012
This commit is contained in:
Ansgar Becker
2009-11-23 20:11:13 +00:00
parent 50f0108864
commit a1d0d0c0ec
2 changed files with 69 additions and 59 deletions

View File

@ -133,7 +133,6 @@ type
function Mince(PathToMince: String; InSpace: Integer): String; function Mince(PathToMince: String; InSpace: Integer): String;
function MakeInt( Str: String ) : Int64; function MakeInt( Str: String ) : Int64;
function MakeFloat( Str: String ): Extended; function MakeFloat( Str: String ): Extended;
function FloatStr(Val: String): String;
function esc(Text: WideString; ProcessJokerChars: Boolean = false; sql_version: integer = 50000): WideString; function esc(Text: WideString; ProcessJokerChars: Boolean = false; sql_version: integer = 50000): WideString;
function ScanNulChar(Text: WideString): Boolean; function ScanNulChar(Text: WideString): Boolean;
function ScanLineBreaks(Text: WideString): TLineBreaks; function ScanLineBreaks(Text: WideString): TLineBreaks;
@ -144,6 +143,7 @@ type
function getFilesFromDir( dir: String; pattern: String = '*.*'; hideExt: Boolean = false ): TStringList; function getFilesFromDir( dir: String; pattern: String = '*.*'; hideExt: Boolean = false ): TStringList;
function goodfilename( str: String ): String; function goodfilename( str: String ): String;
function FormatNumber( str: String ): String; Overload; function FormatNumber( str: String ): String; Overload;
function UnformatNumber(Val: String): String;
function FormatNumber( int: Int64 ): String; Overload; function FormatNumber( int: Int64 ): String; Overload;
function FormatNumber( flt: Double; decimals: Integer = 0 ): String; Overload; function FormatNumber( flt: Double; decimals: Integer = 0 ): String; Overload;
procedure setLocales; procedure setLocales;
@ -953,7 +953,7 @@ begin
// Remove 0x. // Remove 0x.
if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2); if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2);
// Unformat float values // Unformat float values
if GridData.Columns[i].DatatypeCat = dtcReal then Data := FloatStr(Data); if GridData.Columns[i].DatatypeCat in [dtcInteger, dtcReal] then Data := UnformatNumber(Data);
// Escape encloser characters inside data per de-facto CSV. // Escape encloser characters inside data per de-facto CSV.
Data := WideStringReplace(Data, Encloser, Encloser + Encloser, [rfReplaceAll]); Data := WideStringReplace(Data, Encloser, Encloser + Encloser, [rfReplaceAll]);
// Special handling for NULL (MySQL-ism, not de-facto CSV: unquote value) // Special handling for NULL (MySQL-ism, not de-facto CSV: unquote value)
@ -1031,7 +1031,7 @@ begin
// Remove 0x. // Remove 0x.
if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2); if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2);
// Unformat float values // Unformat float values
if GridData.Columns[i].DatatypeCat = dtcReal then Data := FloatStr(Data); if GridData.Columns[i].DatatypeCat in [dtcInteger, dtcReal] then Data := UnformatNumber(Data);
// Escape XML control characters in data. // Escape XML control characters in data.
Data := htmlentities(Data); Data := htmlentities(Data);
// Add data and cell end tag. // Add data and cell end tag.
@ -1109,7 +1109,7 @@ begin
// Remove 0x. // Remove 0x.
if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2); if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2);
// Unformat float values // Unformat float values
if GridData.Columns[i].DatatypeCat = dtcReal then Data := FloatStr(Data); if GridData.Columns[i].DatatypeCat in [dtcInteger, dtcReal] then Data := UnformatNumber(Data);
// Add data and cell end tag. // Add data and cell end tag.
tmp := tmp + esc(Data); tmp := tmp + esc(Data);
end; end;
@ -1506,18 +1506,6 @@ begin
end; end;
{**
Unformat a formatted float value. Used for CSV export and composing WHERE clauses for grid editing.
}
function FloatStr(Val: String): String;
begin
Result := Val;
if ThousandSeparator <> DecimalSeparator then
Result := StringReplace(Val, ThousandSeparator, '', [rfReplaceAll]);
Result := StringReplace(Val, DecimalSeparator, '.', [rfReplaceAll]);
end;
{*** {***
Attempt to do string replacement faster than StringReplace and WideStringReplace. Attempt to do string replacement faster than StringReplace and WideStringReplace.
} }
@ -1834,19 +1822,35 @@ end;
{*** {**
Return a formatted number from a string Unformat a formatted integer or float. Used for CSV export and composing WHERE clauses for grid editing.
by first converting it to a float and then to the desired format }
function UnformatNumber(Val: String): String;
begin
Result := Val;
if ThousandSeparator <> DecimalSeparator then
Result := StringReplace(Result, ThousandSeparator, '', [rfReplaceAll]);
Result := StringReplace(Result, DecimalSeparator, '.', [rfReplaceAll]);
end;
{***
Return a formatted integer or float from a string
@param string Text containing a number @param string Text containing a number
@return string @return string
} }
function FormatNumber( str: String ): String; Overload; function FormatNumber( str: String ): String; Overload;
var
i, p: Integer;
begin begin
if str <> '' then Result := str;
result := FormatNumber( StrToFloat( str ) ) Result := StringReplace(Result, '.', DecimalSeparator, [rfReplaceAll]);
else p := Pos(DecimalSeparator, Result);
result := FormatNumber( 0 ); if p = 0 then p := Length(Result)+1;
if p > 0 then for i:=p-1 downto 2 do begin
if (p-i) mod 3 = 0 then
Insert(ThousandSeparator, Result, i);
end;
end; end;

View File

@ -4030,10 +4030,11 @@ begin
ActiveGridResult.Rows[i].Loaded := True; ActiveGridResult.Rows[i].Loaded := True;
SetLength(ActiveGridResult.Rows[i].Cells, Results.ColumnCount); SetLength(ActiveGridResult.Rows[i].Cells, Results.ColumnCount);
for j:=0 to Results.ColumnCount-1 do begin for j:=0 to Results.ColumnCount-1 do begin
if ActiveGridResult.Columns[j].DatatypeCat = dtcBinary then case ActiveGridResult.Columns[j].DatatypeCat of
ActiveGridResult.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)) dtcInteger, dtcReal: ActiveGridResult.Rows[i].Cells[j].Text := FormatNumber(Results.Col(j));
else dtcBinary: ActiveGridResult.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j));
ActiveGridResult.Rows[i].Cells[j].Text := Results.Col(j); else ActiveGridResult.Rows[i].Cells[j].Text := Results.Col(j);
end;
ActiveGridResult.Rows[i].Cells[j].IsNull := Results.IsNull(j); ActiveGridResult.Rows[i].Cells[j].IsNull := Results.IsNull(j);
end; end;
Results.Next; Results.Next;
@ -6443,10 +6444,11 @@ begin
SetLength(res.Rows[Node.Index].Cells, Results.ColumnCount); SetLength(res.Rows[Node.Index].Cells, Results.ColumnCount);
i := Node.Index; i := Node.Index;
for j := 0 to Results.ColumnCount - 1 do begin for j := 0 to Results.ColumnCount - 1 do begin
if res.Columns[j].DatatypeCat = dtcBinary then case res.Columns[j].DatatypeCat of
res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)) dtcInteger, dtcReal: res.Rows[i].Cells[j].Text := FormatNumber(Results.Col(j));
else dtcBinary: res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j));
res.Rows[i].Cells[j].Text := Results.Col(j); else res.Rows[i].Cells[j].Text := Results.Col(j);
end;
res.Rows[i].Cells[j].IsNull := Results.IsNull(j); res.Rows[i].Cells[j].IsNull := Results.IsNull(j);
end; end;
res.Rows[Node.Index].Loaded := True; res.Rows[Node.Index].Loaded := True;
@ -6524,10 +6526,11 @@ begin
for i:=start to start+limit-1 do begin for i:=start to start+limit-1 do begin
SetLength(res.Rows[i].Cells, Results.ColumnCount); SetLength(res.Rows[i].Cells, Results.ColumnCount);
for j:=0 to Results.ColumnCount-1 do begin for j:=0 to Results.ColumnCount-1 do begin
if res.Columns[j].DatatypeCat = dtcBinary then case res.Columns[j].DatatypeCat of
res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)) dtcInteger, dtcReal: res.Rows[i].Cells[j].Text := FormatNumber(Results.Col(j));
else dtcBinary: res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j));
res.Rows[i].Cells[j].Text := Results.Col(j); else res.Rows[i].Cells[j].Text := Results.Col(j);
end;
res.Rows[i].Cells[j].IsNull := Results.IsNull(j); res.Rows[i].Cells[j].IsNull := Results.IsNull(j);
end; end;
res.Rows[i].Loaded := True; res.Rows[i].Loaded := True;
@ -6809,13 +6812,14 @@ begin
for i := 0 to Length(DataGridResult.Columns) - 1 do begin for i := 0 to Length(DataGridResult.Columns) - 1 do begin
if Row.Cells[i].Modified then begin if Row.Cells[i].Modified then begin
Val := Row.Cells[i].NewText; Val := Row.Cells[i].NewText;
if DataGridResult.Columns[i].DatatypeCat = dtcReal then case DataGridResult.Columns[i].DatatypeCat of
Val := FloatStr(Val) dtcInteger, dtcReal: Val := UnformatNumber(Val);
else if DataGridResult.Columns[i].DatatypeCat = dtcBinary then begin dtcBinary: begin
CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".'); CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".');
if Val = '0x' then Val := esc(''); if Val = '0x' then Val := esc('');
end else end;
Val := esc(Val); else Val := esc(Val);
end;
if Row.Cells[i].NewIsNull then Val := 'NULL'; if Row.Cells[i].NewIsNull then Val := 'NULL';
sql := sql + ' ' + mask(DataGridResult.Columns[i].Name) + '=' + Val + ', '; sql := sql + ' ' + mask(DataGridResult.Columns[i].Name) + '=' + Val + ', ';
end; end;
@ -6898,13 +6902,11 @@ begin
// Find old value of key column // Find old value of key column
KeyVal := Row.Cells[j].Text; KeyVal := Row.Cells[j].Text;
// Quote if needed // Quote if needed
if DataGridResult.Columns[j].DatatypeCat = dtcReal then case DataGridResult.Columns[j].DatatypeCat of
KeyVal := FloatStr(KeyVal) dtcInteger, dtcReal: KeyVal := UnformatNumber(KeyVal);
else if DataGridResult.Columns[j].DatatypeCat = dtcBinary then begin dtcBinary: if KeyVal = '0x' then KeyVal := esc('');
if KeyVal = '0x' then else KeyVal := esc(KeyVal);
KeyVal := esc(''); end;
end else
KeyVal := esc(KeyVal);
if Row.Cells[j].IsNull then KeyVal := ' IS NULL' if Row.Cells[j].IsNull then KeyVal := ' IS NULL'
else KeyVal := '=' + KeyVal; else KeyVal := '=' + KeyVal;
@ -7018,14 +7020,15 @@ begin
if Row.Cells[i].Modified then begin if Row.Cells[i].Modified then begin
Cols := Cols + mask(DataGridResult.Columns[i].Name) + ', '; Cols := Cols + mask(DataGridResult.Columns[i].Name) + ', ';
Val := Row.Cells[i].NewText; Val := Row.Cells[i].NewText;
if DataGridResult.Columns[i].DatatypeCat = dtcReal then case DataGridResult.Columns[i].DatatypeCat of
Val := FloatStr(Val) dtcInteger, dtcReal: Val := UnformatNumber(Val);
else if DataGridResult.Columns[i].DatatypeCat = dtcBinary then begin dtcBinary: begin
CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".'); CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".');
if Val = '0x' then if Val = '0x' then
Val := esc(''); Val := esc('');
end else end;
Val := esc(Val); else Val := esc(Val);
end;
if Row.Cells[i].NewIsNull then Val := 'NULL'; if Row.Cells[i].NewIsNull then Val := 'NULL';
Vals := Vals + Val + ', '; Vals := Vals + Val + ', ';
end; end;
@ -7202,8 +7205,11 @@ begin
' WHERE ' + GetWhereClause(Row, @DataGridResult.Columns) ' WHERE ' + GetWhereClause(Row, @DataGridResult.Columns)
; ;
Results := Connection.GetResults(sql); Results := Connection.GetResults(sql);
if Col.DatatypeCat = dtcBinary then Cell.Text := '0x' + BinToWideHex(Results.Col(0)) case Col.DatatypeCat of
dtcInteger, dtcReal: Cell.Text := FormatNumber(Results.Col(0));
dtcBinary: Cell.Text := '0x' + BinToWideHex(Results.Col(0));
else Cell.Text := Results.Col(0); else Cell.Text := Results.Col(0);
end;
Cell.IsNull := Results.IsNull(0); Cell.IsNull := Results.IsNull(0);
end else end else
Result := False; Result := False;