/*
 * Copyright (C) 2008 Search Solution Corporation. All rights reserved by Search Solution.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of the <ORGANIZATION> nor the names of its contributors
 *   may be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 */

#define _CRT_SECURE_NO_WARNINGS

/************************************************************************
* IMPORTED SYSTEM HEADER FILES
************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "php_globals.h"
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"

#include "zend_exceptions.h"

#ifdef PHP_WIN32
#include <winsock.h>
#endif

/************************************************************************
* OTHER IMPORTED HEADER FILES
************************************************************************/

#include "php_cubrid.h"
#include "php_cubrid_version.h"
#include <cas_cci.h>
#include <fcntl.h>

/************************************************************************
* PRIVATE DEFINITIONS
************************************************************************/

#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif

#if PHP_MINOR_VERSION < 3
#define zend_parse_parameters_none() zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "")
#endif

#define CUBRID_LOB_READ_BUF_SIZE    8192

/* EXECUTE */
#define CUBRID_INCLUDE_OID	    1
#define CUBRID_ASYNC		    2
#define CUBRID_EXEC_QUERY_ALL       4

/* ARRAY */
typedef enum
{
    CUBRID_NUM = 1,
    CUBRID_ASSOC = 2,
    CUBRID_BOTH = CUBRID_NUM | CUBRID_ASSOC,
    CUBRID_OBJECT = 4,
    CUBRID_LOB = 8,
} T_CUBRID_ARRAY_TYPE;

/* CURSOR ORIGIN */
typedef enum
{
    CUBRID_CURSOR_FIRST = CCI_CURSOR_FIRST,
    CUBRID_CURSOR_CURRENT = CCI_CURSOR_CURRENT,
    CUBRID_CURSOR_LAST = CCI_CURSOR_LAST,
} T_CUBRID_CURSOR_ORIGIN;

/* SCHEMA */
#define CUBRID_SCH_CLASS		CCI_SCH_CLASS
#define CUBRID_SCH_VCLASS		CCI_SCH_VCLASS
#define CUBRID_SCH_QUERY_SPEC		CCI_SCH_QUERY_SPEC
#define CUBRID_SCH_ATTRIBUTE		CCI_SCH_ATTRIBUTE
#define CUBRID_SCH_CLASS_ATTRIBUTE	CCI_SCH_CLASS_ATTRIBUTE
#define CUBRID_SCH_METHOD		CCI_SCH_METHOD
#define CUBRID_SCH_CLASS_METHOD		CCI_SCH_CLASS_METHOD
#define CUBRID_SCH_METHOD_FILE		CCI_SCH_METHOD_FILE
#define CUBRID_SCH_SUPERCLASS		CCI_SCH_SUPERCLASS
#define CUBRID_SCH_SUBCLASS		CCI_SCH_SUBCLASS
#define CUBRID_SCH_CONSTRAINT		CCI_SCH_CONSTRAINT
#define CUBRID_SCH_TRIGGER		CCI_SCH_TRIGGER
#define CUBRID_SCH_CLASS_PRIVILEGE	CCI_SCH_CLASS_PRIVILEGE
#define CUBRID_SCH_ATTR_PRIVILEGE	CCI_SCH_ATTR_PRIVILEGE
#define CUBRID_SCH_DIRECT_SUPER_CLASS	CCI_SCH_DIRECT_SUPER_CLASS
#define CUBRID_SCH_PRIMARY_KEY		CCI_SCH_PRIMARY_KEY
#define CUBRID_SCH_IMPORTED_KEYS        CCI_SCH_IMPORTED_KEYS
#define CUBRID_SCH_EXPORTED_KEYS        CCI_SCH_EXPORTED_KEYS
#define CUBRID_SCH_CROSS_REFERENCE      CCI_SCH_CROSS_REFERENCE

/* ERROR FACILITY */
typedef enum
{
    CUBRID_FACILITY_DBMS = 1,
    CUBRID_FACILITY_CAS,
    CUBRID_FACILITY_CCI,
    CUBRID_FACILITY_CLIENT,
} T_FACILITY_CODE;

/* error codes */
#define CUBRID_ER_INVALID_SQL_TYPE 		-30002
#define CUBRID_ER_CANNOT_GET_COLUMN_INFO 	-30003
#define CUBRID_ER_INVALID_PARAM 		-30006
#define CUBRID_ER_NOT_SUPPORTED_TYPE 		-30008
#define CUBRID_ER_TRANSFER_FAIL 		-30011
#define CUBRID_ER_PHP				-30012
#define CUBRID_ER_PARAM_UNBIND                  -30015
#define CUBRID_ER_INVALID_PARAM_TYPE            -30022
#define CUBRID_ER_END                           -31000
/* CAUTION! Also add the error message string to db_error[] */

/* Maximum length for the Cubrid data types. 
 *
 * The max len of LOB is the max file size creatable in an external storage, 
 * so we ca't give the max len of LOB type, just use 1G. Please ignore it. 
 */
#define MAX_CUBRID_CHAR_LEN   1073741823
#define MAX_LEN_INTEGER	      (10 + 1)
#define MAX_LEN_SMALLINT      (5 + 1)
#define MAX_LEN_BIGINT	      (19 + 1)
#define MAX_LEN_FLOAT	      (14 + 1)
#define MAX_LEN_DOUBLE	      (28 + 1)
#define MAX_LEN_MONETARY      (28 + 2)
#define MAX_LEN_DATE	      10
#define MAX_LEN_TIME	      8
#define MAX_LEN_TIMESTAMP     23
#define MAX_LEN_DATETIME      MAX_LEN_TIMESTAMP
#define MAX_LEN_OBJECT	      MAX_CUBRID_CHAR_LEN
#define MAX_LEN_SET	      MAX_CUBRID_CHAR_LEN
#define MAX_LEN_MULTISET      MAX_CUBRID_CHAR_LEN
#define MAX_LEN_SEQUENCE      MAX_CUBRID_CHAR_LEN
#define MAX_LEN_LOB           MAX_CUBRID_CHAR_LEN

/* Max Cubrid supported charsets */
#define MAX_DB_CHARSETS 6

/* In CCI, CCI_U_TYPE_UNKNOWN == CCI_U_TYPE_NULL == 0, 
 * so use below macro to denote the UNKNOWN type */
#define U_TYPE_UNKNOWN -1
static struct cubrid_type2name_st{
  int   type;
  char* name;
} cubrid_type2name[] = {
    {CCI_U_TYPE_UNKNOWN ,      "unknown"},
    {CCI_U_TYPE_CHAR,     "char"},
    {CCI_U_TYPE_STRING,     "varchar"},
    {CCI_U_TYPE_NCHAR,     "nchar"},
    {CCI_U_TYPE_VARNCHAR,     "varnchar"},
    {CCI_U_TYPE_BIT,     "bit"},
    {CCI_U_TYPE_VARBIT,     "varbit"},
    {CCI_U_TYPE_NUMERIC,     "numeric"},
    {CCI_U_TYPE_INT,     "integer"},
    {CCI_U_TYPE_SHORT,     "smallint"},
    {CCI_U_TYPE_MONETARY,     "monetary"},
    {CCI_U_TYPE_FLOAT,     "float"},
    {CCI_U_TYPE_DOUBLE,     "double"},
    {CCI_U_TYPE_DATE,     "date"},
    {CCI_U_TYPE_TIME,     "time"},
    {CCI_U_TYPE_TIMESTAMP,     "timestamp"},
    {CCI_U_TYPE_SET,     "set"},
    {CCI_U_TYPE_MULTISET,     "multiset"},
    {CCI_U_TYPE_SEQUENCE,     "sequence"},
    {CCI_U_TYPE_OBJECT,     "object"},
    {CCI_U_TYPE_BIGINT,     "bigint"},
    {CCI_U_TYPE_DATETIME,     "datetime"},
    {CCI_U_TYPE_BLOB,     "blob"},
    {CCI_U_TYPE_CLOB,     "clob"},
    {CCI_U_TYPE_ENUM,     "enum"},
};

typedef struct
{
    int err_code;
    char *err_msg;
} DB_ERROR_INFO;

/* Define addtion error info */
static const DB_ERROR_INFO db_error[] = {
    {CUBRID_ER_INVALID_SQL_TYPE, "Invalid API call"},
    {CUBRID_ER_CANNOT_GET_COLUMN_INFO, "Cannot get column info"},
    {CUBRID_ER_INVALID_PARAM, "Invalid parameter"},
    {CUBRID_ER_NOT_SUPPORTED_TYPE, "Invalid type"},
    {CUBRID_ER_TRANSFER_FAIL, "Lob transfering error"},
    {CUBRID_ER_PHP, "PHP error"},
    {CUBRID_ER_PARAM_UNBIND, "Some parameter not binded"},
    {CUBRID_ER_INVALID_PARAM_TYPE, "Invalid db parameter type"},
};

typedef struct
{
    const char *charset_name;
    const char *charset_desc;
    const char *space_char;
    int charset_id;
    int default_collation;
    int space_size;
} DB_CHARSET;

/* Define Cubrid supported charsets, 
 * now we only use charset_name, so just set space_char to empty */
static const DB_CHARSET db_charsets[] = {
    {"ascii", "US English charset - ASCII encoding", "", 0, 0, 1},
    {"raw-bits", "Uninterpreted bits - Raw encoding", "", 1, 0, 1},
    {"raw-bytes", "Uninterpreted bytes - Raw encoding", "", 2, 0, 1},
    {"iso8859-1", "Latin 1 charset - ISO 8859 encoding", "", 3, 0, 1},
    {"ksc-euc", "KSC 5601 1990 charset - EUC encoding", "", 4, 0, 2},
    {"utf-8", "UNICODE charset - UTF-8 encoding", " ", 5, 0, 1},
    {"", "Unknown encoding", "", -1, 0, 0}
};

typedef struct
{
    char *type_name;
    T_CCI_U_TYPE cubrid_u_type;
    int len;
} DB_TYPE_INFO;

/* Define Cubrid supported date types */
static const DB_TYPE_INFO db_type_info[] = {
    {"NULL", CCI_U_TYPE_NULL, 0},
    {"UNKNOWN", U_TYPE_UNKNOWN, MAX_LEN_OBJECT},

    {"CHAR", CCI_U_TYPE_CHAR, -1},
    {"STRING", CCI_U_TYPE_STRING, -1},
    {"NCHAR", CCI_U_TYPE_NCHAR, -1},
    {"VARNCHAR", CCI_U_TYPE_VARNCHAR, -1},

    {"BIT", CCI_U_TYPE_BIT, -1},
    {"VARBIT", CCI_U_TYPE_VARBIT, -1},

    {"NUMERIC", CCI_U_TYPE_NUMERIC, -1},
    {"NUMBER", CCI_U_TYPE_NUMERIC, -1},
    {"INT", CCI_U_TYPE_INT, MAX_LEN_INTEGER},
    {"SHORT", CCI_U_TYPE_SHORT, MAX_LEN_SMALLINT},
    {"BIGINT", CCI_U_TYPE_BIGINT, MAX_LEN_BIGINT},
    {"MONETARY", CCI_U_TYPE_MONETARY, MAX_LEN_MONETARY},

    {"FLOAT", CCI_U_TYPE_FLOAT, MAX_LEN_FLOAT},
    {"DOUBLE", CCI_U_TYPE_DOUBLE, MAX_LEN_DOUBLE},

    {"DATE", CCI_U_TYPE_DATE, MAX_LEN_DATE},
    {"TIME", CCI_U_TYPE_TIME, MAX_LEN_TIME},
    {"DATETIME", CCI_U_TYPE_DATETIME, MAX_LEN_DATETIME},
    {"TIMESTAMP", CCI_U_TYPE_TIMESTAMP, MAX_LEN_TIMESTAMP},

    {"SET", CCI_U_TYPE_SET, MAX_LEN_SET},
    {"MULTISET", CCI_U_TYPE_MULTISET, MAX_LEN_MULTISET},
    {"SEQUENCE", CCI_U_TYPE_SEQUENCE, MAX_LEN_SEQUENCE},
    {"RESULTSET", CCI_U_TYPE_RESULTSET, -1},

    {"OBJECT", CCI_U_TYPE_OBJECT, MAX_LEN_OBJECT},
    {"BLOB", CCI_U_TYPE_BLOB, MAX_LEN_LOB},
    {"CLOB", CCI_U_TYPE_CLOB, MAX_LEN_LOB},
    {"ENUM", CCI_U_TYPE_ENUM, -1},
};

/* DB parameters */
#define CUBRID_PARAM_ISOLATION_LEVEL    CCI_PARAM_ISOLATION_LEVEL
#define CUBRID_PARAM_LOCK_TIMEOUT       CCI_PARAM_LOCK_TIMEOUT

#define CUBRID_AUTOCOMMIT_FALSE    CCI_AUTOCOMMIT_FALSE
#define CUBRID_AUTOCOMMIT_TRUE     CCI_AUTOCOMMIT_TRUE

/* Define CUBRID DB parameters */
typedef struct
{
    T_CCI_DB_PARAM parameter_id;
    const char *parameter_name;
} DB_PARAMETER;

static const DB_PARAMETER db_parameters[] = {
    {CCI_PARAM_ISOLATION_LEVEL, "PARAM_ISOLATION_LEVEL"},
    {CCI_PARAM_LOCK_TIMEOUT, "PARAM_LOCK_TIMEOUT"},
    {CCI_PARAM_MAX_STRING_LENGTH, "PARAM_MAX_STRING_LENGTH"},
    {CCI_PARAM_AUTO_COMMIT, "PARAM_AUTO_COMMIT"}
};

/************************************************************************
* PRIVATE TYPE DEFINITIONS
************************************************************************/

#ifdef PHP_WIN32
typedef __int64 php_cubrid_int64_t;
typedef unsigned __int64 php_cubrid_uint64_t;

#define stricmp(s1, s2) _stricmp(s1, s2)
#define strnicmp(s1, s2, n) _strnicmp(s1, s2, n)
#define open(fd, mode, flag) _open(fd, mode, flag)
#define write(fd, buf, size) _write(fd, buf, size)
#define read(fd, buf, size) _read(fd, buf, size)
#define close(fd) _close(fd)
#define unlink(file_name) _unlink(file_name)
#define strtoll(nptr, endptr, base) _strtoi64(nptr, endptr, base)

#else
typedef long long int php_cubrid_int64_t;
typedef unsigned long long int php_cubrid_uint64_t;
#endif

typedef void *T_CCI_LOB;

typedef struct
{
    T_CCI_LOB lob;
    T_CCI_U_TYPE type;
    php_cubrid_int64_t size;
} T_CUBRID_LOB;

typedef struct cubrid_request T_CUBRID_REQUEST;
typedef struct cubrid_lob2 T_CUBRID_LOB2;

typedef struct linked_list_node {
    void *data;
    struct linked_list_node *next;
} LINKED_LIST_NODE;

typedef struct linked_list {
    LINKED_LIST_NODE *head;
    LINKED_LIST_NODE *tail;
} LINKED_LIST;

typedef struct
{
    T_CUBRID_ERROR recent_error;
    int handle;
    int persistent;

    int affected_rows;
    T_CCI_CUBRID_STMT sql_type;

    LINKED_LIST *unclosed_requests;
} T_CUBRID_CONNECT;

struct cubrid_request
{
    T_CUBRID_CONNECT *conn;

    int handle;
    int col_count;
    int row_count;
    int l_prepare;
    int bind_num;
    int *field_lengths;
    short *l_bind;
    int fetch_field_auto_index;
    T_CCI_CUBRID_STMT sql_type;
    T_CCI_COL_INFO *col_info;
    T_CUBRID_LOB *lob;

    T_CUBRID_LOB2 **lob2_list;

};

struct cubrid_lob2
{
    T_CUBRID_CONNECT *connect;
    T_CUBRID_REQUEST *request;

    T_CCI_LOB lob;
    T_CCI_U_TYPE type;
    php_cubrid_int64_t pos;
};

/************************************************************************
* PRIVATE FUNCTION PROTOTYPES
************************************************************************/

static void php_cubrid_init_globals(zend_cubrid_globals *cubrid_globals);

static void close_cubrid_pconnect(zend_rsrc_list_entry *rsrc TSRMLS_DC);
static void close_cubrid_connect(zend_rsrc_list_entry *rsrc TSRMLS_DC);
static void close_cubrid_request(zend_rsrc_list_entry *rsrc TSRMLS_DC);
static void close_cubrid_lob(zend_rsrc_list_entry *rsrc TSRMLS_DC);
static void close_cubrid_lob2(zend_rsrc_list_entry *rsrc TSRMLS_DC);

static void close_cubrid_request_internal(T_CUBRID_REQUEST *req);
static void close_cubrid_lob_internal(T_CUBRID_LOB *lob);

static int init_error(void);
static int set_error(T_FACILITY_CODE facility, int code, char *msg, ...);
static int init_error_link(T_CUBRID_CONNECT *conn);
static int set_error_link(T_CUBRID_CONNECT *conn, int code, char *msg, ...);
static int get_error_msg(int err_code, char *buf, int buf_size);
static int handle_error(int err_code, T_CCI_ERROR *error, T_CUBRID_CONNECT *conn);

static void php_cubrid_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent);
static void php_cubrid_do_connect_with_url(INTERNAL_FUNCTION_PARAMETERS, int persistent);

static T_CUBRID_CONNECT *new_cubrid_connect(int persistent);
static T_CUBRID_REQUEST *new_cubrid_request(void);
static T_CUBRID_LOB *new_cubrid_lob(void);
static T_CUBRID_LOB2 *new_cubrid_lob2(void);
static void register_cubrid_request(T_CUBRID_CONNECT *conn, T_CUBRID_REQUEST *req);

static void php_cubrid_set_default_conn(int id TSRMLS_DC);
static void php_cubrid_set_default_req(int id TSRMLS_DC);
static void php_cubrid_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, long type, int is_object);

static int fetch_a_row(zval *arg, T_CUBRID_CONNECT *connect, int req_handle, T_CUBRID_REQUEST *reqeust, int type TSRMLS_DC);
static int type2str(T_CCI_COL_INFO *column_info, char *type_name, int type_name_len);

static int cubrid_make_set(HashTable *ht, T_CCI_SET *set);
static int cubrid_add_index_array(zval *arg, uint index, T_CCI_SET in_set TSRMLS_DC);
static int cubrid_add_assoc_array(zval *arg, char *key, T_CCI_SET in_set TSRMLS_DC);
static int cubrid_array_destroy(HashTable *ht ZEND_FILE_LINE_DC);

static int numeric_type(T_CCI_U_TYPE type);
static int get_cubrid_u_type_by_name(const char *type_name);
static int get_cubrid_u_type_len(T_CCI_U_TYPE type);

static int cubrid_lob_new(int con_h_id, T_CCI_LOB *lob, T_CCI_U_TYPE type, T_CCI_ERROR *err_buf);
static php_cubrid_int64_t cubrid_lob_size(T_CCI_LOB lob, T_CCI_U_TYPE type);
static int cubrid_lob_write(int con_h_id, T_CCI_LOB lob, T_CCI_U_TYPE type, php_cubrid_int64_t start_pos, int length, const char *buf, T_CCI_ERROR *err_buf);
static int cubrid_lob_read(int con_h_id, T_CCI_LOB lob, T_CCI_U_TYPE type, php_cubrid_int64_t start_pos, int length, char *buf, T_CCI_ERROR *err_buf);
static int cubrid_lob_free(T_CCI_LOB lob, T_CCI_U_TYPE type);

static char *php_cubrid_int64_to_str(php_cubrid_int64_t i64 TSRMLS_DC);

static int cubrid_get_charset_internal(int conn, T_CCI_ERROR *error);

static void linked_list_append(LINKED_LIST *list, void *data);
static void linked_list_delete(LINKED_LIST *list, void *data);

/************************************************************************
* INTERFACE VARIABLES
************************************************************************/

char *cci_client_name = "PHP";

ZEND_BEGIN_ARG_INFO(arginfo_cubrid_version, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_connect, 0, 0, 3)
    ZEND_ARG_INFO(0, host)
    ZEND_ARG_INFO(0, port)
    ZEND_ARG_INFO(0, dbname)
    ZEND_ARG_INFO(0, userid)
    ZEND_ARG_INFO(0, passwd)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_pconnect, 0, 0, 3)
    ZEND_ARG_INFO(0, host)
    ZEND_ARG_INFO(0, port)
    ZEND_ARG_INFO(0, dbname)
    ZEND_ARG_INFO(0, userid)
    ZEND_ARG_INFO(0, passwd)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_connect_with_url, 0, 0, 1)
    ZEND_ARG_INFO(0, url)
    ZEND_ARG_INFO(0, userid)
    ZEND_ARG_INFO(0, passwd)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_pconnect_with_url, 0, 0, 1)
    ZEND_ARG_INFO(0, url)
    ZEND_ARG_INFO(0, userid)
    ZEND_ARG_INFO(0, passwd)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_close, 0, 0, 0)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_prepare, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, prepare_stmt)
    ZEND_ARG_INFO(0, option)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_bind, 0, 0, 3)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, bind_index)
    ZEND_ARG_INFO(0, bind_value)
    ZEND_ARG_INFO(0, bind_value_type)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_execute, 0, 0, 1)
    ZEND_ARG_INFO(0, id)
    ZEND_ARG_INFO(0, sql_stmt)
    ZEND_ARG_INFO(0, option)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_batch_execute, 0, 0, 1)
    ZEND_ARG_INFO(0, id)
    ZEND_ARG_INFO(0, sql_stmt)
    ZEND_ARG_INFO(0, option)
