在 std::string 或 char 数组中间使用 NULL char 进行更新

UPDATE with NULL char in the middle of std::string or char array

我们正在使用 Oracle 12c 数据库和带有 OCCI 的 CentOS7 进行连接。我们试图向数据库中插入一个 char 数组,但是这个 char 数组中间有一个 NUL 字符。当我们使用 statement->setString 函数时,更新是成功的,但是一旦它看到 NUL 字符,它只会将 NUL 字符放在那里。请参阅此示例代码及其输出。

使用 setString 的示例代码:

static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
    Environment *env = Environment::createEnvironment();

    Connection *conn = env->createConnection(user, pwd, connectionString);

    Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = :1 WHERE customer_id = :2");

    stmt->setString(1, std::string("GEO[=11=]RGE              ", 20));
    stmt->setInt(2, 10);

    stmt->setString(1, std::string(adrs_first_name, sizeof(adrs_first_name)));

    oracle::occi::Statement::Status status = stmt->execute();

    conn->terminateStatement(stmt);
    conn->commit();
}

更新后访问数据库:

SELECT first_name FROM my_customers WHERE customer_id = 10;

GEO

SELECT rawtohex(first_name) FROM my_customers WHERE customer_id = 10;

47454F0000000000000000000000000000000000

不过我本以为会是

47454F0047452032322020202020202020202020

所以我尝试使用 oracle::occi::Bytes-- 这个错误与

ORA - 12899:列 "MAIN_USER"."MY_CUSTOMERS"."FIRST_NAME" 的值太大(实际:40,最大值:20)

使用 setBytes 的示例代码:

static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
    Environment *env = Environment::createEnvironment();
    Connection *conn = env->createConnection(user, pwd, connectionString);
    Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = :1 WHERE customer_id = :2");
    std::string s("GEO[=14=]RGE              ", 20);
    oracle::occi::Bytes bytes((unsigned char *)s.c_str(), 20, 0, env);
    stmt->setBytes(1, bytes);
    stmt->setInt(2, 10);
    try
    {

        oracle::occi::Statement::Status status = stmt->execute();
    }
    catch (oracle::occi::SQLException &e)
    {
        std::cout << "Error " << e.getErrorCode() << ": " << e.what() << std::endl;
    }
    conn->terminateStatement(stmt);
    conn->commit();
}

输出:

Error 12899 : ORA - 12899 : value too large for column "MAIN_USER"."MY_CUSTOMERS"."FIRST_NAME" (actual : 40, maximum : 20)

所以我尝试通过将 oracle::occi::Bytes 构造函数的第二个参数更改为 10 来发送一半的字节,它成功了但是在从数据库读取值后我意识到它是十六进制的字符串表示字符的值。所以我现在的问题是为什么 Oracle12c 在我传递 oracle::occi:Bytes.

时将十六进制值作为字符串

示例代码使用实际长度的一半和 setBytes:

static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
    Environment *env = Environment::createEnvironment();
    Connection *conn = env->createConnection(user, pwd, connectionString);
    Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = :1 WHERE customer_id = :2");

    std::string s("GEO[=16=]RGE              ", 20);
    oracle::occi::Bytes bytes((unsigned char *)s.c_str(), 10, 0, env);

    stmt->setBytes(1, bytes);
    stmt->setInt(2, 10);
    try
    {
        oracle::occi::Statement::Status status = stmt->execute();
    }
    catch (oracle::occi::SQLException &e)
    {
        std::cout << "Error " << e.getErrorCode() << ": " << e.what() << std::endl;
    }

    conn->terminateStatement(stmt);
    conn->commit();
}

更新后访问数据库: SELECT first_name 从 my_customers 哪里 customer_id = 10;

47454F00524745202020

注意:此查询未使用 rawtohex 换行——这是数据库中的实际字符数组值。

这里是table定义:

DESCRIBE MAIN_USER.MY_CUSTOMERS

Name                           Null Type
------------------------------ ---- --------------
CUSTOMER_ID                         NUMBER(10)
FIRST_NAME                          CHAR(20 CHAR)

这是我们的 Oracle 实例信息: Oracle Database 12c 企业版 12.1.0.2.0 版 - 64 位生产 随着分区,真正的应用集群,自动存储管理,OLAP, 高级分析和真实应用测试选项

我们正在使用 Oracle occi 客户端 12.1 64 位

对于那些可能偶然发现这个问题的人,我最终与 Oracle 支持人员交谈,他们告诉我没有办法做我想做的事(如预期的那样),但是 Byte 解决方法让我们更接近了,我们能够获取由 setBytes 传入的十六进制字符串并将其转换为原始数据,然后将该原始数据转换为最终为 "working" 的 varchar2 - 但是我不知道我们将 运行 稍后,但到目前为止它似乎有效。

代码:

static void Run(const std::string &connectionString, const std::string &user, const std::string &pwd)
{
    Environment *env = Environment::createEnvironment();
    Connection *conn = env->createConnection(user, pwd, connectionString);
    Statement *stmt = conn->createStatement("UPDATE my_customers SET first_name = utl_raw.cast_to_varchar2(hextoraw(:1)) WHERE customer_id = :2");

    std::string s("GEO[=10=]RGE              ", 20);
    oracle::occi::Bytes bytes((unsigned char *)s.c_str(), 20, 0, env);

    stmt->setBytes(1, bytes);
    stmt->setInt(2, 10);
    try
    {
        oracle::occi::Statement::Status status = stmt->execute();
    }
    catch (oracle::occi::SQLException &e)
    {
        std::cout << "Error " << e.getErrorCode() << ": " << e.what() << std::endl;
    }

    conn->terminateStatement(stmt);
    conn->commit();
}

然后查看数据库:

SELECT rawtohex(first_name) FROM my_customers WHERE customer_id = 10;

47454F0052474520202020202020202020202020

所以它似乎有效