Nobody/Anonymous
2012-02-05
i am facing two issues with oci_write2 for CLOBs.
First as input parameter3 "max. characters" should be the maximum number
of chars. But if i supply only one char less i will get this "ORA-24811: less
data provided for writing than indicated". Looks your description is not valid
here.
Second if the string contains one backslash character, one byte less will be
written as counted by strlen. Also OCI_LobGetLength will have this byte less.
This works only if i provide the strlen to the fourth parameter max bytes
instead of third parameter max chars as stated in first issue. I am using
ocilib 3.9.2 with Oracle Instandclient 11.2 against an Oracle11XE Database all
on Centos6.2 64bit
Vincent Rogier
2012-02-06
Hi,
What charset mode are you using ? Can you reproduce with 3.9.3 ?
1 / Cannot reproduce it with the few informations you gave... Need some code
example
2/ Can you be more precise ? And an example of input value (with the code that
writes into the lob) and written value into database.
Nobody/Anonymous
2012-02-06
Here is a test code. after investigating some hours more i suspect mor the
german umlaut as problem then the backslash. Also the second problem looks
related to this. try to handover the lenght instead of param3 into param4 in
oci_lobwrite2. it will not raise the ora error, but will show one byte less.
#include <ocilib.h> /* testcase ora 24811 * create table test_clob(c clob); */ static OCI_Connection *cn = NULL; static OCI_Statement *st = NULL; void err_handler(OCI_Error *err) { printf( "code : ORA-%05i\n" "msg : %s\n" "sql : %s\n" "pos :%d\n", OCI_ErrorGetOCICode(err), OCI_ErrorGetString(err), OCI_GetSql(OCI_ErrorGetStatement(err)), OCI_GetSqlErrorPos(OCI_ErrorGetStatement(err)) ); } int main() { OCI_Lob *lob; char * text="Test öffnen\n";//string is 13 bytes long if (!OCI_Initialize(err_handler, NULL, OCI_ENV_DEFAULT)) return EXIT_FAILURE; OCI_EnableWarnings(TRUE); cn = OCI_ConnectionCreate("XE", "icinga", "icinga", OCI_SESSION_DEFAULT); if (cn) { st = OCI_StatementCreate(cn); lob=OCI_LobCreate(cn,OCI_CLOB); OCI_Prepare(st, "insert into test_clob(c) values (:lob)"); if (!OCI_BindLob(st,MT(":lob"),lob)) { printf( "Bind Lob :lob failed\n"); return EXIT_FAILURE; } if (OCI_Execute(st)) { bind_clob(st,":lob",text,lob); } OCI_Commit(cn); if (lob) OCI_LobFree(lob); }else{ printf("connection failed\n"); } return EXIT_SUCCESS; } int bind_clob(OCI_Statement *st, char * bindname, char * text, OCI_Lob * lob) { char * fname="ido2db_oci_bind_lob"; OCI_Connection * cn=NULL; unsigned long len=0; unsigned int cb=0; unsigned int bb=0; cn=OCI_StatementGetConnection(st); len=strlen(text); cb=len; OCI_LobTruncate(lob,0); printf("expect to write %u bytes\n",cb); if (OCI_LobWrite2(lob,DT(text),&cb,&bb)) { // if (OCI_LobWrite2(lob,DT(text),&bb,&cb)) { //second try with excanging the parameters printf( "%u Bytes(total %u)written\n",cb,len); }else{ printf("write failed\n"); } cb=OCI_LobGetLength(lob); printf("loblen %u bytes\n",cb); if (cb==len) { printf( "Lob verified\n"); }else{ printf( "Lob write failed :len %lu!= %lu bytes written \n",len,cb); } return 0; }
$ make
$./test_ociwrite
expect to write 13 bytes
code : ORA-24811
msg : ORA-24811: less data provided for writing than indicated
sql : (null)
pos :0
write failed
loblen 0 bytes
Lob write failed :len 13!= 0 bytes written
$ vi test_ociwrite.c
$ make
$ ./test_ociwrite
expect to write 13 bytes
13 Bytes(total 13)written
loblen 12 bytes
Lob write failed :len 13!= 12 bytes written
Vincent Rogier
2012-02-06
what are your client and server charsets ?
Vincent Rogier
2012-02-06
if you're using UFT8, the errors are normal...
In your example, the 'char_count' param of OCI_LobWrite2() should by 11 and
not 12 (value returned by strlen) as it is the number of characters (or
codepoint). When the 'byte_count' is set to 0, it is computed using strlen()
if UTF8 is used. So, your passing 12 for 'char_count' and ocilib computes 12
for 'byte_count'. As there is a codepoint 'ö' that is coded on 2 bytes in
UTF8, the OCI internal call OCILobWrite2() throws an error as char_count
should have been 11....
Vincent Rogier
2012-02-06
In the later case, there is no error as OCI_LobGetLength() returns the number
of character (or codepoint) which, in your case, you're comparing to the
number of bytes. As you're using UTF8 as default runtime C locale, it can't
match.
Nobody/Anonymous
2012-02-06
Yes, default is UTF8 (LANG=de_DE.utf8). Ocilib is compile with
OCI_CHARSET_ANSI, which should be aware of UTF8
What do you suggest to solve this? is there is a strlen variant which is can
retrieve the correct number of bytes i have to pass to oci_lobwrite2 or how to
deal with it?
Vincent Rogier
2012-02-06
If you want to use :
OCI_LobWrite() => pass the number of characters (not size like strlen() does with UTF8) computed with a UTF8 aware version of strlen()
OCI_LobWrite2() => if you want to use 'char_count', same as OCI_LobWrite() otherwise pass the number of bytes using strlen() when using 'byte_count'
The "understanding problem" with your example, because your locale is set to
UTF8, is that the assignment
char * text="Test öffnen\n";
allocates 2 bytes for 'ö'.... and then strlen() computes the size (13 bytes)
and not characters count.
The 2nd run that you're showing as failed in fact had not fail at all. You've
asked to write 13 bytes (which it did !). But OCI_LobGetLength() returns the
number of characters or codepoint written... which is 12 in your case.
So the second way was correct ! It is simply your verification test that as
wrong.
PS : here is a UT8 aware version of strlen() (taken from ocilib source in
string.c)
int OCI_StringUTF8Length ( const char *str ) { int size = 0; while (*str) { if ((*str & 0xc0) != 0x80) { size++; } str++; } return size; }