ZEND_END_ARG_INFO()


ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_next_result, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_affected_rows, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_close_request, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_fetch, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_current_oid, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_column_types, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_column_names, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_move_cursor, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
    ZEND_ARG_INFO(0, origin)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_num_rows, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_num_cols, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_get, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_put, 0, 0, 3)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr)
    ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_drop, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_is_instance, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lock_read, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lock_write, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_get_class_name, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_schema, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, schema_type)
    ZEND_ARG_INFO(0, class_name)
    ZEND_ARG_INFO(0, attr_name)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_col_size, 0, 0, 3)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr_name)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_col_get, 0, 0, 3)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr_name)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_set_add, 0, 0, 4)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr_name)
    ZEND_ARG_INFO(0, set_element)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_set_drop, 0, 0, 4)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr_name)
    ZEND_ARG_INFO(0, set_element)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_seq_insert, 0, 0, 5)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr_name)
    ZEND_ARG_INFO(0, index)
    ZEND_ARG_INFO(0, set_element)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_seq_put, 0, 0, 5)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr_name)
    ZEND_ARG_INFO(0, index)
    ZEND_ARG_INFO(0, set_element)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_seq_drop, 0, 0, 4)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, oid)
    ZEND_ARG_INFO(0, attr_name)
    ZEND_ARG_INFO(0, index)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_get_autocommit, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_set_autocommit, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, mode)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_commit, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_rollback, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_cubrid_error_msg, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_cubrid_error_code, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_cubrid_error_code_facility, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_cubrid_errno, 0)
    ZEND_ARG_INFO(0, conn)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_cubrid_error, 0)
    ZEND_ARG_INFO(0, conn)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_field_name, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_field_table, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_field_type, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_field_flags, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_data_seek, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_fetch_array, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_fetch_assoc, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_fetch_row, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_fetch_field, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_num_fields, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_free_result, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_fetch_lengths, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_fetch_object, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, class_name)
    ZEND_ARG_INFO(0, params)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_field_seek, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_field_len, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_result, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, row)
    ZEND_ARG_INFO(0, field)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_unbuffered_query, 0, 0, 1)
    ZEND_ARG_INFO(0, query)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_query, 0, 0, 1)
    ZEND_ARG_INFO(0, query)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_get_charset, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_client_encoding, 0, 0, 0)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO(arginfo_cubrid_get_client_info, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_get_server_info, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_real_escape_string, 0, 0, 1)
    ZEND_ARG_INFO(0, unescaped_string)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_get_db_parameter, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_set_db_parameter, 0, 0, 3)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, param_type)
    ZEND_ARG_INFO(0, param_value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_list_dbs, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_db_name, 0, 0, 2)
    ZEND_ARG_INFO(0, result)
    ZEND_ARG_INFO(0, row)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lnsert_id, 0, 0, 0)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_ping, 0, 0, 0)
    ZEND_ARG_INFO(0, conn_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_new, 0, 0, 1)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_bind, 0, 0, 3)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, bind_index)
    ZEND_ARG_INFO(0, bind_value)
    ZEND_ARG_INFO(0, bind_value_type)
ZEND_END_ARG_INFO()
 
ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_export, 0, 0, 2)
    ZEND_ARG_INFO(0, lob2_id)
    ZEND_ARG_INFO(0, file_name)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_import, 0, 0, 2)
    ZEND_ARG_INFO(0, lob2_id)
    ZEND_ARG_INFO(0, file_name)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_read, 0, 0, 2)
    ZEND_ARG_INFO(0, lob2_id)
    ZEND_ARG_INFO(0, len)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_write, 0, 0, 2)
    ZEND_ARG_INFO(0, lob2_id)
    ZEND_ARG_INFO(0, buf)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_tell, 0, 0, 1)
    ZEND_ARG_INFO(0, lob2_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_tell64, 0, 0, 1)
    ZEND_ARG_INFO(0, lob2_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_seek, 0, 0, 2)
    ZEND_ARG_INFO(0, lob2_id)
    ZEND_ARG_INFO(0, offset)
    ZEND_ARG_INFO(0, origin)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_seek64, 0, 0, 2)
    ZEND_ARG_INFO(0, lob2_id)
    ZEND_ARG_INFO(0, offset)
    ZEND_ARG_INFO(0, origin)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_size, 0, 0, 1)
    ZEND_ARG_INFO(0, lob2_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_size64, 0, 0, 1)
    ZEND_ARG_INFO(0, lob2_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob2_close, 0, 0, 1)
    ZEND_ARG_INFO(0, lob2_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob_get, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, sql_stmt)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob_size, 0, 0, 1)
    ZEND_ARG_INFO(0, lob_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob_export, 0, 0, 3)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, lob_id)
    ZEND_ARG_INFO(0, file_name)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob_send, 0, 0, 2)
    ZEND_ARG_INFO(0, conn_id)
    ZEND_ARG_INFO(0, lob_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_lob_close, 0, 0, 1)
    ZEND_ARG_INFO(0, lob_id_array)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_get_query_timeout, 0, 0, 1)
    ZEND_ARG_INFO(0, req_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_cubrid_set_query_timeout, 0, 0, 2)
    ZEND_ARG_INFO(0, req_id)
    ZEND_ARG_INFO(0, timeout)
ZEND_END_ARG_INFO()

zend_function_entry cubrid_functions[] = {
    ZEND_FE(cubrid_version, arginfo_cubrid_version)
    ZEND_FE(cubrid_connect, arginfo_cubrid_connect)
    ZEND_FE(cubrid_pconnect, arginfo_cubrid_pconnect)
    ZEND_FE(cubrid_connect_with_url, arginfo_cubrid_connect_with_url)
    ZEND_FE(cubrid_pconnect_with_url, arginfo_cubrid_pconnect_with_url)
    ZEND_FE(cubrid_close, arginfo_cubrid_close)
    ZEND_FE(cubrid_prepare, arginfo_cubrid_prepare)
    ZEND_FE(cubrid_bind, arginfo_cubrid_bind)
    ZEND_FE(cubrid_execute, arginfo_cubrid_execute)
    ZEND_FE(cubrid_batch_execute, arginfo_cubrid_batch_execute)
    ZEND_FE(cubrid_next_result, arginfo_cubrid_next_result)
    ZEND_FE(cubrid_affected_rows, arginfo_cubrid_affected_rows)
    ZEND_FE(cubrid_close_request, arginfo_cubrid_close_request)
    ZEND_FE(cubrid_fetch, arginfo_cubrid_fetch)
    ZEND_FE(cubrid_current_oid, arginfo_cubrid_current_oid)
    ZEND_FE(cubrid_column_types, arginfo_cubrid_column_types)
    ZEND_FE(cubrid_column_names, arginfo_cubrid_column_names)
    ZEND_FE(cubrid_move_cursor, arginfo_cubrid_move_cursor)
    ZEND_FE(cubrid_num_rows, arginfo_cubrid_num_rows)
    ZEND_FE(cubrid_num_cols, arginfo_cubrid_num_cols)
    ZEND_FE(cubrid_get, arginfo_cubrid_get)
    ZEND_FE(cubrid_put, arginfo_cubrid_put)
    ZEND_FE(cubrid_drop, arginfo_cubrid_drop)
    ZEND_FE(cubrid_is_instance, arginfo_cubrid_is_instance)
    ZEND_FE(cubrid_lock_read, arginfo_cubrid_lock_read)
    ZEND_FE(cubrid_lock_write, arginfo_cubrid_lock_write)
    ZEND_FE(cubrid_get_class_name, arginfo_cubrid_get_class_name)
    ZEND_FE(cubrid_schema, arginfo_cubrid_schema)
    ZEND_FE(cubrid_col_size, arginfo_cubrid_col_size)
    ZEND_FE(cubrid_col_get, arginfo_cubrid_col_get)
    ZEND_FE(cubrid_set_add, arginfo_cubrid_set_add)
    ZEND_FE(cubrid_set_drop, arginfo_cubrid_set_drop)
    ZEND_FE(cubrid_seq_insert, arginfo_cubrid_seq_insert)
    ZEND_FE(cubrid_seq_put, arginfo_cubrid_seq_put)
    ZEND_FE(cubrid_seq_drop, arginfo_cubrid_seq_drop)
    ZEND_FE(cubrid_get_autocommit, arginfo_cubrid_get_autocommit)
    ZEND_FE(cubrid_set_autocommit, arginfo_cubrid_set_autocommit)
    ZEND_FE(cubrid_commit, arginfo_cubrid_commit)
    ZEND_FE(cubrid_rollback, arginfo_cubrid_rollback)
    ZEND_FE(cubrid_error_msg, arginfo_cubrid_error_msg)
    ZEND_FE(cubrid_error_code, arginfo_cubrid_error_code)
    ZEND_FE(cubrid_error_code_facility, arginfo_cubrid_error_code_facility)
    ZEND_FE(cubrid_errno, arginfo_cubrid_errno)
    ZEND_FE(cubrid_error, arginfo_cubrid_error)
    ZEND_FE(cubrid_field_name, arginfo_cubrid_field_name)
    ZEND_FE(cubrid_field_table, arginfo_cubrid_field_table)
    ZEND_FE(cubrid_field_type, arginfo_cubrid_field_type)
    ZEND_FE(cubrid_field_flags, arginfo_cubrid_field_flags)
    ZEND_FE(cubrid_data_seek, arginfo_cubrid_data_seek)
    ZEND_FE(cubrid_fetch_array, arginfo_cubrid_fetch_array)
    ZEND_FE(cubrid_fetch_assoc, arginfo_cubrid_fetch_assoc)
    ZEND_FE(cubrid_fetch_row, arginfo_cubrid_fetch_row)
    ZEND_FE(cubrid_fetch_field, arginfo_cubrid_fetch_field)
    ZEND_FE(cubrid_num_fields, arginfo_cubrid_num_fields)
    ZEND_FE(cubrid_free_result, arginfo_cubrid_free_result)
    ZEND_FE(cubrid_fetch_lengths, arginfo_cubrid_fetch_lengths)
    ZEND_FE(cubrid_fetch_object, arginfo_cubrid_fetch_object)
    ZEND_FE(cubrid_field_seek, arginfo_cubrid_field_seek)
    ZEND_FE(cubrid_field_len, arginfo_cubrid_field_len)
    ZEND_FE(cubrid_result, arginfo_cubrid_result)
    ZEND_FE(cubrid_get_charset, arginfo_cubrid_get_charset)
    ZEND_FE(cubrid_client_encoding, arginfo_cubrid_client_encoding)
    ZEND_FE(cubrid_unbuffered_query, arginfo_cubrid_unbuffered_query)
    ZEND_FE(cubrid_query, arginfo_cubrid_query)
    ZEND_FE(cubrid_get_client_info, arginfo_cubrid_get_client_info)
    ZEND_FE(cubrid_get_server_info, arginfo_cubrid_get_server_info)
    ZEND_FE(cubrid_real_escape_string, arginfo_cubrid_real_escape_string)
    ZEND_FE(cubrid_get_db_parameter, arginfo_cubrid_get_db_parameter)
    ZEND_FE(cubrid_set_db_parameter, arginfo_cubrid_set_db_parameter)
    ZEND_FE(cubrid_list_dbs, arginfo_cubrid_list_dbs)
    ZEND_FE(cubrid_db_name, arginfo_cubrid_db_name)
    ZEND_FE(cubrid_insert_id, arginfo_cubrid_lnsert_id)
    ZEND_FE(cubrid_ping, arginfo_cubrid_ping)

    ZEND_FE(cubrid_lob2_new, arginfo_cubrid_lob2_new)
    ZEND_FE(cubrid_lob2_bind, arginfo_cubrid_lob2_bind)
    ZEND_FE(cubrid_lob2_export, arginfo_cubrid_lob2_export)
    ZEND_FE(cubrid_lob2_import, arginfo_cubrid_lob2_import)
    ZEND_FE(cubrid_lob2_read, arginfo_cubrid_lob2_read)
    ZEND_FE(cubrid_lob2_write, arginfo_cubrid_lob2_write)
    ZEND_FE(cubrid_lob2_tell, arginfo_cubrid_lob2_tell)
    ZEND_FE(cubrid_lob2_tell64, arginfo_cubrid_lob2_tell64)
    ZEND_FE(cubrid_lob2_seek, arginfo_cubrid_lob2_seek)
    ZEND_FE(cubrid_lob2_seek64, arginfo_cubrid_lob2_seek64)
    ZEND_FE(cubrid_lob2_size, arginfo_cubrid_lob2_size)
    ZEND_FE(cubrid_lob2_size64, arginfo_cubrid_lob2_size64)
    ZEND_FE(cubrid_lob2_close, arginfo_cubrid_lob2_close)

    ZEND_FE(cubrid_lob_get, arginfo_cubrid_lob_get)
    ZEND_FE(cubrid_lob_size, arginfo_cubrid_lob_size)
    ZEND_FE(cubrid_lob_export, arginfo_cubrid_lob_export)
    ZEND_FE(cubrid_lob_send, arginfo_cubrid_lob_send)
    ZEND_FE(cubrid_lob_close, arginfo_cubrid_lob_close)
    ZEND_FE(cubrid_get_query_timeout, arginfo_cubrid_get_query_timeout)
    ZEND_FE(cubrid_set_query_timeout, arginfo_cubrid_set_query_timeout)
    ZEND_FALIAS(cubrid_close_prepare, cubrid_close_request, NULL) 
    ZEND_FALIAS(cubrid_disconnect, cubrid_close, NULL) 
    {NULL, NULL, NULL}
};

zend_module_entry cubrid_module_entry = {
    STANDARD_MODULE_HEADER,
    "CUBRID",
    cubrid_functions,
    ZEND_MINIT(cubrid),
    ZEND_MSHUTDOWN(cubrid),
    ZEND_RINIT(cubrid),
    ZEND_RSHUTDOWN(cubrid),
    ZEND_MINFO(cubrid),
    NO_VERSION_YET,
    STANDARD_MODULE_PROPERTIES
};

ZEND_DECLARE_MODULE_GLOBALS(cubrid)

/************************************************************************
* CUBRID PHP.INI SETTINGS
************************************************************************/

ZEND_INI_BEGIN()
/* maybe add settings later */
ZEND_INI_END()

/************************************************************************
* PRIVATE VARIABLES
************************************************************************/

/* resource type */
static int le_pconnect, le_connect, le_request, le_lob, le_lob2;

/************************************************************************
* IMPLEMENTATION OF CALLBACK FUNCTION (EXPORT/INIT/SHUTDOWN/INFO)
************************************************************************/

#if defined(COMPILE_DL_CUBRID)
ZEND_GET_MODULE(cubrid)
#endif

ZEND_MINIT_FUNCTION(cubrid)
{
    REGISTER_INI_ENTRIES();

    cci_init();

    ZEND_INIT_MODULE_GLOBALS(cubrid, php_cubrid_init_globals, NULL);

    le_pconnect = zend_register_list_destructors_ex(NULL, close_cubrid_pconnect, "CUBRID Connect Persistent", module_number);
    le_connect = zend_register_list_destructors_ex(close_cubrid_connect, NULL, "CUBRID Connect", module_number);
    le_request = zend_register_list_destructors_ex(close_cubrid_request, NULL, "CUBRID Request", module_number);
    le_lob = zend_register_list_destructors_ex(close_cubrid_lob, NULL, "CUBRID Lob", module_number);
    le_lob2 = zend_register_list_destructors_ex(close_cubrid_lob2, NULL, "CUBRID Lob2", module_number);

    Z_TYPE(cubrid_module_entry) = type;

    init_error();

    REGISTER_LONG_CONSTANT("CUBRID_INCLUDE_OID", CUBRID_INCLUDE_OID, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_ASYNC", CUBRID_ASYNC, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_EXEC_QUERY_ALL", CUBRID_EXEC_QUERY_ALL, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("CUBRID_NUM", CUBRID_NUM, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_ASSOC", CUBRID_ASSOC, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_BOTH", CUBRID_BOTH, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_OBJECT", CUBRID_OBJECT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_LOB", CUBRID_LOB, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("CUBRID_CURSOR_FIRST", CUBRID_CURSOR_FIRST, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_CURSOR_CURRENT", CUBRID_CURSOR_CURRENT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_CURSOR_LAST", CUBRID_CURSOR_LAST, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("CUBRID_PARAM_ISOLATION_LEVEL", CUBRID_PARAM_ISOLATION_LEVEL, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_PARAM_LOCK_TIMEOUT", CUBRID_PARAM_LOCK_TIMEOUT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_AUTOCOMMIT_FALSE", CUBRID_AUTOCOMMIT_FALSE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_AUTOCOMMIT_TRUE", CUBRID_AUTOCOMMIT_TRUE, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("TRAN_REP_CLASS_COMMIT_INSTANCE", TRAN_REP_CLASS_COMMIT_INSTANCE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("TRAN_REP_CLASS_REP_INSTANCE", TRAN_REP_CLASS_REP_INSTANCE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("TRAN_SERIALIZABLE", TRAN_SERIALIZABLE, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("CUBRID_SCH_CLASS", CUBRID_SCH_CLASS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_VCLASS", CUBRID_SCH_VCLASS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_QUERY_SPEC", CUBRID_SCH_QUERY_SPEC, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_ATTRIBUTE", CUBRID_SCH_ATTRIBUTE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_CLASS_ATTRIBUTE", CUBRID_SCH_CLASS_ATTRIBUTE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_METHOD", CUBRID_SCH_METHOD, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_CLASS_METHOD", CUBRID_SCH_CLASS_METHOD, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_METHOD_FILE", CUBRID_SCH_METHOD_FILE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_SUPERCLASS", CUBRID_SCH_SUPERCLASS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_SUBCLASS", CUBRID_SCH_SUBCLASS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_CONSTRAINT", CUBRID_SCH_CONSTRAINT, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_TRIGGER", CUBRID_SCH_TRIGGER, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_CLASS_PRIVILEGE", CUBRID_SCH_CLASS_PRIVILEGE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_ATTR_PRIVILEGE", CUBRID_SCH_ATTR_PRIVILEGE, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_DIRECT_SUPER_CLASS", CUBRID_SCH_DIRECT_SUPER_CLASS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_PRIMARY_KEY", CUBRID_SCH_PRIMARY_KEY, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_IMPORTED_KEYS", CUBRID_SCH_IMPORTED_KEYS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_EXPORTED_KEYS", CUBRID_SCH_EXPORTED_KEYS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_SCH_CROSS_REFERENCE", CUBRID_SCH_CROSS_REFERENCE, CONST_CS | CONST_PERSISTENT);

    REGISTER_LONG_CONSTANT("CUBRID_FACILITY_DBMS", CUBRID_FACILITY_DBMS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_FACILITY_CAS", CUBRID_FACILITY_CAS, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_FACILITY_CCI", CUBRID_FACILITY_CCI, CONST_CS | CONST_PERSISTENT);
    REGISTER_LONG_CONSTANT("CUBRID_FACILITY_CLIENT", CUBRID_FACILITY_CLIENT, CONST_CS | CONST_PERSISTENT);

    return SUCCESS;
}

ZEND_MSHUTDOWN_FUNCTION(cubrid)
{
    UNREGISTER_INI_ENTRIES();
    cci_end();

    return SUCCESS;
}

ZEND_RINIT_FUNCTION(cubrid)
{
	CUBRID_G(last_connect_id) = -1;
	CUBRID_G(last_request_id) = -1;

	CUBRID_G(recent_error).code = 0;
	CUBRID_G(recent_error).facility = 0;
	CUBRID_G(recent_error).msg[0] = 0;

	return SUCCESS;
}

static int php_cubrid_persistent_helper(zend_rsrc_list_entry *le TSRMLS_DC)
{
    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval;
    LINKED_LIST_NODE *head, *p;

    if (Z_TYPE_P(le) != le_pconnect) {
        return 0;
    }

    connect = (T_CUBRID_CONNECT *)le->ptr;

    head = connect->unclosed_requests->head;

    for (p = head->next; p != NULL; p = head->next) {
        T_CUBRID_REQUEST *req = (T_CUBRID_REQUEST *)p->data;
        req->conn = NULL;
        req->handle = 0;

        if (req->lob2_list != NULL) {
            int i;
            for (i = 0; i < req->col_count; i++) {
                if (req->lob2_list[i]) {
                    req->lob2_list[i]->connect = NULL;
                    req->lob2_list[i] = NULL;
                }
            }
        }

        p->data = NULL; 
        head->next = p->next;
        efree(p);
    }

    connect->unclosed_requests->tail = head;

    if ((cubrid_retval = cci_end_tran (connect->handle, CCI_TRAN_ROLLBACK, &error)) < 0) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot rollback transaction when shutdown request");
        return -1;
    }

    return 0;
}

ZEND_RSHUTDOWN_FUNCTION(cubrid)
{
    zend_hash_apply(&EG(persistent_list), (apply_func_t)php_cubrid_persistent_helper TSRMLS_CC);
    return SUCCESS;
}

ZEND_MINFO_FUNCTION(cubrid)
{
    int major, minor, patch;
    char info[128];

    cci_get_version(&major, &minor, &patch);

    snprintf(info, sizeof(info), "%d.%d.%d", major, minor, patch);
    
    php_info_print_table_start();
    php_info_print_table_header(2, "CUBRID support", "enabled");
    php_info_print_table_row(2, "Driver Version", PHP_CUBRID_VERSION);
    php_info_print_table_row(2, "CCI Version", info);
    php_info_print_table_end();

    DISPLAY_INI_ENTRIES();
}

/************************************************************************
* IMPLEMENTATION OF CUBRID API
************************************************************************/

ZEND_FUNCTION(cubrid_version)
{
    if (zend_parse_parameters_none() == FAILURE) {
	return;
    }

    RETURN_STRINGL(PHP_CUBRID_VERSION, strlen(PHP_CUBRID_VERSION), 1);
}

static int check_connect_alive(T_CUBRID_CONNECT *connect)
{
    int req_handle = 0;

    T_CCI_ERROR error;
    int cubrid_retval = 0, connected = 0;
    int result = 0, ind = 0;
    char *query = "SELECT 1 FROM db_root";

    init_error();
    init_error_link(connect);

    if ((cubrid_retval = cci_prepare(connect->handle, query, 0, &error)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        return 0;
    }

    req_handle = cubrid_retval;

    if ((cubrid_retval = cci_execute(req_handle, 0, 0, &error)) < 0) {
        goto HANDLE_ERROR;
    }

    cci_close_req_handle(req_handle);

    return 1;

HANDLE_ERROR:
    cci_close_req_handle(req_handle);
    return 0;
}

static void php_cubrid_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent)
{
    char *host = NULL, *dbname = NULL, *userid = NULL, *passwd = NULL, *attr = NULL;
    long port = 0;
    int host_len, dbname_len, userid_len, passwd_len, attr_len;

    int cubrid_conn, cubrid_retval = 0;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;

    char hashed_details[1024] = {'\0'};
    int hashed_details_length;

	char connect_url[2048] = {'\0'};

    zend_bool new_link = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls|ssbs", 
		&host, &host_len, &port, &dbname, &dbname_len, 
		&userid, &userid_len, &passwd, &passwd_len, &new_link, &attr, &attr_len) == FAILURE) {
	return;
    }

    if (!userid) {
	userid = CUBRID_G(default_userid);
    }

    if (!passwd) {
	passwd = CUBRID_G(default_passwd);
    }

    snprintf(hashed_details, sizeof(hashed_details), "CUBRID:%s:%d:%s:%s:%s", host, (int)port, dbname, userid, passwd);
    hashed_details_length = strlen(hashed_details);

	if (attr) {
		snprintf(connect_url, sizeof(connect_url), "cci:CUBRID:%s:%d:%s:%s:%s:?%s", host, (int)port, dbname, userid, passwd, attr);
	} else {
		snprintf(connect_url, sizeof(connect_url), "cci:CUBRID:%s:%d:%s:%s:%s:", host, (int)port, dbname, userid, passwd);
	}

    if (persistent) {
        zend_rsrc_list_entry *le;

        /* try to find if we already have this link in our persistent list */
        if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_length+1, (void **) &le) == FAILURE) { /* we don't */
            zend_rsrc_list_entry new_le;

			if ((cubrid_conn = cci_connect_with_url_ex(connect_url, userid, passwd, &error)) < 0) {
				handle_error(cubrid_conn, &error, NULL);
				RETURN_FALSE;
			}
    
            if ((cubrid_retval = cci_end_tran(cubrid_conn, CCI_TRAN_COMMIT, &error)) < 0) {
                handle_error(cubrid_retval, &error, NULL);
                RETURN_FALSE;
            }
    
            connect = new_cubrid_connect(1);
            if (!connect) RETURN_FALSE;
            connect->handle = cubrid_conn;
    
            Z_TYPE(new_le) = le_pconnect;
            new_le.ptr = connect;
            if (zend_hash_update(&EG(persistent_list), hashed_details, hashed_details_length + 1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) {
                free(connect);
                RETURN_FALSE;
            }
        } else { /* The link is in our list of persistent connections */
    
            if (Z_TYPE_P(le) != le_pconnect) {
                RETURN_FALSE;
            }
    
            connect = (T_CUBRID_CONNECT *) le->ptr;
    
            if (!check_connect_alive(connect)) {
				if ((cubrid_conn = cci_connect_with_url_ex(connect_url, userid, passwd, &error)) < 0) {
                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect");
                    zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_length+1);
                    RETURN_FALSE;
                }
    
                connect->handle = cubrid_conn;
            }
    
            if ((cubrid_retval = cci_end_tran(connect->handle, CCI_TRAN_COMMIT, &error)) < 0) {
                handle_error(cubrid_retval, &error, NULL);
                free(connect);
                RETURN_FALSE;
            }
        }
    
        CUBRID_G(last_request_id) = -1;
        
        ZEND_REGISTER_RESOURCE(return_value, connect, le_pconnect);
   
    } else { /* non persistent */

        zend_rsrc_list_entry *index_ptr, new_index_ptr;
    
        if (!new_link && zend_hash_find(&EG(regular_list), hashed_details, hashed_details_length+1, (void **) &index_ptr) == SUCCESS) {
            int type;
            long link;
            void *ptr;
    
            if (Z_TYPE_P(index_ptr) != le_index_ptr) {
                RETURN_FALSE;
            }
    
            link = (long) index_ptr->ptr;
            ptr = zend_list_find(link, &type);
            if (ptr && (type == le_connect || type == le_pconnect)) {
                zend_list_addref(link);
                Z_LVAL_P(return_value) = link;
                php_cubrid_set_default_conn(link TSRMLS_CC);
                Z_TYPE_P(return_value) = IS_RESOURCE;
                return;
            } else {
                zend_hash_del(&EG(regular_list), hashed_details, hashed_details_length+1);
            }
        }

		if ((cubrid_conn = cci_connect_with_url_ex(connect_url, userid, passwd, &error)) < 0) {
			handle_error(cubrid_conn, &error, NULL);
			RETURN_FALSE;
		}
    
        CUBRID_G(last_request_id) = -1;
    
        if ((cubrid_retval = cci_end_tran(cubrid_conn, CCI_TRAN_COMMIT, &error)) < 0) {
    	handle_error(cubrid_retval, &error, NULL);
    	cci_disconnect(cubrid_conn, &error);
    	RETURN_FALSE;
        }
    
        connect = new_cubrid_connect(0);
        if (!connect) RETURN_FALSE;
        connect->handle = cubrid_conn;
        
        ZEND_REGISTER_RESOURCE(return_value, connect, le_connect);
    
        new_index_ptr.ptr = (void *) Z_LVAL_P(return_value);
        Z_TYPE(new_index_ptr) = le_index_ptr;
        if (zend_hash_update(&EG(regular_list), hashed_details, hashed_details_length+1, (void *)&new_index_ptr, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) {
            RETURN_FALSE;
        }
    }

    php_cubrid_set_default_conn(Z_LVAL_P(return_value) TSRMLS_CC);
}

