RETCODE SQL_API OPENSEARCHAPI_GetData()

in sql-odbc/src/sqlodbc/results.c [760:1003]


RETCODE SQL_API OPENSEARCHAPI_GetData(HSTMT hstmt, SQLUSMALLINT icol,
                              SQLSMALLINT fCType, PTR rgbValue,
                              SQLLEN cbValueMax, SQLLEN *pcbValue) {
    CSTR func = "OPENSEARCHAPI_GetData";
    QResultClass *res;
    StatementClass *stmt = (StatementClass *)hstmt;
    UInt2 num_cols;
    SQLLEN num_rows;
    OID field_type;
    int atttypmod;
    void *value = NULL;
    RETCODE result = SQL_SUCCESS;
    char get_bookmark = FALSE;
    SQLSMALLINT target_type;
    int precision = -1;
#ifdef WITH_UNIXODBC
    SQLCHAR dum_rgb[2] = "\0\0";
#endif /* WITH_UNIXODBC */

    MYLOG(OPENSEARCH_TRACE, "entering stmt=%p icol=%d\n", stmt, icol);

    if (!stmt) {
        SC_log_error(func, NULL_STRING, NULL);
        return SQL_INVALID_HANDLE;
    }
    res = SC_get_Curres(stmt);

    if (STMT_EXECUTING == stmt->status) {
        SC_set_error(stmt, STMT_SEQUENCE_ERROR,
                     "Can't get data while statement is still executing.",
                     func);
        return SQL_ERROR;
    }

    if (stmt->status != STMT_FINISHED) {
        SC_set_error(stmt, STMT_STATUS_ERROR,
                     "GetData can only be called after the successful "
                     "execution on a SQL statement",
                     func);
        return SQL_ERROR;
    }

#ifdef WITH_UNIXODBC
    if (NULL == rgbValue) /* unixODBC allows rgbValue is NULL? */
    {
        cbValueMax = 0;
        rgbValue = dum_rgb; /* to avoid a crash */
    }
#endif /* WITH_UNIXODBC */
    if (SQL_ARD_TYPE == fCType) {
        ARDFields *opts;
        BindInfoClass *binfo = NULL;

        opts = SC_get_ARDF(stmt);
        if (0 == icol)
            binfo = opts->bookmark;
        else if (icol <= opts->allocated && opts->bindings)
            binfo = &opts->bindings[icol - 1];
        if (binfo) {
            target_type = binfo->returntype;
            MYLOG(OPENSEARCH_DEBUG, "SQL_ARD_TYPE=%d\n", target_type);
            precision = binfo->precision;
        } else {
            SC_set_error(stmt, STMT_STATUS_ERROR,
                         "GetData can't determine the type via ARD", func);
            return SQL_ERROR;
        }
    } else
        target_type = fCType;
    if (icol == 0) {
        if (stmt->options.use_bookmarks == SQL_UB_OFF) {
            SC_set_error(
                stmt, STMT_COLNUM_ERROR,
                "Attempt to retrieve bookmark with bookmark usage disabled",
                func);
            return SQL_ERROR;
        }

        /* Make sure it is the bookmark data type */
        switch (target_type) {
            case SQL_C_BOOKMARK:
            case SQL_C_VARBOOKMARK:
                break;
            default:
                MYLOG(
                    OPENSEARCH_ALL,
                    "GetData Column 0 is type %d not of type SQL_C_BOOKMARK\n",
                    target_type);
                SC_set_error(stmt, STMT_PROGRAM_TYPE_OUT_OF_RANGE,
                             "Column 0 is not of type SQL_C_BOOKMARK", func);
                return SQL_ERROR;
        }

        get_bookmark = TRUE;
    } else {
        /* use zero-based column numbers */
        icol--;

        /* make sure the column number is valid */
        num_cols = QR_NumPublicResultCols(res);
        if (icol >= num_cols) {
            SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
                         "Invalid column number.", func);
            return SQL_ERROR;
        }
    }

