c++occi

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


We are using Oracle 12c database and CentOS7 with OCCI to connect. We are trying to insert a char array into the database, but this char array has a NUL character in the middle. When we use the statement->setString function the update is successful however once it sees the NUL char it only puts NUL chars there after. See this example code and it's output.

Example Code using 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\0RGE              ", 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();
}

Accessing the Database After the Update:

SELECT first_name FROM my_customers WHERE customer_id = 10;

GEO

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

47454F0000000000000000000000000000000000

However I would have expected it to be

47454F0047452032322020202020202020202020

So I tried using oracle::occi::Bytes-- this errors out with

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

Example Code using 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\0RGE              ", 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();
}

Output:

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

So I tried sending in half the bytes by changing the 2nd parameter of the oracle::occi::Bytes constructor to 10, and it succeeded however after reading the value from the database I realized it is a string representation of the hex value of the chars. So my question at this point is why is Oracle12c putting the hex value as a string when I pass oracle::occi:Bytes.

Example Code using half the actual length and 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\0RGE              ", 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();
}

Accessing the Database After the Update : SELECT first_name FROM my_customers WHERE customer_id = 10;

47454F00524745202020

NOTE : This query did not wrap with rawtohex-- this is the actual char array value in the database.

Here is the table definition:

DESCRIBE MAIN_USER.MY_CUSTOMERS

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

Here is our Oracle instance information : Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP, Advanced Analytics and Real Application Testing options

We are using Oracle occi client 12.1 64bit


Solution

  • For those that may stumble upon this, I ended up talking with Oracle Support and they told me there wasn't a way to do what I wanted (as expected) however the Byte workaround got us closer and we were able to take the hex string being passed in by the setBytes and turn it into raw data and then convert that raw data to a varchar2 which ended up "working" -- however I don't know what implications we will run into later, but so far it seems to work.

    Code:

    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\0RGE              ", 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();
    }
    

    Then checking the database:

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

    So it seems to work