[英]Delphi ADO BCD Decimal field value set inproperly
I have created a bug on the Embarcadero quality portal.我在 Embarcadero 质量门户上创建了一个错误。 But maybe someone has a workaround to make the test project work on these marks:
但也许有人有一个解决方法来使测试项目在这些标记上工作:
Issue Details问题详情
The bug appears when you have Windows regional settings : dot as grouping symbol and comma as decimal separator.当您有 Windows区域设置时会出现该错误:点作为分组符号,逗号作为小数分隔符。
When you try to set the value to Decimal field in MS-ACCESS database using AdoConnection + AdoTable, you get actual value multiplied with scale, ie: you set 12,354 you get 12354 for Decimal(15,3) field in MDB.当您尝试使用 AdoConnection + AdoTable 将值设置为 MS-ACCESS 数据库中的 Decimal 字段时,您会得到实际值乘以比例,即:您设置 12,354,您会在 MDB 中为 Decimal(15,3) 字段设置 12354。
Test project https://github.com/IgorKaplya/AdoBcdBug测试项目https://github.com/IgorKaplya/AdoBcdBug
unit Unit1;
interface
uses
System.SysUtils, System.Variants, System.Classes,
Vcl.Controls, Vcl.Forms, Vcl.StdCtrls,
Data.DB, FireDAC.Comp.Client, Data.Win.ADODB, FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf,
FireDAC.Phys.Intf, FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.MSAcc,
FireDAC.Phys.MSAccDef, FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt,
FireDAC.Comp.DataSet;
type
TForm1 = class(TForm)
conFDac: TFDConnection;
TableFireDac: TFDTable;
mmLog: TMemo;
conAdo: TADOConnection;
TableAdo: TADOTable;
btnTestBcdAdo: TButton;
btnBcdTestFDac: TButton;
btnSimpleAdo: TButton;
btnSimpleFDac: TButton;
procedure btnBcdTestFDacClick(Sender: TObject);
procedure btnSimpleAdoClick(Sender: TObject);
procedure btnSimpleFDacClick(Sender: TObject);
procedure btnTestBcdAdoClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FTestField: string;
FTestTable: string;
procedure AddInvitationMessage;
procedure EnsureTestTableExists;
procedure InitializeAdoConnection;
procedure InitializeFDacConnection;
procedure SetupTableComponents;
procedure TestBCD(const ATable: TDataSet);
procedure TestSimple(const ATable: TDataSet);
{ Private declarations }
public
property TestField: string read FTestField write FTestField;
property TestTable: string read FTestTable write FTestTable;
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
Data.FmtBcd;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
AddInvitationMessage();
InitializeAdoConnection;
EnsureTestTableExists();
InitializeFDacConnection;
SetupTableComponents();
end;
procedure TForm1.AddInvitationMessage;
const
description_message =
'Hi, the bug appears when you have Windows regional settings: dot as grouping symbol and comma as decimal sepearator';
affected_message: array[Boolean] of string = ('NOT AFFECTED', 'AFFECTED');
var
systemIsAffected: Boolean;
begin
mmLog.Lines.Add(description_message);
mmLog.Lines.Add(Format(' Your grouping symbol: %s', [QuotedStr(FormatSettings.ThousandSeparator)]));
mmLog.Lines.Add(Format(' Your decimal separator: %s', [QuotedStr(FormatSettings.DecimalSeparator)]));
systemIsAffected :=
SameText(FormatSettings.ThousandSeparator, '.') and
SameText(FormatSettings.DecimalSeparator, ',');
mmLog.Lines.Add('This system should be '+ affected_message[systemIsAffected]);
end;
procedure TForm1.btnBcdTestFDacClick(Sender: TObject);
begin
TestBCD(TableFireDac);
end;
procedure TForm1.btnSimpleAdoClick(Sender: TObject);
begin
TestSimple(TableAdo);
end;
procedure TForm1.btnSimpleFDacClick(Sender: TObject);
begin
TestSimple(TableFireDac);
end;
procedure TForm1.InitializeFDacConnection;
begin
conFDac.DriverName := 'MSAcc';
conFDac.Params.Database := '.\TestBase.mdb';
conFDac.Connected := True;
TableFireDac.Connection := conFDac;
end;
procedure TForm1.InitializeAdoConnection;
const
ado_jet_connection_string =
'Provider=Microsoft.Jet.OLEDB.4.0;'+
'Data Source=TestBase.mdb;'+
'Mode=ReadWrite;'+
'Persist Security Info=False;';
ado_ace_connection_string =
'Provider=Microsoft.ACE.OLEDB.12.0;'+
'Data Source=TestBase.mdb;'+
'Mode=ReadWrite;'+
'Persist Security Info=False;';
begin
conAdo.ConnectionString := ado_ace_connection_string;
conAdo.Connected := True;
TableAdo.Connection := conAdo;
end;
procedure TForm1.EnsureTestTableExists;
var
dummy: Integer;
tables: TStringList;
begin
TestTable := 'test_table';
TestField := 'test_field';
tables := TStringList.Create;
tables.Sorted := true;
try
conAdo.GetTableNames(tables);
if tables.Find(TestTable, dummy) then
conAdo.Execute(format('drop table %s', [TestTable]));
conAdo.Execute(format('create table %s (%s decimal(15,3))', [TestTable, TestField]));
finally
tables.Free;
end;
end;
procedure TForm1.SetupTableComponents;
begin
TableAdo.TableName := TestTable;
TableFireDac.TableName := TestTable;
end;
procedure TForm1.TestBCD(const ATable: TDataSet);
var
doubleValue: Double;
bcd, normalizedBcd: TBCD;
postedValue, reloadedValue: string;
begin
mmLog.Lines.Add('');
mmLog.Lines.Add('TestBCD: '+ATable.Name);
doubleValue := 12.34567;
mmLog.Lines.Add('Double: '+ doubleValue.ToString);
bcd := DoubleToBcd(doubleValue);
mmLog.Lines.Add('BCD: '+ BcdToStr(bcd));
NormalizeBcd(bcd, normalizedBcd, 15, 3);
mmLog.Lines.Add('Normalized BCD: '+BcdToStr(normalizedBcd));
ATable.Open;
ATable.Insert;
ATable.FieldByName(TestField).AsBCD := normalizedBcd;
ATable.Post;
postedValue := ATable.FieldByName(TestField).AsString;
mmLog.Lines.Add('Posted: ' + postedValue);
ATable.Close;
ATable.Open;
ATable.Last;
reloadedValue := ATable.FieldByName(TestField).AsString;
mmLog.Lines.Add('Reloaded: '+reloadedValue);
Assert(reloadedValue.Equals(postedValue), 'Reloaded value is not equal to posted.');
end;
procedure TForm1.btnTestBcdAdoClick(Sender: TObject);
begin
TestBCD(TableAdo);
end;
procedure TForm1.TestSimple(const ATable: TDataSet);
var
stringValue: String;
postedValue, reloadedValue: string;
begin
mmLog.Lines.Add('');
mmLog.Lines.Add('TestSimple: '+ATable.Name);
stringValue := '12,345';
mmLog.Lines.Add('String: '+ stringValue);
ATable.Open;
ATable.Insert;
ATable[TestField] := stringValue;
ATable.Post;
postedValue := ATable.FieldByName(TestField).AsString;
mmLog.Lines.Add('Posted: ' + postedValue);
ATable.Close;
ATable.Open;
ATable.Last;
reloadedValue := ATable.FieldByName(TestField).AsString;
mmLog.Lines.Add('Reloaded: '+reloadedValue);
Assert(reloadedValue.Equals(postedValue), 'Reloaded value is not equal to posted.');
end;
end.
The output in mmLog is mmLog 中的 output 是
Hi, the bug appears when you have Windows regional settings: dot as grouping symbol and comma as decimal sepearator
Your grouping symbol: '.'
Your decimal separator: ','
This system should be AFFECTED
TestBCD: TableAdo
Double: 12,34567
BCD: 12,34567
Normalized BCD: 12,345
Posted: 12,345
Reloaded: 12345
TestBCD: TableFireDac
Double: 12,34567
BCD: 12,34567
Normalized BCD: 12,345
Posted: 12,345
Reloaded: 12,345
TestSimple: TableAdo
String: 12,345
Posted: 12,345
Reloaded: 12345
TestSimple: TableFireDac
String: 12,345
Posted: 12,345
Reloaded: 12,345
Actual value stored in database存储在数据库中的实际值
This piece of VBA code works fine (can be found in TestVBA.mdb in https://github.com/IgorKaplya/AdoBcdBug )这段 VBA 代码工作正常(可以在https://github.com/IgorKaplya/AdoBcdBug的 TestVBA.mdb 中找到)
Public Function ConnectToOtherDB() As ADODB.Connection
Dim conn As ADODB.Connection
Set conn = New ADODB.Connection
conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & CurrentProject.Path & "\TestVBA_OtherBase.mdb;" & _
"Mode=ReadWrite;" & _
"Persist Security Info=False;"
Set ConnectToOtherDB = conn
End Function
Public Sub TestBCD()
Dim conn As ADODB.Connection
Set conn = ConnectToOtherDB()
conn.Execute "drop table test_table"
conn.Execute "create table test_table (test_field decimal(15,3))"
conn.Close
conn.Open
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
rs.Open "test_table", conn, adOpenDynamic, adLockOptimistic, adCmdTable
rs.AddNew
rs!test_field = "21,345"
rs.Update
rs.Close
Set rs = Nothing
End Sub
The result in TestVBA_OtherBase database is 21,345
TestVBA_OtherBase 数据库中的结果是
21,345
Not an actual answer, but I ended up switching back to float field in MS Access.不是一个实际的答案,但我最终切换回 MS Access 中的浮点字段。 Another solution could be to switch from ADO to FireDAC.
另一种解决方案可能是从 ADO 切换到 FireDAC。 There is still hope, that Embarcadero will help to resolve this one day, watch RSP-34075 for progress.
仍有希望,Embarcadero 有一天会帮助解决这个问题,请注意 RSP-34075 的进展。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.