ZEND_FUNCTION(cubrid_pconnect)
{
    php_cubrid_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}

ZEND_FUNCTION(cubrid_connect)
{
    php_cubrid_do_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}

static void php_cubrid_do_connect_with_url(INTERNAL_FUNCTION_PARAMETERS, int persistent)
{
    char *url = NULL, *userid = NULL, *passwd = NULL;
    int url_len, userid_len, passwd_len;
    char buf[4096] = { '\0' };
    char hashed_details[4096] = { '\0' };
    int hashed_details_length;

    int cubrid_conn, cubrid_retval = 0;

    T_CUBRID_CONNECT *connect = NULL;
    T_CCI_ERROR error;

    zend_bool new_link = 0;

    init_error();

    if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, "s|ssb", 
		&url, &url_len, &userid, &userid_len, &passwd, &passwd_len, &new_link) == FAILURE) {
	return;
    }

    if (strncasecmp (url, "cci:", 4) != 0) {
        snprintf (buf, sizeof(buf), "cci:%s", url);
    }
    else {
        strncpy (buf, url, sizeof(buf));
    }

    if (!userid) {
        userid = (char *) "";
    }

    if (!passwd) {
	passwd = (char *) "";
    }

    snprintf(hashed_details, sizeof(hashed_details), "%s:%s:%s", buf, userid, passwd);
    hashed_details_length = strlen(hashed_details);

    if (persistent) {
        zend_rsrc_list_entry *le;

        /* try to find if we already have this link in our persistent list */
        if (zend_hash_find(&EG(persistent_list), hashed_details, hashed_details_length + 1, (void **) &le) == FAILURE) { /* we don't */
            zend_rsrc_list_entry new_le;
   
            if ((cubrid_conn = cci_connect_with_url_ex(buf, userid, passwd, &error)) < 0) {
                handle_error(cubrid_conn, &error, NULL);
                RETURN_FALSE;
            }
    
            if ((cubrid_retval = cci_end_tran(cubrid_conn, CCI_TRAN_COMMIT, &error)) < 0) {
                handle_error(cubrid_retval, &error, NULL);
                cci_disconnect(cubrid_conn, &error);
                RETURN_FALSE;
            }
    
            connect = new_cubrid_connect(1);
            if (!connect) RETURN_FALSE;
            connect->handle = cubrid_conn;
    
            Z_TYPE(new_le) = le_pconnect;
            new_le.ptr = connect;
            if (zend_hash_update(&EG(persistent_list), hashed_details, hashed_details_length + 1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) {
                free(connect);
                RETURN_FALSE;
            }
    
        } else {
    
            if (Z_TYPE_P(le) != le_pconnect) {
                RETURN_FALSE;
            }
    
            connect = (T_CUBRID_CONNECT *) le->ptr;
    
            if (!check_connect_alive(connect)) {
                if ((cubrid_conn = cci_connect_with_url_ex(buf, userid, passwd, &error)) < 0) {
                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Link to server lost, unable to reconnect");
                    zend_hash_del(&EG(persistent_list), hashed_details, hashed_details_length+1);
                    RETURN_FALSE;
                }
    
                connect->handle = cubrid_conn;
            }
    
            if ((cubrid_retval = cci_end_tran(connect->handle, CCI_TRAN_COMMIT, &error)) < 0) {
                handle_error(cubrid_retval, &error, NULL);
                free(connect);
                RETURN_FALSE;
            }
        }
    
        CUBRID_G(last_request_id) = -1;
    
        ZEND_REGISTER_RESOURCE(return_value, connect, le_pconnect);

    } else { /* non persistent */

        zend_rsrc_list_entry *index_ptr, new_index_ptr;
    
        if (!new_link && zend_hash_find(&EG(regular_list), hashed_details, hashed_details_length + 1, (void **) &index_ptr) == SUCCESS) {
            int type;
            long link;
            void *ptr;
    
            if (Z_TYPE_P(index_ptr) != le_index_ptr) {
                RETURN_FALSE;
            }
    
            link = (long) index_ptr->ptr;
            ptr = zend_list_find(link, &type);
            if (ptr && (type == le_connect || type == le_pconnect)) {
                zend_list_addref(link);
                Z_LVAL_P(return_value) = link;
                php_cubrid_set_default_conn(link TSRMLS_CC);
                Z_TYPE_P(return_value) = IS_RESOURCE;
                return;
            } else {
                zend_hash_del(&EG(regular_list), hashed_details, hashed_details_length+1);
            }
        }
    
        if ((cubrid_conn = cci_connect_with_url_ex(buf, userid, passwd, &error)) < 0) {
    	handle_error(cubrid_conn, &error, NULL);
    	RETURN_FALSE;
        }
    
        CUBRID_G(last_request_id) = -1;
    
        if ((cubrid_retval = cci_end_tran(cubrid_conn, CCI_TRAN_COMMIT, &error)) < 0) {
    	handle_error(cubrid_retval, &error, NULL);
    	cci_disconnect(cubrid_conn, &error);
    	RETURN_FALSE;
        }
    
        connect = new_cubrid_connect(0);
        if (!connect) RETURN_FALSE;
        connect->handle = cubrid_conn;
    
        ZEND_REGISTER_RESOURCE(return_value, connect, le_connect);
    
        new_index_ptr.ptr = (void *)Z_LVAL_P(return_value);
        Z_TYPE(new_index_ptr) = le_index_ptr;
        if (zend_hash_update(&EG(regular_list), hashed_details, hashed_details_length + 1, (void *)&new_index_ptr, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) {
            RETURN_FALSE;
        }
    }

    php_cubrid_set_default_conn(Z_LVAL_P(return_value) TSRMLS_CC);
}

ZEND_FUNCTION(cubrid_pconnect_with_url)
{
    php_cubrid_do_connect_with_url(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}

ZEND_FUNCTION(cubrid_connect_with_url)
{
    php_cubrid_do_connect_with_url(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}

ZEND_FUNCTION(cubrid_close)
{
    zval *conn_id = NULL;
    T_CUBRID_CONNECT *connect = NULL;
    
    int res_id = -1;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &conn_id) == FAILURE) {
	return;
    }

    if (conn_id) {
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
        if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE;
        }

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    res_id = conn_id ? Z_RESVAL_P(conn_id) : CUBRID_G(last_connect_id);
    zend_list_delete(res_id);

    /* On an explicit close of the default connection it had a refcount of 2,
     * so we need one more call */
    if (!conn_id || (conn_id && Z_RESVAL_P(conn_id) == CUBRID_G(last_connect_id))) {
        CUBRID_G(last_connect_id) = -1;
        CUBRID_G(last_request_id) = -1;

        if (conn_id) {
            zend_list_delete(res_id);
        }
    }
    
    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_prepare)
{
    zval *conn_id = NULL;
    char *query = NULL;
    long option = 0;
    int query_len;

    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_REQUEST *request = NULL;
    T_CCI_ERROR error;

    int cubrid_retval = 0, request_handle = -1;
    int i;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &conn_id, &query, &query_len, &option) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    request = new_cubrid_request();
    if (!request) {
        handle_error(CCI_ER_REQ_HANDLE, NULL, connect);
        RETURN_FALSE;
    }
    request->conn = connect;

    if ((cubrid_retval = cci_prepare(connect->handle, query, 
                    (char) ((option & CUBRID_INCLUDE_OID) ? CCI_PREPARE_INCLUDE_OID : 0), &error)) < 0) {
        close_cubrid_request_internal(request);
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }

    request_handle = cubrid_retval;

    request->handle = request_handle;
    request->bind_num = cci_get_bind_num(request_handle);

    if (request->bind_num > 0) {
        request->l_bind = (short *) safe_emalloc(request->bind_num, sizeof(short), 0);
        for (i = 0; i < request->bind_num; i++) {
            request->l_bind[i] = 0;
        }
    }

    request->l_prepare = 1;
    request->fetch_field_auto_index = 0;

    ZEND_REGISTER_RESOURCE(return_value, request, le_request);
    php_cubrid_set_default_req(Z_LVAL_P(return_value) TSRMLS_CC);
    register_cubrid_request(connect, request);
}
static char* cubrid_str2bit(char* str)
{
    int i=0,len=0,t=0;
    char* buf=NULL;
    int shift = 8;

    if(str == NULL)
        return NULL;
    len = strlen(str);

    if(0 == len%shift)
        t =1;

    buf = (char*)emalloc(len/shift+1+1);
    memset(buf,0,len/shift+1+1);

    for(i=0;i<len;i++)
    {
        if(str[len-i-1] == '1')
        {
            buf[len/shift - i/shift-t] |= (1<<(i%shift)); 
        }
        else if(str[len-i-1] == '0')
        {
        	//nothing
        }
        else
        {
            return NULL;
        }
    }
    return buf;
}

ZEND_FUNCTION(cubrid_bind)
{
    zval *req_id= NULL, *bind_value = NULL;
    char *bind_value_type = NULL;
    long bind_index = -1;
    int bind_value_type_len;

    T_CUBRID_REQUEST *request = NULL;
    T_CCI_ERROR error;

    T_CCI_U_TYPE u_type = -1;
    T_CCI_A_TYPE a_type = -1;

    char *value = NULL;
    long value_len = 0;

    T_CCI_BIT *bit_value = NULL;
    T_CCI_LOB lob = NULL;

    php_stream *stm = NULL;
    char* lobfile_name = NULL;
    char buf[CUBRID_LOB_READ_BUF_SIZE];
    php_cubrid_int64_t lob_start_pos = 0;
    size_t lob_read_size = 0;

    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz|s", 
                &req_id, &bind_index, &bind_value, &bind_value_type, &bind_value_type_len) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (bind_index < 1 || bind_index > request->bind_num) {
	RETURN_FALSE;
    }

    if (!bind_value_type) {
	u_type = CCI_U_TYPE_STRING;
    } else {
	u_type = get_cubrid_u_type_by_name(bind_value_type);
	/* collection type should be made by cci_set_make before calling cci_bind_param */
	if (u_type == CCI_U_TYPE_SET || u_type == CCI_U_TYPE_MULTISET || u_type == CCI_U_TYPE_SEQUENCE) {
	    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bind value type unknown : %s\n", bind_value_type);
	    RETURN_FALSE;
	}
       if(u_type == CCI_U_TYPE_ENUM)
       {
           u_type = CCI_U_TYPE_STRING;
       }
    }

    if (u_type == CCI_U_TYPE_NULL || Z_TYPE_P(bind_value) == IS_NULL) {
        cubrid_retval = cci_bind_param(request->handle, bind_index, CCI_A_TYPE_STR, NULL, u_type, 0);
    } else if (u_type == U_TYPE_UNKNOWN) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bind value type unknown : %s\n", bind_value_type);
        RETURN_FALSE;
    } else {
        if (u_type == CCI_U_TYPE_BLOB || u_type == CCI_U_TYPE_CLOB) {
            if (Z_TYPE_P(bind_value) == IS_RESOURCE) {
                php_stream_from_zval_no_verify(stm, &bind_value);
                if (!stm) {
                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected a stream resource when param type is LOB\n");
                    RETURN_FALSE;
                }
            } else {
                /* file name */
                convert_to_string(bind_value);
                lobfile_name = Z_STRVAL_P(bind_value);

                if (!(stm = php_stream_open_wrapper(lobfile_name, "r", REPORT_ERRORS, NULL))) {
                    RETURN_FALSE;
                }
            }
        } else {
            convert_to_string(bind_value);
        }

        value = Z_STRVAL_P(bind_value);
        value_len = Z_STRLEN_P(bind_value);

        switch (u_type) {
        case CCI_U_TYPE_BLOB:
                a_type = CCI_A_TYPE_BLOB;
                break;
        case CCI_U_TYPE_CLOB:
                a_type = CCI_A_TYPE_CLOB;
                break;
        case CCI_U_TYPE_BIT:
        case CCI_U_TYPE_VARBIT:
                a_type = CCI_A_TYPE_BIT;
                break;
        default:
                a_type = CCI_A_TYPE_STR;
                break;
        }
        
        if (u_type == CCI_U_TYPE_BLOB || u_type == CCI_U_TYPE_CLOB) {
            if ((cubrid_retval = cubrid_lob_new(request->conn->handle, &lob, u_type, &error)) < 0) {
                handle_error(cubrid_retval, &error, request->conn);

                if (lobfile_name) {
                    php_stream_close(stm);
                }

                RETURN_FALSE; 
            }

            while (!php_stream_eof(stm)) { 
                lob_read_size = php_stream_read(stm, buf, CUBRID_LOB_READ_BUF_SIZE); 

                if ((cubrid_retval = cubrid_lob_write(request->conn->handle, lob, u_type, 
                                lob_start_pos, lob_read_size, buf, &error)) < 0) {
                    handle_error(cubrid_retval, &error, request->conn);
                    php_stream_close(stm);
                    
                    RETURN_FALSE; 
                }        

                lob_start_pos += lob_read_size;
            }
           
            if (lobfile_name) {
                php_stream_close(stm);
            }

            cubrid_retval = cci_bind_param(request->handle, bind_index, a_type, (void *) lob, u_type, CCI_BIND_PTR); 

            request->lob = new_cubrid_lob();
            if (!request->lob) {
                handle_error(CCI_ER_NO_MORE_MEMORY, NULL, request->conn);
                RETURN_FALSE;
            }
            request->lob->lob = lob;
            request->lob->type = u_type;
        } else if (u_type == CCI_U_TYPE_BIT) {
            bit_value = (T_CCI_BIT *) emalloc(sizeof(T_CCI_BIT));            
            bit_value->buf = cubrid_str2bit(value);

            if(bit_value->buf == NULL)
            {
                handle_error(CCI_ER_TYPE_CONVERSION, NULL, request->conn);
                RETURN_FALSE;             
            }
            bit_value->size = strlen(bit_value[0].buf ); 

            cubrid_retval = cci_bind_param(request->handle, bind_index, a_type, (void *) bit_value, u_type, 0);
            efree(bit_value->buf);
            efree(bit_value);
        } else {
            cubrid_retval = cci_bind_param(request->handle, bind_index, a_type, value, u_type, 0);
        }
    }

    if (cubrid_retval != 0 || !request->l_bind) {
	handle_error(cubrid_retval, NULL, request->conn);
	RETURN_FALSE;
    }

    request->l_bind[bind_index - 1] = 1;

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_batch_execute)
{
    char exec_flag = 0;
    int count = 0, i = 0, n_executed = 0, err_code = 0;
    T_CUBRID_CONNECT *connect = NULL;
    T_CCI_ERROR error;    
    T_CCI_QUERY_RESULT *result;
    zval *id = NULL, *param = NULL;
    zval **z_item;
    char** sql = NULL;
    
    init_error(); 
	if (zend_parse_parameters(2 TSRMLS_CC, "rz", &id, &param) == FAILURE) {
        handle_error(CUBRID_ER_INVALID_PARAM_TYPE, NULL, connect);
	    RETURN_FALSE;
	}
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    if(IS_ARRAY != Z_TYPE_P(param)|| NULL == connect){
        handle_error(CUBRID_ER_INVALID_PARAM, NULL, connect);
        RETURN_FALSE;
    }
    count= zend_hash_num_elements(Z_ARRVAL_P(param));
    sql = malloc(count * sizeof(void*));
    if(NULL == sql){
        handle_error(CUBRID_ER_INVALID_PARAM, NULL, connect);
        RETURN_FALSE;
    }
    for (i = 0; i < count; i ++) {
        zend_hash_get_current_data(Z_ARRVAL_P(param), (void**) &z_item);
        zend_hash_move_forward(Z_ARRVAL_P(param));
        convert_to_string_ex(z_item); 
        sql[i] = Z_STRVAL_PP(z_item);
    }
    n_executed = cci_execute_batch (connect->handle, count, sql, &result, &error);
    if (n_executed < 0){
        free(sql);
        handle_error(n_executed, &error, connect);
        RETURN_FALSE;
    }
    free(sql);

    array_init(return_value);
    for (i = 0; i < n_executed; ++i){
        zval *item;
        MAKE_STD_ZVAL(item);
        array_init(item);
        add_index_long(item, 0, result[i].err_no);
        if (NULL == result[i].err_msg)
            add_index_string(item, 1, "", 1);
        else
            add_index_string(item, 1, result[i].err_msg, 1);
        add_index_zval(return_value, i, item);
    }
    err_code = cci_query_result_free (result, n_executed);    
    if (err_code < 0)    
    {     
        handle_error(n_executed, &error, connect); 
        RETURN_FALSE;
    }    
    
}

