Documentation
//-----------------------------------------------------------------------------
// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
// This program is free software: you can modify it and/or redistribute it
// under the terms of:
//
// (i)  the Universal Permissive License v 1.0 or at your option, any
//      later version (http://oss.oracle.com/licenses/upl); and/or
//
// (ii) the Apache License v 2.0. (http://www.apache.org/licenses/LICENSE-2.0)
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// TestRowIds.c
//   Test suite for testing all the rowid related test cases.
//-----------------------------------------------------------------------------

#include "TestLib.h"

//-----------------------------------------------------------------------------
// dpiTest_2000_convRowIdToStrAndVerifyOnRegTab()
//   Prepare and execute any query that selects rowid on a regular table.
// Convert one of these rowids to string by calling dpiRowid_getStringValue()
// and perform a second query specifically for the row matching that rowid.
//-----------------------------------------------------------------------------
int dpiTest_2000_convRowIdToStrAndVerifyOnRegTab(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sqlQuery1 = "select rowid from TestStrings where IntCol = 7";
    const char *sqlQuery2 = "select IntCol from TestStrings where rowid = :1";
    uint32_t bufferRowIndex, rowidAsStringLength;
    dpiData *queryValue, bindValue;
    dpiNativeTypeNum nativeTypeNum;
    const char *rowidAsString;
    dpiStmt *stmt1, *stmt2;
    dpiConn *conn;
    int found;

    // perform first query to get rowid
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sqlQuery1, strlen(sqlQuery1), NULL, 0,
            &stmt1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt1, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt1, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for first query!");
    if (dpiStmt_getQueryValue(stmt1, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiRowid_getStringValue(queryValue->value.asRowid, &rowidAsString,
            &rowidAsStringLength) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // perform second query to get row using rowid
    if (dpiConn_prepareStmt(conn, 0, sqlQuery2, strlen(sqlQuery2), NULL, 0,
            &stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setBytes(&bindValue, (char*) rowidAsString, rowidAsStringLength);
    if (dpiStmt_bindValueByPos(stmt2, 1, DPI_NATIVE_TYPE_BYTES,
            &bindValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt2, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt2, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for second query!");
    if (dpiStmt_getQueryValue(stmt2, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, queryValue->value.asInt64,
            7) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // cleanup
    if (dpiStmt_release(stmt1) < 0)
        dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_2001_convRowIdToStrAndVerifyOnIndexTab()
//   Prepare and execute any query that selects rowid on an index organized
// table. convert one of these rowids to string by calling
// dpiRowid_getStringValue() and perform a second query specifically
// for the row matching that rowid.
//-----------------------------------------------------------------------------
int dpiTest_2001_convRowIdToStrAndVerifyOnIndexTab(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sqlQuery1 = "select rowid from TestOrgIndex where IntCol = 3";
    const char *sqlQuery2 = "select IntCol from TestOrgIndex where rowid = :1";
    uint32_t bufferRowIndex, rowidAsStringLength;
    dpiData *queryValue, bindValue;
    dpiNativeTypeNum nativeTypeNum;
    const char *rowidAsString;
    dpiStmt *stmt1, *stmt2;
    dpiConn *conn;
    int found;

    // perform first query to get rowid
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sqlQuery1, strlen(sqlQuery1), NULL, 0,
            &stmt1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt1, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt1, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for first query!");
    if (dpiStmt_getQueryValue(stmt1, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiRowid_getStringValue(queryValue->value.asRowid, &rowidAsString,
            &rowidAsStringLength) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // perform second query to get row using rowid
    if (dpiConn_prepareStmt(conn, 0, sqlQuery2, strlen(sqlQuery2), NULL, 0,
            &stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiData_setBytes(&bindValue, (char*) rowidAsString, rowidAsStringLength);
    if (dpiStmt_bindValueByPos(stmt2, 1, DPI_NATIVE_TYPE_BYTES,
            &bindValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt2, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt2, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for second query!");
    if (dpiStmt_getQueryValue(stmt2, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, queryValue->value.asInt64,
            3) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // cleanup
    if (dpiStmt_release(stmt1) < 0)
        dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_2002_releaseRowIdTwice()
//   Fetch any rowid; close statement that fetched that rowid; call
// dpiRowid_release() twice (error DPI-1002).
//-----------------------------------------------------------------------------
int dpiTest_2002_releaseRowIdTwice(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sql = "select rowid from TestOrgIndex where IntCol = 6";
    dpiNativeTypeNum nativeTypeNum;
    uint32_t bufferRowIndex;
    dpiData *rowidValue;
    dpiRowid *rowid;
    dpiStmt *stmt;
    dpiConn *conn;
    int found;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sql, strlen(sql), NULL, 0, &stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase, "row not found!");
    if (dpiStmt_getQueryValue(stmt, 1, &nativeTypeNum, &rowidValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    rowid = rowidValue->value.asRowid;
    if (dpiRowid_addRef(rowid) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiRowid_release(rowid) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    dpiRowid_release(rowid);
    if (dpiTestCase_expectError(testCase, "DPI-1002:") < 0)
        return DPI_FAILURE;

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_2003_verifyGetStrValWithNullRowId()
//   Call dpiRowid_getStringValue() with a NULL rowid (error DPI-1002).
//-----------------------------------------------------------------------------
int dpiTest_2003_verifyGetStrValWithNullRowId(dpiTestCase *testCase,
        dpiTestParams *params)
{
    uint32_t rowidAsStringLength;
    const char *rowidAsString;
    dpiConn *conn;

    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    dpiRowid_getStringValue(NULL, &rowidAsString, &rowidAsStringLength);
    if (dpiTestCase_expectError(testCase, "DPI-1002:") < 0)
        return DPI_FAILURE;

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_2004_verifySpecificRowIdOnRegTab()
//   Prepare and execute any query which selects a rowid from a regular table.
// Use one of these rowids to perform a second query specifically for the row
// matching that rowid.
//-----------------------------------------------------------------------------
int dpiTest_2004_verifySpecificRowIdOnRegTab(dpiTestCase *testCase,
        dpiTestParams *params)
{
    const char *sqlQuery1 = "select rowid from TestStrings where IntCol = 9";
    const char *sqlQuery2 = "select IntCol from TestStrings where rowid = :1";
    dpiNativeTypeNum nativeTypeNum;
    uint32_t bufferRowIndex;
    dpiStmt *stmt1, *stmt2;
    dpiData *queryValue;
    dpiConn *conn;
    int found;

    // perform first query to get rowid
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sqlQuery1, strlen(sqlQuery1), NULL, 0,
            &stmt1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt1, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt1, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for first query!");
    if (dpiStmt_getQueryValue(stmt1, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // perform second query to get row using rowid
    if (dpiConn_prepareStmt(conn, 0, sqlQuery2, strlen(sqlQuery2), NULL, 0,
            &stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindValueByPos(stmt2, 1, DPI_NATIVE_TYPE_ROWID,
            queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt2, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt2, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for second query!");
    if (dpiStmt_getQueryValue(stmt2, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, queryValue->value.asInt64,
            9) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // cleanup
    if (dpiStmt_release(stmt1) < 0)
        dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// dpiTest_2005_verifySpecificRowIdOnIndexTab()
//   prepare and execute any query which selects rowid on an index organized
// table. use one of these rowids to perform a second query specifically
// for the row matching that rowid.
//-----------------------------------------------------------------------------
int dpiTest_2005_verifySpecificRowIdOnIndexTab(dpiTestCase *testCase,
                                        dpiTestParams *params)
{
    const char *sqlQuery1 = "select rowid from TestOrgIndex where IntCol = 8";
    const char *sqlQuery2 = "select IntCol from TestOrgIndex where rowid = :1";
    dpiNativeTypeNum nativeTypeNum;
    uint32_t bufferRowIndex;
    dpiStmt *stmt1, *stmt2;
    dpiData *queryValue;
    dpiConn *conn;
    int found;

    // perform first query to get rowid
    if (dpiTestCase_getConnection(testCase, &conn) < 0)
        return DPI_FAILURE;
    if (dpiConn_prepareStmt(conn, 0, sqlQuery1, strlen(sqlQuery1), NULL, 0,
            &stmt1) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt1, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt1, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for first query!");
    if (dpiStmt_getQueryValue(stmt1, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // perform second query to get row using rowid
    if (dpiConn_prepareStmt(conn, 0, sqlQuery2, strlen(sqlQuery2), NULL, 0,
            &stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_bindValueByPos(stmt2, 1, DPI_NATIVE_TYPE_ROWID,
            queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_execute(stmt2, 0, NULL) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_fetch(stmt2, &found, &bufferRowIndex) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (!found)
        return dpiTestCase_setFailed(testCase,
                "row not found for second query!");
    if (dpiStmt_getQueryValue(stmt2, 1, &nativeTypeNum, &queryValue) < 0)
        return dpiTestCase_setFailedFromError(testCase);
    if (dpiTestCase_expectUintEqual(testCase, queryValue->value.asInt64,
            8) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    // cleanup
    if (dpiStmt_release(stmt1) < 0)
        dpiTestCase_setFailedFromError(testCase);
    if (dpiStmt_release(stmt2) < 0)
        return dpiTestCase_setFailedFromError(testCase);

    return DPI_SUCCESS;
}


//-----------------------------------------------------------------------------
// main()
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
    dpiTestSuite_initialize(2000);
    dpiTestSuite_addCase(dpiTest_2000_convRowIdToStrAndVerifyOnRegTab,
            "fetch rowid and refetch row via string rep (normal table)");
    dpiTestSuite_addCase(dpiTest_2001_convRowIdToStrAndVerifyOnIndexTab,
            "fetch rowid and refetch row via string rep (index org table)");
    dpiTestSuite_addCase(dpiTest_2002_releaseRowIdTwice,
            "call dpiRowid_release() twice");
    dpiTestSuite_addCase(dpiTest_2003_verifyGetStrValWithNullRowId,
            "verify dpiRowid_getStringValue() with NULL rowid");
    dpiTestSuite_addCase(dpiTest_2004_verifySpecificRowIdOnRegTab,
            "fetch rowid and refetch row (normal table)");
    dpiTestSuite_addCase(dpiTest_2005_verifySpecificRowIdOnIndexTab,
            "fetch rowid and refetch row (index org table)");
    return dpiTestSuite_run();
}