(请参阅使用堆栈跟踪编辑 #1 和在帖子末尾使用解决方法编辑 #2)
在对TSQLQuery.FieldByName().AsString -> TStringStream Corrupts Data 进行故障排除时,我发现 aTSQLQuery.FieldByName().AsBytes
只会varchar(max)
正确传输 1MB 的数据。
- 使用 WireShark,我验证了所有数据都正确地传递给了 Delphi 应用程序。
- 我验证它总是将正确数量的字节写入输出文件,但任何超过1MB的字节都是空字节。
- 此外,
TSQLQuery.FieldByName().AsString
并且.AsWideString
还表现出相同的行为。
什么会导致.AsBytes
向 提供正确的字节数TFileStream
,但null
所有字节都超过 1MB?
测试用例
这个测试用例创建了两个输出文件。 Plus14.txt
是 1MB + 14 字节。 Plus36.txt
是 1MB + 36 字节。在这两种情况下,超过 1MB 的字节都是null
字节值。我什至尝试了一个 16MB 的字符串。输出文件的前 1MB 是正确的;接下来的 15MB 都是null
字节。
SQL 服务器
use tempdb
go
create procedure RunMe
as
declare @s1 varchar(max), @s2 varchar(max)
set @s1 = '0123456789ABCDEF'
set @s2 = @s1 + @s1 + @s1 + @s1 + @s1 + @s1 + @s1 + @s1 -- 128 bytes
set @s1 = @s2 + @s2 + @s2 + @s2 + @s2 + @s2 + @s2 + @s2 -- 1,024 bytes
set @s2 = @s1 + @s1 + @s1 + @s1 + @s1 + @s1 + @s1 + @s1 -- 8,192 bytes
set @s1 = @s2 + @s2 + @s2 + @s2 + @s2 + @s2 + @s2 + @s2 -- 65,536 bytes
set @s2 = @s1 + @s1 + @s1 + @s1 + @s1 + @s1 + @s1 + @s1 -- 524,288 bytes
set @s1 = @s2 + @s2 -- 1,048,576 bytes
set @s2 = @s1 + 'this is a test' -- 1MB + 14 bytes
set @s1 = @s1 + 'of the emergency broadcasting system' -- 1MB + 36 bytes
select @s2 as Plus14, @s1 as Plus36
go
grant execute on RunMe to public
go
德尔福 DFM
默认形式,TSQLConnection
将其放在上面(和一个TButton
):
object SQLConnection1: TSQLConnection
DriverName = 'MSSQL'
GetDriverFunc = 'getSQLDriverMSSQL'
LibraryName = 'dbxmss.dll'
LoginPrompt = False
Params.Strings = (
'User_Name=user'
'Password=password'
'SchemaOverride=%.dbo'
'DriverUnit=Data.DBXMSSQL'
'DriverPackageLoader=TDBXDynalinkDriverLoader,DBXCommonDriver160.' +
'bpl'
'DriverAssemblyLoader=Borland.Data.TDBXDynalinkDriverLoader,Borla' +
'nd.Data.DbxCommonDriver,Version=16.0.0.0,Culture=neutral,PublicK' +
'eyToken=91d62ebb5b0d1b1b'
'MetaDataPackageLoader=TDBXMsSqlMetaDataCommandFactory,DbxMSSQLDr' +
'iver160.bpl'
'MetaDataAssemblyLoader=Borland.Data.TDBXMsSqlMetaDataCommandFact' +
'ory,Borland.Data.DbxMSSQLDriver,Version=16.0.0.0,Culture=neutral' +
',PublicKeyToken=91d62ebb5b0d1b1b'
'GetDriverFunc=getSQLDriverMSSQL'
'LibraryName=dbxmss.dll'
'VendorLib=sqlncli10.dll'
'VendorLibWin64=sqlncli10.dll'
'HostName=localhost'
'Database=tempdb'
'MaxBlobSize=-1'
'LocaleCode=0000'
'IsolationLevel=ReadCommitted'
'OSAuthentication=False'
'PrepareSQL=True'
'BlobSize=-1'
'ErrorResourceFile='
'OS Authentication=True'
'Prepare SQL=False')
VendorLib = 'sqlncli10.dll'
Left = 8
Top = 8
end
德尔福 PAS
的代码TButton.OnClick
:
procedure TForm1.Button1Click(Sender: TObject);
var qry: TSQLQuery;
procedure save(str: string);
var data: TBytes; fs: TFileStream;
begin
fs := TFileStream.Create(Format('c:\%s.txt', [str]), fmCreate);
try
data := qry.FieldByName(str).AsBytes;
if data <> nil then
fs.WriteBuffer(data[0], Length(data));
finally
FreeAndNil(fs);
end;
end;
begin
SQLConnection1.Open;
qry := TSQLQuery.Create(nil);
try
qry.MaxBlobSize := -1;
qry.SQLConnection := SQLConnection1;
qry.SQL.Text := 'set nocount on; exec RunMe';
qry.Open;
save('Plus14');
save('Plus36');
finally
FreeAndNil(qry);
end;
SQLConnection1.Close;
end;
<<< 编辑 #1 - 堆栈跟踪 >>>
我追踪了 Embarcadero 的代码,找到了null
字节第一次出现的地方。
FMethodTable.FDBXRow_GetBytes
Data.DBXDynalink.TDBXDynalinkByteReader.GetBytes(0,0,(...),0,1048590,True)
Data.SqlExpr.TCustomSQLDataSet.GetFieldData(1,$7EC80018)
Data.SqlExpr.TCustomSQLDataSet.GetFieldData(???,$7EC80018)
Data.DB.TDataSet.GetFieldData($66DB18,$7EC80018,True)
Data.SqlExpr.TSQLBlobStream.ReadBlobData
Data.SqlExpr.TSQLBlobStream.Read((no value),1048590)
System.Classes.TStream.ReadBuffer((no value),1048590)
1MB + 14bData.DB.TBlobField.GetAsBytes
Unit1.save('Plus14')
FDBXRow_GetBytes
返回时为Value: TBytes
1048590 字节,null
为最后 14 个字节设置值。
我不确定下一步该尝试什么。任何帮助是极大的赞赏。
<<< 编辑 #2 - 解决方法 >>>
我设置SQLConnection1.MaxBlobSize := 2097152
了,现在所有字节都正确地流式传输到输出文件。所以问题似乎只发生在.MaxBlobSize = -1
.
既然我找到了解决方法,解决问题的紧迫性就消失了。但是,如果可能的话,我仍然想开始-1
工作,因为我数据库中的值有时会超过 50 兆。因此,仍然感谢任何建议或帮助。
<<< 编辑 #3 - 错误报告 >>>
我向 Embarcadero (QC #108475) 提交了错误报告。一旦错误被确认/修复,我会报告。
<<< 编辑 #4 - 升级的错误报告 >>>
我今天发现使用这种解决方法有时会导致 aTClientDataSet
抛出EOleException
带有文本“灾难性故障”的错误。显然 aTClientDataSet
更喜欢 a MaxBlobSize := '-1';
。因此,我在 Embarcadero 上报了错误报告。希望他们很快会为此提供修复或更好的解决方法。