ZEND_FUNCTION(cubrid_execute)
{
    zval *id = NULL, *param = NULL;
    char *sql_stmt = NULL;
    long option = 0;
    int sql_stmt_len;

    char exec_flag = 0;

    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_REQUEST *request = NULL;
    T_CCI_ERROR error;
    int exec_retval = 0;

    T_CCI_COL_INFO *res_col_info;
    T_CCI_CUBRID_STMT res_sql_type;
    int res_col_count = 0;

    int cubrid_retval = 0;
    int req_handle = 0;
    int l_prepare = 0;
    int i;
    int is_prepare_and_execute_mode = 0;

    init_error();

    switch (ZEND_NUM_ARGS()) {
    case 1:
	/* It must be req_id */
	if (zend_parse_parameters(1 TSRMLS_CC, "r", &id) == FAILURE) {
	    return;
	}

	ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &id, -1, "CUBRID-Request", le_request);

	break;
    case 2:
	/* Param may be conn_id + sql_stmt or req_id + option */
	if (zend_parse_parameters(2 TSRMLS_CC, "rz", &id, &param) == FAILURE) {
	    return;
	}

	switch (Z_TYPE_P(param)) {
	case IS_STRING:
	    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &id, -1, "CUBRID-Connect", le_connect, le_pconnect);
	    sql_stmt = Z_STRVAL_P(param);	
	    sql_stmt_len = Z_STRLEN_P(param);
            is_prepare_and_execute_mode = 1;

	    break;
	case IS_LONG:
	    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &id, -1, "CUBRID-Request", le_request);
	    option = Z_LVAL_P(param);	

	    break;
	default:
	    RETURN_FALSE;
	}

	break;
    case 3:
	/* It must be conn_id + sql_stmt + option */
	if (zend_parse_parameters(3 TSRMLS_CC, "rsl", 
		    &id, &sql_stmt, &sql_stmt_len, &option) == FAILURE) {
	    return;
	}

	ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &id, -1, "CUBRID-Connect", le_connect, le_pconnect);

	break;
    default:
        WRONG_PARAM_COUNT;
    }

    if (connect == NULL && request != NULL) {
        connect = request->conn;
        req_handle = request->handle;

        init_error_link(connect);

        if (!request->l_prepare) {
            handle_error(CCI_ER_REQ_HANDLE, NULL, connect);
            RETURN_FALSE;
        }

        l_prepare = request->l_prepare;

        if (request->bind_num > 0) {
	    for (i = 0; i < request->bind_num; i++) {
		if (!request->l_bind[i]) {
                    handle_error(CUBRID_ER_PARAM_UNBIND, NULL, connect);
		    RETURN_FALSE;
		}
	    }
	}
    } else if (connect != NULL && request == NULL) {
        init_error_link(connect);

        request = new_cubrid_request();
        if (!request) {
            handle_error(CCI_ER_REQ_HANDLE, NULL, connect);
            RETURN_FALSE;
        }
        request->conn = connect;

        if (!is_prepare_and_execute_mode) {
            if ((cubrid_retval = cci_prepare(connect->handle, sql_stmt,
                    (char)((option & CUBRID_INCLUDE_OID) ? CCI_PREPARE_INCLUDE_OID : 0), &error)) < 0) {
                goto ERR_CUBRID_EXECUTE;
            }
        } else {
            if ((cubrid_retval =
                        cci_prepare_and_execute(connect->handle, sql_stmt, 0,
                            &exec_retval, &error)) < 0) {
				if(cubrid_retval == CAS_ER_NOT_IMPLEMENTED) {
					if ((cubrid_retval = cci_prepare(connect->handle, sql_stmt, 0, &error)) < 0) {
						goto ERR_CUBRID_EXECUTE;
					}
					if ((exec_retval = cci_execute(cubrid_retval, 0, 0, &error)) < 0) {
						cubrid_retval = exec_retval;
						goto ERR_CUBRID_EXECUTE;
					}
				} else {
					goto ERR_CUBRID_EXECUTE;
				}
            }
        }

        req_handle = cubrid_retval;
        request->handle = req_handle;
        request->l_prepare = 1;
        request->fetch_field_auto_index = 0;

        if ((request->bind_num = cci_get_bind_num(req_handle)) > 0) {
            request->l_bind = (short *) safe_emalloc(request->bind_num, sizeof(short), 0);
            for (i = 0; i < request->bind_num; i++) {
                request->l_bind[i] = 0;
            }
        }
    }

    if (!is_prepare_and_execute_mode) {
        if (option & CUBRID_EXEC_QUERY_ALL) {
            exec_flag |= CCI_EXEC_QUERY_ALL;
        } else if (option & CUBRID_ASYNC) {
            exec_flag |= CCI_EXEC_ASYNC;
        }
    
        if ((exec_retval = cci_execute(req_handle, exec_flag, 0, &error)) < 0) {
            cubrid_retval = exec_retval;
            goto ERR_CUBRID_EXECUTE;
        }
    }
    
    res_col_info = cci_get_result_info(req_handle, &res_sql_type, &res_col_count);
    if (res_sql_type == CUBRID_STMT_SELECT && !res_col_info) {
        cubrid_retval = CUBRID_ER_CANNOT_GET_COLUMN_INFO;
        goto ERR_CUBRID_EXECUTE;
    }

    if (request->lob) {
        close_cubrid_lob_internal(request->lob);
        request->lob = NULL;
    }

    request->col_info = res_col_info;
    request->sql_type = res_sql_type;
    request->col_count = res_col_count;

    connect->sql_type = res_sql_type;

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
        request->row_count = exec_retval;
        if (cubrid_retval > 0 && request->field_lengths == NULL) {
            request->field_lengths = (int *) emalloc (sizeof(int) * res_col_count);
        }

        cubrid_retval = cci_cursor(req_handle, 1, CCI_CURSOR_CURRENT, &error);
        if (cubrid_retval < 0 && cubrid_retval != CCI_ER_NO_MORE_DATA) {
            goto ERR_CUBRID_EXECUTE;
        } 
        
        break;
    case CUBRID_STMT_INSERT:
    case CUBRID_STMT_UPDATE:
    case CUBRID_STMT_DELETE:
        connect->affected_rows = exec_retval;
        break;
    case CUBRID_STMT_CALL:
        request->row_count = exec_retval;
        connect->affected_rows = exec_retval;
    default: 
        break;
    }

    if (!l_prepare) {
        ZEND_REGISTER_RESOURCE(return_value, request, le_request);
        php_cubrid_set_default_req(Z_LVAL_P(return_value) TSRMLS_CC);
        register_cubrid_request(connect, request);
        return;
    }

    RETURN_TRUE;

ERR_CUBRID_EXECUTE:
    if (!l_prepare) {
        close_cubrid_request_internal(request);
    }
    handle_error(cubrid_retval, &error, connect);
    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_next_result)
{
    zval *req_id = NULL;
    T_CUBRID_REQUEST *request;

    T_CCI_COL_INFO *res_col_info;
    T_CCI_CUBRID_STMT res_sql_type;
    int res_col_count = 0;

    int cubrid_retval = 0;
    T_CCI_ERROR error;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);
   
    cubrid_retval = cci_next_result(request->handle, &error);
    if (cubrid_retval == CAS_ER_NO_MORE_RESULT_SET) {
        RETURN_FALSE;
    }

    if (cubrid_retval < 0) {
        handle_error(cubrid_retval, &error, request->conn);
        RETURN_FALSE;
    }

    if (request->field_lengths != NULL) {
        efree(request->field_lengths);
        request->field_lengths = NULL;
    }

    if (request->lob2_list != NULL) {
        int i;
        for (i = 0; i < request->col_count; i++) {
            if (request->lob2_list[i]) {
                request->lob2_list[i]->connect = NULL;
                request->lob2_list[i] = NULL;
            }
        }

        efree(request->lob2_list);
        request->lob2_list = NULL;
    }

    res_col_info = cci_get_result_info(request->handle, &res_sql_type, &res_col_count);
    if (res_sql_type == CUBRID_STMT_SELECT && !res_col_info) {
	RETURN_FALSE;
    }

    request->col_info = res_col_info;
    request->sql_type = res_sql_type;
    request->col_count = res_col_count;

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	request->row_count = cubrid_retval;
        request->field_lengths = (int *) emalloc (sizeof(int) * res_col_count); 
        break;
    case CUBRID_STMT_INSERT:
    case CUBRID_STMT_UPDATE:
    case CUBRID_STMT_DELETE:
        request->conn->affected_rows = cubrid_retval;
        break;
    case CUBRID_STMT_CALL:
        request->row_count = cubrid_retval;
    default:
        break;
    }

    cubrid_retval = cci_cursor(request->handle, 1, CCI_CURSOR_CURRENT, &error);
    if (cubrid_retval < 0 && cubrid_retval != CCI_ER_NO_MORE_DATA) {
        handle_error(cubrid_retval, &error, request->conn);
        RETURN_FALSE;
    }

    request->fetch_field_auto_index = 0;

    request->conn->sql_type = request->sql_type;

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_affected_rows)
{
    zval *id = NULL;
    void *ptr = NULL;
    int type;
    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_REQUEST *request = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &id) == FAILURE) {
	return;
    }

    if (id) {
        ptr = (void *)zend_fetch_resource(&id TSRMLS_CC, -1, NULL, &type, 3, le_connect, le_pconnect, le_request);

        if (type == le_request) {
            request = (T_CUBRID_REQUEST *) ptr;
            connect = request->conn;

        } else if (type == le_connect || type == le_pconnect) {
            connect = (T_CUBRID_CONNECT *) ptr;

        } else {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "A invalid resource has been input.\n");
            RETURN_FALSE;
        }

    } else {
	if (CUBRID_G(last_connect_id) == -1) {
	  RETURN_FALSE;
	}
        
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    switch (connect->sql_type) {
    case CUBRID_STMT_INSERT:
    case CUBRID_STMT_UPDATE:
    case CUBRID_STMT_DELETE:
        RETURN_LONG(connect->affected_rows);
    default:
        handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, connect);
        RETURN_LONG(-1);
    }
}

ZEND_FUNCTION(cubrid_close_request)
{
    zval *req_id= NULL;
    T_CUBRID_REQUEST *request;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);
    zend_list_delete(Z_RESVAL_P(req_id));

    /* On an explicit close of the default request it had a refcount of 2,
     * so we need one more call */
    if (Z_RESVAL_P(req_id) == CUBRID_G(last_request_id)) {
        CUBRID_G(last_request_id) = -1;

        zend_list_delete(Z_RESVAL_P(req_id));
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_current_oid)
{
    zval *req_id= NULL;

    T_CUBRID_REQUEST *request;
    T_CCI_ERROR error;

    char oid_buf[1024];
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (request->sql_type != CUBRID_STMT_SELECT) {
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_FALSE;
    }

    if ((cubrid_retval = cci_fetch(request->handle, &error)) < 0) {
        handle_error(cubrid_retval, &error, request->conn);
        RETURN_FALSE;
    }

    if ((cubrid_retval = cci_get_cur_oid(request->handle, oid_buf)) < 0) {
        handle_error(cubrid_retval, NULL, request->conn);
        RETURN_FALSE;
    }

    RETURN_STRING(oid_buf, 1);
}

ZEND_FUNCTION(cubrid_column_types)
{
    zval *req_id= NULL;
    T_CUBRID_REQUEST *request;

    char full_type_name[128] = {'\0'};
    int i;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);
    
    array_init(return_value);

    for (i = 0; i < request->col_count; i++) {
	if (type2str(&request->col_info[i], full_type_name, sizeof(full_type_name)) < 0) {
	    handle_error(CCI_ER_TYPE_CONVERSION, NULL, request->conn);
            cubrid_array_destroy(return_value->value.ht ZEND_FILE_LINE_CC);
	    RETURN_FALSE;
	}

	add_index_stringl(return_value, i, full_type_name, strlen(full_type_name), 1);
    }

    return;
}

ZEND_FUNCTION(cubrid_column_names)
{
    zval *req_id = NULL;
    T_CUBRID_REQUEST *request;
    char *column_name;

    int i;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);
    
    array_init(return_value);

    for (i = 0; i < request->col_count; i++) {
	column_name = CCI_GET_RESULT_INFO_NAME(request->col_info, i + 1);
	add_index_stringl(return_value, i, column_name, strlen(column_name), 1);
    }

    return;
}

ZEND_FUNCTION(cubrid_move_cursor)
{
    zval *req_id = NULL;
    long offset = 0, origin = CUBRID_CURSOR_CURRENT;

    T_CUBRID_REQUEST *request = NULL;

    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|l", &req_id, &offset, &origin) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    cubrid_retval = cci_cursor(request->handle, offset, origin, &error);
    if (cubrid_retval < 0) {
	handle_error(cubrid_retval, &error, request->conn);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_num_rows)
{
    zval *req_id = NULL;
    T_CUBRID_REQUEST *request = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	RETURN_LONG(request->row_count);
    default:
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_FALSE;
    }
}

ZEND_FUNCTION(cubrid_num_cols)
{
    zval *req_id;
    T_CUBRID_REQUEST *request = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);
    
    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	RETURN_LONG(request->col_count);
    default:
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_FALSE;
    }
}

ZEND_FUNCTION(cubrid_get)
{
    zval *conn_id = NULL, *attr_name = NULL;
    char *oid = NULL;
    int oid_len;
    int attr_count = -1;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;
    int i;

    zval **elem_buf = NULL;
    char **attr = NULL;
    int request_handle;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|z", &conn_id, &oid, &oid_len, &attr_name) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if (attr_name) {
	switch (Z_TYPE_P(attr_name)) {
	case IS_STRING:
	    attr_count = 1;
	    attr = (char **) safe_emalloc(attr_count + 1, sizeof(char *), 0);

	    convert_to_string_ex(&attr_name);

	    attr[0] = estrndup(Z_STRVAL_P(attr_name), Z_STRLEN_P(attr_name));
	    attr[1] = NULL;

	    break;
	case IS_ARRAY:
	    attr_count = zend_hash_num_elements(HASH_OF(attr_name));
	    attr = (char **) safe_emalloc(attr_count + 1, sizeof(char *), 0);

	    for (i = 0; i <= attr_count; i++) {
		attr[i] = NULL;
	    }

	    for (i = 0; i < attr_count; i++) {
		if (zend_hash_index_find(HASH_OF(attr_name), i, (void **) &elem_buf) == FAILURE) {
		    handle_error(CUBRID_ER_INVALID_PARAM, NULL, connect);
		    goto ERR_CUBRID_GET;
		}
		convert_to_string_ex(elem_buf);
		attr[i] = estrdup(Z_STRVAL_P(*elem_buf));
	    }
	    attr[i] = NULL;

	    break;
	default:
	    handle_error(CUBRID_ER_INVALID_PARAM, NULL, connect);
	    RETURN_FALSE;
	}
    }

    cubrid_retval = cci_oid_get(connect->handle, oid, attr, &error);
    if (cubrid_retval < 0) {
	handle_error(cubrid_retval, &error, connect);
	goto ERR_CUBRID_GET;
    }
    request_handle = cubrid_retval;

    /* free memory before return */
    for (i = 0; i < attr_count; i++) {
 	if (attr[i]) {
	    efree(attr[i]);   
	}
    }

    if (attr) {
	efree(attr);
    }

    if (attr_name && Z_TYPE_P(attr_name) == IS_STRING) {
	char *result;
	int ind;

	cubrid_retval = cci_get_data(request_handle, 1, CCI_A_TYPE_STR, &result, &ind);
	if (cubrid_retval < 0) {
	    handle_error(cubrid_retval, &error, connect);
	    RETURN_FALSE;
	}

	if (ind < 0) {
	    RETURN_FALSE;
	} else {
	    RETURN_STRINGL(result, ind, 1);
	}
    } else {
	if ((cubrid_retval = fetch_a_row(return_value, connect, request_handle, NULL, CUBRID_ASSOC TSRMLS_CC)) != SUCCESS) {
	    handle_error(cubrid_retval, NULL, connect);
	    RETURN_FALSE;
	}
    }

    return;

ERR_CUBRID_GET:

    for (i = 0; i < attr_count; i++) {
 	if (attr[i]) {
	    efree(attr[i]);   
	}
    }

    if (attr) {
	efree(attr);
    }
    
    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_put)
{
    zval *conn_id = NULL, *attr_value = NULL;
    char *oid = NULL, *attr = NULL;
    int oid_len, attr_len;

    char **attr_name = NULL;
    int attr_count = 0;
    int *attr_type = NULL;

    T_CUBRID_CONNECT *connect;
    T_CCI_SET temp_set = NULL;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    void **value = NULL;
    char *key = NULL;
    ulong index;
    zval **data = NULL;
    int i;

    init_error();

    switch (ZEND_NUM_ARGS()) {
    case 3:
	if (zend_parse_parameters(3 TSRMLS_CC, "rsa", &conn_id, &oid, &oid_len, &attr_value) == FAILURE) {
	    return;
	}

	ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

	attr_count = zend_hash_num_elements(HASH_OF(attr_value));
	zend_hash_internal_pointer_reset(HASH_OF(attr_value));

	attr_name = (char **) safe_emalloc(attr_count + 1, sizeof(char *), 0);
	value = safe_emalloc(attr_count + 1, sizeof(char *), 0);
	attr_type = (int *) safe_emalloc(attr_count + 1, sizeof(int), 0);

	if (attr_count > 0) {
	    for (i = 0; i < attr_count; i++) {
		if (zend_hash_get_current_key(HASH_OF(attr_value), &key, &index, 1) == HASH_KEY_NON_EXISTANT) {
		    break;
		}

		attr_name[i] = (char *) safe_emalloc(strlen(key) + 1, sizeof(char), 0);
		strlcpy(attr_name[i], key, strlen(key) + 1);
		value[i] = NULL;
		attr_type[i] = 0;

		efree(key);

		zend_hash_get_current_data(HASH_OF(attr_value), (void **) &data);
		switch (Z_TYPE_PP(data)) {
		case IS_NULL:
		    value[i] = NULL;

		    break;
		case IS_LONG:
		case IS_DOUBLE:
		    convert_to_string_ex(data);
		case IS_STRING:
		    value[i] = (char *) safe_emalloc(Z_STRLEN_PP(data) + 1, sizeof(char), 0);
		    strlcpy(value[i], Z_STRVAL_PP(data), Z_STRLEN_PP(data) + 1);
		    attr_type[i] = CCI_A_TYPE_STR;

		    break;
		case IS_ARRAY:
		    cubrid_retval = cubrid_make_set(HASH_OF(*data), &temp_set);
		    if (cubrid_retval < 0) {
			handle_error(cubrid_retval, NULL, connect);
			goto ERR_CUBRID_PUT;
		    }

		    value[i] = temp_set;
		    attr_type[i] = CCI_A_TYPE_SET;

		    break;
		case IS_OBJECT:
		case IS_BOOL:
		case IS_RESOURCE:
		case IS_CONSTANT:
		    cubrid_retval = -1;
		    handle_error(CUBRID_ER_NOT_SUPPORTED_TYPE, NULL, connect);
		    goto ERR_CUBRID_PUT;
		}

		zend_hash_move_forward(HASH_OF(attr_value));
	    }

	    attr_name[attr_count] = NULL;
	    value[attr_count] = NULL;	
	}

	break;
    case 4:
	if (zend_parse_parameters(4 TSRMLS_CC, "rssz", 
		    &conn_id, &oid, &oid_len, &attr, &attr_len, &attr_value) == FAILURE) {
	    return;
	}

	ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

	attr_count = 1;

	attr_name = (char **) safe_emalloc(attr_count + 1, sizeof(char *), 0);
	value = safe_emalloc(attr_count + 1, sizeof(char *), 0);
	attr_type = safe_emalloc(attr_count + 1, sizeof(int), 0);

	attr_name[0] = (char *) safe_emalloc (attr_len + 1, sizeof(char), 0);
	strlcpy(attr_name[0], attr, attr_len + 1);
	attr_name[1] = NULL;

	value[0] = NULL;
	attr_type[0] = 0;

	switch (Z_TYPE_P(attr_value)) {
	case IS_NULL:
	    value[0] = NULL;
	    
	    break;
	case IS_LONG:
	case IS_DOUBLE:
	    convert_to_string_ex(&attr_value);
	case IS_STRING:
	    value[0] = (char *) safe_emalloc(Z_STRLEN_P(attr_value) + 1, sizeof(char), 0);
	    strlcpy(value[0], Z_STRVAL_P(attr_value), Z_STRLEN_P(attr_value) + 1);
	    attr_type[0] = CCI_A_TYPE_STR;

	    break;
	case IS_ARRAY:
	    cubrid_retval = cubrid_make_set(HASH_OF(attr_value), &temp_set);
	    if (cubrid_retval < 0) {
		handle_error(cubrid_retval, NULL, connect);
		goto ERR_CUBRID_PUT;
	    }

	    value[0] = temp_set;
	    attr_type[0] = CCI_A_TYPE_SET;

	    break;
	case IS_OBJECT:
	case IS_BOOL:
	case IS_RESOURCE:
	case IS_CONSTANT:
	    cubrid_retval = -1;
	    handle_error(CUBRID_ER_NOT_SUPPORTED_TYPE, NULL, connect);
	    goto ERR_CUBRID_PUT;
	}
	value[1] = NULL;

	break;
    default:
	WRONG_PARAM_COUNT;
    }

    cubrid_retval = cci_oid_put2(connect->handle, oid, attr_name, value, attr_type, &error);
    if (cubrid_retval < 0) {
	handle_error(cubrid_retval, &error, connect);
	goto ERR_CUBRID_PUT;
    }

ERR_CUBRID_PUT:

    if (attr_name) {
	for (i = 0; i < attr_count; i++) {
	    if (attr_name[i])
		efree(attr_name[i]);
	}
	efree(attr_name);
    }

    if (value) {
	for (i = 0; i < attr_count; i++) {
	    switch (attr_type[i]) {
	    case CCI_A_TYPE_SET:
		cci_set_free(value[i]);
		break;
	    default:
		if (value[i]) {
		    efree(value[i]);
		}
		break;
	    }
	}
	efree(value);
    }

    if (attr_type) {
	efree(attr_type);
    }

    if (cubrid_retval < 0) {
	RETURN_FALSE;
    } else {
	RETURN_TRUE;
    }
}

