TSQLQuery somente transmite primeiro 1 MB de dados corretamente para strings grandes
(veja Editar # 1 com rastreamento de pilha e Editar # 2 com solução alternativa no final do post)
Solução de problemasTSQLQuery.FieldByName (). AsString -> TStringStream corrompe dados, Achei que umTSQLQuery.FieldByName().AsBytes
irá transmitir apenas exatamente 1 MB devarchar(max)
dados corretamente.
TSQLQuery.FieldByName().AsString
e.AsWideString
também exibem o mesmo comportamento.O que causaria.AsBytes
para fornecer o número correto de bytes aoTFileStream
, masnull
todos os bytes que excedem 1MB?
Este caso de teste cria dois arquivos de saída.Plus14.txt
é 1 MB + 14 bytes.Plus36.txt
é 1 MB + 36 bytes. Em ambos os casos, os bytes com mais de 1 MB sãonull
valores de byte. Eu até tentei uma string de 16MB. Os primeiros 1MB do arquivo de saída estavam corretos; os próximos 15MB foram todosnull
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 DFMFormulário padrão, com esteTSQLConnection
caiu sobre ele (e umTButton
):
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 PASO código para oTButton.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;
<<< Editar # 1 - Stack Trace >>>Eu tracei o código do Embarcadero e encontrei o lugar onde onull
bytes aparecem pela primeira vez.
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 + 14bData.DB.TBlobField.GetAsBytes
Unit1.save('Plus14')
QuandoFDBXRow_GetBytes
retorna,Value: TBytes
é 1048590 bytes, comnull
valores definidos para os últimos 14 bytes.
Não tenho certeza do que tentar em seguida. Qualquer ajuda é muito apreciada.
<<< Editar # 2 - Solução alternativa >>>eu colocoSQLConnection1.MaxBlobSize := 2097152
e agora todos os bytes são enviados para os arquivos de saída corretamente. Então, o problema só parece ocorrer quando.MaxBlobSize = -1
.
A urgência de corrigir o problema desapareceu agora que encontrei uma solução alternativa. No entanto, eu ainda gostaria de receber-1
para trabalhar, se possível, pois os valores do meu banco de dados, às vezes, excedem 50 megas. Portanto, qualquer sugestão ou ajuda ainda é apreciada.
Eu arquivei um relatório de bug com o Embarcadero (QC # 108475). Vou relatar quando o bug for confirmado / corrigido.
<<< Edit # 4 - Relatório de Bug Escalado >>>Eu encontrei hoje que usando esta solução, por vezes, faz com que umTClientDataSet
jogar umEOleException
com o texto 'Falha catastrófica'. Aparentemente umTClientDataSet
prefere umMaxBlobSize := '-1';
. Consequentemente, eu escalei o relatório de erros na Embarcadero. Espero que eles forneçam uma correção ou uma melhor solução para isso em breve.