#include <stdlib.h>
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>
#include <stdarg.h>
#include "database.h"
#include "cjson/cJSON.h"

/* 保存列信息的数据结构 */
typedef struct column_info
{
    char name[30];
    SQLSMALLINT nameLength;

    SQLSMALLINT dateType;
    SQLULEN columnSize;
    SQLSMALLINT decimalDigits;
    SQLSMALLINT nullable;
}COLUMN_INFO_T;

/*********************************************************************************
  * Description:  
  *       打印异常信息
  * Input:  
  *       hEnv - 环境变量
  *       hDbc - 连接句柄
  *       hStmt - 新分配的数据结构的句柄缓冲区的指针
  * Output: 
  *       无
  * Return: 
  *       无
  * Others: 
  *       无
**********************************************************************************/
static void DumpODBCLog(SQLHENV hEnv, SQLHDBC hDbc, SQLHSTMT hStmt)
{
    SQLTCHAR        szError[501];
    SQLTCHAR        szSqlState[10];
    SQLINTEGER  nNativeError;
    SQLSMALLINT nErrorMsg;

    if (hStmt != NULL)
    {
        while ( SQLError( hEnv, hDbc, hStmt, szSqlState, &nNativeError, szError, 500, &nErrorMsg ) == SQL_SUCCESS )
        {
            printf( "%s\n", szError);
        }
    }

    if (hDbc != NULL)
    {
        while ( SQLError( hEnv, hDbc, 0, szSqlState, &nNativeError, szError, 500, &nErrorMsg ) == SQL_SUCCESS )
        {
            printf( "%s\n", szError);
        }
    }

    if (hEnv != NULL)
    {
        while ( SQLError( hEnv, 0, 0, szSqlState, &nNativeError, szError, 500, &nErrorMsg ) == SQL_SUCCESS )
        {
            printf( "%s\n", szError);
        }
    }

    return;
}

/*********************************************************************************
  * Description:  
  *       连接数据库操作
  * Input:  
  *       module_id - 模块ID,每个模块需要向数据库模块设置模块ID,用于打印、统计使用
  * Output:
  *       无
  * Return:
  *       数据库句柄
  *       NULL - 连接数据库失败
  *       非NULL - 连接数据库成功
  * Others:
  *       无
**********************************************************************************/
void * connect_database (int module_id)
{
    SQLHENV henv = NULL;
    SQLHDBC hdbc = NULL;
    SQLRETURN ret;

    SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    SQLSetEnvAttr( henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0 );
    SQLAllocHandle( SQL_HANDLE_DBC, henv, &hdbc );

    SQLSetConnectAttr( hdbc, SQL_ATTR_ODBC_CURSORS, (SQLPOINTER)SQL_CUR_USE_ODBC, 0 );
	
    ret = SQLConnect( hdbc, "meng", SQL_NTS, "", SQL_NTS, "", SQL_NTS );
    if (ret != SQL_SUCCESS)
    {
        return NULL;
    }

    return hdbc;

}

/*********************************************************************************
  * Description:  
  *       释放数据库连接
  * Input:  
  *       module_id - 模块ID,每个模块需要向数据库模块设置模块ID,用于打印、统计使用
  *       db_handle - 数据库句柄
  * Output:
  *       无
  * Return:
  *       DB_RET_PARAM_NULL - 入参指针为NULL
  *       DB_RET_ERR - 释放失败
  *       DB_RET_OK - 释放成功
  * Others:
  *       无
**********************************************************************************/
int disconnect_database (int module_id, void * db_handle)
{
    SQLRETURN ret;
	
    if (NULL == db_handle)
    {
        return DB_RET_PARAM_NULL;
    }

    SQLDisconnect(db_handle);
	
    ret = SQLFreeHandle(SQL_HANDLE_DBC, db_handle);
    if (ret != SQL_SUCCESS)
    {
        return DB_RET_ERR;
    }
    
    return DB_RET_OK;
}
    