ZEND_FUNCTION(cubrid_drop)
{
    zval *conn_id = NULL;
    char *oid = NULL;
    int oid_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &conn_id, &oid, &oid_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_oid(connect->handle, CCI_OID_DROP, oid, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE; 
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_is_instance)
{
    zval *conn_id = NULL;
    char *oid = NULL;
    int oid_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &conn_id, &oid, &oid_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_oid(connect->handle, CCI_OID_IS_INSTANCE, oid, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_LONG(-1);
    }

    RETURN_LONG(cubrid_retval);
}

ZEND_FUNCTION(cubrid_lock_read)
{
    zval *conn_id = NULL;
    char *oid = NULL;
    int oid_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &conn_id, &oid, &oid_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_oid(connect->handle, CCI_OID_LOCK_READ, oid, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE; 
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lock_write)
{
    zval *conn_id = NULL;
    char *oid = NULL;
    int oid_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &conn_id, &oid, &oid_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_oid(connect->handle, CCI_OID_LOCK_WRITE, oid, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE; 
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_get_class_name)
{
    zval *conn_id = NULL;
    char *oid = NULL;
    int oid_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;
    char out_buf[1024];

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &conn_id, &oid, &oid_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_oid_get_class_name(connect->handle, oid, out_buf, 1024, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_STRING(out_buf, 1);
}

ZEND_FUNCTION(cubrid_schema)
{
    zval *conn_id = NULL;
    char *class_name = NULL, *attr_name = NULL;
    long schema_type;
    int class_name_len, attr_name_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;

    int flag = 0;
    int cubrid_retval = 0;
    int request_handle;
    int i = 0;
    zval *temp_element = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|ss", 
		&conn_id, &schema_type, &class_name, &class_name_len, &attr_name, &attr_name_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    switch (schema_type) {
    case CUBRID_SCH_CLASS:
    case CUBRID_SCH_VCLASS:
    case CUBRID_SCH_TRIGGER:
	flag = CCI_CLASS_NAME_PATTERN_MATCH;
	break;
    case CUBRID_SCH_ATTRIBUTE:
    case CUBRID_SCH_CLASS_ATTRIBUTE:
	flag = CCI_ATTR_NAME_PATTERN_MATCH;
	break;
    default:
	flag = 0;
	break;
    }

    if ((cubrid_retval = cci_schema_info(connect->handle, 
		    schema_type, class_name, attr_name, (char) flag, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    request_handle = cubrid_retval;

    array_init(return_value);

    for (i = 0; ; i++) {
	cubrid_retval = cci_cursor(request_handle, 1, CCI_CURSOR_CURRENT, &error);
	if (cubrid_retval == CCI_ER_NO_MORE_DATA) {
	    break;
	}

	if (cubrid_retval < 0) {
	    handle_error(cubrid_retval, &error, connect);
            goto ERR_CUBRID_SCHEMA;
	}

	if ((cubrid_retval = cci_fetch(request_handle, &error)) < 0) {
	    handle_error(cubrid_retval, &error, connect);
            goto ERR_CUBRID_SCHEMA;
	}

	MAKE_STD_ZVAL(temp_element);
	if ((cubrid_retval = fetch_a_row(temp_element, connect, request_handle, NULL, CUBRID_ASSOC TSRMLS_CC)) != SUCCESS) {
	    handle_error(cubrid_retval, NULL, connect);
	    FREE_ZVAL(temp_element);
            goto ERR_CUBRID_SCHEMA;
	}

	zend_hash_index_update(Z_ARRVAL_P(return_value), i, (void *) &temp_element, sizeof(zval *), NULL);
    }

    cci_close_req_handle(request_handle);

    return;

ERR_CUBRID_SCHEMA:

    cubrid_array_destroy(return_value->value.ht ZEND_FILE_LINE_CC);
    cci_close_req_handle(request_handle);

    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_col_size)
{
    zval *conn_id = NULL;
    char *oid = NULL, *attr_name = NULL;
    int oid_len, attr_name_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;
    int col_size = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", 
		&conn_id, &oid, &oid_len, &attr_name, &attr_name_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);
    
    if ((cubrid_retval = cci_col_size(connect->handle, oid, attr_name, &col_size, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_LONG(col_size);
}

ZEND_FUNCTION(cubrid_col_get)
{
    zval *conn_id = NULL;
    char *oid = NULL, *attr_name = NULL;
    int oid_len, attr_name_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    char *res_buf;
    int ind;
    int col_size;
    int col_type;
    int request_handle;
    int i = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", 
		&conn_id, &oid, &oid_len, &attr_name, &attr_name_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_col_get(connect->handle, oid, attr_name, &col_size, &col_type, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    request_handle = cubrid_retval;

    array_init(return_value);

    for (i = 0; ;i++) {
	cubrid_retval = cci_cursor(request_handle, 1, CCI_CURSOR_CURRENT, &error);
	if (cubrid_retval == CCI_ER_NO_MORE_DATA) {
	    break;
	}

	if (cubrid_retval < 0) {
	    handle_error(cubrid_retval, &error, connect);
            goto ERR_CUBRID_COL_GET;
	}

	if ((cubrid_retval = cci_fetch(request_handle, &error)) < 0) {
	    handle_error(cubrid_retval, &error, connect);
            goto ERR_CUBRID_COL_GET;
	}

	if ((cubrid_retval = cci_get_data(request_handle, 1, CCI_A_TYPE_STR, &res_buf, &ind)) < 0) {
	    handle_error(cubrid_retval, NULL, connect);
            goto ERR_CUBRID_COL_GET;
	}

	if (ind < 0) {
	    add_index_unset(return_value, i);
	} else {
	    add_index_stringl(return_value, i, res_buf, ind, 1);
	}
    }

    cci_close_req_handle(request_handle);

    return;

ERR_CUBRID_COL_GET:

    cubrid_array_destroy(return_value->value.ht ZEND_FILE_LINE_CC);
    cci_close_req_handle(request_handle);

    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_set_add)
{
    zval *conn_id = NULL;
    char *oid = NULL, *attr_name = NULL, *set_element = NULL;
    int oid_len, attr_name_len, set_element_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss", 
		&conn_id, &oid, &oid_len, &attr_name, &attr_name_len, &set_element, &set_element_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_col_set_add(connect->handle, oid, attr_name, set_element, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_set_drop)
{
    zval *conn_id = NULL;
    char *oid = NULL, *attr_name = NULL, *set_element = NULL;
    int oid_len, attr_name_len, set_element_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsss", 
		&conn_id, &oid, &oid_len, &attr_name, &attr_name_len, &set_element, &set_element_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_col_set_drop(connect->handle, oid, attr_name, set_element, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_seq_insert)
{
    zval *conn_id = NULL;
    char *oid = NULL, *attr_name = NULL, *seq_element = NULL;
    long index = -1;
    int oid_len, attr_name_len, seq_element_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssls", 
		&conn_id, &oid, &oid_len, &attr_name, &attr_name_len, &index, 
		&seq_element, &seq_element_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_col_seq_insert(connect->handle, oid, attr_name, index, seq_element, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_seq_put)
{
    zval *conn_id = NULL;
    char *oid = NULL, *attr_name = NULL, *seq_element = NULL;
    long index = -1;
    int oid_len, attr_name_len, seq_element_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssls", 
		&conn_id, &oid, &oid_len, &attr_name, &attr_name_len, &index, 
		&seq_element, &seq_element_len) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_col_seq_put(connect->handle, oid, attr_name, index, seq_element, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_seq_drop)
{
    zval *conn_id = NULL;
    char *oid = NULL, *attr_name = NULL;
    long index = -1;
    int oid_len, attr_name_len;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssl", 
		&conn_id, &oid, &oid_len, &attr_name, &attr_name_len, &index) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_col_seq_drop(connect->handle, oid, attr_name, index, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_get_autocommit)
{
    zval *conn_id = NULL;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    CCI_AUTOCOMMIT_MODE mode;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((mode = cci_get_autocommit(connect->handle)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        return; 
    }

    if (mode == CCI_AUTOCOMMIT_TRUE) {
	RETURN_TRUE;
    }

    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_set_autocommit)
{
    zval *conn_id = NULL;
    zend_bool param = FALSE;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    CCI_AUTOCOMMIT_MODE mode;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rb", &conn_id, &param) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    mode = param ? CCI_AUTOCOMMIT_TRUE : CCI_AUTOCOMMIT_FALSE;
    if ((cubrid_retval = cci_set_autocommit(connect->handle, mode)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }
	
    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_commit)
{
    zval *conn_id = NULL;
    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_end_tran(connect->handle, CCI_TRAN_COMMIT, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_rollback)
{
    zval *conn_id = NULL;
    
    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if ((cubrid_retval = cci_end_tran(connect->handle, CCI_TRAN_ROLLBACK, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_error_msg)
{
    if (zend_parse_parameters_none() == FAILURE) {
	return;
    }

    RETURN_STRING(CUBRID_G(recent_error).msg, 1);
}

ZEND_FUNCTION(cubrid_error_code)
{
    if (zend_parse_parameters_none() == FAILURE) {
	return;
    }

    RETURN_LONG(CUBRID_G(recent_error).code);
}

ZEND_FUNCTION(cubrid_error_code_facility)
{
    if (zend_parse_parameters_none() == FAILURE) {
	return;
    }

    RETURN_LONG(CUBRID_G(recent_error).facility);
}

ZEND_FUNCTION(cubrid_error)
{
    zval *conn_id = NULL;
    T_CUBRID_CONNECT *connect = NULL;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &conn_id) == FAILURE) {
	return;
    }
    
    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
	if (CUBRID_G(last_connect_id) == -1) {
            if (CUBRID_G(recent_error).msg[0] != 0) {
                RETURN_STRING(CUBRID_G(recent_error).msg, 1);
            } else {
                RETURN_FALSE;
            }
        } 

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    RETURN_STRING(connect->recent_error.msg, 1);
}

ZEND_FUNCTION(cubrid_errno)
{
    zval *conn_id = NULL;
    T_CUBRID_CONNECT *connect = NULL;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &conn_id) == FAILURE) {
	return;
    }

    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
	if (CUBRID_G(last_connect_id) == -1) {
            if (CUBRID_G(recent_error).code != 0) {
                RETURN_LONG(CUBRID_G(recent_error).code);
            } else {
                RETURN_FALSE;
            }
        } 

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    RETURN_LONG(connect->recent_error.code);
}

ZEND_FUNCTION(cubrid_field_name)
{
    zval *req_id = NULL;
    long offset = -1;

    T_CUBRID_REQUEST *request;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (offset < 0 || offset >= request->col_count) {
	handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
	RETURN_FALSE;
    }

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	RETURN_STRING(request->col_info[offset].col_name, 1);
    default:
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_LONG(-1);
    }
}

ZEND_FUNCTION(cubrid_field_table)
{
    zval *req_id = NULL;
    long offset = -1;

    T_CUBRID_REQUEST *request;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (offset < 0 || offset >= request->col_count) {
	handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
	RETURN_FALSE;
    }

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	RETURN_STRING(request->col_info[offset].class_name, 1);
    default:
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_LONG(-1);
    }
}

ZEND_FUNCTION(cubrid_field_type)
{
    zval *req_id = NULL;
    long offset = -1;
    T_CUBRID_REQUEST *request;
    char string_type[128] = {'\0'};

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (offset < 0 || offset >= request->col_count) {
	handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
	RETURN_FALSE;
    }

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	type2str(&request->col_info[offset], string_type, sizeof(string_type));
	RETURN_STRING(string_type, 1);
    default:
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_LONG(-1);
    }
}

ZEND_FUNCTION(cubrid_field_flags)
{
    zval *req_id = NULL; 
    long offset = -1;
    T_CUBRID_REQUEST *request;
    int n;
    char sz[1024] = {'\0'};

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (offset < 0 || offset >= request->col_count) {
	handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
	RETURN_FALSE;
    }

    if (request->col_info[offset].is_non_null) {
	strcat(sz, "not_null ");
    }

    if (request->col_info[offset].is_primary_key) {
	strcat(sz, "primary_key ");
    }

    if (request->col_info[offset].is_unique_key) {
	strcat(sz, "unique_key ");
    }

    if (request->col_info[offset].is_foreign_key) {
	strcat(sz, "foreign_key ");
    }

    if (request->col_info[offset].is_auto_increment) {
	strcat(sz, "auto_increment ");
    }

    if (request->col_info[offset].is_shared) {
	strcat(sz, "shared ");
    }

    if (request->col_info[offset].is_reverse_index) {
	strcat(sz, "reverse_index ");
    }

    if (request->col_info[offset].is_reverse_unique) {
	strcat(sz, "reverse_unique ");
    }

    if (request->col_info[offset].ext_type == CCI_U_TYPE_TIMESTAMP) {
	strcat(sz, "timestamp ");
    }

    n = strlen(sz);
    if (n > 0 && sz[n - 1] == ' ') {
	sz[n - 1] = 0;
    }

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	RETURN_STRING(sz, 1);
    default:
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_LONG(-1);
    }
}

ZEND_FUNCTION(cubrid_data_seek)
{
    zval *req_id = NULL;
    long offset = -1;

    T_CUBRID_REQUEST *request;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (request->row_count == 0) {
	php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of rows is NULL.\n");
	RETURN_FALSE;
    }

    if ((cubrid_retval = cci_cursor(request->handle, offset + 1, CUBRID_CURSOR_FIRST, &error)) < 0) {
	handle_error(cubrid_retval, &error, request->conn);
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_fetch)
{
    php_cubrid_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
}

ZEND_FUNCTION(cubrid_fetch_array)
{
    php_cubrid_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
}

ZEND_FUNCTION(cubrid_fetch_assoc)
{
    php_cubrid_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, CUBRID_ASSOC, 0);
}

ZEND_FUNCTION(cubrid_fetch_row)
{
    php_cubrid_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, CUBRID_NUM, 0);
}

ZEND_FUNCTION(cubrid_fetch_object)
{
    php_cubrid_fetch_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, CUBRID_BOTH | CUBRID_OBJECT, 1);
}

ZEND_FUNCTION(cubrid_fetch_field)
{
    zval *req_id = NULL;
    long offset = 0;

    T_CUBRID_REQUEST *request = NULL;

    zend_bool is_numeric = 0, is_blob = 0;
    int max_length = 0;

    int res = 0;
    char *buffer = NULL;
    char string_type[128];

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (ZEND_NUM_ARGS() == 1) {
	offset = request->fetch_field_auto_index++;

        if (offset >= request->col_count) {
            RETURN_FALSE;
        }

    } else if (ZEND_NUM_ARGS() == 2) {
	request->fetch_field_auto_index = offset + 1;
    
        if (offset < 0 || offset >= request->col_count) {
    	    handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
    	    RETURN_FALSE;
        }
    }

    array_init(return_value);

    is_numeric = numeric_type(request->col_info[offset].ext_type);
    max_length = 0;
    is_blob = (request->col_info[offset].ext_type == CCI_U_TYPE_BLOB)?1:0;

    add_assoc_string(return_value, "name", request->col_info[offset].col_name, 1);
    add_assoc_string(return_value, "table", request->col_info[offset].class_name, 1);

    if (is_numeric) {
        if (request->col_info[offset].default_value[0] == '\0') {
            add_assoc_null(return_value, "def");
        } else {
            add_assoc_string(return_value, "def", request->col_info[offset].default_value, 1);
        }
    } else {
        add_assoc_string(return_value, "def", request->col_info[offset].default_value, 1);
    }

    add_assoc_long(return_value, "max_length", max_length);
    add_assoc_long(return_value, "not_null", request->col_info[offset].is_non_null);
    add_assoc_long(return_value, "primary_key", request->col_info[offset].is_primary_key);
    add_assoc_long(return_value, "unique_key", request->col_info[offset].is_unique_key);
    add_assoc_long(return_value, "multiple_key", !request->col_info[offset].is_unique_key);
    add_assoc_long(return_value, "numeric", is_numeric);
    add_assoc_long(return_value, "blob", is_blob);

    type2str(&request->col_info[offset], string_type, sizeof(string_type));
    add_assoc_string(return_value, "type", string_type, 1);

    add_assoc_long(return_value, "unsigned", 0);
    add_assoc_long(return_value, "zerofill", 0);

    if (return_value->type == IS_ARRAY) {
	convert_to_object(return_value);
    }

    return;
}

ZEND_FUNCTION(cubrid_num_fields)
{
    zval *req_id = NULL;
    T_CUBRID_REQUEST *request;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
	RETURN_LONG(request->col_count);
    default:
	handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, request->conn);
	RETURN_LONG(-1);
    }
}

ZEND_FUNCTION(cubrid_free_result)
{
    zval *req_id = NULL;
    T_CUBRID_REQUEST *request;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	RETURN_FALSE;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if ((cubrid_retval = cci_fetch_buffer_clear(request->handle)) < 0) {
	handle_error(cubrid_retval, NULL, request->conn); 
	RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_fetch_lengths)
{
    zval *req_id = NULL;

    T_CUBRID_REQUEST *request;
    int col;
    long len = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (request->field_lengths == NULL) {
        handle_error (CUBRID_ER_CANNOT_GET_COLUMN_INFO, NULL, request->conn);
        RETURN_FALSE;
    }

    array_init(return_value);

    for (col = 0; col < request->col_count; col++) {
	add_index_long(return_value, col, request->field_lengths[col]);
    }

    return;
}

ZEND_FUNCTION(cubrid_field_seek)
{
    zval *req_id = NULL;
    long offset = 0;
    T_CUBRID_REQUEST *request = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (offset < 0 || offset > request->col_count - 1) {
	handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
	RETURN_FALSE;
    }

    /* Set the offset which will be used by cubrid_fetch_field() */
    request->fetch_field_auto_index = offset;

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_field_len)
{
    zval *req_id = NULL;
    long offset = 0;

    T_CUBRID_REQUEST *request;
    long len = 0;
    T_CCI_U_TYPE type;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &req_id, &offset) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if (offset < 0 || offset > request->col_count - 1) {
	handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
	RETURN_FALSE;
    }

    type = CCI_GET_COLLECTION_DOMAIN(CCI_GET_RESULT_INFO_TYPE(request->col_info, offset + 1));
    if ((len = get_cubrid_u_type_len(type)) == -1) {
	len = CCI_GET_RESULT_INFO_PRECISION(request->col_info, offset + 1); 
	if (type == CCI_U_TYPE_NUMERIC) {
	    len += 2; /* "," + "-" */
	}
    }

    if (CCI_IS_COLLECTION_TYPE(CCI_GET_RESULT_INFO_TYPE(request->col_info, offset + 1))) {
	len = MAX_LEN_SET;
    }

    RETURN_LONG(len);
}

ZEND_FUNCTION(cubrid_result)
{
    zval *req_id = NULL;
    long row_offset = 0;
    zval *column = NULL;

    long col_offset = 0;
    char *col_name = NULL;
    long col_name_len = 0;

    int cubrid_retval = 0;
    int i;

    T_CUBRID_REQUEST *request = NULL;
    T_CCI_ERROR error;

    char *res_buf = NULL;
    int ind = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|z", &req_id, &row_offset, &column) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if ((cubrid_retval = cci_cursor(request->handle, row_offset + 1, 
		    CCI_CURSOR_FIRST, &error)) == CCI_ER_NO_MORE_DATA) {
	RETURN_FALSE;
    } else if (cubrid_retval < 0) {
        handle_error(cubrid_retval, &error, request->conn);
        RETURN_FALSE;
    }

    if (column) {
	switch (Z_TYPE_P(column)) {
	case IS_STRING: {
            char *table_name = NULL, *field_name = NULL, *tmp = NULL;

	    convert_to_string_ex(&column);
	    col_name = Z_STRVAL_P(column);
	    col_name_len = Z_STRLEN_P(column);

            tmp = strchr(col_name, '.');

	    for (i = 0; i < request->col_count; i++) {
		if (tmp == NULL) {
                    if (strcmp(request->col_info[i].col_name, col_name) == 0) {
		        col_offset = i;
		        break;
                    }
		} else { 
                    if ((strcmp(request->col_info[i].col_name, tmp + 1) == 0) && 
                            (strncmp(request->col_info[i].class_name, col_name, tmp - col_name) == 0)) {
                        col_offset = i;
                        break;
                    }
                }
	    }

	    if (i == request->col_count) {
		handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
		RETURN_FALSE;
	    }
        }
	    break;
	case IS_LONG:
	    convert_to_long_ex(&column);
	    col_offset = Z_LVAL_P(column); 

	    if (col_offset < 0 || col_offset >= request->col_count) {
		handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
		RETURN_FALSE;
	    }

	    break;
	default:
	    handle_error(CCI_ER_COLUMN_INDEX, NULL, request->conn);
	    RETURN_FALSE;
	}
    }

    if ((cubrid_retval = cci_fetch(request->handle, &error)) < 0) {
	handle_error(cubrid_retval, &error, request->conn);
	RETURN_FALSE;
    }

    if ((cubrid_retval = cci_get_data(request->handle, col_offset + 1, 
		    CCI_A_TYPE_STR, &res_buf, &ind)) < 0) {
	handle_error(cubrid_retval, NULL, request->conn);
	RETURN_FALSE;
    }

    if (ind == -1) {
	return; 
    } else {
	RETURN_STRINGL(res_buf, ind, 1);
    }
}

ZEND_FUNCTION(cubrid_unbuffered_query)
{
    zval *conn_id = NULL;
    char *query = NULL;
    int query_len;

    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_REQUEST *request = NULL;
    T_CCI_ERROR error;

    T_CCI_COL_INFO *res_col_info = NULL;
    T_CCI_CUBRID_STMT res_sql_type = 0;
    int res_col_count = 0;

    int cubrid_retval = 0;
    int req_handle = 0;
    int req_id = 0;

    init_error ();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &query, &query_len, &conn_id) == FAILURE) {
	return;
    }

    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
	if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE; 
        } 

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    request = new_cubrid_request();
    if (!request) {
        handle_error(CCI_ER_REQ_HANDLE, NULL, connect);
        RETURN_FALSE;
    }
    request->conn = connect;

    if ((cubrid_retval = cci_prepare(connect->handle, query, 0, &error)) < 0) {
        goto ERR_CUBRID_UNBUFFERED_QUERY;
    }

    req_handle = cubrid_retval;
    request->handle = req_handle;

    if ((cubrid_retval = cci_execute(req_handle, CCI_EXEC_ASYNC, 0, &error)) < 0) {
        goto ERR_CUBRID_UNBUFFERED_QUERY;
    }

    res_col_info = cci_get_result_info(req_handle, &res_sql_type, &res_col_count);
    request->sql_type = res_sql_type;

    if (res_sql_type == CUBRID_STMT_SELECT && !res_col_info) {
        cubrid_retval = CUBRID_ER_CANNOT_GET_COLUMN_INFO;
        goto ERR_CUBRID_UNBUFFERED_QUERY;

    } else if (res_sql_type == CUBRID_STMT_SELECT) {
        request->col_info = res_col_info;
        request->col_count = res_col_count;
    }

    connect->sql_type = request->sql_type;
    request->fetch_field_auto_index = 0;

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
        request->row_count = cubrid_retval;
        if (cubrid_retval > 0 && request->field_lengths == NULL) {
            request->field_lengths = (int *) emalloc (sizeof(int) * res_col_count);
        }

        cubrid_retval = cci_cursor(req_handle, 1, CCI_CURSOR_CURRENT, &error);
        if (cubrid_retval < 0 && cubrid_retval != CCI_ER_NO_MORE_DATA) {
            goto ERR_CUBRID_UNBUFFERED_QUERY;
        }

        CUBRID_G(last_request_id) = req_id;
        connect->affected_rows = 0;

        break;
    case CUBRID_STMT_INSERT:
    case CUBRID_STMT_UPDATE:
    case CUBRID_STMT_DELETE:
        connect->affected_rows = cubrid_retval;
        break;
    case CUBRID_STMT_CALL:
        request->row_count = cubrid_retval;
        connect->affected_rows = cubrid_retval;
    default:
        break;
    }

    req_id = ZEND_REGISTER_RESOURCE(return_value, request, le_request);
    register_cubrid_request(connect, request);
    php_cubrid_set_default_req(Z_LVAL_P(return_value) TSRMLS_CC);

    if (request->sql_type == CUBRID_STMT_SELECT) {
        return;
    }

    RETURN_TRUE;

ERR_CUBRID_UNBUFFERED_QUERY:
    close_cubrid_request_internal(request);
    handle_error(cubrid_retval, &error, connect);
    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_query)
{
    zval *conn_id = NULL;
    char *query = NULL;
    int query_len;

    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_REQUEST *request = NULL;
    T_CCI_ERROR error;

    T_CCI_COL_INFO *res_col_info = NULL;
    T_CCI_CUBRID_STMT res_sql_type = 0;
    int res_col_count = 0;

    int cubrid_retval = 0;
    int exec_retval = 0;
    int req_handle = 0;
    int req_id = 0;

    init_error ();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &query, &query_len, &conn_id) == FAILURE) {
	return;
    }

    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
	if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE; 
        } 

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    request = new_cubrid_request();
    if (!request) {
        handle_error(CCI_ER_REQ_HANDLE, NULL, connect);
        RETURN_FALSE;
    }
    request->conn = connect;

	if ((cubrid_retval =
		cci_prepare_and_execute(connect->handle, query, 0, &exec_retval, &error)) < 0) {
			if(cubrid_retval == CAS_ER_NOT_IMPLEMENTED) {
				if ((cubrid_retval = cci_prepare(connect->handle, query, 0, &error)) < 0) {
					goto ERR_CUBRID_QUERY;
				}
				if ((exec_retval = cci_execute(cubrid_retval, 0, 0, &error)) < 0) {
					cubrid_retval = exec_retval;
					goto ERR_CUBRID_QUERY;
				}
			} else {
				goto ERR_CUBRID_QUERY;
			}
	}

    req_handle = cubrid_retval;
    request->handle = req_handle;

    res_col_info = cci_get_result_info(req_handle, &res_sql_type, &res_col_count);
    request->sql_type = res_sql_type;

    if (res_sql_type == CUBRID_STMT_SELECT && !res_col_info) {
        cubrid_retval = CUBRID_ER_CANNOT_GET_COLUMN_INFO;
        goto ERR_CUBRID_QUERY;

    } else if (res_sql_type == CUBRID_STMT_SELECT) {
        request->col_info = res_col_info;
        request->col_count = res_col_count;
    }

    connect->sql_type = request->sql_type;
    request->fetch_field_auto_index = 0;

    switch (request->sql_type) {
    case CUBRID_STMT_SELECT:
        request->row_count = exec_retval;
        if (cubrid_retval > 0 && request->field_lengths == NULL) {
            request->field_lengths = (int *) emalloc (sizeof(int) * res_col_count);
        }

        cubrid_retval = cci_cursor(req_handle, 1, CCI_CURSOR_CURRENT, &error);
        if (cubrid_retval < 0 && cubrid_retval != CCI_ER_NO_MORE_DATA) {
            goto ERR_CUBRID_QUERY;
        }

        CUBRID_G(last_request_id) = req_id;
        connect->affected_rows = -1;

        break;
    case CUBRID_STMT_INSERT:
    case CUBRID_STMT_UPDATE:
    case CUBRID_STMT_DELETE:
        CUBRID_G(last_request_id) = req_id;
        connect->affected_rows = exec_retval;
        break;
    case CUBRID_STMT_CALL:
        request->row_count = exec_retval;
        connect->affected_rows = exec_retval;
    default:
        break;
    }

    req_id = ZEND_REGISTER_RESOURCE(return_value, request, le_request);
    php_cubrid_set_default_req(Z_LVAL_P(return_value) TSRMLS_CC);
    register_cubrid_request(connect, request);

    if (request->sql_type == CUBRID_STMT_SELECT) {
        return;
    }

    RETURN_TRUE;

