add cursor support
[m6w6/ext-pq] / src / php_pqconn.c
index 5031426561dafd6f2da433575565ca9d42e713a8..79fa9a0422d58ca8bd74779b5b32a401d7bfada8 100644 (file)
@@ -31,6 +31,7 @@
 #include "php_pqres.h"
 #include "php_pqstm.h"
 #include "php_pqtxn.h"
+#include "php_pqcur.h"
 
 zend_class_entry *php_pqconn_class_entry;
 static zend_object_handlers php_pqconn_object_handlers;
@@ -73,10 +74,11 @@ static void php_pqconn_object_free(void *o TSRMLS_DC)
        fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj);
 #endif
        if (obj->intern) {
+               php_pq_callback_dtor(&obj->intern->onevent);
                php_resource_factory_handle_dtor(&obj->intern->factory, obj->intern->conn TSRMLS_CC);
                php_resource_factory_dtor(&obj->intern->factory);
-               php_pq_callback_dtor(&obj->intern->onevent);
                zend_hash_destroy(&obj->intern->listeners);
+               zend_hash_destroy(&obj->intern->converters);
                zend_hash_destroy(&obj->intern->eventhandlers);
                efree(obj->intern);
                obj->intern = NULL;
@@ -311,12 +313,40 @@ static void php_pqconn_object_read_options(zval *object, void *o, zval *return_v
        }
 }
 
+static int apply_read_event_handler_ex(void *p, void *arg TSRMLS_DC)
+{
+       HashTable *rv = arg;
+       zval *zcb = php_pq_callback_to_zval(p);
+
+       zend_hash_next_index_insert(rv, &zcb, sizeof(zval *), NULL);
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+static int apply_read_event_handlers(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+{
+       HashTable *evhs = p, *rv = va_arg(argv, HashTable *);
+       zval *entry, **entry_ptr;
+
+       MAKE_STD_ZVAL(entry);
+       array_init_size(entry, zend_hash_num_elements(evhs));
+
+       if (key->nKeyLength) {
+               zend_hash_add(rv, key->arKey, key->nKeyLength, &entry, sizeof(zval *), (void *) &entry_ptr);
+       } else {
+               zend_hash_index_update(rv, key->h, &entry, sizeof(zval *), (void *) &entry_ptr);
+       }
+
+       zend_hash_apply_with_argument(evhs, apply_read_event_handler_ex, Z_ARRVAL_PP(entry_ptr) TSRMLS_CC);
+
+       return ZEND_HASH_APPLY_KEEP;
+}
 static void php_pqconn_object_read_event_handlers(zval *object, void *o, zval *return_value TSRMLS_DC)
 {
        php_pqconn_object_t *obj = o;
 
        array_init(return_value);
-       zend_hash_copy(Z_ARRVAL_P(return_value), &obj->intern->eventhandlers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+       zend_hash_apply_with_arguments(&obj->intern->eventhandlers TSRMLS_CC, apply_read_event_handlers, 1, Z_ARRVAL_P(return_value) TSRMLS_CC);
 }
 
 static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
@@ -462,7 +492,7 @@ static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle
                zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj);
 
                /* release instance data */
-               memset(evdata, 0, sizeof(*evdata));
+               //memset(evdata, 0, sizeof(*evdata));
                efree(evdata);
        }
 }
