TSQLQuery überträgt nur die ersten 1 MB Daten korrekt für große Zeichenfolgen
(Siehe Edit # 1 mit Stack-Trace und Edit # 2 mit Workaround am Ende des Posts.)
Während der FehlersucheTSQLQuery.FieldByName (). AsString -> TStringStream beschädigt DatenIch fand, dass einTSQLQuery.FieldByName().AsBytes
wird nur genau 1MB von streamenvarchar(max)
Daten richtig.
TSQLQuery.FieldByName().AsString
und.AsWideString
zeigen auch das gleiche Verhalten.Was würde dazu führen.AsBytes
um die richtige Anzahl von Bytes an die zu liefernTFileStream
, abernull
alle Bytes, die 1 MB überschreiten?
Dieser Testfall erstellt zwei Ausgabedateien.Plus14.txt
beträgt 1 MB + 14 Byte.Plus36.txt
beträgt 1 MB + 36 Byte. In beiden Fällen sind die Bytes größer als 1 MBnull
Byte-Werte. Ich habe sogar einen 16MB-String ausprobiert. Die ersten 1 MB der Ausgabedatei waren korrekt. die nächsten 15MB waren allenull
Bytes.
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
Delphi DFMStandardformular, mit diesemTSQLConnection
darauf fallen gelassen (und einsTButton
):
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
Delphi PASDer Code für dieTButton.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;
<<< Edit # 1 - Stack Trace >>>Ich ging den Code von Embarcadero durch und fand den Ort, an dem dienull
Bytes erscheinen zuerst.
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)
1 MB + 14 bData.DB.TBlobField.GetAsBytes
Unit1.save('Plus14')
WannFDBXRow_GetBytes
kehrt zurück,Value: TBytes
ist 1048590 Bytes, mitnull
Werte für die letzten 14 Bytes eingestellt.
Ich bin mir nicht sicher, was ich als nächstes versuchen soll. Jede Hilfe wird sehr geschätzt.
<<< Edit # 2 - Workaround >>>ich setzeSQLConnection1.MaxBlobSize := 2097152
, und jetzt werden alle Bytes korrekt in die Ausgabedateien übertragen. Das Problem scheint also nur dann aufzutreten, wenn.MaxBlobSize = -1
.
Die Dringlichkeit, das Problem zu beheben, ist jetzt weg, da ich eine Problemumgehung gefunden habe. Ich würde es aber trotzdem gerne bekommen-1
zu arbeiten, wenn möglich, da die Werte aus meiner Datenbank manchmal 50 MB überschreiten. Vorschläge oder Hilfe sind also immer noch willkommen.
Ich habe einen Fehlerbericht bei Embarcadero (QC # 108475) eingereicht. Ich melde mich zurück, sobald der Fehler bestätigt / behoben wurde.
<<< Edit # 4 - Eskalierter Bug Report >>>Ich habe heute festgestellt, dass die Verwendung dieser Problemumgehung manchmal a verursachtTClientDataSet
werfenEOleException
mit dem Text 'Katastrophales Versagen'. Anscheinend einTClientDataSet
bevorzugt aMaxBlobSize := '-1';
. Infolgedessen habe ich den Fehlerbericht bei Embarcadero eskaliert. Hoffentlich werden sie bald eine Lösung oder eine bessere Lösung dafür finden.