ERR_CUBRID_QUERY:
    close_cubrid_request_internal(request);
    handle_error(cubrid_retval, &error, connect);
    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_get_charset)
{
    zval *conn_id = NULL;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;

    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);
    
    if ((cubrid_retval = cubrid_get_charset_internal (connect->handle, &error)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }

    RETURN_STRING((char *) db_charsets[cubrid_retval].charset_name, 1);
}

ZEND_FUNCTION(cubrid_client_encoding)
{
    zval *conn_id = NULL;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;

    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &conn_id) == FAILURE) {
	return;
    }

    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
	if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE; 
        } 

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    if ((cubrid_retval = cubrid_get_charset_internal (connect->handle, &error)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }

    RETURN_STRING((char *) db_charsets[cubrid_retval].charset_name, 1);
}

ZEND_FUNCTION(cubrid_get_client_info)
{
    int major, minor, patch;
    char info[128];

    init_error();

    if (zend_parse_parameters_none() == FAILURE) {
	return;
    }

    cci_get_version(&major, &minor, &patch);

    snprintf(info, sizeof(info), "%d.%d.%d", major, minor, patch);

    RETURN_STRING(info, 1);
}

ZEND_FUNCTION(cubrid_get_server_info)
{
    zval *conn_id = NULL;
    T_CUBRID_CONNECT *connect;

    char buf[64];

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_id) == FAILURE) {
	return;
    }
    
    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    cci_get_db_version(connect->handle, buf, sizeof(buf));

    RETURN_STRING(buf, 1);
}

ZEND_FUNCTION(cubrid_real_escape_string)
{
    zval *conn_id = NULL;

    char *unescaped_str = NULL;
    int unescaped_str_len = 0;

    char *escaped_str;
    int escaped_str_len = 0;

    int cubrid_retval = 0;
    T_CUBRID_CONNECT *connect = NULL;
    T_CCI_ERROR error;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", 
		&unescaped_str, &unescaped_str_len, &conn_id) == FAILURE) {
	return;
    }

    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
        if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE;
        }

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    escaped_str = (char *) emalloc(2 * unescaped_str_len + 16);
    if (!escaped_str) {
        handle_error(CCI_ER_NO_MORE_MEMORY, NULL, connect);
        RETURN_FALSE;
    }

    memset(escaped_str, 0, 2 * unescaped_str_len + 16);

    if ((cubrid_retval = cci_escape_string (connect->handle,
                    escaped_str, unescaped_str, unescaped_str_len, &error)) < 0) {
        free (escaped_str);
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }

    RETURN_STRINGL(escaped_str, cubrid_retval, 0);
}

ZEND_FUNCTION(cubrid_get_db_parameter)
{
    zval *conn_id = NULL;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;

    int cubrid_retval = 0;
    int i, val;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    array_init(return_value);

    for (i = CCI_PARAM_FIRST; i <= CCI_PARAM_LAST; i++) {
	if ((cubrid_retval = cci_get_db_parameter(connect->handle, (T_CCI_DB_PARAM) i, (void *) &val, &error)) < 0) {
	    handle_error(cubrid_retval, &error, connect);
            cubrid_array_destroy(return_value->value.ht ZEND_FILE_LINE_CC);
	    RETURN_FALSE;
	}

	add_assoc_long(return_value, (char *) db_parameters[i - 1].parameter_name, val);
    }

    return;
}

ZEND_FUNCTION(cubrid_set_db_parameter)
{
    zval *conn_id = NULL;
    long param_type, param_value;

    T_CUBRID_CONNECT *connect;
    T_CCI_ERROR error;

    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rll", &conn_id, &param_type, &param_value) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    switch (param_type) {
    case CUBRID_PARAM_ISOLATION_LEVEL:
        if ((cubrid_retval = cci_set_isolation_level(connect->handle, param_value, &error)) < 0) {
            handle_error(cubrid_retval, &error, connect);
            RETURN_FALSE; 
        }

        break;
    case CUBRID_PARAM_LOCK_TIMEOUT:
        if ((cubrid_retval = cci_set_db_parameter(connect->handle, param_type, (void *) &param_value, &error)) < 0) {
            handle_error(cubrid_retval, &error, connect);
            RETURN_FALSE;
        }

        break;
    default:
        handle_error(CUBRID_ER_INVALID_PARAM_TYPE, NULL, connect);
        RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_db_name)
{
    zval *db_list = NULL;
    long db_index = 0;

    int db_list_size = 0;
    zval **elem_buf = NULL;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al", &db_list, &db_index) == FAILURE) {
	return;
    }

    db_list_size = zend_hash_num_elements(HASH_OF(db_list));
    if (db_index < 0 || db_index >= db_list_size) {
        RETURN_FALSE; 
    }

    zend_hash_internal_pointer_reset(HASH_OF(db_list));
    if (zend_hash_index_find(HASH_OF(db_list), db_index, (void **) &elem_buf) == FAILURE) {
        RETURN_FALSE; 
    }
    convert_to_string_ex(elem_buf);

    RETURN_STRINGL(Z_STRVAL_P(*elem_buf), Z_STRLEN_P(*elem_buf), 1);
}

ZEND_FUNCTION(cubrid_list_dbs) 
{
    zval *conn_id = NULL;
    char *query = "SELECT LIST_DBS()";

    T_CUBRID_CONNECT *connect = NULL;
    T_CCI_ERROR error;

    int cubrid_retval = 0;
    int request_handle = 0;

    char *buffer = NULL;
    int ind = 0;

    int i;
    char *pos = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &conn_id) == FAILURE) {
	return;
    }

    if (conn_id) {
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
        if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE;
        }

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    array_init(return_value);
   
    if ((cubrid_retval = cci_prepare(connect->handle, query, 0, &error)) < 0) {
	handle_error(cubrid_retval, &error, connect);
	RETURN_FALSE;
    }

    request_handle = cubrid_retval;

    if ((cubrid_retval = cci_execute(request_handle, CCI_EXEC_ASYNC, 0, &error)) < 0) {
        goto ERR_CUBRID_LIST_DBS_HANDLE;
    }

    cubrid_retval = cci_cursor(request_handle, 1, CCI_CURSOR_CURRENT, &error);
    if (cubrid_retval < 0 && cubrid_retval != CCI_ER_NO_MORE_DATA) {
        goto ERR_CUBRID_LIST_DBS_HANDLE;
    }

    if ((cubrid_retval = cci_fetch(request_handle, &error)) < 0) {
        goto ERR_CUBRID_LIST_DBS_HANDLE;
    }

    if ((cubrid_retval = cci_get_data(request_handle, 1, CCI_A_TYPE_STR, &buffer, &ind)) < 0) {
        goto ERR_CUBRID_LIST_DBS_HANDLE;
    }


    /* Databases names are separated by spaces */
    i = 0;
    if (ind != -1) {
	pos = strtok(buffer, " ");
	if (pos) {
	    while (pos != NULL) {
		add_index_stringl(return_value, i++, pos, strlen(pos), 1);
		pos = strtok(NULL, " ");
	    }
	} else {
	    add_index_stringl(return_value, 0, buffer, strlen(buffer), 1);
	}
    } else {
        goto ERR_CUBRID_LIST_DBS;
    }

    cci_close_req_handle(request_handle);
    return;
ERR_CUBRID_LIST_DBS_HANDLE:
	handle_error(cubrid_retval, &error, connect);
ERR_CUBRID_LIST_DBS:

    cci_close_req_handle(request_handle);
    cubrid_array_destroy(return_value->value.ht ZEND_FILE_LINE_CC);

    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_insert_id)
{
    zval *conn_id = NULL;

    T_CUBRID_CONNECT *connect = NULL;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    char *last_id = NULL;
    char *out_str = NULL;

    init_error();

    if (CUBRID_G(last_request_id) == -1) {
	RETURN_FALSE;
    }

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &conn_id) == FAILURE) {
        return;
    }

    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
        if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE; 
        } 

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    switch (connect->sql_type) {
    case CUBRID_STMT_INSERT:
        if (connect->affected_rows < 1) {
            RETURN_LONG(0);
        }

        if ((cubrid_retval = cci_get_last_insert_id(connect->handle, &last_id, &error)) < 0) {
            handle_error(cubrid_retval, &error, connect);
            RETURN_FALSE;
        } 
        
        if (last_id == NULL) {
            RETURN_LONG(0);
        } else {
            out_str = estrndup(last_id, strlen(last_id));
            RETURN_STRINGL(out_str, strlen(out_str), 0);
        } 

        break;
    case CUBRID_STMT_SELECT:
    case CUBRID_STMT_UPDATE:
    case CUBRID_STMT_DELETE:
        RETURN_LONG(0);
    default:
        handle_error(CUBRID_ER_INVALID_SQL_TYPE, NULL, connect);
        RETURN_FALSE;
    }
}

ZEND_FUNCTION(cubrid_ping)
{
    zval *conn_id = NULL;

    T_CUBRID_CONNECT *connect = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &conn_id) == FAILURE) {
        return;
    }

    if (conn_id){
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
	if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE; 
        } 

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    if (!check_connect_alive(connect)) {
        RETURN_FALSE;
    } 

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob2_new)
{
    zval *conn_id = NULL;
    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_LOB2 *cubrid_lob = NULL;

    char *type = NULL;
    int type_len = 0, cubrid_retval = 0;

    T_CCI_U_TYPE u_type = -1;
    T_CCI_LOB lob = NULL;
    T_CCI_ERROR error;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|rs", &conn_id, &type, &type_len) == FAILURE) {
        return;
    }

    if (conn_id) {
        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    } else {
        if (CUBRID_G(last_connect_id) == -1) {
            RETURN_FALSE;
        }

        ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, NULL, CUBRID_G(last_connect_id), "CUBRID-Connect", le_connect, le_pconnect);
    }

    init_error_link(connect);

    if (!type) {
        type = "BLOB";
    }

    u_type = get_cubrid_u_type_by_name(type);
    if (!(u_type == CCI_U_TYPE_BLOB || u_type == CCI_U_TYPE_CLOB)) {
        handle_error(CUBRID_ER_NOT_SUPPORTED_TYPE, NULL, connect);
        RETURN_FALSE;
    }

    if ((cubrid_retval = cubrid_lob_new(connect->handle, &lob, u_type, &error)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }

    cubrid_lob = new_cubrid_lob2();
    if (!cubrid_lob) {
        handle_error(CCI_ER_NO_MORE_MEMORY, NULL, connect);
        RETURN_FALSE;
    }

    cubrid_lob->lob = lob;
    cubrid_lob->type = u_type;
    cubrid_lob->connect = connect;

    ZEND_REGISTER_RESOURCE(return_value, cubrid_lob, le_lob2);
}

ZEND_FUNCTION(cubrid_lob2_bind)
{
    zval *req_id = NULL, *bind_value = NULL;
    char *bind_value_type = NULL;
    long bind_index = 0, bind_value_type_len;

    T_CUBRID_REQUEST *request = NULL;
    T_CCI_ERROR error;
    T_CUBRID_LOB2 *lob2 = NULL;
    T_CCI_LOB lob = NULL;

    T_CCI_U_TYPE u_type = -1;
    T_CCI_A_TYPE a_type = -1;

    char *value = NULL;
    long value_len = 0;
    
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz|s",
                &req_id, &bind_index, &bind_value, &bind_value_type, &bind_value_type_len) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    if (!request->conn) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "CUBRID-Request is not a valid resource");
        return;
    }

    init_error_link(request->conn);

    if (bind_index < 0 || bind_index > request->bind_num) {
        RETURN_FALSE;
    }

    if (!bind_value_type) {
        u_type = CCI_U_TYPE_BLOB;
    } else {
        u_type = get_cubrid_u_type_by_name(bind_value_type);
    }

    if (Z_TYPE_P(bind_value) == IS_RESOURCE) {
        ZEND_FETCH_RESOURCE(lob2, T_CUBRID_LOB2 *, &bind_value, -1, "CUBRID-Lob2", le_lob2);

        if (bind_value_type != NULL && lob2->type != u_type) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Wrong type, the type you create is not %s.", bind_value_type);
            RETURN_FALSE;
        }

        lob = lob2->lob;
        u_type = lob2->type;
    } else {
        value = Z_STRVAL_P(bind_value);
        value_len = Z_STRLEN_P(bind_value);

        if (!(u_type == CCI_U_TYPE_BLOB || u_type == CCI_U_TYPE_CLOB)) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function only can be used to bind BLOB/CLOB.");
            RETURN_FALSE;
        }

        if ((cubrid_retval = cubrid_lob_new(request->conn->handle, &lob, u_type, &error)) < 0) {
            handle_error(cubrid_retval, &error, request->conn);
            RETURN_FALSE;
        }

        if ((cubrid_retval = cubrid_lob_write(request->conn->handle, lob, u_type, 0, value_len, value, &error)) < 0) {
            cubrid_lob_free(lob, u_type);
            handle_error(cubrid_retval, &error, request->conn);
            RETURN_FALSE;
        }
    }
    
    switch (u_type) {
    case CCI_U_TYPE_BLOB:
        a_type = CCI_A_TYPE_BLOB;
        break;
    case CCI_U_TYPE_CLOB:
        a_type = CCI_A_TYPE_CLOB;
        break;
    default:
        RETURN_FALSE;
    }
    
    cubrid_retval = cci_bind_param(request->handle, bind_index, a_type, (void *) lob, u_type, CCI_BIND_PTR);
    if (cubrid_retval != 0 || !request->l_bind) {
        handle_error(cubrid_retval, NULL, request->conn);
        RETURN_FALSE;
    }

    request->l_bind[bind_index - 1] = 1;

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob2_export)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;
    T_CCI_ERROR error;

    char *file_name = NULL, *buf = NULL;
    int file_name_len = 0, cubrid_retval = 0;
    int fd, size;
	int flags = 0;
    php_cubrid_int64_t lob_size, pos = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &lob2_id, &file_name, &file_name_len) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

#ifdef PHP_WIN32
	flags = O_BINARY;
#else
	flags = 0;
#endif

    if ((fd = open(file_name, flags | O_CREAT | O_WRONLY | O_EXCL, 0666)) < 0) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "The file that you want to export lob object may have existed.");
        RETURN_FALSE;
    }

    lob_size = cubrid_lob_size(lob->lob, lob->type);
    if (lob_size == 0) {
        close(fd);
        RETURN_TRUE;
    }

    buf = (char *) emalloc (sizeof(char) * CUBRID_LOB_READ_BUF_SIZE);
    if (!buf) {
        handle_error(CCI_ER_NO_MORE_MEMORY, NULL, lob->connect);
        goto ERR_CUBRID_LOB2_EXPORT;
    }

    while (1) {
        size = cubrid_lob_read(lob->connect->handle, lob->lob, lob->type, pos, CUBRID_LOB_READ_BUF_SIZE, buf, &error);

        cubrid_retval = size;

        if (cubrid_retval < 0) {
            handle_error(cubrid_retval, &error, lob->connect);
            goto ERR_CUBRID_LOB2_EXPORT;
        }

        if ((cubrid_retval = write(fd, buf, size)) < 0) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot write the file that you want to export lob object");
            goto ERR_CUBRID_LOB2_EXPORT;
        }

        pos += size;
        if (pos == lob_size) break;

        if (pos > lob_size) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Abnormal error happend in cubrid_lob2_export.\n");
            goto ERR_CUBRID_LOB2_EXPORT;
        }
    }

    efree(buf);
    close(fd);
    RETURN_TRUE;

ERR_CUBRID_LOB2_EXPORT:
    if (fd > 0) {
        close(fd);
        unlink(file_name);
    }
    if (buf) efree(buf);
    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_lob2_import)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;
    T_CCI_ERROR error;

    char *file_name = NULL;
    char buf[CUBRID_LOB_READ_BUF_SIZE] = {'\0'};
    int file_name_len = 0, cubrid_retval = 0;
    int fd, size;
    php_cubrid_int64_t pos = 0;
    int flags;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &lob2_id, &file_name, &file_name_len) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

#ifdef PHP_WIN32
    flags = O_RDONLY | O_BINARY;
#else
    flags = O_RDONLY;
#endif

    if ((fd = open(file_name, flags, 0400)) < 0) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot open the file that you want to import lob object.");
        RETURN_FALSE;
    }

    while (1) {
        if ((size = read(fd, buf, CUBRID_LOB_READ_BUF_SIZE)) < 0) {
            close(fd);
            cubrid_lob_free(lob->lob, lob->type);
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot read the file that you want to import lob object.");
            RETURN_FALSE;
        }

        if (size == 0) break;

        if ((cubrid_retval = cubrid_lob_write(lob->connect->handle, lob->lob, lob->type, pos, size, buf, &error)) < 0) {
            close(fd);
            cubrid_lob_free(lob->lob, lob->type);
            handle_error(cubrid_retval, &error, lob->connect);
            RETURN_FALSE;
        }

        pos += size;
    }

    close(fd);
    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob2_read)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;
    T_CCI_ERROR error;
    long len = 0;
    int cubrid_retval = 0;
    char *buf = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &lob2_id, &len) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

    if (lob->pos == cubrid_lob_size(lob->lob, lob->type)) {
        RETURN_FALSE;
    }

    buf = (char *) emalloc (sizeof(char) * len + 1);
    if (!buf) {
        handle_error(CCI_ER_NO_MORE_MEMORY, NULL, lob->connect);
        return;
    }

    if ((cubrid_retval = cubrid_lob_read(lob->connect->handle, lob->lob, lob->type, lob->pos, len, buf, &error)) < 0) {
        efree(buf);
        handle_error(cubrid_retval, &error, lob->connect);
        return;
    }

    if (cubrid_retval == len) {
        buf[len] = '\0';
        lob->pos += len;
        RETURN_STRINGL(buf, len, 0);
    } else {
        buf[cubrid_retval] = '\0';
        lob->pos += cubrid_retval;
        RETURN_STRINGL(buf, cubrid_retval, 0);
    }
}

ZEND_FUNCTION(cubrid_lob2_write)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;
    T_CCI_ERROR error;
    int cubrid_retval = 0;
    char *buf = NULL;
    long buf_len = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &lob2_id, &buf, &buf_len) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

    if ((cubrid_retval = cubrid_lob_write(lob->connect->handle, lob->lob, lob->type, lob->pos, buf_len, buf, &error)) < 0) {
        handle_error(cubrid_retval, &error, lob->connect);
        RETURN_FALSE;
    }

    lob->pos += buf_len;
    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob2_tell)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lob2_id) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

    RETURN_LONG((long)lob->pos);
}

ZEND_FUNCTION(cubrid_lob2_tell64)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;
    char *pos = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lob2_id) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

    pos = php_cubrid_int64_to_str(lob->pos TSRMLS_CC);

    RETURN_STRINGL(pos, strlen(pos), 0);
}

ZEND_FUNCTION(cubrid_lob2_seek)
{
    zval *lob2_id = NULL;

    long offset = 0, origin = CUBRID_CURSOR_CURRENT;
    php_cubrid_int64_t size;

    T_CUBRID_LOB2 *lob = NULL;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|l", &lob2_id, &offset, &origin) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    size = cubrid_lob_size(lob->lob, lob->type);

    if (origin == CUBRID_CURSOR_CURRENT) {
        if ((offset < 0 && -offset > lob->pos) || (offset > 0 && (size - lob->pos) < offset)) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "offet(%d) is out of range, please input a proper number.", (int)offset);
            RETURN_FALSE;
        }

        lob->pos += offset;
        RETURN_TRUE;
    }

    if (offset < 0 || offset > size) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset(%d) is not correct, it can't be a negative number or larger than size", (int)offset);
        RETURN_FALSE;
    }
    
    if (origin == CUBRID_CURSOR_FIRST) {
        lob->pos = offset;
    } else if (origin == CUBRID_CURSOR_LAST) {
        lob->pos = size - offset;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob2_seek64)
{
    zval *lob2_id = NULL;

    char *offset = NULL, *end_offset = NULL;
    int offset_len = 0;
    long origin = CUBRID_CURSOR_CURRENT;
    php_cubrid_int64_t size, index;

    T_CUBRID_LOB2 *lob = NULL;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &lob2_id, &offset, &offset_len, &origin) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    size = cubrid_lob_size(lob->lob, lob->type);

    if (offset_len >= 65) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "string(%s) is out of range for the lob field you have chosen, so please check the offset you give and the lob length.", offset);
        RETURN_FALSE;
    }

    index = strtoll(offset, &end_offset, 10);
    if (end_offset == offset) {
        handle_error(CUBRID_ER_INVALID_PARAM, NULL, lob->connect);
        RETURN_FALSE;
    }

    if (origin == CUBRID_CURSOR_CURRENT) {
        if ((index < 0 && -index > lob->pos) || (index > 0 && (size - lob->pos) < index)) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset(%s) is out of range for the lob field you have chosen, so please check the offset you give and the lob length.", offset);
            RETURN_FALSE;
        }

        lob->pos += index;
    }else{
        if (index < 0) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset(%s) must not be a negative number, so please check the offset you give.", offset);
            RETURN_FALSE;
        }

        if (index > size) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "offset(%s) is out of range for the lob field you have chosen, so please check the offset you give and the lob length.", offset);
            RETURN_FALSE;
        }        
    }

    if (origin == CUBRID_CURSOR_FIRST) {
        lob->pos = index;
    } else if (origin == CUBRID_CURSOR_LAST) {
        lob->pos = size - index;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob2_size)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;

    int cubrid_retval = 0;
    php_cubrid_int64_t size;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lob2_id) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

    size = cubrid_lob_size(lob->lob, lob->type);

    RETURN_LONG((long)size);
}

