Writing utf8 to database
Writing utf8 to database
Hello,
I use ODAC version 5.80.0.40 to connect to our database (which is Oracle 10g R10.2.0.2.0). The development tool is Borland 2006 for Windows, language is C++.
I have created a small project where I started using the TntWare visual components and where I set the NLS_LANG to GERMAN_GERMANY.ATL32UTF8 .
If I use the following options for the session object I can read and write utf8 from a CLOB field but not from an varchar2 field.
CharLength = 0
Charset = AL32UTF8
UseUnicode = false
If I use the following options for the session object I can read and write utf8 from a varchar2 field but not from an clob field.
CharLength = 0
Charset = AL32UTF8
UseUnicode = true
So where is my problem? I´d like to use both the varchar2 and the clob field with one session.
I use ODAC version 5.80.0.40 to connect to our database (which is Oracle 10g R10.2.0.2.0). The development tool is Borland 2006 for Windows, language is C++.
I have created a small project where I started using the TntWare visual components and where I set the NLS_LANG to GERMAN_GERMANY.ATL32UTF8 .
If I use the following options for the session object I can read and write utf8 from a CLOB field but not from an varchar2 field.
CharLength = 0
Charset = AL32UTF8
UseUnicode = false
If I use the following options for the session object I can read and write utf8 from a varchar2 field but not from an clob field.
CharLength = 0
Charset = AL32UTF8
UseUnicode = true
So where is my problem? I´d like to use both the varchar2 and the clob field with one session.
In case the UseUnicode mode is set to false. I insert an character like
U+0F60 འ e0 bd a0 TIBETAN LETTER -A
and everything looks fine.
When I post the record the inserted letter turns into an ? in the form as well as in the database.
ONLY for the field that is linked to the varchar2 field if I do the same in the field linked to clob field everthing works fine.
U+0F60 འ e0 bd a0 TIBETAN LETTER -A
and everything looks fine.
When I post the record the inserted letter turns into an ? in the form as well as in the database.
ONLY for the field that is linked to the varchar2 field if I do the same in the field linked to clob field everthing works fine.
If you set the UseUnicode option to True, the Unicode CLOB values are read and written incorrectly because TNT controls cannot work with Unicode CLOBs. There are two ways to solve this problem:
1. You can use the TTntMemo component for CLOB fields instead of TTntDBMemo. In this case you will have to read and write values for such fields manually by using the GetLob method or the AsWideString property of TOraLob. For example:
2. You can modify source code of TNT controls to access TMemoField using the GetLob method. Below is the example. Note, this code is posted "As is". We do not guarantee that this code is fully working. Find corresponding procedures in the TntDB unit and replace them with the following ones:
1. You can use the TTntMemo component for CLOB fields instead of TTntDBMemo. In this case you will have to read and write values for such fields manually by using the GetLob method or the AsWideString property of TOraLob. For example:
Code: Select all
TntMemo1.Text := OraTable1.GetLob('TEXT').AsWideString; // reading from the field
OraTable1.GetLob('TEXT').AsWideString := TntMemo1.Text; // writing to the fieldCode: Select all
function GetAsWideString(Field: TField): WideString;
var
WideField: IWideStringField;
Blob: TBlob;
begin
if (Field is TMemoField) and (Field.DataSet is TCustomDADataSet) and not Field.IsNull then begin
Blob := TCustomDADataSet(Field.DataSet).GetBlob(Field.FieldName);
if Blob.IsUnicode then
Result := Blob.AsWideString
else
Result := Blob.AsString;
Exit;
end;
if Field.GetInterface(IWideStringField, WideField) then
Result := WideField.AsWideString
else if (Field is TWideStringField{TNT-ALLOW TWideStringField}) then begin
if Field.IsNull then
// This fixes a bug in TWideStringField.GetAsWideString which does not handle Null at all.
Result := ''
else
Result := TWideStringField{TNT-ALLOW TWideStringField}(Field).Value
end else
Result := Field.AsString{TNT-ALLOW AsString};
end;Code: Select all
procedure SetAsWideString(Field: TField; const Value: WideString);
var
WideField: IWideStringField;
Blob: TBlob;
begin
if (Field is TMemoField) and (Field.DataSet is TCustomDADataSet) then begin
Blob := TCustomDADataSet(Field.DataSet).GetBlob(Field.FieldName);
if Blob.IsUnicode then
Blob.AsWideString := Value
else
Blob.AsString := Value;
TBlobField(Field).Modified := True;
Exit;
end;
if Field.GetInterface(IWideStringField, WideField) then
WideField.AsWideString := Value
else if (Field is TWideStringField{TNT-ALLOW TWideStringField}) then
TWideStringField{TNT-ALLOW TWideStringField}(Field).Value := Value
else
Field.AsString{TNT-ALLOW AsString} := Value;
end;Ok, I tried the second example.
First I changed the functions in TntDB.pas.
Then I switched UseUnicode to true;
Table column (clob) is defined as TTntMemoField.
I insert Unicode text into the two fields (varchar2, clob) and post the edits.
In the form everything looks fine, but in the database the string stored in the clob is not utf8. Only the string stored in the varchar2 field was converted to utf8 on post.
So this only seems to work for the form, but writing back to the database is still wrong for the clob.
It seems as if there is missing some kind of conversion between utf8(db) and unicode(form) which should be done by the Tnt components?!?
First I changed the functions in TntDB.pas.
Then I switched UseUnicode to true;
Table column (clob) is defined as TTntMemoField.
I insert Unicode text into the two fields (varchar2, clob) and post the edits.
In the form everything looks fine, but in the database the string stored in the clob is not utf8. Only the string stored in the varchar2 field was converted to utf8 on post.
So this only seems to work for the form, but writing back to the database is still wrong for the clob.
It seems as if there is missing some kind of conversion between utf8(db) and unicode(form) which should be done by the Tnt components?!?
Yes characterset in db is AL32UTF8.
I have send a small demo project to [email protected] as you requested.
I have send a small demo project to [email protected] as you requested.
Make sure that code you have added to TntDB.pas is used when COMPILER_10_UP is defined. For the last version of the TNT controls you can use the code below.
If you are using fields of the TTntMemoField type and the UseUnicode option of TOraSession is set to True, you should set the TTntMemoField.EncodingMode property to emNone.
If you are using fields of the TTntMemoField type and the UseUnicode option of TOraSession is set to True, you should set the TTntMemoField.EncodingMode property to emNone.
Code: Select all
function GetAsWideString(Field: TField): WideString;
var
Blob: TBlob;
{$IFNDEF COMPILER_10_UP}
WideField: IWideStringField;
{$ENDIF}
begin
if (Field is TMemoField) and (Field.DataSet is TCustomDADataSet) and not Field.IsNull then begin
Blob := TCustomDADataSet(Field.DataSet).GetBlob(Field.FieldName);
if Blob.IsUnicode then
Result := Blob.AsWideString
else
Result := Blob.AsString;
Exit;
end;
{$IFDEF COMPILER_10_UP}
if (Field.ClassType = TMemoField{TNT-ALLOW TMemoField}) then
Result := VarToWideStr(Field.AsVariant) { works for NexusDB BLOB Wide }
else
Result := Field.AsWideString
{$ELSE}
if Field.GetInterface(IWideStringField, WideField) then
Result := WideField.AsWideString
else if (Field is TWideStringField{TNT-ALLOW TWideStringField}) then
begin
if Field.IsNull then
// This fixes a bug in TWideStringField.GetAsWideString which does not handle Null at all.
Result := ''
else
Result := TWideStringField{TNT-ALLOW TWideStringField}(Field).Value
end else if (Field is TMemoField{TNT-ALLOW TMemoField}) then
Result := VarToWideStr(Field.AsVariant) { works for NexusDB BLOB Wide }
else
Result := Field.AsString{TNT-ALLOW AsString};
{$ENDIF}
end;Code: Select all
procedure SetAsWideString(Field: TField; const Value: WideString);
var
Blob: TBlob;
{$IFNDEF COMPILER_10_UP}
WideField: IWideStringField;
{$ENDIF}
begin
if (Field is TMemoField) and (Field.DataSet is TCustomDADataSet) then begin
Blob := TCustomDADataSet(Field.DataSet).GetBlob(Field.FieldName);
if Blob.IsUnicode then
Blob.AsWideString := Value
else
Blob.AsString := Value;
TBlobField(Field).Modified := True;
Exit;
end;
{$IFDEF COMPILER_10_UP}
if (Field.ClassType = TMemoField{TNT-ALLOW TMemoField}) then
Field.AsVariant := Value { works for NexusDB BLOB Wide }
else
Field.AsWideString := Value;
{$ELSE}
if Field.GetInterface(IWideStringField, WideField) then
WideField.AsWideString := Value
else if (Field is TWideStringField{TNT-ALLOW TWideStringField}) then
TWideStringField{TNT-ALLOW TWideStringField}(Field).Value := Value
else if (Field is TMemoField{TNT-ALLOW TMemoField}) then
Field.AsVariant := Value { works for NexusDB BLOB Wide }
else
Field.AsString{TNT-ALLOW AsString} := Value;
{$ENDIF}
end;When I try to compile the TntDB.pas with the edits you sent I get several Pascal Errors.
[Pascal Fehler] TntDB.pas(288): E2003 Undefinierter Bezeichner: 'TBlob'
[Pascal Fehler] TntDB.pas(293): E2003 Undefinierter Bezeichner: 'TCustomDADataSet'
[Pascal Fehler] TntDB.pas(293): E2015 Operator ist auf diesen Operandentyp nicht anwendbar
[Pascal Fehler] TntDB.pas(293): E2015 Operator ist auf diesen Operandentyp nicht anwendbar
[Pascal Fehler] TntDB.pas(294): E2066 Operator oder Semikolon fehlt
[Pascal Fehler] TntDB.pas(295): E2029 'THEN' erwartet, aber Bezeichner 'IsUnicode' gefunden
[Pascal Fehler] TntDB.pas(323): E2029 Anweisung erwartet, aber 'PROCEDURE' gefunden
[Pascal Fehler] TntDB.pas(325): E2003 Undefinierter Bezeichner: 'TBlob'
[Pascal Fehler] TntDB.pas(330): E2003 Undefinierter Bezeichner: 'TCustomDADataSet'
[Pascal Fehler] TntDB.pas(330): E2015 Operator ist auf diesen Operandentyp nicht anwendbar
[Pascal Fehler] TntDB.pas(331): E2066 Operator oder Semikolon fehlt
[Pascal Fehler] TntDB.pas(332): E2029 'THEN' erwartet, aber Bezeichner 'IsUnicode' gefunden
[Pascal Fehler] TntDB.pas(356): E2029 Anweisung erwartet, aber 'FUNCTION' gefunden
[Pascal Fataler Fehler] TntUnicodeVcl.dpk(69): F2063 Verwendete Unit '..\..\Source\TntDB.pas' kann nicht compiliert werden
So I think there is still something missing?! Where do you get the TBlob and the 'TCustomDADataSet'?
[Pascal Fehler] TntDB.pas(288): E2003 Undefinierter Bezeichner: 'TBlob'
[Pascal Fehler] TntDB.pas(293): E2003 Undefinierter Bezeichner: 'TCustomDADataSet'
[Pascal Fehler] TntDB.pas(293): E2015 Operator ist auf diesen Operandentyp nicht anwendbar
[Pascal Fehler] TntDB.pas(293): E2015 Operator ist auf diesen Operandentyp nicht anwendbar
[Pascal Fehler] TntDB.pas(294): E2066 Operator oder Semikolon fehlt
[Pascal Fehler] TntDB.pas(295): E2029 'THEN' erwartet, aber Bezeichner 'IsUnicode' gefunden
[Pascal Fehler] TntDB.pas(323): E2029 Anweisung erwartet, aber 'PROCEDURE' gefunden
[Pascal Fehler] TntDB.pas(325): E2003 Undefinierter Bezeichner: 'TBlob'
[Pascal Fehler] TntDB.pas(330): E2003 Undefinierter Bezeichner: 'TCustomDADataSet'
[Pascal Fehler] TntDB.pas(330): E2015 Operator ist auf diesen Operandentyp nicht anwendbar
[Pascal Fehler] TntDB.pas(331): E2066 Operator oder Semikolon fehlt
[Pascal Fehler] TntDB.pas(332): E2029 'THEN' erwartet, aber Bezeichner 'IsUnicode' gefunden
[Pascal Fehler] TntDB.pas(356): E2029 Anweisung erwartet, aber 'FUNCTION' gefunden
[Pascal Fataler Fehler] TntUnicodeVcl.dpk(69): F2063 Verwendete Unit '..\..\Source\TntDB.pas' kann nicht compiliert werden
So I think there is still something missing?! Where do you get the TBlob and the 'TCustomDADataSet'?
In your sample project you write Unicode data to the TEST_COL field incorrectly. The way you use works when the UseUnicode option of the TOraSession component is False and the Charset property is set to AL32UTF8.
But when the UseUnicode option is set to True, you should convert data to UTF-16 (WideString) and write it through TOraLob object. Use the AsWideString property or the Write method of TOraLob.
But when the UseUnicode option is set to True, you should convert data to UTF-16 (WideString) and write it through TOraLob object. Use the AsWideString property or the Write method of TOraLob.
Code: Select all
otb_test->GetLob("TEST_COL")->AsWideString = TntMemo1->Text;