vrogier / ocilib

OCILIB (C and C++ Drivers for Oracle) - Open source C and C++ library for accessing Oracle databases

Home Page:http://www.ocilib.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

OCI_GetString() returns NULL for CLOB columns is the CLOB row value was filled with empty_clob()

iliasaz opened this issue · comments

Hi,

I see this issue already reported (#112), and a fix seemed to be provided, but I still see the issue in 4.7.4. Here is my test case:

create table test_ociclob (col clob);
insert into test_ociclob values(null);
insert into test_ociclob values('');
insert into test_ociclob values(empty_clob());
commit;
select col, dbms_lob.getlength(col) from test_ociclob;

The first and the second rows return correct values (empty strings), and the last one return a null pointer. Has the behavior around handling empty clobs changed? Or maybe the fix didn't make it in this release?

Thanks!

Hi,

Can you provide a sample code to reproduce the issue?
I presume the C code is using OCI_GetString() ?
Insertion order is not necessary the same as default select order.
In your example, no way to correlate a given inserted row to a row in the resultset.
Add another column with an numeric id for example.
The first insert you made is a NULL value. In this case, OCI_GetString() behaviour is to return NULL, not an empty string.
While other ones ('"' and empty_clob()) insert a non NULL clob.
In this case, OCI_GetString() returns an empty string.

Regards,

Vincent

Just tried with following code:

create table test_ociclob (name varchar2(50), value clob);
insert into test_ociclob values('NULL CLOB', null);
insert into test_ociclob values('EMPTY STRING', '');
insert into test_ociclob values('EMPTY CLOB', empty_clob());
insert into test_ociclob values('CLOB WITH VALUE', 'ABC');
void err_handler(OCI_Error* err)
{
    printf(OCI_ErrorGetString(err));
}

int main(void)
{
    OCI_Initialize(err_handler, NULL, OCI_ENV_DEFAULT);
   
    OCI_Connection* cn = OCI_ConnectionCreate("localhost:1521/db19c", "usr", "pwd", OCI_SESSION_DEFAULT);
    OCI_Statement* st = OCI_StatementCreate(cn);

    OCI_ExecuteStmt(st, "select name, value, dbms_lob.getlength(value) from test_ociclob");
    OCI_Resultset* rs = OCI_GetResultset(st);
    while (OCI_FetchNext(rs))
    {
        printf("name=%s, value=%s, size=%d\n", OCI_GetString(rs, 1), OCI_GetString(rs, 2), OCI_GetInt(rs, 3));
    }

    OCI_Cleanup();
}

result is:

name=NULL CLOB, value=(null), size=0
name=EMPTY STRING, value=(null), size=0
name=EMPTY CLOB, value=(null), size=0
name=CLOB WITH VALUE, value=ABC, size=3
  • with NULL CLOB, oracle client reports a NULL value => OCI_GetString() returns NULL
  • with EMPTY STRING, oracle client reports a NULL value => OCI_GetString() returns NULL
  • with EMPTY CLOB, oracle client reports a NOT NULL value with a CLOB LENGTH = 0 => OCILIB allocates a empty string then copy 0 bytes from the clob into the string and instead of returning this empty string, it returns NULL due to wrong internal call return value check.

With the fix we get the following:

name=NULL CLOB, value=(null), size=0
name=EMPTY STRING, value=(null), size=0
name=EMPTY CLOB, value=, size=0
name=CLOB WITH VALUE, value=ABC, size=3

I commit this fix in the v4.7.5 branch

Hi Vincent,

Your example makes sense - thank you for correcting me and for fixing the issue as you identified it.

As I dug deeper into it today I realized that the root cause of my issue is different. Indeed, OCI_GetString returns null on both null clob and empty string. It wasn't a problem in my code because I checked OCI_IsNull before calling OCI_GetString. And in case of null value and empty string, OCI_IsNull returns True, so I didn't even call OCI_GetString in these cases.
With empty clob, OCI_IsNull returns False, and OCI_GetString used to return null (before your fix), which was inconsistent and led to a problem in my code. Now that both OCI_GetString and OCI_IsNull return consistent results, it should be fine.

Thanks for looking into it!

With 4.7.5 fix:

while (OCI_FetchNext(rs))
    {
        printf("name=%s, value=%s, size=%d, isNull=%d\n", OCI_GetString(rs, 1), OCI_GetString(rs, 2), OCI_GetInt(rs, 3), OCI_IsNull(rs, 2));
    }

name=NULL CLOB, value=(null), size=0, isNull=1
name=EMPTY STRING, value=(null), size=0, isNull=1
name=EMPTY CLOB, value=, size=0, isNull=0
name=CLOB WITH VALUE, value=ABC, size=3, isNull=0