ZEND_FUNCTION(cubrid_lob2_size64)
{
    zval *lob2_id = NULL;

    T_CUBRID_LOB2 *lob = NULL;

    int cubrid_retval = 0;
    php_cubrid_int64_t lob_size;
    char *size = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lob2_id) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    init_error_link(lob->connect);

    lob_size = cubrid_lob_size(lob->lob, lob->type);

    size = php_cubrid_int64_to_str(lob_size TSRMLS_CC);

    RETURN_STRINGL(size, strlen(size), 0);
}

ZEND_FUNCTION(cubrid_lob2_close)
{
    zval *lob2_id = NULL;
    T_CUBRID_LOB2 *lob = NULL;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lob2_id) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB2 *, &lob2_id, -1, "CUBRID-Lob2", le_lob2);

    if (!(lob->connect && lob->lob)) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid CUBRID-Lob2 resource");
        RETURN_FALSE;
    }

    zend_list_delete(Z_RESVAL_P(lob2_id));

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob_get)
{
    zval *conn_id = NULL;
    char *sql_stmt = NULL;
    int sql_stmt_len;

    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_LOB *cubrid_lob = NULL;

    T_CCI_LOB lob = NULL;
    T_CCI_ERROR error;

    int req_handle = 0;
    int cubrid_retval = 0;
    int ind = 0;

    T_CCI_A_TYPE a_type = -1;
    T_CCI_U_TYPE u_type = -1;

    T_CCI_COL_INFO *res_col_info;
    T_CCI_SQLX_CMD cmd_type;

    int column_count = 0;
    int row_count = 0;
    int res_id = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &conn_id, &sql_stmt, &sql_stmt_len) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);

    init_error_link(connect);

    if (!sql_stmt) {
        RETURN_FALSE;
    }

    if ((cubrid_retval = cci_prepare(connect->handle, sql_stmt, 0, &error)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }

    req_handle = cubrid_retval;

    array_init(return_value);

    res_col_info = cci_get_result_info(req_handle, &cmd_type, &column_count);
    if (!res_col_info || cmd_type != CUBRID_STMT_SELECT) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Get result info fail or sql type is not select");
        goto ERR_CUBRID_LOB_GET;
    }

    if (column_count > 1) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "More than one columns returned");
        goto ERR_CUBRID_LOB_GET;
    }

    u_type = CCI_GET_RESULT_INFO_TYPE(res_col_info, 1);

    if (u_type == CCI_U_TYPE_BLOB) {
        a_type = CCI_A_TYPE_BLOB; 
    } else if (u_type == CCI_U_TYPE_CLOB) {
        a_type = CCI_A_TYPE_CLOB; 
    } else {
        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Column type is not BLOB or CLOB.");
        goto ERR_CUBRID_LOB_GET;
    }

    if ((cubrid_retval = cci_execute(req_handle, 0, 0, &error)) < 0) {
        handle_error(cubrid_retval, &error, connect);
        RETURN_FALSE;
    }

    while (1) {
        cubrid_retval = cci_cursor(req_handle, 1, CCI_CURSOR_CURRENT, &error);
        if (cubrid_retval == CCI_ER_NO_MORE_DATA) {
            break;
        }

        if (cubrid_retval < 0) {
            handle_error(cubrid_retval, &error, connect);
            goto ERR_CUBRID_LOB_GET;
        }

        if ((cubrid_retval = cci_fetch(req_handle, &error)) < 0) {
            handle_error(cubrid_retval, &error, connect);
            goto ERR_CUBRID_LOB_GET;
        }

        if ((cubrid_retval = cci_get_data(req_handle, 1, a_type, (void *) &lob, &ind)) < 0) {
            handle_error(cubrid_retval, NULL, connect);
            goto ERR_CUBRID_LOB_GET;
        }

        if (ind < 0) {
            goto ERR_CUBRID_LOB_GET;
        }

        cubrid_lob = new_cubrid_lob();
        if (!cubrid_lob) {
            handle_error(CCI_ER_NO_MORE_MEMORY, NULL, connect);
            RETURN_FALSE;
        }
        cubrid_lob->lob = lob;
        cubrid_lob->type = u_type;
        cubrid_lob->size = cubrid_lob_size(lob, u_type);

        res_id = ZEND_REGISTER_RESOURCE(NULL, cubrid_lob, le_lob);
        add_index_resource(return_value, row_count, res_id);

        row_count++;
    }

    cci_close_req_handle(req_handle);
    return;

ERR_CUBRID_LOB_GET:

    cubrid_array_destroy(return_value->value.ht ZEND_FILE_LINE_CC);
    cci_close_req_handle(req_handle);

    RETURN_FALSE;
}

ZEND_FUNCTION(cubrid_lob_size)
{
    zval *lob_id = NULL;
    T_CUBRID_LOB *lob = NULL;

    char *id = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &lob_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB *, &lob_id, -1, "CUBRID-Lob", le_lob);

    id = php_cubrid_int64_to_str(lob->size TSRMLS_CC);

    RETURN_STRINGL(id, strlen(id), 0);
}

ZEND_FUNCTION(cubrid_lob_export)
{
    zval *conn_id = NULL, *lob_id = NULL;
    char *file_name = NULL;
    int file_name_len = 0;

    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_LOB *lob = NULL;
    T_CCI_ERROR error;

    php_stream *stm = NULL;
    
    char buf[CUBRID_LOB_READ_BUF_SIZE];
    php_cubrid_int64_t lob_size = 0, start_pos = 0;
    int write_len = 0;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrs", &conn_id, &lob_id, &file_name, &file_name_len) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB *, &lob_id, -1, "CUBRID-Lob", le_lob);

    init_error_link(connect);

    if (!(stm = php_stream_open_wrapper(file_name, "w", REPORT_ERRORS, NULL))) {
        RETURN_FALSE;
    }

    lob_size = lob->size;

    while (lob_size > 0) {
        if (lob_size < CUBRID_LOB_READ_BUF_SIZE) {
            write_len = (int)lob_size; 
        } else {
            write_len = CUBRID_LOB_READ_BUF_SIZE; 
        }

        if ((cubrid_retval = cubrid_lob_read(connect->handle, lob->lob, lob->type, start_pos, write_len, buf, &error)) < 0) {
            handle_error(cubrid_retval, &error, connect);
            php_stream_close(stm);

            RETURN_FALSE;
        }
        php_stream_write(stm, buf, write_len);

        start_pos += CUBRID_LOB_READ_BUF_SIZE;
        lob_size -= CUBRID_LOB_READ_BUF_SIZE;
    }

    php_stream_close(stm);

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob_send) 
{
    zval *conn_id = NULL, *lob_id = NULL;

    T_CUBRID_CONNECT *connect = NULL;
    T_CUBRID_LOB *lob = NULL;
    T_CCI_ERROR error;

    char buf[CUBRID_LOB_READ_BUF_SIZE];
    php_cubrid_int64_t lob_size = 0, start_pos = 0;
    int write_len = 0;
    int cubrid_retval = 0;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr", &conn_id, &lob_id) == FAILURE) {
	return;
    }

    ZEND_FETCH_RESOURCE2(connect, T_CUBRID_CONNECT *, &conn_id, -1, "CUBRID-Connect", le_connect, le_pconnect);
    ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB *, &lob_id, -1, "CUBRID-Lob", le_lob);

    init_error_link(connect);

    lob_size = lob->size;

    while (lob_size > 0) {
        if (lob_size < CUBRID_LOB_READ_BUF_SIZE) {
            write_len = (int)lob_size; 
        } else {
            write_len = CUBRID_LOB_READ_BUF_SIZE; 
        }

        if ((cubrid_retval = cubrid_lob_read(connect->handle, lob->lob, lob->type, start_pos, write_len, buf, &error)) < 0) {
            handle_error(cubrid_retval, &error, connect);
            RETURN_FALSE;
        }

        if (PHPWRITE(buf, write_len) != write_len) {
	    handle_error(CUBRID_ER_TRANSFER_FAIL, NULL, connect);
            RETURN_FALSE;
	}

        start_pos += CUBRID_LOB_READ_BUF_SIZE;
        lob_size -= CUBRID_LOB_READ_BUF_SIZE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_lob_close)
{
    zval *lob_id_array = NULL;
    T_CUBRID_LOB *lob = NULL;

    int lob_id_count = 0, i;
    zval **data = NULL;

    init_error();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &lob_id_array) == FAILURE) {
	return;
    }

    lob_id_count = zend_hash_num_elements(HASH_OF(lob_id_array));
    zend_hash_internal_pointer_reset(HASH_OF(lob_id_array));

    for (i = 0; i < lob_id_count; i++) {
        zend_hash_get_current_data(HASH_OF(lob_id_array), (void **) &data);

        ZEND_FETCH_RESOURCE(lob, T_CUBRID_LOB *, data, -1, "CUBRID-Lob", le_lob);
        zend_list_delete(Z_RESVAL_PP(data));

        zend_hash_move_forward(HASH_OF(lob_id_array));
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_set_query_timeout)
{
    zval *req_id = NULL;
    T_CUBRID_REQUEST *request;
    long timeout = 0;
    int cubrid_retval = 0;

    init_error ();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &req_id, &timeout) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link (request->conn);

    if ((cubrid_retval = cci_set_query_timeout (request->handle, timeout)) < 0) {
        handle_error (cubrid_retval, NULL, request->conn);
        RETURN_FALSE;
    }

    RETURN_TRUE;
}

ZEND_FUNCTION(cubrid_get_query_timeout)
{
    zval *req_id = NULL;
    T_CUBRID_REQUEST *request;
    int timeout;

    init_error ();

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &req_id) == FAILURE) {
        return;
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link (request->conn);

    if ((timeout = cci_get_query_timeout (request->handle)) < 0) {
        handle_error (timeout, NULL, request->conn);
        RETURN_FALSE;
    }

    RETURN_LONG (timeout);
}

/************************************************************************
* PRIVATE FUNCTIONS IMPLEMENTATION
************************************************************************/

static void php_cubrid_set_default_conn(int id TSRMLS_DC)
{
    if (CUBRID_G(last_connect_id) != -1) {
        zend_list_delete(CUBRID_G(last_connect_id));
    }

    CUBRID_G(last_connect_id) = id;
    zend_list_addref(id);
}

static void php_cubrid_set_default_req(int id TSRMLS_DC)
{
    if (CUBRID_G(last_request_id) != -1) {
        zend_list_delete(CUBRID_G(last_request_id));
    }

    CUBRID_G(last_request_id) = id;
    zend_list_addref(id);
}

static void close_cubrid_connect(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
    T_CUBRID_CONNECT *conn = (T_CUBRID_CONNECT *)rsrc->ptr;
    T_CCI_ERROR error;
    LINKED_LIST_NODE *p;
    LINKED_LIST_NODE *head = conn->unclosed_requests->head;

    /* When calling cci_disconnect, all request handle in cci will be released,
     * just like calling cci_close_req_handle. So we must prevent the PHP
     * garbage collector calling cci_close_req_handle again in close_cubrid_request.
     */

    for (p = head->next; p != NULL ; p = head->next) {
        int i;
        T_CUBRID_REQUEST *req = (T_CUBRID_REQUEST *)p->data;
        req->conn = NULL;
        req->handle = 0;

        if (req->lob2_list) {
            for (i = 0; i < req->col_count; i++) {
                if (req->lob2_list[i]) {
                    req->lob2_list[i]->connect = NULL;
                    req->lob2_list[i] = NULL;
                }
            }
        }

        p->data = NULL;

        head->next = p->next;
        efree(p);
    }

    if (conn->handle) {
	/* CCI_ER_USED_CONNECTION == -20044 */
	while (cci_disconnect(conn->handle, &error) == -20044) {
	    cci_cancel(conn->handle);
	}
    }

    efree(head);
    efree(conn->unclosed_requests);
    efree(conn);
}

static void close_cubrid_pconnect(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
    T_CUBRID_CONNECT *conn = (T_CUBRID_CONNECT *)rsrc->ptr;

    T_CCI_ERROR error;

    if (conn->handle) {
	/* CCI_ER_USED_CONNECTION == -20044 */
	while (cci_disconnect(conn->handle, &error) == -20044) {
	    cci_cancel(conn->handle);
	}
    }

    free(conn->unclosed_requests->head);
    free(conn->unclosed_requests);
    free(conn);
}

static int is_connection_exist(T_CUBRID_REQUEST *req)
{
    return req->conn ? 1 : 0;
}

static void close_cubrid_request_internal(T_CUBRID_REQUEST * req)
{
    if (is_connection_exist(req)) {
        linked_list_delete(req->conn->unclosed_requests, (void *)req);
        cci_close_req_handle(req->handle);
    }

    if (req->l_bind) {
        efree(req->l_bind);
    }

    if (req->field_lengths) {
        efree(req->field_lengths);
    }

    if (req->lob2_list) {
        int i;
        for (i = 0; i < req->col_count; i++) {
            if (req->lob2_list[i]) {
                req->lob2_list[i]->connect = NULL;
                req->lob2_list[i] = NULL;
            }
        }

        efree(req->lob2_list);
    }

    efree(req);
}

static void close_cubrid_request(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
    T_CUBRID_REQUEST *req = (T_CUBRID_REQUEST *)rsrc->ptr;

    close_cubrid_request_internal(req);
}


static void close_cubrid_lob_internal(T_CUBRID_LOB *lob)
{
    if (lob->lob) {
        cubrid_lob_free(lob->lob, lob->type);
    }

    efree(lob);
}

static void close_cubrid_lob(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
    T_CUBRID_LOB *lob = (T_CUBRID_LOB *)rsrc->ptr;

    close_cubrid_lob_internal (lob);
}

static void close_cubrid_lob2(zend_rsrc_list_entry * rsrc TSRMLS_DC)
{
    T_CUBRID_LOB2 *lob = (T_CUBRID_LOB2 *)rsrc->ptr;

    cubrid_lob_free(lob->lob, lob->type);
    efree(lob);
}

static void php_cubrid_init_globals(zend_cubrid_globals * cubrid_globals)
{
    cubrid_globals->recent_error.code = 0;
    cubrid_globals->recent_error.facility = 0;
    cubrid_globals->recent_error.msg[0] = 0;

    cubrid_globals->last_connect_id = -1;
    cubrid_globals->last_request_id = -1;

    cubrid_globals->default_userid = "PUBLIC";
    cubrid_globals->default_passwd = "";
}

static int init_error(void)
{
    set_error(0, 0, "");

    return SUCCESS;
}

static int set_error(T_FACILITY_CODE facility, int code, char *msg, ...)
{
    va_list args;
    TSRMLS_FETCH();

    CUBRID_G(recent_error).facility = facility;
    CUBRID_G(recent_error).code = code;

    va_start(args, msg);
    snprintf(CUBRID_G(recent_error).msg, 1024, msg, args);
    va_end(args);

    return SUCCESS;
}

static int init_error_link(T_CUBRID_CONNECT *conn)
{
    if (conn) {
        set_error_link(conn, 0, "");
    }

    return SUCCESS;
}

static int set_error_link(T_CUBRID_CONNECT *conn, int code, char *msg, ...)
{
    va_list args;

    conn->recent_error.code = code;

    va_start(args, msg);
    snprintf(conn->recent_error.msg, 1024, msg, args);
    va_end(args);

    return SUCCESS;
}

static int get_error_msg(int err_code, char *buf, int buf_size)
{
    const char *err_msg = "";
    int size = sizeof(db_error) / sizeof(db_error[0]);
    int i;

    if (err_code > CCI_ER_END) {
	return cci_get_err_msg(err_code, buf, buf_size);
    }

    for (i = 0; i < size; i++) {
	if (err_code == db_error[i].err_code) {
	    err_msg = db_error[i].err_msg;
	    break;
	}
    }

    if (i == size) {
	err_msg = "Unknown Error";
    }

    strlcpy(buf, err_msg, buf_size);

    return SUCCESS;
}

static int handle_error(int err_code, T_CCI_ERROR * error, T_CUBRID_CONNECT *conn)
{
    int real_err_code = 0;
    char *real_err_msg = NULL;

    T_FACILITY_CODE facility = CUBRID_FACILITY_CLIENT;

    char err_msg[1024] = { 0 };
    char *facility_msg = NULL;

    if (err_code == CCI_ER_DBMS) {
	facility = CUBRID_FACILITY_DBMS;
	facility_msg = "DBMS";
	if (error) {
	    real_err_code = error->err_code;
	    real_err_msg = error->err_msg;
	} else {
	    real_err_code = 0;
	    real_err_msg = "Unknown DBMS error";
	}
    } else {
	if (err_code > CAS_ER_IS) {
	    facility = CUBRID_FACILITY_CAS;
	    facility_msg = "CAS";
	} else if (err_code > CCI_ER_END) {
	    facility = CUBRID_FACILITY_CCI;
	    facility_msg = "CCI";
	} else if (err_code > CUBRID_ER_END) {
	    facility = CUBRID_FACILITY_CLIENT;
	    facility_msg = "CLIENT";
	} else {
	    real_err_code = -1;
	    real_err_msg = NULL;
	    return FAILURE;
	}

	if (get_error_msg(err_code, err_msg, (int) sizeof(err_msg)) < 0) {
	    strlcpy(err_msg, "Unknown error message", sizeof(err_msg));
	}

	real_err_code = err_code;
	real_err_msg = err_msg;
    }

    set_error(facility, real_err_code, real_err_msg);

    if(conn) {
        set_error_link(conn, real_err_code, real_err_msg);
    }

    php_error(E_WARNING, "Error: %s, %d, %s", facility_msg, real_err_code, real_err_msg);

    return SUCCESS;
}

static int fetch_a_row(zval *arg, T_CUBRID_CONNECT *connect, int req_handle, T_CUBRID_REQUEST *request, int type TSRMLS_DC)
{
    T_CCI_COL_INFO *column_info = NULL;
    T_CCI_U_TYPE column_type;

    LINKED_LIST_NODE *cur= NULL;

    int column_count = 0;
    char *column_name;

    int cubrid_retval = 0;
    int null_indicator;
    int i;

    if ((column_info = cci_get_result_info(req_handle, NULL, &column_count)) == NULL) {
	return CUBRID_ER_CANNOT_GET_COLUMN_INFO;
    }

    array_init(arg);

    for (i = 0; i < column_count; i++) { 
        column_type = CCI_GET_RESULT_INFO_TYPE(column_info, i + 1);
	column_name = CCI_GET_RESULT_INFO_NAME(column_info, i + 1);

        if (CCI_IS_SET_TYPE(column_type) || CCI_IS_MULTISET_TYPE(column_type) || CCI_IS_SEQUENCE_TYPE(column_type)) {
	    T_CCI_SET res_buf = NULL;

	    if ((cubrid_retval = cci_get_data(req_handle, i + 1, CCI_A_TYPE_SET, &res_buf, &null_indicator)) < 0) {
		goto ERR_FETCH_A_ROW;
	    }

	    if (null_indicator >= 0) {
		if (type & CUBRID_NUM) {
		    cubrid_retval = cubrid_add_index_array(arg, i, res_buf TSRMLS_CC);
		} 
		
		if (type & CUBRID_ASSOC) {
		    cubrid_retval = cubrid_add_assoc_array(arg, column_name, res_buf TSRMLS_CC);
		}
		
		if (cubrid_retval < 0) {
		    cci_set_free(res_buf);
		    goto ERR_FETCH_A_ROW;
		}

		cci_set_free(res_buf);
	    }

	} else if ((type & CUBRID_LOB) && (column_type == CCI_U_TYPE_BLOB || column_type == CCI_U_TYPE_CLOB)) {
            T_CCI_LOB lob = NULL;
            T_CUBRID_LOB2 *cubrid_lob = NULL;
            int res_id = 0;

            if (column_type == CCI_U_TYPE_BLOB) {
                if ((cubrid_retval = cci_get_data(req_handle, i + 1, CCI_A_TYPE_BLOB, (void *) &lob, &null_indicator)) < 0) {
                    goto ERR_FETCH_A_ROW;
                }
            } else {
                if ((cubrid_retval = cci_get_data(req_handle, i + 1, CCI_A_TYPE_CLOB, (void *) &lob, &null_indicator)) < 0) {
                    goto ERR_FETCH_A_ROW;
                }
            }

            if (null_indicator >= 0) {
                cubrid_lob = new_cubrid_lob2();
                if (!cubrid_lob) {
                    cubrid_lob_free(lob, column_type);
                    cubrid_retval = CCI_ER_NO_MORE_MEMORY;
                    goto ERR_FETCH_A_ROW;
                }

                cubrid_lob->lob = lob;
                cubrid_lob->type = column_type;
                cubrid_lob->connect = connect;

                res_id = ZEND_REGISTER_RESOURCE(NULL, cubrid_lob, le_lob2);

                if (request) {
                    request->lob2_list[i] = cubrid_lob;
                }

                if (type & CUBRID_NUM) {
                    add_index_resource(arg, i, res_id);
                }

                if (type & CUBRID_ASSOC) {
                    add_assoc_resource(arg, column_name, res_id);
                }
            }

        } else {
	    char *res_buf = NULL;

	    if ((cubrid_retval = cci_get_data(req_handle, i + 1, CCI_A_TYPE_STR, &res_buf, &null_indicator)) < 0) {
		goto ERR_FETCH_A_ROW;
	    }

	    if (null_indicator >= 0) {
		if (type & CUBRID_NUM) {
		    add_index_stringl(arg, i, res_buf, null_indicator, 1);
		} 
		
		if (type & CUBRID_ASSOC) {
		    add_assoc_stringl(arg, column_name, res_buf, null_indicator, 1);
		}
	    }
	}

        if (request && request->field_lengths != NULL) {
            if (null_indicator != -1) {
                request->field_lengths[i] = null_indicator;
            } else {
                request->field_lengths[i] = 0;
            }
        }

        if (null_indicator < 0) {
            if (type & CUBRID_NUM) {
                add_index_unset(arg, i);
            }

            if (type & CUBRID_ASSOC) {
                add_assoc_unset(arg, column_name);
            }
        }
    }

    return SUCCESS;

ERR_FETCH_A_ROW:
    cubrid_array_destroy(arg->value.ht ZEND_FILE_LINE_CC);
    return cubrid_retval;
}

