ORA-24811 with OCI_Lobwrite2

2012-02-05
2012-09-26
  • Nobody/Anonymous
    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
    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
    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
    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
    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
    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
    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;
    }
    
     

Cancel   Add attachment