From a1d0d0c0ec197932ba3cda6acf21d13a56b2c8bc Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Mon, 23 Nov 2009 20:11:13 +0000 Subject: [PATCH] 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 --- source/helpers.pas | 50 +++++++++++++++-------------- source/main.pas | 78 +++++++++++++++++++++++++--------------------- 2 files changed, 69 insertions(+), 59 deletions(-) diff --git a/source/helpers.pas b/source/helpers.pas index 2a3df2a6..5c15eee6 100644 --- a/source/helpers.pas +++ b/source/helpers.pas @@ -133,7 +133,6 @@ type function Mince(PathToMince: String; InSpace: Integer): String; function MakeInt( Str: String ) : Int64; function MakeFloat( Str: String ): Extended; - function FloatStr(Val: String): String; function esc(Text: WideString; ProcessJokerChars: Boolean = false; sql_version: integer = 50000): WideString; function ScanNulChar(Text: WideString): Boolean; function ScanLineBreaks(Text: WideString): TLineBreaks; @@ -144,6 +143,7 @@ type function getFilesFromDir( dir: String; pattern: String = '*.*'; hideExt: Boolean = false ): TStringList; function goodfilename( str: String ): String; function FormatNumber( str: String ): String; Overload; + function UnformatNumber(Val: String): String; function FormatNumber( int: Int64 ): String; Overload; function FormatNumber( flt: Double; decimals: Integer = 0 ): String; Overload; procedure setLocales; @@ -953,7 +953,7 @@ begin // Remove 0x. if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2); // 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. Data := WideStringReplace(Data, Encloser, Encloser + Encloser, [rfReplaceAll]); // Special handling for NULL (MySQL-ism, not de-facto CSV: unquote value) @@ -1031,7 +1031,7 @@ begin // Remove 0x. if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2); // 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. Data := htmlentities(Data); // Add data and cell end tag. @@ -1109,7 +1109,7 @@ begin // Remove 0x. if GridData.Columns[i].DatatypeCat = dtcBinary then Delete(Data, 1, 2); // 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. tmp := tmp + esc(Data); end; @@ -1506,18 +1506,6 @@ begin 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. } @@ -1834,19 +1822,35 @@ end; -{*** - Return a formatted number from a string - by first converting it to a float and then to the desired format +{** + Unformat a formatted integer or float. Used for CSV export and composing WHERE clauses for grid editing. +} +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 @return string } function FormatNumber( str: String ): String; Overload; +var + i, p: Integer; begin - if str <> '' then - result := FormatNumber( StrToFloat( str ) ) - else - result := FormatNumber( 0 ); + Result := str; + Result := StringReplace(Result, '.', DecimalSeparator, [rfReplaceAll]); + p := Pos(DecimalSeparator, Result); + 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; diff --git a/source/main.pas b/source/main.pas index 26cb6464..5d6c8373 100644 --- a/source/main.pas +++ b/source/main.pas @@ -4030,10 +4030,11 @@ begin ActiveGridResult.Rows[i].Loaded := True; SetLength(ActiveGridResult.Rows[i].Cells, Results.ColumnCount); for j:=0 to Results.ColumnCount-1 do begin - if ActiveGridResult.Columns[j].DatatypeCat = dtcBinary then - ActiveGridResult.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)) - else - ActiveGridResult.Rows[i].Cells[j].Text := Results.Col(j); + case ActiveGridResult.Columns[j].DatatypeCat of + dtcInteger, dtcReal: ActiveGridResult.Rows[i].Cells[j].Text := FormatNumber(Results.Col(j)); + dtcBinary: ActiveGridResult.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)); + else ActiveGridResult.Rows[i].Cells[j].Text := Results.Col(j); + end; ActiveGridResult.Rows[i].Cells[j].IsNull := Results.IsNull(j); end; Results.Next; @@ -6443,10 +6444,11 @@ begin SetLength(res.Rows[Node.Index].Cells, Results.ColumnCount); i := Node.Index; for j := 0 to Results.ColumnCount - 1 do begin - if res.Columns[j].DatatypeCat = dtcBinary then - res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)) - else - res.Rows[i].Cells[j].Text := Results.Col(j); + case res.Columns[j].DatatypeCat of + dtcInteger, dtcReal: res.Rows[i].Cells[j].Text := FormatNumber(Results.Col(j)); + dtcBinary: res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)); + else res.Rows[i].Cells[j].Text := Results.Col(j); + end; res.Rows[i].Cells[j].IsNull := Results.IsNull(j); end; res.Rows[Node.Index].Loaded := True; @@ -6524,10 +6526,11 @@ begin for i:=start to start+limit-1 do begin SetLength(res.Rows[i].Cells, Results.ColumnCount); for j:=0 to Results.ColumnCount-1 do begin - if res.Columns[j].DatatypeCat = dtcBinary then - res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)) - else - res.Rows[i].Cells[j].Text := Results.Col(j); + case res.Columns[j].DatatypeCat of + dtcInteger, dtcReal: res.Rows[i].Cells[j].Text := FormatNumber(Results.Col(j)); + dtcBinary: res.Rows[i].Cells[j].Text := '0x' + BinToWideHex(Results.Col(j)); + else res.Rows[i].Cells[j].Text := Results.Col(j); + end; res.Rows[i].Cells[j].IsNull := Results.IsNull(j); end; res.Rows[i].Loaded := True; @@ -6809,13 +6812,14 @@ begin for i := 0 to Length(DataGridResult.Columns) - 1 do begin if Row.Cells[i].Modified then begin Val := Row.Cells[i].NewText; - if DataGridResult.Columns[i].DatatypeCat = dtcReal then - Val := FloatStr(Val) - else if DataGridResult.Columns[i].DatatypeCat = dtcBinary then begin - CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".'); - if Val = '0x' then Val := esc(''); - end else - Val := esc(Val); + case DataGridResult.Columns[i].DatatypeCat of + dtcInteger, dtcReal: Val := UnformatNumber(Val); + dtcBinary: begin + CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".'); + if Val = '0x' then Val := esc(''); + end; + else Val := esc(Val); + end; if Row.Cells[i].NewIsNull then Val := 'NULL'; sql := sql + ' ' + mask(DataGridResult.Columns[i].Name) + '=' + Val + ', '; end; @@ -6898,13 +6902,11 @@ begin // Find old value of key column KeyVal := Row.Cells[j].Text; // Quote if needed - if DataGridResult.Columns[j].DatatypeCat = dtcReal then - KeyVal := FloatStr(KeyVal) - else if DataGridResult.Columns[j].DatatypeCat = dtcBinary then begin - if KeyVal = '0x' then - KeyVal := esc(''); - end else - KeyVal := esc(KeyVal); + case DataGridResult.Columns[j].DatatypeCat of + dtcInteger, dtcReal: KeyVal := UnformatNumber(KeyVal); + dtcBinary: if KeyVal = '0x' then KeyVal := esc(''); + else KeyVal := esc(KeyVal); + end; if Row.Cells[j].IsNull then KeyVal := ' IS NULL' else KeyVal := '=' + KeyVal; @@ -7018,14 +7020,15 @@ begin if Row.Cells[i].Modified then begin Cols := Cols + mask(DataGridResult.Columns[i].Name) + ', '; Val := Row.Cells[i].NewText; - if DataGridResult.Columns[i].DatatypeCat = dtcReal then - Val := FloatStr(Val) - else if DataGridResult.Columns[i].DatatypeCat = dtcBinary then begin - CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".'); - if Val = '0x' then - Val := esc(''); - end else - Val := esc(Val); + case DataGridResult.Columns[i].DatatypeCat of + dtcInteger, dtcReal: Val := UnformatNumber(Val); + dtcBinary: begin + CheckHex(Copy(Val, 3), 'Invalid hexadecimal string given in field "' + DataGridResult.Columns[i].Name + '".'); + if Val = '0x' then + Val := esc(''); + end; + else Val := esc(Val); + end; if Row.Cells[i].NewIsNull then Val := 'NULL'; Vals := Vals + Val + ', '; end; @@ -7202,8 +7205,11 @@ begin ' WHERE ' + GetWhereClause(Row, @DataGridResult.Columns) ; Results := Connection.GetResults(sql); - if Col.DatatypeCat = dtcBinary then Cell.Text := '0x' + BinToWideHex(Results.Col(0)) - else Cell.Text := 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); + end; Cell.IsNull := Results.IsNull(0); end else Result := False;