static T_CUBRID_CONNECT *new_cubrid_connect(int persistent)
{
    T_CUBRID_CONNECT *connect = NULL;

    if (persistent) {
        connect = (T_CUBRID_CONNECT *) malloc(sizeof(T_CUBRID_CONNECT));
        if (!connect) {
            return NULL;
        }

        connect->unclosed_requests = 
            (LINKED_LIST *) malloc(sizeof(LINKED_LIST));
        if (!connect->unclosed_requests) {
            goto ERR_CUBRID_CONNECT;
        }

        connect->unclosed_requests->head =
            (LINKED_LIST_NODE *) malloc(sizeof(LINKED_LIST_NODE));
        if (!connect->unclosed_requests->head) {
            goto ERR_CUBRID_CONNECT;
        }
    } else {
        connect = (T_CUBRID_CONNECT *) emalloc(sizeof(T_CUBRID_CONNECT));
        if (!connect) {
            return NULL;
        }

        connect->unclosed_requests = 
            (LINKED_LIST *) emalloc(sizeof(LINKED_LIST));
        if (!connect->unclosed_requests) {
            goto ERR_CUBRID_CONNECT;
        }

        connect->unclosed_requests->head =
            (LINKED_LIST_NODE *) emalloc(sizeof(LINKED_LIST_NODE));
        if (!connect->unclosed_requests->head) {
            goto ERR_CUBRID_CONNECT;
        }
    }

    connect->unclosed_requests->head->data = NULL;
    connect->unclosed_requests->head->next = NULL;
    connect->unclosed_requests->tail = connect->unclosed_requests->head;

    connect->recent_error.code = 0;
    connect->recent_error.msg[0] = 0;
    connect->handle = 0;

    connect->persistent = persistent;

    connect->affected_rows = 0;
    connect->sql_type = 0;

    return connect;

ERR_CUBRID_CONNECT:
    if (connect->unclosed_requests) {
        if (persistent) {
            free(connect->unclosed_requests);
        } else {
            efree(connect->unclosed_requests);
        }
    }

    if (connect) {
        if (persistent) {

            free(connect);
        } else {
            efree(connect);
        }
    }

    return NULL;
}

static T_CUBRID_REQUEST *new_cubrid_request(void)
{
    T_CUBRID_REQUEST *request = (T_CUBRID_REQUEST *) emalloc (sizeof(T_CUBRID_REQUEST));
    
    if (!request) return NULL;

    request->lob2_list = NULL;

    request->conn = NULL;
    request->handle = 0;
    request->row_count = -1;
    request->col_count = -1;
    request->sql_type = 0;
    request->bind_num = -1;
    request->l_bind = NULL;
    request->l_prepare = 0;
    request->field_lengths = NULL;
    request->lob = NULL;

    return request;
}

static T_CUBRID_LOB *new_cubrid_lob(void)
{
    T_CUBRID_LOB *lob = (T_CUBRID_LOB *) emalloc(sizeof(T_CUBRID_LOB));

    if (!lob) return NULL;

    lob->lob = NULL;
    lob->size = 0;
    lob->type = CCI_U_TYPE_BLOB;

    return lob;
}

static T_CUBRID_LOB2 *new_cubrid_lob2(void)
{
    T_CUBRID_LOB2 *lob = (T_CUBRID_LOB2 *) emalloc(sizeof(T_CUBRID_LOB2));
    
    if (!lob) return NULL;

    lob->connect = NULL;
    lob->pos = 0;
    lob->type = CCI_U_TYPE_BLOB;

    return lob;
}

static void register_cubrid_request(T_CUBRID_CONNECT *conn, T_CUBRID_REQUEST *req)
{
    linked_list_append(conn->unclosed_requests, (void *)req);
}

static int cubrid_add_index_array(zval *arg, uint index, T_CCI_SET in_set TSRMLS_DC)
{
    zval *tmp_ptr;

    int i;
    int res;
    int ind;
    char *buffer;

    int set_size = cci_set_size(in_set);

    MAKE_STD_ZVAL(tmp_ptr);
    array_init(tmp_ptr);

    for (i = 0; i < set_size; i++) {
	res = cci_set_get(in_set, i + 1, CCI_A_TYPE_STR, &buffer, &ind);
	if (res < 0) {
	    cubrid_array_destroy(HASH_OF(tmp_ptr) ZEND_FILE_LINE_CC);
	    FREE_ZVAL(tmp_ptr);
	    return res;
	}

	if (ind < 0) {
	    add_index_unset(tmp_ptr, i);
	} else {
	    add_index_string(tmp_ptr, i, buffer, 1);
	}
    }

    res = zend_hash_index_update(HASH_OF(arg), index, (void *) &tmp_ptr, sizeof(zval *), NULL);
    if (res == FAILURE) {
	cubrid_array_destroy(HASH_OF(tmp_ptr) ZEND_FILE_LINE_CC);
	FREE_ZVAL(tmp_ptr);
	return CUBRID_ER_PHP;
    }

    return 0;
}

static int cubrid_add_assoc_array(zval *arg, char *key, T_CCI_SET in_set TSRMLS_DC)
{
    zval *tmp_ptr;

    int i;
    int ind;
    char *buffer;
    int cubrid_retval = 0;

    int set_size = cci_set_size(in_set);

    MAKE_STD_ZVAL(tmp_ptr);
    array_init(tmp_ptr);

    for (i = 0; i < set_size; i++) {
	if ((cubrid_retval = cci_set_get(in_set, i + 1, CCI_A_TYPE_STR, &buffer, &ind)) < 0) {
	    cubrid_array_destroy(HASH_OF(tmp_ptr) ZEND_FILE_LINE_CC);
	    FREE_ZVAL(tmp_ptr);
	    return cubrid_retval;
	}

	if (ind < 0) {
	    add_index_unset(tmp_ptr, i);
	} else {
	    add_index_string(tmp_ptr, i, buffer, 1);
	}
    }

    if ((cubrid_retval = zend_hash_update(HASH_OF(arg), key, strlen(key) + 1, 
		    (void *) &tmp_ptr, sizeof(zval *), NULL)) == FAILURE) {
	cubrid_array_destroy(HASH_OF(tmp_ptr) ZEND_FILE_LINE_CC);
	FREE_ZVAL(tmp_ptr);
	return CUBRID_ER_PHP;
    }

    return 0;
}

static int cubrid_array_destroy(HashTable * ht ZEND_FILE_LINE_DC)
{
    zend_hash_destroy(ht);
    FREE_HASHTABLE_REL(ht);
    return SUCCESS;
}

static int cubrid_make_set(HashTable *ht, T_CCI_SET *set)
{
    void **set_array = NULL;
    int *set_null = NULL;
    char *key;
    ulong index;
    zval **data;

    int set_size;
    int i;
    int error_code;
    int cubrid_retval = 0;

    set_size = zend_hash_num_elements(ht);
    set_array = (void **) safe_emalloc(set_size, sizeof(void *), 0);

    for (i = 0; i < set_size; i++) {
	set_array[i] = NULL;
    }

    set_null = (int *) safe_emalloc(set_size, sizeof(int), 0);

    zend_hash_internal_pointer_reset(ht);
    for (i = 0; i < set_size; i++) {
	if (zend_hash_get_current_key(ht, &key, &index, 0) == HASH_KEY_NON_EXISTANT) {
	    break;
	}

	zend_hash_get_current_data(ht, (void **) &data);
	switch (Z_TYPE_PP(data)) {
	case IS_NULL:
	    set_array[i] = NULL;
	    set_null[i] = 1;

	    break;
	case IS_LONG:
	case IS_DOUBLE:
	    convert_to_string_ex(data);
	case IS_STRING:
	    set_array[i] = Z_STRVAL_PP(data);
	    set_null[i] = 0;

	    break;
	default:
	    error_code = CUBRID_ER_NOT_SUPPORTED_TYPE;
	    goto ERR_CUBRID_MAKE_SET;
	}

	zend_hash_move_forward(ht);
    }

    if ((cubrid_retval = cci_set_make(set, CCI_U_TYPE_STRING, set_size, set_array, set_null)) < 0) {
	*set = NULL;
	error_code = cubrid_retval;
	goto ERR_CUBRID_MAKE_SET;
    }

    efree(set_array);
    efree(set_null);

    return 0;

ERR_CUBRID_MAKE_SET:

    if (set_array) {
	efree(set_array);
    }

    if (set_null) {
	efree(set_null);
    }

    return error_code;
}

static int type2str(T_CCI_COL_INFO * column_info, char *type_name, int type_name_len)
{
    char buf[64] = {'\0'};
    int u_type;
	int i = 0;
	int len = (sizeof(cubrid_type2name) / sizeof(cubrid_type2name[0]));

    u_type = CCI_GET_COLLECTION_DOMAIN(column_info->ext_type);

	for(i = 0;i<len; i++) {
      if (cubrid_type2name[i].type == u_type) {
        snprintf(buf, sizeof(buf), cubrid_type2name[i].name);
      }
    }   
	if(buf[0] == '\0')
		snprintf(buf, sizeof(buf), cubrid_type2name[0].name);
	
    if (CCI_IS_SET_TYPE(column_info->ext_type)) {
        snprintf(type_name, type_name_len, "set(%s)", buf);
    } else if (CCI_IS_MULTISET_TYPE(column_info->ext_type)) {
        snprintf(type_name, type_name_len, "multiset(%s)", buf);
    } else if (CCI_IS_SEQUENCE_TYPE(column_info->ext_type)) {
        snprintf(type_name, type_name_len, "sequence(%s)", buf);
    } else {
        snprintf(type_name, type_name_len, "%s", buf);
    }

    return 0;
}

static int numeric_type(T_CCI_U_TYPE type)
{
    if (type == CCI_U_TYPE_NUMERIC || 
	type == CCI_U_TYPE_INT ||
	type == CCI_U_TYPE_SHORT || 
	type == CCI_U_TYPE_FLOAT ||
	type == CCI_U_TYPE_DOUBLE || 
	type == CCI_U_TYPE_BIGINT ||
	type == CCI_U_TYPE_MONETARY) {
	return 1;
    } else {
	return 0;
    }
}

static int get_cubrid_u_type_by_name(const char *type_name)
{
    int i;
    int size = sizeof(db_type_info) / sizeof(db_type_info[0]);

    for (i = 0; i < size; i++) {
	if (strcasecmp(type_name, db_type_info[i].type_name) == 0) {
	    return db_type_info[i].cubrid_u_type;
	}
    }

    return U_TYPE_UNKNOWN;
}

static int get_cubrid_u_type_len(T_CCI_U_TYPE type)
{
    int i;
    int size = sizeof(db_type_info) / sizeof(db_type_info[0]);
    DB_TYPE_INFO type_info;

    for (i = 0; i < size; i++) {
	type_info = db_type_info[i];
	if (type == type_info.cubrid_u_type) {
	    return type_info.len;
	}
    }

    return 0;
}

static int cubrid_lob_new(int con_h_id, T_CCI_LOB *lob, T_CCI_U_TYPE type, T_CCI_ERROR *err_buf)
{
    return (type == CCI_U_TYPE_BLOB) ? 
        cci_blob_new(con_h_id, lob, err_buf) : cci_clob_new(con_h_id, lob, err_buf);
}

static php_cubrid_int64_t cubrid_lob_size(T_CCI_LOB lob, T_CCI_U_TYPE type)
{
    return (type == CCI_U_TYPE_BLOB) ? cci_blob_size(lob) : cci_clob_size(lob);
}

static int cubrid_lob_write(int con_h_id, T_CCI_LOB lob, T_CCI_U_TYPE type, php_cubrid_int64_t start_pos, int length, const char *buf, T_CCI_ERROR *err_buf)
{
    return (type == CCI_U_TYPE_BLOB) ? 
        cci_blob_write(con_h_id, lob, start_pos, length, buf, err_buf) : 
        cci_clob_write(con_h_id, lob, start_pos, length, buf, err_buf);
}

static int cubrid_lob_read(int con_h_id, T_CCI_LOB lob, T_CCI_U_TYPE type, php_cubrid_int64_t start_pos, int length, char *buf, T_CCI_ERROR *err_buf)
{
    return (type == CCI_U_TYPE_BLOB) ?
        cci_blob_read(con_h_id, lob, start_pos, length, buf, err_buf) :
        cci_clob_read(con_h_id, lob, start_pos, length, buf, err_buf);
}

static int cubrid_lob_free(T_CCI_LOB lob, T_CCI_U_TYPE type)
{
    return (type == CCI_U_TYPE_BLOB) ?
        cci_blob_free(lob) : cci_clob_free(lob);
}

/* method learn from pdo */
static const char digit_vec[] = "0123456789";
static char *php_cubrid_int64_to_str(php_cubrid_int64_t i64 TSRMLS_DC)
{
    char buffer[65];
    char outbuf[65] = "";
    register char *p;
    long long_val;
    char *dst = outbuf;

    if (i64 < 0) {
        i64 = -i64;
        *dst++ = '-';
    }

    if (i64 == 0) {
        *dst++ = '0';
        *dst++ = '\0';
        return estrdup(outbuf);
    }

    p = &buffer[sizeof(buffer)-1];
    *p = '\0';

    while ((php_cubrid_uint64_t)i64 > (php_cubrid_uint64_t)LONG_MAX) {
        php_cubrid_uint64_t quo = (php_cubrid_uint64_t)i64 / (unsigned int)10;
        unsigned int rem = (unsigned int)(i64 - quo*10U);
        *--p = digit_vec[rem];
        i64 = (php_cubrid_int64_t)quo;
    }
    long_val = (long)i64;
    while (long_val != 0) {
        long quo = long_val / 10;
        *--p = digit_vec[(unsigned int)(long_val - quo * 10)];
        long_val = quo;
    }
    while ((*dst++ = *p++) != 0)
            ;
    *dst = '\0';
    return estrdup(outbuf);
}

static int cubrid_get_charset_internal(int conn, T_CCI_ERROR *error)
{
    char *query = "SELECT charset FROM db_root";
    char *buffer;

    int cubrid_retval = 0;
    int request_handle = 0;
    int ind, index = -1;

    if ((cubrid_retval = cci_prepare(conn, query, 0, error)) < 0) {
        goto ERR_CUBRID_GET_CHARSET_INTERNAL;
    }

    request_handle = cubrid_retval;

    if ((cubrid_retval = cci_execute(request_handle, CCI_EXEC_ASYNC, 0, error)) < 0) {
        goto ERR_CUBRID_GET_CHARSET_INTERNAL;
    }

    cubrid_retval = cci_cursor(request_handle, 1, CCI_CURSOR_CURRENT, error);
    if (cubrid_retval < 0 && cubrid_retval != CCI_ER_NO_MORE_DATA) {
        goto ERR_CUBRID_GET_CHARSET_INTERNAL;
    }

    if ((cubrid_retval = cci_fetch(request_handle, error)) < 0) {
        goto ERR_CUBRID_GET_CHARSET_INTERNAL;
    }

    if ((cubrid_retval = cci_get_data(request_handle, 1, CCI_A_TYPE_STR, &buffer, &ind)) < 0) {
        goto ERR_CUBRID_GET_CHARSET_INTERNAL;
    }

    if (ind != -1) {
	index = atoi(buffer);
    } else {
	goto ERR_CUBRID_GET_CHARSET_INTERNAL;
    }

    if (index < 0 || index > MAX_DB_CHARSETS) {
	index = MAX_DB_CHARSETS;
    }

    cci_close_req_handle(request_handle);
    return index;

ERR_CUBRID_GET_CHARSET_INTERNAL:

    if (request_handle > 0) {
        cci_close_req_handle(request_handle);
    }

    return cubrid_retval;
}

static void php_cubrid_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, long type, int is_object)
{
    zval *req_id = NULL, *ctor_params = NULL, *param = NULL;
    zend_class_entry *ce = NULL;

    T_CUBRID_REQUEST *request;
    T_CCI_ERROR error;
    int cubrid_retval = 0;

    init_error();

    if (is_object) {
        char *class_name = NULL;
        int class_name_len = 0;
        long tmp = 0;

        switch (ZEND_NUM_ARGS()) {
        case 1:
            if (zend_parse_parameters(1 TSRMLS_CC, "r", &req_id) == FAILURE) {
                return;
            }

            ce = zend_standard_class_def;
            break;

        case 2:
            if (zend_parse_parameters(2 TSRMLS_CC, "rz", &req_id, &param) == FAILURE) {
                return;
            }

            switch (Z_TYPE_P(param)) {
            case IS_STRING:
                class_name = Z_STRVAL_P(param);
                class_name_len = Z_STRLEN_P(param);

                ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);

                break;
            case IS_LONG:
                tmp = Z_LVAL_P(param);
                if (tmp & CUBRID_LOB) {
                    type |= CUBRID_LOB;
                }

                ce = zend_standard_class_def;

                break;
            default:
                return;
            }

            break;
        case 3:
            if (zend_parse_parameters(3 TSRMLS_CC, "rsz", &req_id, &class_name, &class_name_len, &param) == FAILURE) {
                return;
            }

            switch (Z_TYPE_P(param)) {
            case IS_LONG:
                tmp = Z_LVAL_P(param); 
                if (tmp & CUBRID_LOB) {
                    type |= CUBRID_LOB;
                }

                break;

            default:
                ctor_params = param;
            }

            ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);

            break;

        case 4:
            if (zend_parse_parameters(4 TSRMLS_CC, "rszl", 
                        &req_id, &class_name, &class_name_len, &ctor_params, &tmp) == FAILURE) {
                return;
            }

            ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);

            if (tmp & CUBRID_LOB) {
                type |= CUBRID_LOB;
            }

            break;

        default:
            return;
        }

        if (!ce) {
            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find class '%s'", class_name);
            return;
        }

    } else {

        long type2 = 0;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &req_id, &type2) == FAILURE) {
            return;
        }

        if (type2 == 0 && type == 0) {
            type = CUBRID_BOTH;
        } else if (type2 == CUBRID_LOB && type == 0) {
            type = CUBRID_BOTH | CUBRID_LOB;
        } else {
            type |= type2;
        }

        if (type & CUBRID_OBJECT) {
            type |= CUBRID_ASSOC;
        }
    }

    ZEND_FETCH_RESOURCE(request, T_CUBRID_REQUEST *, &req_id, -1, "CUBRID-Request", le_request);

    init_error_link(request->conn);

    if ((type & CUBRID_LOB) && (request->lob2_list == NULL)) {
        request->lob2_list = (T_CUBRID_LOB2 **) emalloc (sizeof(T_CUBRID_LOB2 *) * request->col_count);
        if (!request->lob2_list) {
            handle_error(CCI_ER_NO_MORE_MEMORY, NULL, request->conn);
            return;
        }
        memset(request->lob2_list, 0, sizeof(T_CUBRID_LOB2 *) * request->col_count);
    }

    cubrid_retval = cci_cursor(request->handle, 0, CCI_CURSOR_CURRENT, &error);
    if (cubrid_retval == CCI_ER_NO_MORE_DATA) {
        if (request->field_lengths != NULL) {
            efree (request->field_lengths);
            request->field_lengths = NULL;
        }

        if (request->lob2_list != NULL) {
            int i;
            for (i = 0; i < request->col_count; i++) {
                if (request->lob2_list[i]) {
                    request->lob2_list[i]->connect = NULL;
                    request->lob2_list[i] = NULL;
                }
            }
        }

        RETURN_FALSE;
    } else if (cubrid_retval < 0) {
        handle_error(cubrid_retval, &error, request->conn);
        return;
    }

    if ((cubrid_retval = cci_fetch(request->handle, &error)) < 0) {
        handle_error(cubrid_retval, &error, request->conn);
        return;
    }

    if ((cubrid_retval = fetch_a_row(return_value, request->conn, request->handle, request, type TSRMLS_CC)) != SUCCESS) {
        handle_error(cubrid_retval, NULL, request->conn);
        return;
    }

    if (is_object) {
        zval dataset = *return_value;
        zend_fcall_info fci;
        zend_fcall_info_cache fcc;
        zval *retval_ptr;

        object_and_properties_init(return_value, ce, NULL);
        zend_merge_properties(return_value, Z_ARRVAL(dataset), 1 TSRMLS_CC);

        if (ce->constructor) {
            fci.size = sizeof(fci);
            fci.function_table = &ce->function_table;
            fci.function_name = NULL;
            fci.symbol_table = NULL;
#if PHP_MINOR_VERSION < 3
            fci.object_pp = &return_value;
#else
            fci.object_ptr = return_value;
#endif
            fci.retval_ptr_ptr = &retval_ptr;

            if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
                if (Z_TYPE_P(ctor_params) == IS_ARRAY) {
                    HashTable *ht = Z_ARRVAL_P(ctor_params);
                    Bucket *p;

                    fci.param_count = 0;
                    fci.params = safe_emalloc(sizeof(zval*), ht->nNumOfElements, 0);
                    p = ht->pListHead;
                    while (p != NULL) {
                        fci.params[fci.param_count++] = (zval**)p->pData;
                        p = p->pListNext;
                    }
                } else {
                    zend_throw_exception(zend_exception_get_default(TSRMLS_C), "Parameter ctor_params must be an array", 0 TSRMLS_CC);
                    return;
                }
            } else {
                fci.param_count = 0;
                fci.params = NULL;
            }

            fci.no_separation = 1;

            fcc.initialized = 1;
            fcc.function_handler = ce->constructor;
            fcc.calling_scope = EG(scope);
#if PHP_MINOR_VERSION < 3
            fcc.object_pp = &return_value;
#else
            fcc.called_scope = Z_OBJCE_P(return_value);
            fcc.object_ptr = return_value;
#endif

            if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {
                zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, 
                        "Could not execute %s::%s()", ce->name, ce->constructor->common.function_name);
            } else {
                if (retval_ptr) {
                    zval_ptr_dtor(&retval_ptr);
                }
            }

            if (fci.params) {
                efree(fci.params);
            }
        } else if (ctor_params) {
            zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC,
                    "Class %s does not have a constructor hence you cannot use ctor_params", ce->name);
        }

        if (Z_TYPE_P(return_value) == IS_ARRAY) {
            object_and_properties_init(return_value, ZEND_STANDARD_CLASS_DEF_PTR, Z_ARRVAL_P(return_value));
        }
    }

    if (type & CUBRID_OBJECT) {
        if (return_value->type == IS_ARRAY) {
            convert_to_object(return_value);
        }
    }

    cubrid_retval = cci_cursor (request->handle, 1, CCI_CURSOR_CURRENT, &error);
    if (cubrid_retval < 0 && cubrid_retval != CCI_ER_NO_MORE_DATA) {
        handle_error(cubrid_retval, &error, request->conn);
        return;
    }
}

static void linked_list_append(LINKED_LIST *list, void *data)
{
    LINKED_LIST_NODE *new_node = (LINKED_LIST_NODE *) emalloc(sizeof(LINKED_LIST_NODE));

    if (!new_node) {
        return;
    }

    new_node->data = data;
    new_node->next = NULL;

    list->tail->next = new_node;
    list->tail = new_node;
}

static void linked_list_delete(LINKED_LIST *list, void *data)
{
    LINKED_LIST_NODE *p, *q;

    for (p = list->head, q = p->next; q != NULL; p = q, q = q->next) {
        if (q->data == data) {
            p->next = q->next;

            if (list->tail == q) {
                list->tail = p;
            }

            efree(q);
            break;
        }
    }
}