@@ -494,6 +524,7 @@ static PHP_METHOD(pqconn, __construct) {
                        obj->intern = ecalloc(1, sizeof(*obj->intern));
 
                        zend_hash_init(&obj->intern->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
+                       zend_hash_init(&obj->intern->converters, 0, NULL, ZVAL_PTR_DTOR, 0);
                        zend_hash_init(&obj->intern->eventhandlers, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
 
                        if (flags & PHP_PQCONN_PERSISTENT) {
@@ -596,7 +627,7 @@ static PHP_METHOD(pqconn, listen) {
        zend_error_handling zeh;
        char *channel_str = NULL;
        int channel_len = 0;
-       php_pq_callback_t listener;
+       php_pq_callback_t listener = {{0}};
        STATUS rv;
 
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
@@ -650,7 +681,7 @@ static PHP_METHOD(pqconn, listenAsync) {
        zend_error_handling zeh;
        char *channel_str = NULL;
        int channel_len = 0;
-       php_pq_callback_t listener;
+       php_pq_callback_t listener = {{0}};
        STATUS rv;
 
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
@@ -910,27 +941,11 @@ static PHP_METHOD(pqconn, execParams) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
                        PGresult *res;
-                       int count;
-                       Oid *types = NULL;
-                       char **params = NULL;
-                       HashTable zdtor;
-
-                       ZEND_INIT_SYMTABLE(&zdtor);
-                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
+                       php_pq_params_t *params;
 
-                       if (ztypes) {
-                               php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC);
-                       }
-
-                       res = PQexecParams(obj->intern->conn, query_str, count, types, (const char *const*) params, NULL, NULL, 0);
-
-                       zend_hash_destroy(&zdtor);
-                       if (types) {
-                               efree(types);
-                       }
-                       if (params) {
-                               efree(params);
-                       }
+                       params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, Z_ARRVAL_P(zparams) TSRMLS_CC);
+                       res = PQexecParams(obj->intern->conn, query_str, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0);
+                       php_pq_params_free(&params);
 
                        if (!res) {
                                throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
@@ -972,19 +987,14 @@ static PHP_METHOD(pqconn, execParamsAsync) {
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
-                       int count;
-                       Oid *types = NULL;
-                       char **params = NULL;
-                       HashTable zdtor;
-
-                       ZEND_INIT_SYMTABLE(&zdtor);
-                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
+                       int rc;
+                       php_pq_params_t *params;
 
-                       if (ztypes) {
-                               php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC);
-                       }
+                       params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, Z_ARRVAL_P(zparams) TSRMLS_CC);
+                       rc = PQsendQueryParams(obj->intern->conn, query_str, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0);
+                       php_pq_params_free(&params);
 
-                       if (!PQsendQueryParams(obj->intern->conn, query_str, count, types, (const char *const*) params, NULL, NULL, 0)) {
+                       if (!rc) {
                                throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
                        } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
                                throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
@@ -997,23 +1007,13 @@ static PHP_METHOD(pqconn, execParamsAsync) {
                                }
                                php_pqconn_notify_listeners(obj TSRMLS_CC);
                        }
-
-                       zend_hash_destroy(&zdtor);
-                       if (types) {
-                               efree(types);
-                       }
-                       if (params) {
-                               efree(params);
-                       }
                }
        }
        zend_restore_error_handling(&zeh TSRMLS_CC);
 }
 
-STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, HashTable *typest TSRMLS_DC)
+STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
 {
-       Oid *types = NULL;
-       int count = 0;
        PGresult *res;
        STATUS rv;
 
@@ -1021,16 +1021,7 @@ STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *na
                obj = zend_object_store_get_object(object TSRMLS_CC);
        }
 
-       if (typest) {
-               count = zend_hash_num_elements(typest);
-               php_pq_types_to_array(typest, &types TSRMLS_CC);
-       }
-
-       res = PQprepare(obj->intern->conn, name, query, count, types);
-
-       if (types) {
-               efree(types);
-       }
+       res = PQprepare(obj->intern->conn, name, query, params->type.count, params->type.oids);
 
        if (!res) {
                rv = FAILURE;
@@ -1045,7 +1036,7 @@ STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *na
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2)
-       ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, name)
        ZEND_ARG_INFO(0, query)
        ZEND_ARG_ARRAY_INFO(0, types, 1)
 ZEND_END_ARG_INFO();
@@ -1065,35 +1056,36 @@ static PHP_METHOD(pqconn, prepare) {
 
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
-               } else if (SUCCESS == php_pqconn_prepare(getThis(), obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
-                       php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
+               } else {
+                       php_pq_params_t *params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC);
 
-                       php_pq_object_addref(obj TSRMLS_CC);
-                       stm->conn = obj;
-                       stm->name = estrdup(name_str);
-                       ZEND_INIT_SYMTABLE(&stm->bound);
+                       if (SUCCESS != php_pqconn_prepare(getThis(), obj, name_str, query_str, params TSRMLS_CC)) {
+                               php_pq_params_free(&params);
+                       } else {
+                               php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
 
-                       return_value->type = IS_OBJECT;
-                       return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
+                               php_pq_object_addref(obj TSRMLS_CC);
+                               stm->conn = obj;
+                               stm->name = estrdup(name_str);
+                               stm->params = params;
+                               ZEND_INIT_SYMTABLE(&stm->bound);
+
+                               return_value->type = IS_OBJECT;
+                               return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
+                       }
                }
        }
 }
 
-STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, HashTable *typest TSRMLS_DC)
+STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
 {
        STATUS rv;
-       int count;
-       Oid *types = NULL;
 
        if (!obj) {
                obj = zend_object_store_get_object(object TSRMLS_CC);
        }
 
-       if (typest) {
-               count = php_pq_types_to_array(typest, &types TSRMLS_CC);
-       }
-
-       if (!PQsendPrepare(obj->intern->conn, name, query, count, types)) {
+       if (!PQsendPrepare(obj->intern->conn, name, query, params->type.count, params->type.oids)) {
                rv = FAILURE;
                throw_exce(EX_IO TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn));
        } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
@@ -1105,15 +1097,11 @@ STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const ch
                php_pqconn_notify_listeners(obj TSRMLS_CC);
        }
 
