/*
 * This file is part of the oGRAC project.
 * Copyright (c) 2026 Huawei Technologies Co.,Ltd.
 *
 * oGRAC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * execute.c
 *
 *
 * IDENTIFICATION
 * src/driver/ogodbc/execute.c
 */

#include "execute.h"

SQLRETURN ograc_execute(statement *stmt);

static void __attribute__((constructor)) load_config(void)
{
    (void)load_odbc_config();
}

void clean_env_handle(SQLHENV henv)
{
    environment_class *env = (environment_class *)henv;
    env->error_code = 0;
    env->error_msg = NULL;
    env->err_sign = 0;
}

void clean_conn_handle(SQLHDBC hdbc)
{
    connection_class *conn = (connection_class *)hdbc;
    conn->error_msg = NULL;
    conn->error_code = 0;
    conn->err_sign = 0;
}

static void __attribute__((destructor)) close_config(void)
{
    close_odbc_config();
}

status_t set_conn_attr(connection_class *conn, SQLPOINTER Value, SQLINTEGER StringLength, int32 attr)
{
    int newValue = (SQLINTEGER)(SQLULEN)Value;
    if (newValue < 0) {
        return SQL_ERROR;
    } else if (newValue == 0) {
        newValue = -1;
    }
    return ogconn_set_conn_attr(conn->ogconn, attr, &newValue, StringLength);
}

