TSQLQuery strumieniuje tylko pierwsze 1 MB danych poprawnie dla dużych łańcuchów
(patrz Edycja # 1 ze śledzeniem stosu i Edycja # 2 z obejściem na końcu postu)
Podczas rozwiązywania problemówTSQLQuery.FieldByName (). AsString -> TStringStream Uszkadza dane, Znalazłem, że aTSQLQuery.FieldByName().AsBytes
będzie przesyłać tylko dokładnie 1 MBvarchar(max)
dane poprawnie.
TSQLQuery.FieldByName().AsString
i.AsWideString
wykazują również to samo zachowanie.Co by to spowodowało.AsBytes
dostarczyć prawidłową liczbę bajtów doTFileStream
, alenull
wszystkie bajty przekraczające 1 MB?
Ten przypadek testowy tworzy dwa pliki wyjściowe.Plus14.txt
wynosi 1 MB + 14 bajtów.Plus36.txt
wynosi 1 MB + 36 bajtów. W obu przypadkach bajty są większe niż 1 MBnull
wartości bajtów. Próbowałem nawet łańcucha 16 MB. Pierwsze 1 MB pliku wyjściowego było poprawne; następne 15 MB to wszystkonull
bajty.
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 DFMDomyślny formularz z tymTSQLConnection
spadł na to (i jedenTButton
):
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 PASKod dlaTButton.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;
<<< Edytuj # 1 - Ślad stosu >>>Przeszukałem kod Embarcadero i znalazłem miejsce, w którym znajduje sięnull
najpierw pojawiają się bajty.
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')
GdyFDBXRow_GetBytes
wraca,Value: TBytes
to 1048590 bajtów, znull
wartości ustawione dla ostatnich 14 bajtów.
Nie jestem pewien, co spróbować dalej. Każda pomoc jest bardzo mile widziana.
<<< Edytuj # 2 - Obejście >>>ustawiłemSQLConnection1.MaxBlobSize := 2097152
, a teraz wszystkie bajty są poprawnie przesyłane do plików wyjściowych. Problem pojawia się tylko wtedy, gdy.MaxBlobSize = -1
.
Pilna potrzeba rozwiązania problemu zniknęła, ponieważ znalazłem obejście. Jednak nadal chciałbym się dostać-1
działać, jeśli to możliwe, ponieważ wartości z mojej bazy danych czasami przekraczają 50 megabajtów. Tak więc wszelkie sugestie lub pomoc są nadal doceniane.
Złożyłem raport o błędzie z Embarcadero (QC # 108475). Zgłosię raport, gdy błąd zostanie potwierdzony / naprawiony.
<<< Edytuj # 4 - Raport o eskalowanym błędzie >>>Odkryłem dzisiaj, że użycie tego obejścia czasami powodujeTClientDataSet
rzucićEOleException
z tekstem ”Katastrofalna awaria” Najwyraźniej aTClientDataSet
woli aMaxBlobSize := '-1';
. W związku z tym eskalowałem raport o błędzie w Embarcadero. Mam nadzieję, że wkrótce dostarczą poprawkę lub lepsze obejście tego problemu.