-       if (types) {
-               efree(types);
-       }
-
        return rv;
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2)
-ZEND_ARG_INFO(0, type)
+       ZEND_ARG_INFO(0, name)
        ZEND_ARG_INFO(0, query)
        ZEND_ARG_ARRAY_INFO(0, types, 1)
 ZEND_END_ARG_INFO();
@@ -1133,16 +1121,171 @@ static PHP_METHOD(pqconn, prepareAsync) {
 
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
-               } else if (SUCCESS == php_pqconn_prepare_async(getThis(), obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
-                       php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
+               } else {
+                       php_pq_params_t *params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC);
 
-                       php_pq_object_addref(obj TSRMLS_CC);
-                       stm->conn = obj;
-                       stm->name = estrdup(name_str);
-                       ZEND_INIT_SYMTABLE(&stm->bound);
+                       if (SUCCESS != php_pqconn_prepare_async(getThis(), obj, name_str, query_str, params TSRMLS_CC)) {
+                               php_pq_params_free(&params);
+                       } else {
+                               php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
 
-                       return_value->type = IS_OBJECT;
-                       return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
+                               php_pq_object_addref(obj TSRMLS_CC);
+                               stm->conn = obj;
+                               stm->name = estrdup(name_str);
+                               stm->params = params;
+                               ZEND_INIT_SYMTABLE(&stm->bound);
+
+                               return_value->type = IS_OBJECT;
+                               return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
+                       }
+               }
+       }
+}
+
+static inline char *declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len)
+{
+       size_t decl_len = name_len + query_len + sizeof("DECLARE BINARY INSENSITIVE NO SCROLL  CURSOR WITHOUT HOLD FOR ");
+       char *decl_str;
+
+       decl_str = emalloc(decl_len);
+       decl_len = slprintf(decl_str, decl_len, "DECLARE %s %s %s %s CURSOR %s FOR %s",
+                       name_str,
+                       (flags & PHP_PQ_DECLARE_BINARY) ? "BINARY" : "",
+                       (flags & PHP_PQ_DECLARE_INSENSITIVE) ? "INSENSITIVE" : "",
+                       (flags & PHP_PQ_DECLARE_NO_SCROLL) ? "NO SCROLL" :
+                                       (flags & PHP_PQ_DECLARE_SCROLL) ? "SCROLL" : "",
+                       (flags & PHP_PQ_DECLARE_WITH_HOLD) ? "WITH HOLD" : "",
+                       query_str
+       );
+       return decl_str;
+}
+
+STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
+{
+       PGresult *res;
+       STATUS rv;
+
+       if (!obj) {
+               obj = zend_object_store_get_object(object TSRMLS_CC);
+       }
+
+       res = PQexec(obj->intern->conn, decl);
+
+       if (!res) {
+               rv = FAILURE;
+               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn));
+       } else {
+               rv = php_pqres_success(res TSRMLS_CC);
+               PHP_PQclear(res);
+               php_pqconn_notify_listeners(obj TSRMLS_CC);
+       }
+
+       return rv;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare, 0, 0, 3)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, flags)
+       ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqconn, declare) {
+       zend_error_handling zeh;
+       char *name_str, *query_str;
+       int name_len, query_len;
+       long flags;
+       STATUS rv;
+
+       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
+       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (SUCCESS == rv) {
+               php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (!obj->intern) {
+                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
+               } else {
+                       char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
+
+                       if (SUCCESS != php_pqconn_declare(getThis(), obj, decl TSRMLS_CC)) {
+                               efree(decl);
+                       } else {
+                               php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
+
+                               php_pq_object_addref(obj TSRMLS_CC);
+                               cur->conn = obj;
+                               cur->open = 1;
+                               cur->name = estrdup(name_str);
+                               cur->decl = decl;
+
+                               return_value->type = IS_OBJECT;
+                               return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
+                       }
+               }
+       }
+}
+
+STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
+{
+       STATUS rv;
+
+       if (!obj) {
+               obj = zend_object_store_get_object(object TSRMLS_CC);
+       }
+
+       if (!PQsendQuery(obj->intern->conn, decl)) {
+               rv = FAILURE;
+               throw_exce(EX_IO TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn));
+       } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
+               rv = FAILURE;
+               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
+       } else {
+               rv = SUCCESS;
+               obj->intern->poller = PQconsumeInput;
+               php_pqconn_notify_listeners(obj TSRMLS_CC);
+       }
+
+       return rv;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare_async, 0, 0, 2)
+       ZEND_ARG_INFO(0, name)
+       ZEND_ARG_INFO(0, flags)
+       ZEND_ARG_INFO(0, query)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqconn, declareAsync) {
+       zend_error_handling zeh;
+       char *name_str, *query_str;
+       int name_len, query_len;
+       long flags;
+       STATUS rv;
+
+       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
+       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (SUCCESS == rv) {
+               php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (!obj->intern) {
+                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
+               } else {
+                       char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
+
+                       if (SUCCESS != php_pqconn_declare_async(getThis(), obj, decl TSRMLS_CC)) {
+                               efree(decl);
+                       } else {
+                               php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
+
+                               php_pq_object_addref(obj TSRMLS_CC);
+                               cur->conn = obj;
+                               cur->open = 1;
+                               cur->name = estrdup(name_str);
+                               cur->decl = decl;
+
+                               return_value->type = IS_OBJECT;
+                               return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
+                       }
                }
        }
 }