/*********************************************************************************
  * Description:  
  *       创建数据库表
  * Input:  
  *       module_id - 模块ID,每个模块需要向数据库模块设置模块ID,用于打印、统计使用
  *       db_handle - 数据库句柄
  *       table_name - 创建的表名
  *       sql_str - 创建表的SQL语句
  * Output:
  *       无
  * Return:
  *       DB_RET_PARAM_NULL - 入参指针为NULL
  *       DB_RET_ERR - 释放失败
  *       DB_RET_OK - 释放成功
  * Others:
  *       无
**********************************************************************************/
int create_database_table(int module_id,  void * db_handle,  char * table_name,  char * sql_str)
{
    SQLRETURN ret;
    SQLHSTMT hstmt;

    if ((NULL == db_handle) || (NULL == table_name) || (NULL == sql_str))
    {
        return DB_RET_PARAM_NULL;
    }

    ret = SQLAllocHandle( SQL_HANDLE_STMT, db_handle, &hstmt);
    if ((SQL_INVALID_HANDLE == ret) || (SQL_ERROR == ret))
    {
        return DB_RET_ERR;
    }

    ret = SQLExecDirect(hstmt, sql_str, SQL_NTS);

    SQLCloseCursor(hstmt);
    SQLFreeStmt(hstmt, SQL_DROP);
    
    if (ret != SQL_SUCCESS)
    {
        return DB_RET_ERR;
    }

    return DB_RET_OK;
}

/*********************************************************************************
  * Description:  
  *       对据库表执行插入、删除、更新操作
  * Input:  
  *       module_id - 模块ID,每个模块需要向数据库模块设置模块ID,用于打印、统计使用
  *       db_handle - 数据库句柄
  *       op_type - 操作类型 DB_OP_INSERT:插入数据 DB_OP_DEL:删除数据 DB_OP_UPDATE:更新数据
  *       table_name - 创建的表名
  *       sql_str - 创建表的SQL语句
  *       param_num - 扩展参数组合数量,填写0表示没有,需要防止SQL注入问题,需要使用扩展参数组合
  * Output:
  *       无
  * Return:
  *       DB_RET_PARAM_NULL - 入参指针为NULL
  *       DB_RET_PARAM_ERR - 入参错误
  *       DB_RET_ERR - 操作失败
  *       DB_RET_OK - 操作成功
  * Others:
  *       无
**********************************************************************************/
int update_database(int module_id,  void * db_handle,  int op_type,  char * table_name,  char * sql_str, int param_num, ...)
{
    SQLRETURN ret;
    SQLHSTMT hstmt;
    SQLUSMALLINT RowStatusArray[DB_ROWS];

    if ((NULL == db_handle) || (NULL == table_name) || (NULL == sql_str))
    {
        return DB_RET_PARAM_NULL;
    }    

    if ((op_type != DB_OP_INSERT) && (op_type != DB_OP_DEL) && (op_type != DB_OP_UPDATE))
    {
        return DB_RET_PARAM_ERR;
    }

    SQLAllocHandle(SQL_HANDLE_STMT, db_handle, &hstmt);
    if ((SQL_INVALID_HANDLE == ret) || (SQL_ERROR == ret))
    {
        return DB_RET_ERR;
    }

    SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_VALUES, 0);
    SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0);
    SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)DB_COLUMN, 20 );
    SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0);
    SQLSetCursorName(hstmt, "ORDERCURSOR", SQL_NTS );

    /* 是否存在扩展参数,会采用不同的处理流程 */
    if (0 == param_num)
    {
        ret = SQLExecDirect(hstmt, sql_str, SQL_NTS);
    }
    else
    {
        int i;
        int type;
        int len;
        SQLLEN ind_name = SQL_NTS;
        long long int_temp[DB_ROWS + 1]; 
        va_list ap;
        double double_temp[DB_ROWS + 1];
        

        SQLPrepare(hstmt, sql_str, SQL_NTS);
        va_start(ap, param_num);
        
        /* 执行参数绑定操作 */
        for (i = 1; i <= param_num; i++)
        {
            type = va_arg(ap, int);
            len = va_arg(ap, int);
            
            if (DB_DATA_INT_TYPE == type)
            {
                int_temp[i] = va_arg(ap, long long);
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_C_UBIGINT, len, 0, &int_temp[i], len, &ind_name);
            }
            else if (DB_DATA_STRING_TYPE == type)
            {
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR , len, 0, va_arg(ap, char *), len, &ind_name);
            }
            else if (DB_DATA_FLOAT_TYPE == type)
            {
                double_temp[i] = va_arg(ap, double);
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, len, 0, &double_temp[i], len, &ind_name);
            }
            else 
            {
			    /* 当前只支持以上三种类型,如果不是则打印报错 */   
                printf("\n type error  : %d", type); 
            }
        }

        va_end(ap);

        ret = SQLExecute(hstmt);
    }

    SQLCloseCursor(hstmt);
    SQLFreeStmt(hstmt, SQL_DROP);

    if (ret != SQL_SUCCESS)
    {  
        return DB_RET_ERR;
    }

    return DB_RET_OK;
}

