使用OCI提取原始文件时程序崩溃

问题描述

我正在尝试使用 OCI 库在Oracle表中选择一个LONG RAW列。
出于超出此问题范围的原因,我更喜欢分段读取数据,因此不使用回调。
语句的执行按预期返回OCI_NEED_DATA,但是第一次调用OCIStmtFetch会导致系统错误
错误代码5:在oracommon12.dll中读取地址0的访问冲突。
我有客户端12.1并连接到Oracle 10.g

下面是一个最小化程序,该程序会重现该错误
一切顺利,直到第43行(结果10):它输出99等于'OCI_NEED_DATA'。
程序随后使该行崩溃。

#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*,(void (*)(dvoid*,dvoid*))NULL);
    printf("RESULT 1: %d\n",rc);
    rc = OCIEnvInit(&p_env,OCI_DEFAULT,NULL);
    printf("RESULT 2: %d\n",rc);
    rc = OCIHandleAlloc(p_env,(void**)&p_err,OCI_HTYPE_ERROR,NULL);
    printf("RESULT 3: %d\n",(void**)&p_svc,OCI_HTYPE_SVCCTX,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",(void**)&p_sql,OCI_HTYPE_STMT,NULL);
    printf("RESULT 6: %d\n",rc);
    sprintf(stmt,"SELECT content FROM td_planimetrie WHERE id_file = :x");
    rc = OCIStmtPrepare(p_sql,(text*)stmt,(ub4)strlen(stmt),OCI_NTV_Syntax,OCI_DEFAULT);
    printf("RESULT 7: %d\n",rc);
    rc = OCIBindByName(p_sql,&p_bnd,(text*)":x",-1,(text*)fileid,(sb4)strlen(fileid) + 1,sqlT_STR,OCI_DEFAULT);
    printf("RESULT 8: %d\n",rc);
    rc = OCIDefineByPos(p_sql,&p_dfn,1,sqlT_LBI,OCI_DYNAMIC_FETCH);
    printf("RESULT 9: %d\n",rc);
    rc = OCIStmtExecute(p_svc,p_sql,OCI_DEFAULT);
    printf("RESULT 10: %d\n",rc); // <-- rc equals 99=OCI_NEED_DATA
    rc = OCIStmtFetch(p_sql,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,&hndlp,&type,&in_out,&iter,&idx,&piece);
        printf("RESULT 12b %d,%d,%d\n",(int)rc,(int)type,(int)in_out,(int)iter,(int)idx,(int)piece);
        alen = sizeof(buffer);
        rc = OCIStmtSetPieceInfo(hndlp,type,buffer,&alen,piece,NULL);
        printf("RESULT 12c %d,(int)alen);
        rc = OCIStmtFetch(p_sql,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

解决方法

我自己弄清楚了:

首先,必须将对OCIStmtExecute的调用中的参数 iters 设置为0
这样可以避免崩溃,但不能解决整个问题。

此外,对OCIDefineByPos的调用中的参数 value_sz 必须设置为要读取的列的实际大小。

最后,只有在连续调用OCIStmtSetPieceInfo之后,才能读取作为OCIStmtFetch的第4个参数传递的缓冲区。

这将导致以下代码:

#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*,(void (*)(dvoid*,dvoid*))NULL);
    printf("RESULT 1: %d\n",rc);
    rc = OCIEnvInit(&p_env,OCI_DEFAULT,NULL);
    printf("RESULT 2: %d\n",rc);
    rc = OCIHandleAlloc(p_env,(void**)&p_err,OCI_HTYPE_ERROR,NULL);
    printf("RESULT 3: %d\n",(void**)&p_svc,OCI_HTYPE_SVCCTX,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,NULL);
    printf("RESULT 6: %d\n",rc);
    sprintf(stmt,"SELECT filesize FROM td_planimetrie WHERE id_file = :x");
    rc = OCIStmtPrepare(p_sql,(text*)stmt,(ub4)strlen(stmt),OCI_NTV_SYNTAX,OCI_DEFAULT);
    printf("RESULT 6a: %d\n",rc);
    rc = OCIBindByName(p_sql,&p_bnd,(text*)":x",-1,(text*)fileid,(sb4)strlen(fileid) + 1,SQLT_STR,OCI_DEFAULT);
    printf("RESULT 6b: %d\n",rc);
    rc = OCIDefineByPos(p_sql,&p_dfn,1,&filesize,sizeof(filesize),SQLT_INT,OCI_DEFAULT);
    printf("RESULT 6c: %d\n",rc);
    rc = OCIStmtExecute(p_svc,p_sql,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,NULL);
    printf("RESULT 7: %d\n","SELECT content FROM td_planimetrie WHERE id_file = :x");
    rc = OCIStmtPrepare(p_sql,OCI_DEFAULT);
    printf("RESULT 7a: %d\n",OCI_DEFAULT);
    printf("RESULT 8: %d\n",filesize,SQLT_LBI,OCI_DYNAMIC_FETCH);
    printf("RESULT 9: %d\n",OCI_DEFAULT);
    printf("RESULT 10: %d\n",rc);
    rc = OCIStmtFetch(p_sql,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,&hndlp,&type,&in_out,&iter,&idx,&piece);
        printf("RESULT 12a %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,buffer,&alen,piece,NULL);
        printf("RESULT 12b %d,(int)alen);
        rc = OCIStmtFetch(p_sql,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