@@ -1441,7 +1584,7 @@ static PHP_METHOD(pqconn, on) {
        zend_error_handling zeh;
        char *type_str;
        int type_len;
-       php_pq_callback_t cb;
+       php_pq_callback_t cb = {{0}};
        STATUS rv;
 
        zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
@@ -1461,6 +1604,54 @@ static PHP_METHOD(pqconn, on) {
        }
 }
 
+static int apply_set_converter(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+{
+       zval *tmp, **zoid = p, **zcnv = va_arg(argv, zval **);
+       HashTable *converters = va_arg(argv, HashTable *);
+
+       tmp = *zoid;
+       convert_to_long_ex(&tmp);
+       Z_ADDREF_PP(zcnv);
+       zend_hash_index_update(converters, Z_LVAL_P(tmp), zcnv, sizeof(zval *), NULL);
+       if (tmp != *zoid) {
+               zval_ptr_dtor(&tmp);
+       }
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_set_converter, 0, 0, 1)
+       ZEND_ARG_OBJ_INFO(0, converter, pq\\ConverterInterface, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqconn, setConverter) {
+       STATUS rv;
+       zend_error_handling zeh;
+       zval *zcnv;
+
+       zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
+       rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zcnv, php_pqconv_class_entry);
+       zend_restore_error_handling(&zeh TSRMLS_CC);
+
+       if (SUCCESS == rv) {
+               php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+               if (!obj->intern) {
+                       throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
+               } else {
+                       zval *tmp, *zoids = NULL;
+
+                       zend_call_method_with_0_params(&zcnv, NULL, NULL, "converttypes", &zoids);
+                       tmp = zoids;
+                       convert_to_array_ex(&zoids);
+                       zend_hash_apply_with_arguments(Z_ARRVAL_P(zoids) TSRMLS_CC, apply_set_converter, 2, &zcnv, &obj->intern->converters);
+                       if (tmp != zoids) {
+                               zval_ptr_dtor(&tmp);
+                       }
+                       zval_ptr_dtor(&zoids);
+               }
+       }
+}
+
 static zend_function_entry php_pqconn_methods[] = {
        PHP_ME(pqconn, __construct, ai_pqconn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
        PHP_ME(pqconn, reset, ai_pqconn_reset, ZEND_ACC_PUBLIC)
@@ -1472,6 +1663,8 @@ static zend_function_entry php_pqconn_methods[] = {
        PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC)
+       PHP_ME(pqconn, declare, ai_pqconn_declare, ZEND_ACC_PUBLIC)
+       PHP_ME(pqconn, declareAsync, ai_pqconn_declare_async, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC)
@@ -1485,9 +1678,16 @@ static zend_function_entry php_pqconn_methods[] = {
        PHP_ME(pqconn, startTransactionAsync, ai_pqconn_start_transaction_async, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, trace, ai_pqconn_trace, ZEND_ACC_PUBLIC)
        PHP_ME(pqconn, on, ai_pqconn_on, ZEND_ACC_PUBLIC)
+       PHP_ME(pqconn, setConverter, ai_pqconn_set_converter, ZEND_ACC_PUBLIC)
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqconn)
+{
+       zend_hash_destroy(&php_pqconn_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqconn)
 {
        zend_class_entry ce = {0};
@@ -1502,6 +1702,7 @@ PHP_MINIT_FUNCTION(pqconn)
        php_pqconn_object_handlers.write_property = php_pq_object_write_prop;
        php_pqconn_object_handlers.clone_obj = NULL;
        php_pqconn_object_handlers.get_property_ptr_ptr = NULL;
+       php_pqconn_object_handlers.get_gc = NULL;
        php_pqconn_object_handlers.get_properties = php_pq_object_properties;
        php_pqconn_object_handlers.get_debug_info = php_pq_object_debug_info;