Search code examples
cfetchoracle-call-interfaceraw

Program crashes when fetching long-raw with OCI


I'm trying to SELECT a LONG RAW column in an Oracle table using the OCI library.
For reasons that go beyond the scope of this question, I prefer to fetch the data by pieces, not using a callback therefore.
The execution of the statement returns, as expected, OCI_NEED_DATA but the first call to OCIStmtFetch causes a system error.
ErrorCode 5: Access violation reading address 0 in oracommon12.dll.
I got client 12.1 and connect to Oracle 10.g

Below a minimal program that reproduces the error.
Everything goes well until line 43 (result 10): It prints 99 which equals 'OCI_NEED_DATA'.
The program crashes the line after.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <oci.h>

static const char uid[]    = "XXXX";
static const char pswd[]   = "XXXX";
static const char conn[]   = "XXXX";
static const char fileid[] = "XXXX";

int main(int argc, char argv[])
{
    OCIEnv*    p_env;
    OCIError*  p_err;
    OCISvcCtx* p_svc;
    OCIStmt*   p_sql;
    OCIDefine* p_dfn;
    OCIBind*   p_bnd;
    int        rc;
    char       stmt[256];
    ub1        buffer[8192];

    rc = OCIInitialize(OCI_DEFAULT, NULL, (dvoid* (*)(dvoid*, size_t))NULL, (dvoid* (*)(dvoid*, dvoid*, size_t))NULL, (void (*)(dvoid*, dvoid*))NULL);
    printf("RESULT 1: %d\n", rc);
    rc = OCIEnvInit(&p_env, OCI_DEFAULT, 0, NULL);
    printf("RESULT 2: %d\n", rc);
    rc = OCIHandleAlloc(p_env, (void**)&p_err, OCI_HTYPE_ERROR, 0, NULL);
    printf("RESULT 3: %d\n", rc);
    rc = OCIHandleAlloc(p_env, (void**)&p_svc, OCI_HTYPE_SVCCTX, 0, NULL);
    printf("RESULT 4: %d\n", rc);
    rc = OCILogon(p_env, p_err, &p_svc, (text*)uid, (ub4)strlen(uid), (text*)pswd, (ub4)strlen(pswd), (text*)conn, (ub4)strlen(conn));
    printf("RESULT 5: %d\n", rc);
    rc = OCIHandleAlloc(p_env, (void**)&p_sql, OCI_HTYPE_STMT, 0, NULL);
    printf("RESULT 6: %d\n", rc);
    sprintf(stmt, "SELECT content FROM td_planimetrie WHERE id_file = :x");
    rc = OCIStmtPrepare(p_sql, p_err, (text*)stmt, (ub4)strlen(stmt), OCI_NTV_SYNTAX, OCI_DEFAULT);
    printf("RESULT 7: %d\n", rc);
    rc = OCIBindByName(p_sql, &p_bnd, p_err, (text*)":x", -1, (text*)fileid, (sb4)strlen(fileid) + 1, SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
    printf("RESULT 8: %d\n", rc);
    rc = OCIDefineByPos(p_sql, &p_dfn, p_err, 1, NULL, 0, SQLT_LBI, NULL, NULL, NULL, OCI_DYNAMIC_FETCH);
    printf("RESULT 9: %d\n", rc);
    rc = OCIStmtExecute(p_svc, p_sql, p_err, 1, 0, NULL, NULL, OCI_DEFAULT);
    printf("RESULT 10: %d\n", rc); // <-- rc equals 99=OCI_NEED_DATA
    rc = OCIStmtFetch(p_sql, p_err, 1, 0, OCI_DEFAULT); // <-- This crashes
    printf("RESULT 11: %d\n", rc);
    while (rc == OCI_NEED_DATA)
    {
        void* hndlp;
        ub4   type;
        ub1   in_out;
        ub4   iter;
        ub4   idx;
        ub1   piece;
        ub4   alen;
        printf("RESULT 12a\n");
        rc = OCIStmtGetPieceInfo(p_sql, p_err, &hndlp, &type, &in_out, &iter, &idx, &piece);
        printf("RESULT 12b %d, %d, %d, %d, %d, %d\n", (int)rc, (int)type, (int)in_out, (int)iter, (int)idx, (int)piece);
        alen = sizeof(buffer);
        rc = OCIStmtSetPieceInfo(hndlp, type, p_err, buffer, &alen, piece, NULL, NULL);
        printf("RESULT 12c %d, %d\n", (int)rc, (int)alen);
        rc = OCIStmtFetch(p_sql, p_err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
        printf("RESULT 12d: %d\n", rc);
    }
    printf("RESULT 13: %d\n", rc);

    rc = OCILogoff(p_svc, p_err);
    rc = OCIHandleFree(p_sql, OCI_HTYPE_STMT);
    rc = OCIHandleFree(p_svc, OCI_HTYPE_SVCCTX);
    rc = OCIHandleFree(p_err, OCI_HTYPE_ERROR);

    return (0);

} // main

Solution

  • I figured it out myself:

    First of all, the parameter iters within the call to OCIStmtExecute must be set to 0.
    This avoids the crash but doesn't resolve the whole problem.

    Furthermore, the parameter value_sz within the call to OCIDefineByPos must be set to the actual size of the column being read.

    Finally, the buffer that is passed as the 4th parameter of OCIStmtSetPieceInfo can be read only after the successive call to OCIStmtFetch.

    This results in the following code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <oci.h>
    
    static const char uid[]    = "XXXX";
    static const char pswd[]   = "XXXX";
    static const char conn[]   = "XXXX";
    static const char fileid[] = "XXXX";
    
    int main(int argc, char argv[])
    {
        OCIEnv*       p_env;
        OCIError*     p_err;
        OCISvcCtx*    p_svc;
        OCIStmt*      p_sql;
        OCIDefine*    p_dfn;
        OCIBind*      p_bnd;
        int           rc;
        char          stmt[256];
        unsigned char buffer[8192];
        int           counter;
        long          filesize;
    
        // Connect to DB
        rc = OCIInitialize(OCI_DEFAULT, NULL, (dvoid* (*)(dvoid*, size_t))NULL, (dvoid* (*)(dvoid*, dvoid*, size_t))NULL, (void (*)(dvoid*, dvoid*))NULL);
        printf("RESULT 1: %d\n", rc);
        rc = OCIEnvInit(&p_env, OCI_DEFAULT, 0, NULL);
        printf("RESULT 2: %d\n", rc);
        rc = OCIHandleAlloc(p_env, (void**)&p_err, OCI_HTYPE_ERROR, 0, NULL);
        printf("RESULT 3: %d\n", rc);
        rc = OCIHandleAlloc(p_env, (void**)&p_svc, OCI_HTYPE_SVCCTX, 0, NULL);
        printf("RESULT 4: %d\n", rc);
        rc = OCILogon(p_env, p_err, &p_svc, (text*)uid, (ub4)strlen(uid), (text*)pswd, (ub4)strlen(pswd), (text*)conn, (ub4)strlen(conn));
        printf("RESULT 5: %d\n", rc);
    
        // Determine size of data
        rc = OCIHandleAlloc(p_env, (void**)&p_sql, OCI_HTYPE_STMT, 0, NULL);
        printf("RESULT 6: %d\n", rc);
        sprintf(stmt, "SELECT filesize FROM td_planimetrie WHERE id_file = :x");
        rc = OCIStmtPrepare(p_sql, p_err, (text*)stmt, (ub4)strlen(stmt), OCI_NTV_SYNTAX, OCI_DEFAULT);
        printf("RESULT 6a: %d\n", rc);
        rc = OCIBindByName(p_sql, &p_bnd, p_err, (text*)":x", -1, (text*)fileid, (sb4)strlen(fileid) + 1, SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
        printf("RESULT 6b: %d\n", rc);
        rc = OCIDefineByPos(p_sql, &p_dfn, p_err, 1, &filesize, sizeof(filesize), SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT);
        printf("RESULT 6c: %d\n", rc);
        rc = OCIStmtExecute(p_svc, p_sql, p_err, 1, 0, NULL, NULL, OCI_DEFAULT);
        printf("RESULT 6d: %d %ld\n", rc, filesize);
        rc = OCIHandleFree(p_sql, OCI_HTYPE_STMT);
        printf("RESULT 6d: %d\n", rc);
    
        // Prepare reading data
        rc = OCIHandleAlloc(p_env, (void**)&p_sql, OCI_HTYPE_STMT, 0, NULL);
        printf("RESULT 7: %d\n", rc);
        sprintf(stmt, "SELECT content FROM td_planimetrie WHERE id_file = :x");
        rc = OCIStmtPrepare(p_sql, p_err, (text*)stmt, (ub4)strlen(stmt), OCI_NTV_SYNTAX, OCI_DEFAULT);
        printf("RESULT 7a: %d\n", rc);
        rc = OCIBindByName(p_sql, &p_bnd, p_err, (text*)":x", -1, (text*)fileid, (sb4)strlen(fileid) + 1, SQLT_STR, NULL, NULL, NULL, 0, NULL, OCI_DEFAULT);
        printf("RESULT 8: %d\n", rc);
        rc = OCIDefineByPos(p_sql, &p_dfn, p_err, 1, NULL, filesize, SQLT_LBI, NULL, NULL, NULL, OCI_DYNAMIC_FETCH);
        printf("RESULT 9: %d\n", rc);
        rc = OCIStmtExecute(p_svc, p_sql, p_err, 0, 0, NULL, NULL, OCI_DEFAULT);
        printf("RESULT 10: %d\n", rc);
        rc = OCIStmtFetch(p_sql, p_err, 1, 0, OCI_DEFAULT);
        printf("RESULT 11: %d\n", rc);
    
        // Read data
        unsigned long size_left = filesize;
        while (rc == OCI_NEED_DATA)
        {
            void* hndlp;
            ub4   type;
            ub1   in_out;
            ub4   iter;
            ub4   idx;
            ub1   piece;
            ub4   alen;
            rc = OCIStmtGetPieceInfo(p_sql, p_err, &hndlp, &type, &in_out, &iter, &idx, &piece);
            printf("RESULT 12a %d, %d, %d, %d, %d, %d\n", (int)rc, (int)type, (int)in_out, (int)iter, (int)idx, (int)piece);
            alen = sizeof(buffer);
            memset(buffer, 0xDA, sizeof(buffer)); // some arbitrary data .....
            rc = OCIStmtSetPieceInfo(hndlp, type, p_err, buffer, &alen, piece, NULL, NULL);
            printf("RESULT 12b %d, %d\n", (int)rc, (int)alen);
            rc = OCIStmtFetch(p_sql, p_err, 1, OCI_DEFAULT, OCI_DEFAULT);
            printf("RESULT 12c: %d\n", rc);
    
            // We can use the buffer over here
            long nr_read = (size_left < alen) ? size_left : alen;
            // save 'nr_read' bytes within the buffer
            size_left -= nr_read;
        }
        printf("Size left: %lu\n", size_left);
    
        // Cleaning up...
        rc = OCILogoff(p_svc, p_err);
        rc = OCIHandleFree(p_sql, OCI_HTYPE_STMT);
        rc = OCIHandleFree(p_svc, OCI_HTYPE_SVCCTX);
        rc = OCIHandleFree(p_err, OCI_HTYPE_ERROR);
    
        return (0);
    
    } // main