使用 OCI 获取 long-raw 时程序崩溃

Program crashes when fetching long-raw with OCI

我正在尝试使用 OCI 库 SELECT Oracle table 中的一个 LONG RAW 列。
由于超出这个问题范围的原因,我更喜欢分段获取数据,而不是使用回调。
语句returns的执行,如预期的那样,OCI_NEED_DATA但是第一次调用OCIStmtFetch导致系统错误。
ErrorCode 5:访问冲突读取地址 0 in oracommon12.dll.
我有客户端 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* (*)(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

我自己想出来了:

首先,调用OCIStmtExecute中的参数iters必须设置为0
这避免了崩溃,但没有解决整个问题。

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

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

这导致以下代码:

#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