/*********************************************************************************
  * Description:  
  *       根据指定信息查找数据库
  * Input:  
  *       module_id - 模块ID,每个模块需要向数据库模块设置模块ID,用于打印、统计使用
  *       db_handle - 数据库句柄
  *       table_name - 创建的表名
  *       sql_str - 创建表的SQL语句
  *       begin_num - 从第几条查询结果开始返回
  *       need_num - 应用需要返回的结果条数, 如果need_num = 0,表示返回所有数据
  *       param_num - 扩展参数组合数量,填写0表示没有,需要防止SQL注入问题,需要使用扩展参数组合
  * Output:
  *       return_num - 实际返回的结果条数
  * Return:
  *       NULL - 操作失败      
  *       非NULL - 返回CJSON格式的查询结果
  * Others:
  *       无
**********************************************************************************/
void * select_datebase_by_number(int module_id,  void * db_handle, char * table_name,  char * sql_str, int begin_num, int need_num, int *  return_num, int param_num, ...)
{
    SQLRETURN ret;
    SQLHSTMT hstmt;
    SQLUSMALLINT RowStatusArray[DB_ROWS];
    SQLSMALLINT nameLength = 0;
    SQLSMALLINT dateType = 0;
    SQLULEN  columnSize = 0;
    SQLSMALLINT decimalDigits = 0;
    SQLSMALLINT nullable = 0;
    SQLRETURN  retcode = 0;
    int i = 0; 
    int j = 0;
    COLUMN_INFO_T db_column_info[20];
    int column_nmuber = 0;
    SQLCHAR value[DB_ROWS][DB_COLUMN][DB_ROWS_MAX_LEN];
    int ret_num = 0;
    SQLUINTEGER FetchOrientation;
    SQLLEN  cbValue[DB_ROWS][DB_ROWS_MAX_LEN];
    cJSON *json = NULL;
    char *json_data = NULL;
    cJSON *array = NULL;
    cJSON * obj = NULL;

    if ((NULL == db_handle) || (NULL == table_name) || (NULL == sql_str))
    {
        return NULL;
    }

    if ((begin_num <= 0) || (need_num < 0))
    {
        return NULL;    
    }

    ret = SQLAllocHandle(SQL_HANDLE_STMT, db_handle, &hstmt);
    if ((SQL_INVALID_HANDLE == ret) || (SQL_ERROR == ret))
    {
        return NULL;
    }

    SQLSetStmtAttr( hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_VALUES, 0 );
    SQLSetStmtAttr( hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0 );
    SQLSetStmtAttr( hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)DB_COLUMN, 20 );
    SQLSetStmtAttr( hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0 );
    SQLSetCursorName( hstmt, "ORDERCURSOR", SQL_NTS );

    SQLPrepare( hstmt, sql_str, SQL_NTS );

    column_nmuber = 1;

    /* 解析查询结果中包含的列信息 */
    do
    {

        retcode = SQLDescribeCol(hstmt, column_nmuber, db_column_info[column_nmuber].name, sizeof(db_column_info[column_nmuber].name), &db_column_info[column_nmuber].nameLength, 
                                    &db_column_info[column_nmuber].dateType, &db_column_info[column_nmuber].columnSize, 
                                    &db_column_info[column_nmuber].decimalDigits, &db_column_info[column_nmuber].nullable); 
        if (retcode != SQL_SUCCESS)
        {
            column_nmuber--;    
            break;
        }

        column_nmuber++;
    }while(1);

    /* 如果列为0,返回查询结果 */
    if (0 == column_nmuber)
    {
        SQLCloseCursor(hstmt);
        SQLFreeStmt(hstmt, SQL_DROP);        

        return NULL;
    }

    *return_num = 0;
	
    /* 是否存在扩展参数,会采用不同的处理流程 */
    if (0 == param_num)
    {
        ret = SQLExecDirect(hstmt, sql_str, SQL_NTS);
    }
    else
    {
        int i;
        int type;
        int len;
        SQLLEN ind_name = SQL_NTS;
        long long int_temp[DB_ROWS + 1];
        va_list ap;
        double double_temp[DB_ROWS + 1];

        SQLPrepare(hstmt, sql_str, SQL_NTS);
        
        va_start(ap, param_num);
        
        for (i = 1; i <= param_num; i++)
        {
            type = va_arg(ap, int);
            len = va_arg(ap, int);

            if (DB_DATA_INT_TYPE == type)
            {
                int_temp[i] = va_arg(ap, long long);
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_C_UBIGINT, len, 0, &int_temp[i], len, &ind_name);
            }
            else if (DB_DATA_STRING_TYPE == type)
            {
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR , len, 0, va_arg(ap, char*), len, &ind_name);
            }
            else if (DB_DATA_FLOAT_TYPE == type)
            {
                double_temp[i] = va_arg(ap, double);
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, len, 0, &double_temp[i], len, &ind_name);
            }
            else
            {
			    /* 当前只支持以上三种类型,如果不是则打印报错 */   
                printf("\n type error  : %d", type);                 
            }
        }

        va_end(ap);

        ret = SQLExecute(hstmt);
    }    

    if (ret != SQL_SUCCESS)
    {
        SQLCloseCursor(hstmt);
        SQLFreeStmt(hstmt, SQL_DROP);
        return NULL;
    }

    json = cJSON_CreateObject();
	if (NULL == json)
    {
        SQLCloseCursor(hstmt);
        SQLFreeStmt(hstmt, SQL_DROP);
        return NULL;       	
    }
	
    /* 将应用程序数据缓冲区绑定到结果集中的列 */
    for (i = 1; i <= column_nmuber; i++)
    {   
        if (SQL_BIGINT == db_column_info[i].dateType)
        {
            SQLBindCol(hstmt, i, SQL_C_UBIGINT, value[i][0], DB_ROWS_MAX_LEN, cbValue[i]);
        }
        else if (SQL_CHAR == db_column_info[i].dateType)
        {
            SQLBindCol(hstmt, i, db_column_info[i].dateType, value[i][0], DB_ROWS_MAX_LEN, cbValue[i]);
        }
        else if (SQL_DOUBLE == db_column_info[i].dateType)
        {
            SQLBindCol(hstmt, i, db_column_info[i].dateType, value[i][0], 0, cbValue[i]);
        }
        else
        {
            /* 不支持的类型 */
        }
        
    }
    
    cJSON_AddItemToObject(json, "data", array = cJSON_CreateArray());
	
    /* 查询结果 */
    FetchOrientation = SQL_FETCH_RELATIVE;
	
	/* need_num为0,表示一次返回所有找到的信息 */
	if (0 == need_num)
    {
	    while(1)
        {
            ret = SQLFetchScroll(hstmt, FetchOrientation, begin_num);
            FetchOrientation = SQL_FETCH_NEXT;
			
		    /* 查找失败,表示已经返回所有找到的信息 */
            if (ret != SQL_SUCCESS)
            {
                break;
            }
   
            ret_num++;
 
            cJSON_AddItemToArray(array, obj = cJSON_CreateObject());

            for (j = 1; j <= column_nmuber; j++)
            {
                if (SQL_BIGINT == db_column_info[j].dateType)
                {
                    cJSON_AddNumberToObject(obj, db_column_info[j].name, *((long long *)&(value[j][0])));
                }
                else if (SQL_CHAR == db_column_info[j].dateType)
                {
                    cJSON_AddStringToObject(obj, db_column_info[j].name , value[j][0]);
                }
                else if (SQL_DOUBLE == db_column_info[j].dateType)
                {
                    cJSON_AddNumberToObject(obj, db_column_info[j].name , *((double *)&(value[j][0])));
                }
                else
                {
                    /* 不支持类型,后续增加统计和打印 */
                }

            }
        }
    }
	else
	{
	    for (i = 1; i <= need_num; i++)
        {
            ret = SQLFetchScroll(hstmt, FetchOrientation, begin_num);
            FetchOrientation = SQL_FETCH_NEXT;
			
		    /* 查找失败,表示已经返回所有找到的信息 */
            if (ret != SQL_SUCCESS)
            {
                break;
            }
   
            ret_num++;
 
            cJSON_AddItemToArray(array, obj = cJSON_CreateObject());

            for (j = 1; j <= column_nmuber; j++)
            {
                if (SQL_BIGINT == db_column_info[j].dateType)
                {
                    cJSON_AddNumberToObject(obj, db_column_info[j].name, *((long long *)&(value[j][0])));
                }
                else if (SQL_CHAR == db_column_info[j].dateType)
                {
                    cJSON_AddStringToObject(obj, db_column_info[j].name , value[j][0]);
                }
                else if (SQL_DOUBLE == db_column_info[j].dateType)
                {
                    cJSON_AddNumberToObject(obj, db_column_info[j].name , *((double *)&(value[j][0])));
                }
                else
                {
                    /* 不支持类型,后续增加统计和打印 */
                }

            }
        }		
	}


    SQLCloseCursor(hstmt);
    SQLFreeStmt(hstmt, SQL_DROP);   
 
    *return_num = ret_num;
   
    if (ret_num != 0)
    {         
        json_data = cJSON_Print(json);  
    }

    cJSON_Delete(json);
	
    return json_data;
}

