Error de Parameter.AsString en Oracle / MSSQL - Parámetro.Value 2-byte chars en Oracle
Después de cambiar a FireDAC, tengo problemas para que este código funcione en MSSQL / Oracle:
with DataFormsettings do
begin
Close;
if Params.Count=0 then FetchParams;
Params.ParamByName('TT_EMP_ID').Asinteger := AEmpID;
Params.ParamByName('TT_FORM').AString := UpperCase(AKey);
Open;
if (RecordCount>0) then
S := FieldByName('TT_VIEWDATA').Asstring;
end;
AKey y S son ambas cuerdas.
La declaración abierta da un error
[FireDAC][Phys][MSSQL]-338 Param type changed from [ftString] to [ftWidestring]
[FireDAC][Phys][Ora]-338 Param type changed from [ftString] to [ftWidestring]
cuando se conecta a una base de datos MSSQL u Oracle; no cuando se conecta a FireBird.
Después de laFetchParams
, DataFormsettings.params[1].datatype
siempre es unftString
.
Si reemplazo
Params.ParamByName('TT_FORM').AString := UpperCase(AKey);
con
Params.ParamByName('TT_FORM').Value := UpperCase(AKey);
... no hay errores en la declaración abierta. Pensé que lo había solucionado, aunque realmente no entendí el error. Después de todo, estos deberían ser todos los tipos de cadena de Delphi predeterminados ...
Pero ahora la asignación S falla para Oracle (no FireBird o MSSQL) en el sentido de que veo que se devuelven caracteres de 2 bytes. S contiene:
\'#0'S'#0'o'#0'f'#0't'#0'w'#0'a'#0'r'#0'e'#0'\'#0'T'#0'i'#0'm'#0'e'#0'T'#0'e'#0'l'#0'l'#0'...
Puedo manejar eso con p.
S := TEncoding.Unicode.GetString(FieldByName('TT_VIEWDATA').AsBytes);
para Oracle, pero (por supuesto) cuando se usan los otros dos tipos de bases de datos que no funcionan:
No mapping for the Unicode character exists in the target multi-byte code page
¿Que me estoy perdiendo aqui? Específicamente, me gustaría que las recuperaciones / asignaciones de AsString funcionen.
Nota laEstablecer la propiedad AsString establece la propiedad DataType en ftWideString o ftString comentario en elDocumentación de FireDAC TFDParam.AsString. Parece que la asignación del valor del parámetro simplemente cambia el tipo de ftString a ftWideString (como lo indica el error original).
DataFormSettings
es unTClientDataSet
en una aplicación cliente, conectada a una aplicación de servidor dondeTDataSetProvider
yTFDQuery
residir. La consulta es
select
TT_FORMSETTINGS_ID,
TT_EMP_ID,
TT_FORM,
TT_VERSION,
TT_VIEWDATA
from TT_FORMSETTINGS
where TT_EMP_ID=:TT_EMP_ID
and TT_FORM=:TT_FORM
Las tablas se crearon de la siguiente manera:
FireBird:
CREATE TABLE TT_FORMSETTINGS
(
TT_FORMSETTINGS_ID INTEGER DEFAULT 0 NOT NULL,
TT_EMP_ID INTEGER,
TT_FORM VARCHAR(50),
TT_VERSION INTEGER,
TT_VIEWDATA BLOB SUB_TYPE TEXT SEGMENT SIZE 80,
TT_TAG INTEGER,
TT_TAGTYPE INTEGER,
TT_TAGDATE TIMESTAMP
);
Oráculo:
CREATE TABLE TT_FORMSETTINGS
(
TT_FORMSETTINGS_ID NUMBER(10,0) DEFAULT 0 NOT NULL,
TT_EMP_ID NUMBER(10,0),
TT_FORM VARCHAR(50),
TT_VERSION NUMBER(10,0),
TT_VIEWDATA CLOB,
TT_TAG NUMBER(10,0),
TT_TAGTYPE NUMBER(10,0),
TT_TAGDATE DATE
);
MSSQL:
CREATE TABLE TT_FORMSETTINGS
(
TT_FORMSETTINGS_ID INTEGER NOT NULL CONSTRAINT TT_C0_FORMSETTINGS DEFAULT 0,
TT_EMP_ID INTEGER NULL,
TT_FORM VARCHAR(50) NULL,
TT_VERSION INTEGER NULL,
TT_VIEWDATA TEXT NULL,
TT_TAG INTEGER NULL,
TT_TAGTYPE INTEGER NULL,
TT_TAGDATE DATETIME NULL
);
Lo he comprobadoTT_VIEWDATA
contiene datos correctos en todas las bases de datos; Es una cadena larga que contiene CRLF:
\Software\TimeTell\Demo8\Forms\TFormTileMenu'#$D#$A'Version,1,80502'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu'#$D#$A'Version,4,2'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu...
Notas:
Actualmente estoy probando en SQL Server 2008 y Oracle 10, pero espero que esto no sea diferente para otras versiones.FWIWselect * from NLS_database_PARAMETERS where parameter like '%CHARACTERSET%'
devolucionesNLS_CHARACTERSET=WE8MSWIN1252
yNLS_NCHAR_CHARACTERSET=AL16UTF16
Consulta
SELECT dump(dbms_lob.substr(tt_viewdata,100,1), 1016), tt_viewdata FROM tt_formsettings
confirma que el CLOB contiene bytes ASCII para la página de códigos Win1252:Typ=1 Len=100 CharacterSet=WE8MSWIN1252: 5c,53,6f,66,74,77,61,72,65,5c,54,69,6d,65,54,65,6c,6c,5c,44,65,...
FieldByName().AsANSIString
da los mismos resultados queFieldByName().AsString
Información adicional: Esta es una aplicación heredada con definiciones de campo persistentes en elDataFormsettings
TClientDataset
. TT_VIEWDATA
se define como unTMemoField
:
DataFormsettingsTT_VIEWDATA: TMemoField;
En un pequeño testapp (conectado directamente a Oracle; no cliente-servidor) dejé que Delphi agregara las definiciones de campo y luego dijo:
DataFormsettingsTT_VIEWDATA: TWideMemoField;
Si uso eso en la aplicación principal, Oracle funciona bien pero luego obtengo 'basura' para MSSQL.
También experimenté con la configuración de reglas de mapeo para la conexión de Oracle como (muchas variaciones):
with AConnection.FormatOptions.MapRules.Add do
begin
SourceDataType := dtWideMemo;
TargetDataType := dtMemo;
end;
AConnection.FormatOptions.OwnMapRules := true;
Pero eso no ayudó.