SQLRETURN ograc_prepare(statement *stmt, const SQLCHAR *text)
{
    status_t status;

    del_stmt_param(&stmt->params, SQL_TRUE);
    cm_reset_list(&(stmt->param));
    status = ogconn_prepare(stmt->ctconn_stmt, (const char *)text);
    if (status == OGCONN_SUCCESS) {
        stmt->stmt_flag = NOT_EXEC_STMT;
    }
    del_stmt_param(&stmt->params, SQL_FALSE);
    if (status != OG_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    return SQL_SUCCESS;
}

static SQLLEN get_param_size(sql_input_data *input_data, uint32 offset)
{
    SQLLEN param_ptr = *(input_data->size + offset);

    if (param_ptr == SQL_DATA_AT_EXEC) {
        input_data->is_process = SQL_TRUE;
        return input_data->param_len;
    }
    if (param_ptr == SQL_NTS) {
        return strlen((const char *)input_data->param_value + input_data->param_len * offset);
    }

    if (param_ptr == SQL_DEFAULT_PARAM || param_ptr == SQL_NULL_DATA) {
        return OGCONN_NULL;
    }
    return param_ptr;
}

static SQLRETURN verify_param_ptr(statement *stmt, sql_input_data *input_data)
{
    uint32 index = 0;

    while (index < stmt->param_count) {
        input_data->param_size[index] = (unsigned short)get_param_size(input_data, index);
        index++;
    }
    return SQL_SUCCESS;
}

static SQLRETURN set_sql_params(statement *stmt)
{
    bilist_t *param_list = &stmt->params;
    bilist_node_t *param = param_list->head;
    uint32 param_count = 0;
    sql_input_data *input_data = NULL;
    SQLRETURN ret;

    int attr_res = ogconn_get_stmt_attr(stmt->ctconn_stmt, OGCONN_ATTR_PARAMSET_SIZE,
                                        &param_count, sizeof(uint32), NULL);
    if (attr_res != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    
    stmt->param_count = param_count;
    while (param != NULL) {
        input_data = (sql_input_data *)((char *)param - OFFSET_OF(sql_input_data, data_list));
        param = param->next;
        if (input_data->param_flag != EXEC_PARAM || param_count != input_data->param_count) {
            clean_up_bind_param(input_data);
            input_data->param_count = param_count;
            ret = malloc_bind_param(input_data);
            if (ret != SQL_SUCCESS) {
                stmt->conn->err_sign = 1;
                stmt->conn->error_msg = "Couldn't allocate memory for stmt object.";
                return ret;
            }
            if (input_data->size != NULL) {
                ret = verify_param_ptr(stmt, input_data);
            }
            if (ret != SQL_SUCCESS) {
                return ret;
            }
            ret = bind_param_by_c_type(stmt, input_data);
            if (ret != SQL_SUCCESS) {
                return ret;
            }
            input_data->param_flag = EXEC_PARAM;
        }
    }
    return SQL_SUCCESS;
}

static void get_data_result(statement *stmt, SQLPOINTER *value, unsigned int col_num)
{
    uint32 index = 0;
    uint32 col_count = 0;
    og_generate_result *generate_result = NULL;

    while (col_count < col_num) {
        while (index < stmt->data_rows.count) {
            generate_result = (og_generate_result *)cm_list_get(&(stmt->data_rows), index);
            if (generate_result->ptr == col_count) {
                break;
            }
            index++;
        }
        
        if (stmt->data_rows.count != index) {
            col_count++;
            continue;
        }
        if (value != NULL) {
            *(unsigned int *)value = col_count + 1;
        }
        return;
    }
}

static SQLRETURN og_bind_param_execute(statement *stmt, SQLPOINTER *value)
{
    uint32 col_num = 0;
    uint32 col_num_len = sizeof(col_num);
    og_generate_result *generate_result = NULL;
    status_t status;
    uint32 rows = stmt->data_rows.count;
    uint32 index = 0;

    while (index < rows) {
        generate_result = (og_generate_result *)cm_list_get(&(stmt->data_rows), index);
        if (generate_result->is_recieved == OG_TRUE) {
            index++;
            continue;
        }
        if (value != NULL) {
            *(unsigned int *)value = generate_result->ptr + 1;
        }
        return SQL_PARAM_DATA_AVAILABLE;
    }
    
    status = ogconn_get_stmt_attr(stmt->ctconn_stmt, OGCONN_ATTR_COLUMN_COUNT, &col_num, sizeof(col_num), &col_num_len);
    if (status != OG_SUCCESS) {
        col_num = 0;
    }

    if (col_num > rows) {
        get_data_result(stmt, value, col_num);
        return SQL_PARAM_DATA_AVAILABLE;
    }
    return SQL_SUCCESS;
}

static SQLRETURN set_param_value(statement *stmt, bilist_t *params, SQLPOINTER *value, uint32 index)
{
    sql_input_data *input_data = NULL;

    input_data = generate_bind_param_instance(params->head, index);
    if (input_data == NULL) {
        stmt->conn->err_sign = 1;
        stmt->conn->error_msg = "the parameter value is NULL";
        return SQL_ERROR;
    }
    if (input_data->is_binded) {
        input_data->is_process = SQL_FALSE;
    }
    if (input_data->is_process == SQL_TRUE) {
        if (value != NULL) {
            *value = input_data->param_value;
        }
        return SQL_NEED_DATA;
    }
    return SQL_SUCCESS;
}

SQLRETURN bind_param_value(statement *stmt, SQLPOINTER *value, uint32 process)
{
    uint32 index = 0;
    bilist_t *params = &(stmt->params);
    SQLRETURN ret;

    if (stmt->stmt_flag == NOT_EXEC_STMT) {
        while (index < stmt->params.count) {
            ret = set_param_value(stmt, params, value, index);
            if (ret != SQL_SUCCESS) {
                return ret;
            }
            index++;
        }
        if (process) {
            return ograc_execute(stmt);
        }
        return SQL_SUCCESS;
    }

    if (stmt->stmt_flag == EXEC_STMT) {
        return og_bind_param_execute(stmt, value);
    }
    return SQL_SUCCESS;
}

static SQLRETURN handle_number_convert_err(statement *stmt)
{
    stmt->conn->err_sign = 1;
    stmt->conn->error_msg = "failed to convert number to decimal type.";
    return SQL_ERROR;
}

static SQLRETURN transfer_dec_type(statement *stmt, SQL_NUMERIC_STRUCT *value, dec8_t *dec_value)
{
    dec8_t dec;
    dec8_t precision;
    dec8_t dec_fac;
    dec8_t dec_num;
    dec8_t num_mod;
    dec8_t dec_add;
    dec8_t suffix;
    dec8_t data_dec;
    dec8_t data_num;
    uint32 index = 0;
    uint8 radix = 10;
    int32 scale = (int32)value->scale;

    cm_zero_dec8(dec_value);
    if (value->precision == 0) {
        return SQL_SUCCESS;
    }
    cm_uint32_to_dec8(radix, &dec);
    cm_uint32_to_dec8(DECIMAL_MOD, &num_mod);
    cm_uint32_to_dec8(0, &dec_add);
    while (index < DECIMAL_SIZE) {
        if (value->val[index] != '\0') {
            cm_uint32_to_dec8(index, &suffix);
            cm_uint32_to_dec8((uint32)(value->val[index]), &data_dec);
            if (cm_dec8_power(&num_mod, &suffix, &precision) != OG_SUCCESS) {
                return handle_number_convert_err(stmt);
            }
            if (cm_dec8_multiply(&data_dec, &precision, &data_num) != OG_SUCCESS) {
                return handle_number_convert_err(stmt);
            }
            if (cm_dec8_add(&data_num, &dec_add, dec_value) != OG_SUCCESS) {
                return handle_number_convert_err(stmt);
            }
            dec_add = *dec_value;
        }
        index++;
    }

    cm_int32_to_dec8(-1 * scale, &dec_fac);
    if (cm_dec8_power(&dec, &dec_fac, &precision) != OG_SUCCESS) {
        return handle_number_convert_err(stmt);
    }

    if (cm_dec8_multiply(&dec_num, &precision, dec_value) != OG_SUCCESS) {
        return handle_number_convert_err(stmt);
    }

    if (value->sign == 0) {
        cm_dec8_negate(dec_value);
    }
    return SQL_SUCCESS;
}

typedef SQLRETURN (*transfer_number_func)(statement *stmt,
                                          sql_input_data *input_data,
                                          char *value,
                                          variant_t *data_struct);

typedef struct type_transfer_num_map {
    uint32 sql_c_type;
    transfer_number_func trans_func;
} transfer_num_map;

static SQLRETURN numeric_type_transfer(statement *stmt, sql_input_data *input_data,
                                       char *value, variant_t *data_struct)
{
    status_t status;
    dec8_t dec_data;
    SQLRETURN ret;
    SQL_NUMERIC_STRUCT *numeric_value = (SQL_NUMERIC_STRUCT *)value;

    ret = transfer_dec_type(stmt, numeric_value, &dec_data);
    if (ret != SQL_SUCCESS) {
        stmt->conn->err_sign = 1;
        stmt->conn->error_msg = "invalid input numeric2 type.";
        return ret;
    }
    status = cm_dec8_scale(&dec_data, input_data->number, ROUND_TRUNC);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    cm_dec8_copy(&data_struct->v_dec, &dec_data);
    data_struct->type = OG_TYPE_NUMBER2;
    return SQL_SUCCESS;
}

static SQLRETURN float_type_transfer(statement *stmt, sql_input_data *input_data,
                                     char *value, variant_t *data_struct)
{
    status_t status;

    status = cm_real_to_dec8((double)*(float *)value, &data_struct->v_dec);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    data_struct->type = OG_TYPE_NUMBER2;

    status = cm_dec8_scale(&data_struct->v_dec, input_data->number, ROUND_TRUNC);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    return SQL_SUCCESS;
}

static SQLRETURN int_type_transfer(statement *stmt, sql_input_data *input_data,
                                   char *value, variant_t *data_struct)
{
    data_struct->v_bigint = (int64) * (SQLINTEGER *)value;
    data_struct->type = OG_TYPE_BIGINT;
    return SQL_SUCCESS;
}

static SQLRETURN double_type_transfer(statement *stmt, sql_input_data *input_data,
                                      char *value, variant_t *data_struct)
{
    status_t status;

    status = cm_real_to_dec8(*(double *)value, &data_struct->v_dec);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    data_struct->type = OG_TYPE_NUMBER2;

    status = cm_dec8_scale(&data_struct->v_dec, input_data->number, ROUND_TRUNC);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    return SQL_SUCCESS;
}

static SQLRETURN smallint_type_transfer(statement *stmt, sql_input_data *input_data,
                                        char *value, variant_t *data_struct)
{
    data_struct->v_int = (int32) * (short *)value;
    data_struct->type = OG_TYPE_INTEGER;
    return SQL_SUCCESS;
}

static SQLRETURN unsigned_tinyint_type_transfer(statement *stmt, sql_input_data *input_data,
                                                char *value, variant_t *data_struct)
{
    data_struct->v_int = (int32) * (unsigned char *)value;
    data_struct->type = OG_TYPE_INTEGER;
    return SQL_SUCCESS;
}

static SQLRETURN signed_bigint_type_transfer(statement *stmt, sql_input_data *input_data,
                                             char *value, variant_t *data_struct)
{
    data_struct->v_bigint = *(int64 *)value;
    data_struct->type = OG_TYPE_BIGINT;
    return SQL_SUCCESS;
}

static SQLRETURN unsigned_int_type_transfer(statement *stmt, sql_input_data *input_data,
                                            char *value, variant_t *data_struct)
{
    cm_uint64_to_dec8((uint64) * (SQLUINTEGER *)value, &data_struct->v_dec);
    data_struct->type = OG_TYPE_NUMBER2;
    return SQL_SUCCESS;
}

static SQLRETURN unsigned_bigint_type_transfer(statement *stmt, sql_input_data *input_data,
                                               char *value, variant_t *data_struct)
{
    cm_uint64_to_dec8(*(uint64 *)value, &data_struct->v_dec);
    data_struct->type = OG_TYPE_NUMBER2;
    return SQL_SUCCESS;
}

static SQLRETURN unsigned_smallint_type_transfer(statement *stmt, sql_input_data *input_data,
                                          char *value, variant_t *data_struct)
{
    data_struct->v_int = (int32) * (unsigned short *)value;
    data_struct->type = OG_TYPE_INTEGER;
    return SQL_SUCCESS;
}

static SQLRETURN tinyint_type_transfer(statement *stmt, sql_input_data *input_data,
                                char *value, variant_t *data_struct)
{
    data_struct->v_int = (int32) * (char *)value;
    data_struct->type = OG_TYPE_INTEGER;
    return SQL_SUCCESS;
}

transfer_num_map trans_map[] = {
    {SQL_C_NUMERIC, numeric_type_transfer},
    {SQL_C_LONG, int_type_transfer},
    {SQL_C_SLONG, int_type_transfer},
    {SQL_C_SHORT, smallint_type_transfer},
    {SQL_C_SSHORT, smallint_type_transfer},
    {SQL_C_FLOAT, float_type_transfer},
    {SQL_C_DOUBLE, double_type_transfer},
    {SQL_C_SBIGINT, signed_bigint_type_transfer},
    {SQL_C_UBIGINT, unsigned_bigint_type_transfer},
    {SQL_C_UTINYINT, unsigned_tinyint_type_transfer},
    {SQL_C_TINYINT, tinyint_type_transfer},
    {SQL_C_STINYINT, tinyint_type_transfer},
    {SQL_C_ULONG, unsigned_int_type_transfer},
    {SQL_C_USHORT, unsigned_smallint_type_transfer}
};

static SQLRETURN convert_param_to_decimal(statement *stmt, sql_input_data *input_data, uint32 pos)
{
    text_buf_t str;
    connection_class *conn = stmt->conn;
    str.max_size = OG_BUFF_SIZE;
    str.str = input_data->input_param[pos];
    str.len = 0;
    char *value = input_data->param_value + input_data->param_len * pos;
    SQLRETURN ret = SQL_ERROR;
    uint8 is_bind = OG_FALSE;
    variant_t *data_struct = NULL;
    data_struct = (variant_t *)malloc(sizeof(variant_t));
    int32 trans_map_count = sizeof(trans_map) / sizeof(transfer_num_map);
    transfer_number_func trans_func;

    for (int32 i = 0; i < trans_map_count; i++) {
        if (input_data->sql_type == trans_map[i].sql_c_type) {
            trans_func = trans_map[i].trans_func;
            is_bind = OG_TRUE;
            break;
        }
    }
    if (!is_bind) {
        stmt->conn->err_sign = 1;
        stmt->conn->error_msg = "bind C type is not supported yet.";
        return SQL_ERROR;
    }
    ret = trans_func(stmt, input_data, value, data_struct);
    if (ret != SQL_SUCCESS) {
        return ret;
    }
    if (var_as_string(NULL, data_struct, &str) != OG_SUCCESS) {
        conn->err_sign = 1;
        conn->error_msg = "failed to transfer variant to string";
        return SQL_ERROR;
    }
    uint32 text_len = data_struct->v_text.len;
    data_struct->v_text.str[text_len] = '\0';
    data_struct->is_null = SQL_FALSE;
    input_data->param_size[pos] = text_len;
    return ret;
}

static SQLRETURN get_date_err(statement *stmt)
{
    stmt->conn->err_sign = 1;
    stmt->conn->error_msg = "The value related to the date type is invalid.";
    return SQL_ERROR;
}

static SQLRETURN convert_sql_timestamp_to_ogdate(statement *stmt,
                                                 date_detail_t *value_detail,
                                                 sql_input_data *input_data,
                                                 char *value,
                                                 uint32 *fraction)
{
    uint32 frac_digits = 0;
    uint8 pow_base = 10;
    uint8 digits_base = 9;
    uint32 digit_pow = (uint32)pow(pow_base, (digits_base - input_data->number));
    TIMESTAMP_STRUCT *ts = NULL;
    ts = (TIMESTAMP_STRUCT *)value;
    uint16 year = (uint16)ts->year;
    uint8 max_mon = 12;
    uint8 max_day = 31;
    uint8 max_hour = 23;
    uint8 max_min = 59;
    uint8 max_sec = 59;

    if (year < CM_MIN_YEAR || year > CM_MAX_YEAR) {
        return get_date_err(stmt);
    }
    value_detail->year = (uint16)ts->year;

    if (ts->month <= 0 || ts->month > max_mon) {
        return get_date_err(stmt);
    }
    value_detail->mon = (uint8)ts->month;

    if (ts->day <= 0 || ts->day > max_day) {
        return get_date_err(stmt);
    }
    value_detail->day = (uint8)ts->day;

    if (ts->hour < 0 || ts->hour > max_hour) {
        return get_date_err(stmt);
    }
    value_detail->hour = (uint8)ts->hour;

    if (ts->minute < 0 || ts->minute > max_min) {
        return get_date_err(stmt);
    }
    value_detail->min = (uint8)ts->minute;

    if (ts->second < 0 || ts->second > max_sec) {
        return get_date_err(stmt);
    }
    value_detail->sec = (uint8)ts->second;

    frac_digits = ts->fraction - ts->fraction % digit_pow;
    fraction = &frac_digits;
    return SQL_SUCCESS;
}

static SQLRETURN convert_sql_date_to_ogdate(statement *stmt, date_detail_t *value_detail, char *value)
{
    DATE_STRUCT *dt = NULL;
    dt = (DATE_STRUCT *)value;
    uint16 year = (uint16)dt->year;
    uint8 max_mon = 12;
    uint8 max_day = 31;

    if (year < CM_MIN_YEAR || year > CM_MAX_YEAR) {
        return get_date_err(stmt);
    }
    value_detail->year = (uint16)dt->year;

    if (dt->month <= 0 || dt->month > max_mon) {
        return get_date_err(stmt);
    }
    value_detail->mon = (uint8)dt->month;

    if (dt->day <= 0 || dt->day > max_day) {
        return get_date_err(stmt);
    }
    value_detail->day = (uint8)dt->day;
    return SQL_SUCCESS;
}

static SQLRETURN convert_param_to_timestamp(statement *stmt, sql_input_data *input_data, uint32 pos)
{
    char *value = input_data->param_value + input_data->param_len * pos;
    uint32 data_len = 0;
    uint32 fraction = 0;
    SQLRETURN ret = SQL_SUCCESS;
    status_t status;
    date_detail_t value_detail = {0};
    ogconn_datetime_t datetime;

    status_t attr = ogconn_get_conn_attr(stmt->conn->ogconn,
                                         OGCONN_ATTR_TIMESTAMP_SIZE,
                                         &data_len,
                                         sizeof(data_len),
                                         NULL);
    if (attr != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    if (input_data->sql_type == SQL_C_DATE) {
        ret = convert_sql_date_to_ogdate(stmt, &value_detail, value);
    } else if (input_data->sql_type == SQL_C_TYPE_TIMESTAMP ||  input_data->sql_type == SQL_C_TIMESTAMP) {
        ret = convert_sql_timestamp_to_ogdate(stmt, &value_detail, input_data, value, &fraction);
    } else if (input_data->sql_type == SQL_C_ULONG) {
        cm_decode_time(*(time_t *)value, &value_detail);
    } else {
        stmt->conn->err_sign = 1;
        stmt->conn->error_msg = "bind c type is not supported yet.";
        return SQL_ERROR;
    }
    if (ret != SQL_SUCCESS) {
        return ret;
    }

    datetime = (ogconn_datetime_t)&input_data->input_param[pos];
    status = ogconn_datetime_construct(stmt->ctconn_stmt,
                                        datetime,
                                        OGCONN_TYPE_TIMESTAMP,
                                        value_detail.year,
                                        value_detail.mon,
                                        value_detail.day,
                                        value_detail.hour,
                                        value_detail.min,
                                        value_detail.sec,
                                        fraction,
                                        NULL,
                                        0);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    input_data->param_size[pos] = (uint16)data_len;
    return SQL_SUCCESS;
}

static SQLRETURN convert_db_type_data(statement *stmt, sql_input_data *input_data, uint32 pos)
{
    SQLRETURN ret = SQL_SUCCESS;

    switch (input_data->og_type) {
        case OGCONN_TYPE_DATE:
        case OGCONN_TYPE_TIMESTAMP:
        case OGCONN_TYPE_TIMESTAMP_TZ:
        case OGCONN_TYPE_TIMESTAMP_LTZ:
            ret = convert_param_to_timestamp(stmt, input_data, pos);
            break;
        case OGCONN_TYPE_DECIMAL:
        case OGCONN_TYPE_NUMBER:
        case OGCONN_TYPE_NUMBER2:
            ret = convert_param_to_decimal(stmt, input_data, pos);
            break;
        default:
            return SQL_SUCCESS;
    }
    return ret;
}

static SQLRETURN bind_param_convert(statement *stmt, sql_input_data *input_data)
{
    uint32 pos = 0;
    char *param = NULL;
    SQLLEN *param_ptr = NULL;
    SQLRETURN ret;

    while (pos < input_data->param_count) {
        if (input_data->param_type == OGCONN_OUTPUT) {
            pos++;
            continue;
        }
        if (input_data->size != NULL) {
            param = input_data->param_value + input_data->param_len * pos;
            param_ptr = input_data->size + pos;
            if (*param_ptr == SQL_DEFAULT_PARAM || *param_ptr == SQL_NULL_DATA || param == NULL) {
                pos++;
                continue;
            }
        }
        ret = convert_db_type_data(stmt, input_data, pos);
        if (ret != SQL_SUCCESS) {
            return ret;
        }
        pos++;
    }
    return SQL_SUCCESS;
}

static SQLRETURN input_params_convert(statement *stmt)
{
    bilist_node_t *item_data = (&stmt->params)->head;
    SQLRETURN ret;
    sql_input_data *input_data = NULL;

    while (item_data != NULL) {
        input_data = (sql_input_data *)((char *)item_data - OFFSET_OF(sql_input_data, data_list));
        if (input_data->is_convert) {
            ret = bind_param_convert(stmt, input_data);
            if (ret != SQL_SUCCESS) {
                return ret;
            }
        }
        item_data = item_data->next;
    }
    return SQL_SUCCESS;
}

SQLRETURN ograc_execute(statement *stmt)
{
    SQLRETURN ret;
    status_t status;
    uint32 input_num = 0;
    uint32 row_count = 0;
    uint32 total_param = 0;
    uint32 execute_flag = OGCONN_STMT_NONE;

    status = ogconn_get_stmt_attr(stmt->ctconn_stmt, OGCONN_ATTR_STMT_TYPE, &execute_flag, sizeof(execute_flag), 0);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    status = ogconn_get_stmt_attr(stmt->ctconn_stmt, OGCONN_ATTR_PARAM_COUNT, &input_num, sizeof(input_num), 0);
    if (status != OGCONN_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    if (execute_flag != OGCONN_STMT_EXPLAIN && input_num != stmt->params.count) {
        stmt->conn->err_sign = 1;
        stmt->conn->error_msg = "failed to execute because there are some parameters not binded";
        return SQL_ERROR;
    }

    ret = set_sql_params(stmt);
    if (ret != SQL_SUCCESS) {
        return ret;
    }
    if (SQL_NEED_DATA == bind_param_value(stmt, NULL, SQL_FALSE)) {
        return SQL_NEED_DATA;
    }
    ret = input_params_convert(stmt);
    if (ret != SQL_SUCCESS) {
        return ret;
    }

    status = ogconn_execute(stmt->ctconn_stmt);
    if (status == OGCONN_SUCCESS) {
        stmt->stmt_flag = EXEC_STMT;
        ogconn_get_stmt_attr(stmt->ctconn_stmt, OGCONN_ATTR_OUTPARAM_COUNT, &total_param, sizeof(uint32), NULL);
        if (total_param != 0) {
            if (ogconn_fetch_outparam(stmt->ctconn_stmt, &row_count) == OG_SUCCESS) {
                ret = build_fetch_param(stmt, total_param);
            }
        }
        if (ret != SQL_SUCCESS) {
            return ret;
        }
    }
    if (status != OG_SUCCESS) {
        get_err(stmt->conn);
        return SQL_ERROR;
    }
    return SQL_SUCCESS;
}

SQLRETURN get_sql_error(char *error_msg, int error_code, SQLINTEGER *NativeError,
                        SQLCHAR *MessageText, SQLSMALLINT BufferLength)
{
    uint32 text_len = 0;
    uint32 err_info_len = (uint32)strlen(error_msg);

    if (NativeError != NULL) {
        *NativeError = error_code;
    }

    text_len = (uint32)BufferLength <= err_info_len ? (uint32)BufferLength - 1 : err_info_len;
    if (MessageText != NULL) {
        if (memcpy_s(MessageText, text_len, error_msg, text_len) != 0) {
            return SQL_ERROR;
        }
        MessageText[text_len] = '\0';
    }
    return SQL_SUCCESS;
}