/*********************************************************************************
  * Description:  
  *       释放查询接口申请的内存
  * Input:  
  *       module_id - 模块ID,每个模块需要向数据库模块设置模块ID,用于打印、统计使用
  *       table_name - 创建的表名
  *       memory_ptr - 待释放内存指针
  * Output:
  *       无
  * Return:
  *       DB_RET_PARAM_NULL - 入参指针为NULL
  *       DB_RET_ERR - 操作失败
  *       DB_RET_OK - 操作成功
  * Others:
  *       无
**********************************************************************************/
int free_database_memory(int module_id,  char * table_name,  void * memory_ptr)
{
    if ((NULL == table_name) && (NULL == memory_ptr))
    {
        return DB_RET_PARAM_NULL;
    }
	
    free(memory_ptr);
	
    return DB_RET_OK;
}
/*********************************************************************************
  * Description:  
  *       返回根据指定信息查找数据库的结果的条目数
  * Input:  
  *       module_id - 模块ID,每个模块需要向数据库模块设置模块ID,用于打印、统计使用
  *       db_handle - 数据库句柄
  *       table_name - 创建的表名
  *       sql_str - 创建表的SQL语句
  *       param_num - 扩展参数组合数量,填写0表示没有,需要防止SQL注入问题,需要使用扩展参数组合
  * Output:
  *       ret_num - 实际返回的结果条数
  * Return:
  *       DB_RET_PARAM_NULL - 入参指针为NULL
  *       DB_RET_ERR - 操作失败
  *       DB_RET_OK - 操作成功
  * Others:
  *       无
**********************************************************************************/
int  get_select_datebase_number(int module_id,  void * db_handle, char * table_name,  char * sql_str, int * ret_num, int param_num, ...)
{
    SQLRETURN ret;
    SQLHSTMT hstmt;
    SQLUSMALLINT RowStatusArray[DB_ROWS];
    SQLSMALLINT nameLength = 0;
    SQLSMALLINT dateType = 0;
    SQLULEN  columnSize = 0;
    SQLSMALLINT decimalDigits = 0;
    SQLSMALLINT nullable = 0;
    SQLRETURN  retcode = 0;
    int i = 0; 
    int j = 0;
    COLUMN_INFO_T db_column_info[20];
    int column_nmuber = 0;
    SQLCHAR value[DB_ROWS][DB_COLUMN][DB_ROWS_MAX_LEN];
    int returnNum = 0;
    SQLUINTEGER FetchOrientation;
    SQLLEN  cbValue[DB_ROWS_MAX_LEN][DB_ROWS];
    SQLLEN count;

    if ((NULL == db_handle) || (NULL == table_name) || (NULL == sql_str))
    {
        return DB_RET_PARAM_NULL;
    }
	
    ret = SQLAllocHandle( SQL_HANDLE_STMT, db_handle, &hstmt);
    if ((SQL_INVALID_HANDLE == ret) || (SQL_ERROR == ret))
    {
        return DB_RET_ERR;
    }

    *ret_num = 0;
	
    SQLSetStmtAttr( hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_VALUES, 0 );
    SQLSetStmtAttr( hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER)SQL_CURSOR_STATIC, 0 );
    SQLSetStmtAttr( hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)DB_COLUMN, 20 );
    SQLSetStmtAttr( hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0 );
    SQLSetCursorName( hstmt, "ORDERCURSOR", SQL_NTS );

    SQLPrepare( hstmt, sql_str, SQL_NTS );

    if (0 == param_num)
    {
        ret = SQLExecDirect(hstmt, sql_str, SQL_NTS);
    }
    else
    {
        int i;
        int type;
        int len;
        SQLLEN ind_name = SQL_NTS;
        long long int_temp[DB_ROWS + 1];
        va_list ap;
        double double_temp[DB_ROWS + 1];

        SQLPrepare(hstmt, sql_str, SQL_NTS);
        
        va_start(ap, param_num);
        
        for (i = 1; i <= param_num; i++)
        {
            type = va_arg(ap, int);
            len = va_arg(ap, int);

            if (DB_DATA_INT_TYPE == type)
            {
                int_temp[i] = va_arg(ap, long long);
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_C_UBIGINT, len, 0, &int_temp[i], len, &ind_name);
            }
            else if (DB_DATA_STRING_TYPE == type)
            {
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR , len, 0, va_arg(ap, char*), len, &ind_name);
            }
            else if (DB_DATA_FLOAT_TYPE == type)
            {
                double_temp[i] = va_arg(ap, double);
                ret = SQLBindParameter(hstmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, len, 0, &double_temp[i], len, &ind_name);
            }
            else
            {

            }
        }

        va_end(ap);
		
        ret = SQLExecute(hstmt);
    }

    if (ret != SQL_SUCCESS)
    {
        SQLCloseCursor(hstmt);
        SQLFreeStmt(hstmt, SQL_DROP);
        return DB_RET_ERR;
    }
	
    SQLRowCount( hstmt, &count);

    *ret_num = count;

    SQLCloseCursor(hstmt);
    SQLFreeStmt(hstmt, SQL_DROP);

    return DB_RET_OK;
}