#ifdef __APPLE__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wkeyword-macro"
#endif  // __APPLE__
#define return DONT_CALL_RETURN_FROM_HERE ? ? ?
#ifdef __APPLE__
#pragma clang diagnostic pop
#endif  // __APPLE__
    if (!SC_is_fetchcursor(stmt)) {
        /* make sure we're positioned on a valid row */
        num_rows = QR_get_num_total_tuples(res);
        if ((stmt->currTuple < 0) || (stmt->currTuple >= num_rows)) {
            SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
                         "Not positioned on a valid row for GetData.", func);
            result = SQL_ERROR;
            goto cleanup;
        }
        MYLOG(OPENSEARCH_DEBUG, "     num_rows = " FORMAT_LEN "\n", num_rows);

        if (!get_bookmark) {
            SQLLEN curt = GIdx2CacheIdx(stmt->currTuple, stmt, res);
            value = QR_get_value_backend_row(res, curt, icol);
            MYLOG(OPENSEARCH_DEBUG,
                  "currT=" FORMAT_LEN " base=" FORMAT_LEN " rowset=" FORMAT_LEN
                  "\n",
                  stmt->currTuple, QR_get_rowstart_in_cache(res),
                  SC_get_rowset_start(stmt));
            MYLOG(OPENSEARCH_DEBUG, "     value = '%s'\n", NULL_IF_NULL(value));
        }
    } else {
        /* it's a SOCKET result (backend data) */
        if (stmt->currTuple == -1 || !res || !res->tupleField) {
            SC_set_error(stmt, STMT_INVALID_CURSOR_STATE_ERROR,
                         "Not positioned on a valid row for GetData.", func);
            result = SQL_ERROR;
            goto cleanup;
        }

        if (!get_bookmark) {
            /** value = QR_get_value_backend(res, icol); maybe thiw doesn't work
             */
            SQLLEN curt = GIdx2CacheIdx(stmt->currTuple, stmt, res);
            value = QR_get_value_backend_row(res, curt, icol);
        }
        MYLOG(OPENSEARCH_DEBUG, "  socket: value = '%s'\n", NULL_IF_NULL(value));
    }

    if (get_bookmark) {
        BOOL contents_get = FALSE;

        if (rgbValue) {
            if (SQL_C_BOOKMARK == target_type
                || (SQLLEN)sizeof(UInt4) <= cbValueMax) {
                Int4 bookmark = (int)SC_make_int4_bookmark(stmt->currTuple);
                contents_get = TRUE;
                memcpy(rgbValue, &bookmark, sizeof(bookmark));
            }
        }
        if (pcbValue)
            *pcbValue = sizeof(Int4);

        if (contents_get)
            result = SQL_SUCCESS;
        else {
            SC_set_error(stmt, STMT_TRUNCATED,
                         "The buffer was too small for the GetData.", func);
            result = SQL_SUCCESS_WITH_INFO;
        }
        goto cleanup;
    }

    field_type = QR_get_field_type(res, icol);
    atttypmod = QR_get_atttypmod(res, icol);

    MYLOG(OPENSEARCH_DEBUG,
          "**** icol = %d, target_type = %d, field_type = %d, value = '%s'\n",
          icol, target_type, field_type, NULL_IF_NULL(value));

    SC_set_current_col(stmt, icol);

    result = (RETCODE)copy_and_convert_field(stmt, field_type, atttypmod, value,
                                             target_type, precision, rgbValue,
                                             cbValueMax, pcbValue, pcbValue);

    switch (result) {
        case COPY_OK:
            result = SQL_SUCCESS;
            break;

        case COPY_UNSUPPORTED_TYPE:
            SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR,
                         "Received an unsupported type from OpenSearch.",
                         func);
            result = SQL_ERROR;
            break;

        case COPY_UNSUPPORTED_CONVERSION:
            SC_set_error(stmt, STMT_RESTRICTED_DATA_TYPE_ERROR,
                         "Couldn't handle the necessary data type conversion.",
                         func);
            result = SQL_ERROR;
            break;

        case COPY_RESULT_TRUNCATED:
            SC_set_error(stmt, STMT_TRUNCATED,
                         "The buffer was too small for the GetData.", func);
            result = SQL_SUCCESS_WITH_INFO;
            break;

        case COPY_INVALID_STRING_CONVERSION: /* invalid string */
            SC_set_error(stmt, STMT_STRING_CONVERSION_ERROR,
                         "invalid string conversion occured.", func);
            result = SQL_ERROR;
            break;

        case COPY_GENERAL_ERROR: /* error msg already filled in */
            result = SQL_ERROR;
            break;

        case COPY_NO_DATA_FOUND:
            /* SC_log_error(func, "no data found", stmt); */
            result = SQL_NO_DATA_FOUND;
            break;

        default:
            SC_set_error(
                stmt, STMT_INTERNAL_ERROR,
                "Unrecognized return value from copy_and_convert_field.", func);
            result = SQL_ERROR;
            break;
    }

cleanup:
#undef return
    MYLOG(OPENSEARCH_TRACE, "leaving %d\n", result);
    return result;
}