type converters
authorMichael Wallner <mike@php.net>
Tue, 14 May 2013 11:09:23 +0000 (13:09 +0200)
committerMichael Wallner <mike@php.net>
Tue, 14 May 2013 11:09:23 +0000 (13:09 +0200)
29 files changed:
config.m4
package.xml
src/php_pq_misc.c
src/php_pq_misc.h
src/php_pq_module.c
src/php_pq_object.c
src/php_pq_params.c [new file with mode: 0644]
src/php_pq_params.h [new file with mode: 0644]
src/php_pqcancel.c
src/php_pqcancel.h
src/php_pqconn.c
src/php_pqconn.h
src/php_pqconn_event.c
src/php_pqcopy.c
src/php_pqcopy.h
src/php_pqlob.c
src/php_pqlob.h
src/php_pqres.c
src/php_pqres.h
src/php_pqstm.c
src/php_pqstm.h
src/php_pqtxn.c
src/php_pqtxn.h
src/php_pqtypes.c
src/php_pqtypes.h
tests/cancel001.phpt
tests/conv001.phpt [new file with mode: 0644]
tests/copy001.phpt
tests/types001.phpt

index a6a82b5..79b2f89 100644 (file)
--- a/config.m4
+++ b/config.m4
@@ -39,6 +39,7 @@ if test "$PHP_PQ" != "no"; then
                src/php_pq_misc.c\
                src/php_pq_callback.c\
                src/php_pq_object.c\
+               src/php_pq_params.c\
                src/php_pqcancel.c\
                src/php_pqconn.c\
                src/php_pqconn_event.c\
index cba4011..c686d58 100644 (file)
@@ -34,8 +34,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
  </lead>
  <date>2013-05-03</date>
  <version>
-  <release>0.3.0</release>
-  <api>0.3.0</api>
+  <release>0.4.0dev</release>
+  <api>0.4.0</api>
  </version>
  <stability>
   <release>beta</release>
@@ -43,7 +43,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
  </stability>
  <license>BSD, revised</license>
  <notes><![CDATA[
-* Added parser for result arrays ('{{1,2},{3,4}}')
+* Added pq\ConverterInterface and pq\Connection::setConverter()
 ]]></notes>
  <contents>
   <dir name="/">
@@ -72,6 +72,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
     <file role="src" name="php_pq_module.c" />
     <file role="src" name="php_pq_object.c" />
     <file role="src" name="php_pq_object.h" />
+    <file role="src" name="php_pq_params.c" />
+    <file role="src" name="php_pq_params.h" />
     <file role="src" name="php_pqres.c" />
     <file role="src" name="php_pqres.h" />
     <file role="src" name="php_pqstm.c" />
@@ -93,6 +95,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
     <file role="test" name="bound001.phpt" />
     <file role="test" name="bound002.phpt" />
     <file role="test" name="cancel001.phpt" />
+    <file role="test" name="conv001.phpt" />
     <file role="test" name="copy001.phpt" />
     <file role="test" name="encoding001.phpt" />
     <file role="test" name="exceptions001.phpt" />
index 0f4c83a..fdc03b2 100644 (file)
@@ -66,204 +66,6 @@ int compare_index(const void *lptr, const void *rptr TSRMLS_DC)
        return 0;
 }
 
-static int apply_to_oid(void *p, void *arg TSRMLS_DC)
-{
-       Oid **types = arg;
-       zval **ztype = p;
-
-       if (Z_TYPE_PP(ztype) != IS_LONG) {
-               convert_to_long_ex(ztype);
-       }
-
-       **types = Z_LVAL_PP(ztype);
-       ++*types;
-
-       if (*ztype != *(zval **)p) {
-               zval_ptr_dtor(ztype);
-       }
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-static int apply_to_param_from_array(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
-{
-       zval **zparam = p;
-       unsigned j, *i = va_arg(argv, unsigned *);
-       smart_str *s = va_arg(argv, smart_str *);
-       char *tmp;
-       size_t len;
-       int tmp_len;
-
-       if ((*i)++) {
-               smart_str_appendc(s, ',');
-       }
-
-       switch (Z_TYPE_PP(zparam)) {
-       case IS_NULL:
-               smart_str_appends(s, "NULL");
-               break;
-
-       case IS_BOOL:
-               smart_str_appends(s, Z_BVAL_PP(zparam) ? "t" : "f");
-               break;
-
-       case IS_LONG:
-               smart_str_append_long(s, Z_LVAL_PP(zparam));
-               break;
-
-       case IS_DOUBLE:
-               len = spprintf(&tmp, 0, "%F", Z_DVAL_PP(zparam));
-               smart_str_appendl(s, tmp, len);
-               efree(tmp);
-               break;
-
-       case IS_ARRAY:
-               j = 0;
-               smart_str_appendc(s, '{');
-               zend_hash_apply_with_arguments(Z_ARRVAL_PP(zparam) TSRMLS_CC, apply_to_param_from_array, 2, &j, s);
-               smart_str_appendc(s, '}');
-               break;
-
-       default:
-       {
-               SEPARATE_ZVAL(zparam);
-               if (Z_TYPE_PP(zparam) != IS_STRING) {
-                       convert_to_string(*zparam);
-               }
-
-               tmp = php_addslashes(Z_STRVAL_PP(zparam), Z_STRLEN_PP(zparam), &tmp_len, 0 TSRMLS_CC);
-               smart_str_appendc(s, '"');
-               smart_str_appendl(s, tmp, tmp_len);
-               smart_str_appendc(s, '"');
-
-               if (*zparam != *((zval **) p)) {
-                       zval_ptr_dtor(zparam);
-               }
-               break;
-       }
-       }
-
-       ++(*i);
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-static void array_param_to_string(HashTable *ht, char **str, int *len TSRMLS_DC)
-{
-       smart_str s = {0};
-       unsigned i = 0;
-
-       smart_str_appendc(&s, '{');
-       zend_hash_apply_with_arguments(ht TSRMLS_CC, apply_to_param_from_array, 2, &i, &s);
-       smart_str_appendc(&s, '}');
-
-       smart_str_0(&s);
-       *str = s.c;
-       *len = s.len;
-}
-
-static int apply_to_param(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
-{
-       char ***params;
-       HashTable *zdtor;
-       zval **zparam = p;
-
-       params = (char ***) va_arg(argv, char ***);
-       zdtor = (HashTable *) va_arg(argv, HashTable *);
-
-       switch (Z_TYPE_PP(zparam)) {
-       case IS_NULL:
-               **params = NULL;
-               ++*params;
-               return ZEND_HASH_APPLY_KEEP;
-
-       case IS_BOOL:
-               **params = Z_BVAL_PP(zparam) ? "t" : "f";
-               ++*params;
-               return ZEND_HASH_APPLY_KEEP;
-
-       case IS_DOUBLE:
-               SEPARATE_ZVAL(zparam);
-               Z_TYPE_PP(zparam) = IS_STRING;
-               Z_STRLEN_PP(zparam) = spprintf(&Z_STRVAL_PP(zparam), 0, "%F", Z_DVAL_PP((zval **)p));
-               break;
-
-       case IS_ARRAY:
-       {
-               zval *tmp;
-               MAKE_STD_ZVAL(tmp);
-               Z_TYPE_P(tmp) = IS_STRING;
-               array_param_to_string(Z_ARRVAL_PP(zparam), &Z_STRVAL_P(tmp), &Z_STRLEN_P(tmp) TSRMLS_CC);
-               zparam = &tmp;
-               break;
-       }
-
-       default:
-               convert_to_string_ex(zparam);
-               break;
-       }
-
-       **params = Z_STRVAL_PP(zparam);
-       ++*params;
-
-       if (*zparam != *(zval **)p) {
-               zend_hash_next_index_insert(zdtor, zparam, sizeof(zval *), NULL);
-       }
-       return ZEND_HASH_APPLY_KEEP;
-}
-
-int php_pq_types_to_array(HashTable *ht, Oid **types TSRMLS_DC)
-{
-       int count = zend_hash_num_elements(ht);
-
-       *types = NULL;
-
-       if (count) {
-               Oid *tmp;
-
-               /* +1 for when less types than params are specified */
-               *types = tmp = ecalloc(count + 1, sizeof(**types));
-               zend_hash_apply_with_argument(ht, apply_to_oid, &tmp TSRMLS_CC);
-       }
-
-       return count;
-}
-
-int php_pq_params_to_array(HashTable *ht, char ***params, HashTable *zdtor TSRMLS_DC)
-{
-       int count = zend_hash_num_elements(ht);
-
-       *params = NULL;
-
-       if (count) {
-               char **tmp;
-
-               *params = tmp = ecalloc(count, sizeof(char *));
-               zend_hash_apply_with_arguments(ht TSRMLS_CC, apply_to_param, 2, &tmp, zdtor);
-       }
-
-       return count;
-}
-
-/*
-Oid *php_pq_ntypes_to_array(zend_bool fill, int argc, ...)
-{
-       int i;
-       Oid *oids = ecalloc(argc + 1, sizeof(*oids));
-       va_list argv;
-
-       va_start(argv, argc);
-       for (i = 0; i < argc; ++i) {
-               if (!fill || !i) {
-                       oids[i] = va_arg(argv, Oid);
-               } else {
-                       oids[i] = oids[0];
-               }
-       }
-       va_end(argv);
-
-       return oids;
-}
-*/
-
 zend_class_entry *php_pqdt_class_entry;
 
 ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_to_string, 0, 0, 0)
@@ -303,10 +105,35 @@ zval *php_pqdt_from_string(char *dt_str, size_t dt_len, char *fmt, zval *zv TSRM
        return zv;
 }
 
+zend_class_entry *php_pqconv_class_entry;
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_types, 0, 0, 0)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_from_string, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_to_string, 0, 0, 1)
+       ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO();
+
+zend_function_entry php_pqconv_methods[] = {
+       PHP_ABSTRACT_ME(pqconv, convertTypes, ai_pqconv_convert_types)
+       PHP_ABSTRACT_ME(pqconv, convertFromString, ai_pqconv_convert_from_string)
+       PHP_ABSTRACT_ME(pqconv, convertToString, ai_pqconv_convert_to_string)
+       {0}
+};
+
+
 PHP_MINIT_FUNCTION(pq_misc)
 {
        zend_class_entry **json, ce = {0};
 
+       INIT_NS_CLASS_ENTRY(ce, "pq", "ConverterInterface", php_pqconv_methods);
+       php_pqconv_class_entry = zend_register_internal_interface(&ce TSRMLS_CC);
+
+       memset(&ce, 0, sizeof(ce));
        INIT_NS_CLASS_ENTRY(ce ,"pq", "DateTime", php_pqdt_methods);
        php_pqdt_class_entry = zend_register_internal_class_ex(&ce, php_date_get_date_ce(), "DateTime" TSRMLS_CC);
 
index 07f75a3..0ba99a2 100644 (file)
@@ -30,12 +30,11 @@ int compare_index(const void *lptr, const void *rptr TSRMLS_DC);
 #define PHP_PQerrorMessage(c) rtrim(PQerrorMessage((c)))
 #define PHP_PQresultErrorMessage(r) rtrim(PQresultErrorMessage((r)))
 
-int php_pq_types_to_array(HashTable *ht, Oid **types TSRMLS_DC);
-int php_pq_params_to_array(HashTable *ht, char ***params, HashTable *zdtor TSRMLS_DC);
-
 zend_class_entry *php_pqdt_class_entry;
 zval *php_pqdt_from_string(char *datetime_str, size_t datetime_len, char *fmt, zval *zv TSRMLS_DC);
 
+zend_class_entry *php_pqconv_class_entry;
+
 HashTable *php_pq_parse_array(const char *val_str, size_t val_len, Oid typ TSRMLS_DC);
 zval *php_pq_typed_zval(char *val_str, size_t val_len, Oid typ TSRMLS_DC);
 
index 02cd6db..cb7a4e9 100644 (file)
@@ -69,9 +69,25 @@ static PHP_MINIT_FUNCTION(pq)
        return php_persistent_handle_provide(ZEND_STRL("pq\\Connection"), php_pqconn_get_resource_factory_ops(), NULL, NULL TSRMLS_CC);
 }
 
+#define PHP_MSHUT_CALL(i) do { \
+       if (SUCCESS != PHP_MSHUTDOWN(i)(type, module_number TSRMLS_CC)) { \
+               return FAILURE; \
+       } \
+} while(0)
+
 static PHP_MSHUTDOWN_FUNCTION(pq)
 {
        php_persistent_handle_cleanup(ZEND_STRL("pq\\Connection"), NULL, 0 TSRMLS_CC);
+
+       PHP_MSHUT_CALL(pqlob);
+       PHP_MSHUT_CALL(pqcopy);
+       PHP_MSHUT_CALL(pqtxn);
+       PHP_MSHUT_CALL(pqstm);
+       PHP_MSHUT_CALL(pqres);
+       PHP_MSHUT_CALL(pqtypes);
+       PHP_MSHUT_CALL(pqcancel);
+       PHP_MSHUT_CALL(pqconn);
+
        return SUCCESS;
 }
 
index 704737f..e6640de 100644 (file)
@@ -111,7 +111,7 @@ zval *php_pq_object_read_prop(zval *object, zval *member, int type, const zend_l
 {
        php_pq_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
        php_pq_object_prophandler_t *handler;
-       zval *return_value;
+       zval *return_value = NULL;
 
        if (!obj->intern) {
                zend_error(E_WARNING, "%s not initialized", ancestor(obj->zo.ce)->name);
@@ -124,7 +124,6 @@ zval *php_pq_object_read_prop(zval *object, zval *member, int type, const zend_l
                        handler->read(object, obj, return_value TSRMLS_CC);
                } else {
                        zend_error(E_ERROR, "Cannot access %s properties by reference or array key/index", ancestor(obj->zo.ce)->name);
-                       return_value = NULL;
                }
        } else {
                return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC);
diff --git a/src/php_pq_params.c b/src/php_pq_params.c
new file mode 100644 (file)
index 0000000..0c8278e
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: pq                                                         |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2013, Michael Wallner <mike@php.net>                 |
+    +--------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#      include "config.h"
+#endif
+
+#include <php.h>
+#include <ext/standard/php_string.h>
+#include <ext/standard/php_smart_str.h>
+
+#include <Zend/zend_interfaces.h>
+
+#include <libpq-fe.h>
+
+#include "php_pq.h"
+#include "php_pq_params.h"
+
+void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv)
+{
+       zend_hash_clean(&p->type.conv);
+       zend_hash_copy(&p->type.conv, conv, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
+}
+
+static int apply_to_oid(void *p, void *arg TSRMLS_DC)
+{
+       Oid **types = arg;
+       zval **ztype = p;
+
+       if (Z_TYPE_PP(ztype) != IS_LONG) {
+               convert_to_long_ex(ztype);
+       }
+
+       **types = Z_LVAL_PP(ztype);
+       ++*types;
+
+       if (*ztype != *(zval **)p) {
+               zval_ptr_dtor(ztype);
+       }
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids)
+{
+       p->type.count = oids ? zend_hash_num_elements(oids) : 0;
+       TSRMLS_DF(p);
+
+       if (p->type.oids) {
+               efree(p->type.oids);
+               p->type.oids = NULL;
+       }
+       if (p->type.count) {
+               Oid *ptr = ecalloc(p->type.count + 1, sizeof(*p->type.oids));
+               /* +1 for when less types than params are specified */
+               p->type.oids = ptr;
+               zend_hash_apply_with_argument(oids, apply_to_oid, &ptr TSRMLS_CC);
+       }
+       return p->type.count;
+}
+
+unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type)
+{
+       p->type.oids = safe_erealloc(p->type.oids, ++p->type.count, sizeof(*p->type.oids), sizeof(*p->type.oids));
+       p->type.oids[p->type.count] = 0;
+       p->type.oids[p->type.count-1] = type;
+       return p->type.count;
+}
+
+static int apply_to_param_from_array(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+{
+       zval **zparam = p;
+       unsigned j, *i = va_arg(argv, unsigned *);
+       smart_str *s = va_arg(argv, smart_str *);
+       zval **zconv = va_arg(argv, zval **);
+       char *tmp;
+       size_t len;
+       int tmp_len;
+
+       if ((*i)++) {
+               smart_str_appendc(s, ',');
+       }
+
+       if (zconv) {
+               zval *rv = NULL;
+
+               zend_call_method_with_1_params(zconv, NULL, NULL, "converttostring", &rv, *zparam);
+               convert_to_string(rv);
+               smart_str_appendl(s, Z_STRVAL_P(rv), Z_STRLEN_P(rv));
+               zval_ptr_dtor(&rv);
+       } else {
+               switch (Z_TYPE_PP(zparam)) {
+               case IS_NULL:
+                       smart_str_appends(s, "NULL");
+                       break;
+
+               case IS_BOOL:
+                       smart_str_appends(s, Z_BVAL_PP(zparam) ? "t" : "f");
+                       break;
+
+               case IS_LONG:
+                       smart_str_append_long(s, Z_LVAL_PP(zparam));
+                       break;
+
+               case IS_DOUBLE:
+                       len = spprintf(&tmp, 0, "%F", Z_DVAL_PP(zparam));
+                       smart_str_appendl(s, tmp, len);
+                       efree(tmp);
+                       break;
+
+               case IS_ARRAY:
+                       j = 0;
+                       smart_str_appendc(s, '{');
+                       zend_hash_apply_with_arguments(Z_ARRVAL_PP(zparam) TSRMLS_CC, apply_to_param_from_array, 2, &j, s, zconv);
+                       smart_str_appendc(s, '}');
+                       break;
+
+               default:
+                       SEPARATE_ZVAL(zparam);
+                       if (Z_TYPE_PP(zparam) != IS_STRING) {
+                               convert_to_string(*zparam);
+                       }
+
+                       tmp = php_addslashes(Z_STRVAL_PP(zparam), Z_STRLEN_PP(zparam), &tmp_len, 0 TSRMLS_CC);
+                       smart_str_appendc(s, '"');
+                       smart_str_appendl(s, tmp, tmp_len);
+                       smart_str_appendc(s, '"');
+
+                       if (*zparam != *((zval **) p)) {
+                               zval_ptr_dtor(zparam);
+                       }
+                       break;
+               }
+       }
+       ++(*i);
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+static void array_param_to_string(zval **zconv, HashTable *ht, char **str, int *len TSRMLS_DC)
+{
+       smart_str s = {0};
+       unsigned i = 0;
+
+       smart_str_appendc(&s, '{');
+       zend_hash_apply_with_arguments(ht TSRMLS_CC, apply_to_param_from_array, 3, &i, &s, zconv);
+       smart_str_appendc(&s, '}');
+
+       smart_str_0(&s);
+       *str = s.c;
+       *len = s.len;
+}
+
+static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval **zp)
+{
+       zval **zconv = NULL;
+       Oid type = p->type.count > index ? p->type.oids[index] : 0;
+       TSRMLS_DF(p);
+
+       if (type && SUCCESS == zend_hash_index_find(&p->type.conv, type, (void *) &zconv)) {
+               zval *rv = NULL;
+
+               zend_call_method_with_1_params(zconv, NULL, NULL, "converttostring", &rv, *zp);
+               convert_to_string(rv);
+               p->param.strings[index] = Z_STRVAL_P(rv);
+               zend_hash_next_index_insert(&p->param.dtor, (void *) &rv, sizeof(zval *), NULL);
+       } else {
+               zval **zpp = zp;
+
+               switch (Z_TYPE_PP(zp)) {
+               case IS_NULL:
+                       p->param.strings[index] = NULL;
+                       return;
+
+               case IS_BOOL:
+                       p->param.strings[index] = Z_BVAL_PP(zp) ? "t" : "f";
+                       return;
+
+               case IS_DOUBLE:
+                       SEPARATE_ZVAL(zp);
+                       Z_TYPE_PP(zp) = IS_STRING;
+                       Z_STRLEN_PP(zp) = spprintf(&Z_STRVAL_PP(zp), 0, "%F", Z_DVAL_PP((zval **)p));
+                       break;
+
+               case IS_ARRAY:
+               {
+
+#if HAVE_PHP_PQ_TYPE_H
+#      undef PHP_PQ_TYPE
+#      include "php_pq_type.h"
+#else
+#      define PHP_PQ_TYPE_OF_ARRAY(oid) 0
+#endif
+
+                       zval *tmp;
+                       MAKE_STD_ZVAL(tmp);
+                       Z_TYPE_P(tmp) = IS_STRING;
+                       zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type), (void *) &zconv);
+                       array_param_to_string(zconv, Z_ARRVAL_PP(zp), &Z_STRVAL_P(tmp), &Z_STRLEN_P(tmp) TSRMLS_CC);
+                       zp = &tmp;
+                       break;
+               }
+
+               default:
+                       convert_to_string_ex(zp);
+                       break;
+               }
+
+               p->param.strings[index] = Z_STRVAL_PP(zp);
+
+               if (*zp != *zpp) {
+                       zend_hash_next_index_insert(&p->param.dtor, zp, sizeof(zval *), NULL);
+               }
+       }
+}
+
+static int apply_to_params(void *zp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+{
+       php_pq_params_t *p = (php_pq_params_t *) va_arg(argv, php_pq_params_t *);
+       unsigned *index = (unsigned *) va_arg(argv, unsigned *);
+
+       php_pq_params_set_param(p, (*index)++, zp);
+       return ZEND_HASH_APPLY_KEEP;
+}
+
+unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param)
+{
+       p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0);
+       php_pq_params_set_param(p, p->param.count-1, &param);
+       return p->type.count;
+}
+
+unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params)
+{
+       p->param.count = params ? zend_hash_num_elements(params) : 0;
+       TSRMLS_DF(p);
+
+       if (p->param.strings) {
+               efree(p->param.strings);
+               p->param.strings = NULL;
+       }
+       zend_hash_clean(&p->param.dtor);
+       if (p->param.count) {
+               unsigned index = 0;
+               p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings));
+               zend_hash_apply_with_arguments(params TSRMLS_CC, apply_to_params, 2, p, &index);
+       }
+       return p->param.count;
+}
+
+void php_pq_params_free(php_pq_params_t **p)
+{
+       if (*p) {
+               php_pq_params_set_type_oids(*p, NULL);
+               php_pq_params_set_params(*p, NULL);
+
+               zend_hash_destroy(&(*p)->param.dtor);
+               zend_hash_destroy(&(*p)->type.conv);
+
+               efree(*p);
+               *p = NULL;
+       }
+}
+
+php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params TSRMLS_DC)
+{
+       php_pq_params_t *p = ecalloc(1, sizeof(*p));
+
+       TSRMLS_CF(p);
+       zend_hash_init(&p->type.conv, 0, NULL, ZVAL_PTR_DTOR, 0);
+       zend_hash_init(&p->param.dtor, 0, NULL, ZVAL_PTR_DTOR, 0);
+
+       if (conv) {
+               php_pq_params_set_type_conv(p, conv);
+       }
+       if (oids) {
+               php_pq_params_set_type_oids(p, oids);
+       }
+       if (params) {
+               php_pq_params_set_params(p, params);
+       }
+
+       return p;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/src/php_pq_params.h b/src/php_pq_params.h
new file mode 100644 (file)
index 0000000..00fbad8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    +--------------------------------------------------------------------+
+    | PECL :: pq                                                         |
+    +--------------------------------------------------------------------+
+    | Redistribution and use in source and binary forms, with or without |
+    | modification, are permitted provided that the conditions mentioned |
+    | in the accompanying LICENSE file are met.                          |
+    +--------------------------------------------------------------------+
+    | Copyright (c) 2013, Michael Wallner <mike@php.net>                 |
+    +--------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PQ_PARAMS_H
+#define PHP_PQ_PARAMS_H
+
+typedef struct php_pq_params {
+       struct {
+               HashTable conv;
+               unsigned count;
+               Oid *oids;
+       } type;
+       struct {
+               HashTable dtor;
+               unsigned count;
+               char **strings;
+       } param;
+#ifdef ZTS
+       void ***ts;
+#endif
+} php_pq_params_t;
+
+php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params TSRMLS_DC);
+void php_pq_params_free(php_pq_params_t **p);
+unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params);
+unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids);
+unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type);
+unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param);
+void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv);
+
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
index ba5c0e4..0edcdde 100644 (file)
@@ -144,6 +144,12 @@ static zend_function_entry php_pqcancel_methods[] = {
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqcancel)
+{
+       zend_hash_destroy(&php_pqcancel_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqcancel)
 {
        zend_class_entry ce = {0};
index 12f6522..2f2d185 100644 (file)
@@ -32,6 +32,7 @@ zend_class_entry *php_pqcancel_class_entry;
 zend_object_value php_pqcancel_create_object_ex(zend_class_entry *ce, php_pqcancel_t *intern, php_pqcancel_object_t **ptr TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqcancel);
+PHP_MSHUTDOWN_FUNCTION(pqcancel);
 
 #endif
 
index 2f0d9f8..79bb570 100644 (file)
@@ -77,6 +77,7 @@ static void php_pqconn_object_free(void *o TSRMLS_DC)
                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;
@@ -522,6 +523,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) {
@@ -938,27 +940,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;
+                       php_pq_params_t *params;
 
-                       ZEND_INIT_SYMTABLE(&zdtor);
-                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
-
-                       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));
@@ -1000,19 +986,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;
+                       int rc;
+                       php_pq_params_t *params;
 
-                       ZEND_INIT_SYMTABLE(&zdtor);
-                       count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
-
-                       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));
@@ -1025,23 +1006,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;
 
@@ -1049,16 +1020,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;
@@ -1093,35 +1055,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)) {
@@ -1133,10 +1096,6 @@ 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;
 }
 
@@ -1161,16 +1120,23 @@ 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);
+                       }
                }
        }
 }
@@ -1489,6 +1455,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)
@@ -1513,9 +1527,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};
index 1e5c822..b3ef4e8 100644 (file)
 
 #include <ext/raphf/php_raphf.h>
 #include "php_pq_callback.h"
+#include "php_pq_params.h"
 
 typedef struct php_pqconn {
        PGconn *conn;
        int (*poller)(PGconn *);
        php_resource_factory_t factory;
        HashTable listeners;
+       HashTable converters;
        HashTable eventhandlers;
        php_pq_callback_t onevent;
        unsigned unbuffered:1;
@@ -47,12 +49,13 @@ php_resource_factory_ops_t *php_pqconn_get_resource_factory_ops(void);
 zend_class_entry *php_pqconn_class_entry;
 zend_object_value php_pqconn_create_object_ex(zend_class_entry *ce, php_pqconn_t *intern, php_pqconn_object_t **ptr TSRMLS_DC);
 void php_pqconn_notify_listeners(php_pqconn_object_t *obj TSRMLS_DC);
-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_async(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);
+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 php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC);
 STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqconn);
+PHP_MSHUTDOWN_FUNCTION(pqconn);
 
 #endif
 
index 2c4461a..f509c57 100644 (file)
@@ -69,7 +69,7 @@ static void php_pqconn_event_resultcreate(PGEventResultCreate *event)
                HashTable *evhs;
                TSRMLS_DF(data);
 
-               php_pqres_init_instance_data(event->result, &obj TSRMLS_CC);
+               php_pqres_init_instance_data(event->result, data->obj, &obj TSRMLS_CC);
 
                /* event listener */
                if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("result"), (void *) &evhs)) {
index 77644b1..a427f07 100644 (file)
@@ -308,6 +308,12 @@ static zend_function_entry php_pqcopy_methods[] = {
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqcopy)
+{
+       zend_hash_destroy(&php_pqcopy_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqcopy)
 {
        zend_class_entry ce = {0};
index 665428e..03bdd5b 100644 (file)
@@ -45,6 +45,7 @@ zend_class_entry *php_pqcopy_class_entry;
 zend_object_value php_pqcopy_create_object_ex(zend_class_entry *ce, php_pqcopy_t *intern, php_pqcopy_object_t **ptr TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqcopy);
+PHP_MSHUTDOWN_FUNCTION(pqcopy);
 
 #endif
 
index 1188907..96b495e 100644 (file)
@@ -449,6 +449,12 @@ static zend_function_entry php_pqlob_methods[] = {
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqlob)
+{
+       zend_hash_destroy(&php_pqlob_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqlob)
 {
        zend_class_entry ce = {0};
index aee46bc..2d43718 100644 (file)
@@ -34,6 +34,7 @@ zend_class_entry *php_pqlob_class_entry;
 zend_object_value php_pqlob_create_object_ex(zend_class_entry *ce, php_pqlob_t *intern, php_pqlob_object_t **ptr TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqlob);
+PHP_MSHUTDOWN_FUNCTION(pqlob);
 
 #endif
 
index 782589d..addeab3 100644 (file)
@@ -93,6 +93,7 @@ zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch
 {
        zval *data = NULL;
        int c, cols;
+       php_pqres_object_t *res_obj = PQresultInstanceData(res, php_pqconn_event);
 
        if (data_ptr) {
                data = *data_ptr;
@@ -126,7 +127,22 @@ zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch
                                        break;
                                }
                        } else {
-                               zval *zv = php_pq_typed_zval(PQgetvalue(res, row, c), PQgetlength(res, row, c), PQftype(res, c) TSRMLS_CC);
+                               zval *zv, **zconv;
+
+                               if (res_obj && (SUCCESS == zend_hash_index_find(&res_obj->intern->converters, PQftype(res, c), (void *) &zconv))) {
+                                       zval *tmp = NULL;
+
+                                       MAKE_STD_ZVAL(zv);
+                                       ZVAL_STRINGL(zv, PQgetvalue(res, row, c), PQgetlength(res, row, c), 1);
+                                       zend_call_method_with_1_params(zconv, NULL, NULL, "convertfromstring", &tmp, zv);
+
+                                       if (tmp) {
+                                               zval_ptr_dtor(&zv);
+                                               zv = tmp;
+                                       }
+                               } else {
+                                       zv = php_pq_typed_zval(PQgetvalue(res, row, c), PQgetlength(res, row, c), PQftype(res, c) TSRMLS_CC);
+                               }
 
                                switch (fetch_type) {
                                case PHP_PQRES_FETCH_OBJECT:
@@ -248,13 +264,15 @@ STATUS php_pqres_success(PGresult *res TSRMLS_DC)
        }
 }
 
-void php_pqres_init_instance_data(PGresult *res, php_pqres_object_t **ptr TSRMLS_DC)
+void php_pqres_init_instance_data(PGresult *res, php_pqconn_object_t *conn_obj, php_pqres_object_t **ptr TSRMLS_DC)
 {
        php_pqres_object_t *obj;
        php_pqres_t *r = ecalloc(1, sizeof(*r));
 
        r->res = res;
-       ZEND_INIT_SYMTABLE(&r->bound);
+       zend_hash_init(&r->bound, 0, 0, ZVAL_PTR_DTOR, 0);
+       zend_hash_init(&r->converters, 0, 0, ZVAL_PTR_DTOR, 0);
+       zend_hash_copy(&r->converters, &conn_obj->intern->converters, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
        php_pqres_create_object_ex(php_pqres_class_entry, r, &obj TSRMLS_CC);
 
        PQresultSetInstanceData(res, php_pqconn_event, obj);
@@ -283,6 +301,7 @@ static void php_pqres_object_free(void *o TSRMLS_DC)
                }
 
                zend_hash_destroy(&obj->intern->bound);
+               zend_hash_destroy(&obj->intern->converters);
 
                efree(obj->intern);
                obj->intern = NULL;
@@ -875,6 +894,12 @@ static zend_function_entry php_pqres_methods[] = {
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqres)
+{
+       zend_hash_destroy(&php_pqres_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqres)
 {
        zend_class_entry ce = {0};
index 80a3d44..9786249 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef PHP_PQRES_H
 #define PHP_PQRES_H
 
+#include "php_pqconn.h"
 typedef enum php_pqres_fetch {
        PHP_PQRES_FETCH_ARRAY,
        PHP_PQRES_FETCH_ASSOC,
@@ -31,6 +32,7 @@ typedef struct php_pqres {
        PGresult *res;
        php_pqres_iterator_t *iter;
        HashTable bound;
+       HashTable converters;
 } php_pqres_t;
 
 typedef struct php_pqres_object {
@@ -41,7 +43,7 @@ typedef struct php_pqres_object {
 } php_pqres_object_t;
 
 STATUS php_pqres_success(PGresult *res TSRMLS_DC);
-void php_pqres_init_instance_data(PGresult *res, php_pqres_object_t **ptr TSRMLS_DC);
+void php_pqres_init_instance_data(PGresult *res, php_pqconn_object_t *obj, php_pqres_object_t **ptr TSRMLS_DC);
 zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type, zval **data_ptr TSRMLS_DC);
 
 #include "php_pq_object.h"
@@ -59,6 +61,7 @@ zend_class_entry *php_pqres_class_entry;
 zend_object_value php_pqres_create_object_ex(zend_class_entry *ce, php_pqres_t *intern, php_pqres_object_t **ptr TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqres);
+PHP_MSHUTDOWN_FUNCTION(pqres);
 
 #endif
 
index 831663c..a0ae249 100644 (file)
@@ -58,6 +58,9 @@ static void php_pqstm_object_free(void *o TSRMLS_DC)
                php_pq_object_delref(obj->intern->conn TSRMLS_CC);
                efree(obj->intern->name);
                zend_hash_destroy(&obj->intern->bound);
+               if (obj->intern->params) {
+                       php_pq_params_free(&obj->intern->params);
+               }
                efree(obj->intern);
                obj->intern = NULL;
        }
@@ -134,10 +137,12 @@ static PHP_METHOD(pqstm, __construct) {
                if (!conn_obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
+                       php_pq_params_t *params = php_pq_params_init(&conn_obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC);
+
                        if (async) {
-                               rv = php_pqconn_prepare_async(zconn, conn_obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC);
+                               rv = php_pqconn_prepare_async(zconn, conn_obj, name_str, query_str, params TSRMLS_CC);
                        } else {
-                               rv = php_pqconn_prepare(zconn, conn_obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC);
+                               rv = php_pqconn_prepare(zconn, conn_obj, name_str, query_str, params TSRMLS_CC);
                        }
 
                        if (SUCCESS == rv) {
@@ -146,6 +151,7 @@ static PHP_METHOD(pqstm, __construct) {
                                php_pq_object_addref(conn_obj TSRMLS_CC);
                                stm->conn = conn_obj;
                                stm->name = estrdup(name_str);
+                               stm->params = params;
                                ZEND_INIT_SYMTABLE(&stm->bound);
                                obj->intern = stm;
                        }
@@ -191,25 +197,11 @@ static PHP_METHOD(pqstm, exec) {
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized");
                } else {
-                       int count = 0;
-                       char **params = NULL;
-                       HashTable zdtor;
                        PGresult *res;
 
-                       ZEND_INIT_SYMTABLE(&zdtor);
-
-                       if (zparams) {
-                               count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
-                       } else {
-                               count = php_pq_params_to_array(&obj->intern->bound, &params, &zdtor TSRMLS_CC);
-                       }
-
-                       res = PQexecPrepared(obj->intern->conn->intern->conn, obj->intern->name, count, (const char *const*) params, NULL, NULL, 0);
-
-                       if (params) {
-                               efree(params);
-                       }
-                       zend_hash_destroy(&zdtor);
+                       php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound);
+                       res = PQexecPrepared(obj->intern->conn->intern->conn, obj->intern->name, obj->intern->params->param.count, (const char *const*) obj->intern->params->param.strings, NULL, NULL, 0);
+                       php_pq_params_set_params(obj->intern->params, NULL);
 
                        if (!res) {
                                throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
@@ -241,16 +233,13 @@ static PHP_METHOD(pqstm, execAsync) {
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized");
                } else {
-                       int count;
-                       char **params = NULL;
-                       HashTable zdtor;
+                       int rc;
 
-                       if (zparams) {
-                               ZEND_INIT_SYMTABLE(&zdtor);
-                               count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
-                       }
+                       php_pq_params_set_params(obj->intern->params, zparams ? Z_ARRVAL_P(zparams) : &obj->intern->bound);
+                       rc = PQsendQueryPrepared(obj->intern->conn->intern->conn, obj->intern->name, obj->intern->params->param.count, (const char *const*) obj->intern->params->param.strings, NULL, NULL, 0);
+                       php_pq_params_set_params(obj->intern->params, NULL);
 
-                       if (!PQsendQueryPrepared(obj->intern->conn->intern->conn, obj->intern->name, count, (const char *const*) params, NULL, NULL, 0)) {
+                       if (!rc) {
                                throw_exce(EX_IO TSRMLS_CC, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
                        } else if (obj->intern->conn->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn->intern->conn)) {
                                throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
@@ -263,13 +252,6 @@ static PHP_METHOD(pqstm, execAsync) {
                                obj->intern->conn->intern->poller = PQconsumeInput;
                        }
 
-                       if (params) {
-                               efree(params);
-                       }
-                       if (zparams) {
-                               zend_hash_destroy(&zdtor);
-                       }
-
                        php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
                }
        }
@@ -345,6 +327,12 @@ static zend_function_entry php_pqstm_methods[] = {
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqstm)
+{
+       zend_hash_destroy(&php_pqstm_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqstm)
 {
        zend_class_entry ce = {0};
index 0742859..da8c7d9 100644 (file)
@@ -20,6 +20,7 @@ typedef struct php_pqstm {
        php_pqconn_object_t *conn;
        char *name;
        HashTable bound;
+       php_pq_params_t *params;
 } php_pqstm_t;
 
 typedef struct php_pqstm_object {
@@ -33,6 +34,7 @@ zend_class_entry *php_pqstm_class_entry;
 zend_object_value php_pqstm_create_object_ex(zend_class_entry *ce, php_pqstm_t *intern, php_pqstm_object_t **ptr TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqstm);
+PHP_MSHUTDOWN_FUNCTION(pqstm);
 
 #endif
 
index 021ece4..7d4b8b7 100644 (file)
@@ -872,6 +872,12 @@ static zend_function_entry php_pqtxn_methods[] = {
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqtxn)
+{
+       zend_hash_destroy(&php_pqtxn_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqtxn)
 {
        zend_class_entry ce = {0};
index 36f30b2..4ca8c95 100644 (file)
@@ -44,6 +44,7 @@ zend_class_entry *php_pqtxn_class_entry;
 zend_object_value php_pqtxn_create_object_ex(zend_class_entry *ce, php_pqtxn_t *intern, php_pqtxn_object_t **ptr TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqtxn);
+PHP_MSHUTDOWN_FUNCTION(pqtxn);
 
 #endif
 
index 7092c57..e0dadde 100644 (file)
@@ -229,6 +229,29 @@ static PHP_METHOD(pqtypes, __construct) {
 # define PHP_PQ_OID_TEXT 25
 #endif
 
+static int apply_nsp(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+{
+       zval **zp = p;
+       unsigned pcount, tcount;
+       php_pq_params_t *params = va_arg(argv, php_pq_params_t *);
+       smart_str *str = va_arg(argv, smart_str *);
+
+       tcount = php_pq_params_add_type_oid(params, PHP_PQ_OID_TEXT);
+       pcount = php_pq_params_add_param(params, *zp);
+
+       if (tcount != pcount) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Param/Type count mismatch");
+               return ZEND_HASH_APPLY_STOP;
+       }
+       if (pcount > 1) {
+               smart_str_appendc(str, ',');
+       }
+       smart_str_appendc(str, '$');
+       smart_str_append_unsigned(str, pcount);
+
+       return ZEND_HASH_APPLY_KEEP;
+}
+
 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0)
        ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
 ZEND_END_ARG_INFO();
@@ -252,33 +275,18 @@ static PHP_METHOD(pqtypes, refresh) {
                        if (!nsp || !zend_hash_num_elements(nsp)) {
                                res = PQexec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY " and nspname in ('public', 'pg_catalog')");
                        } else {
-                               int i, count;
-                               Oid *oids;
-                               char **params = NULL;
-                               HashTable zdtor;
                                smart_str str = {0};
+                               php_pq_params_t *params = php_pq_params_init(&obj->intern->conn->intern->converters, NULL, NULL TSRMLS_CC);
 
                                smart_str_appends(&str, PHP_PQ_TYPES_QUERY " and nspname in(");
-                               zend_hash_init(&zdtor, 0, NULL, ZVAL_PTR_DTOR, 0);
-                               count = php_pq_params_to_array(nsp, &params, &zdtor TSRMLS_CC);
-                               oids = ecalloc(count + 1, sizeof(*oids));
-                               for (i = 0; i < count; ++i) {
-                                       oids[i] = PHP_PQ_OID_TEXT;
-                                       if (i) {
-                                               smart_str_appendc(&str, ',');
-                                       }
-                                       smart_str_appendc(&str, '$');
-                                       smart_str_append_unsigned(&str, i+1);
-                               }
+                               zend_hash_apply_with_arguments(nsp TSRMLS_CC, apply_nsp, 2, params, &str);
                                smart_str_appendc(&str, ')');
                                smart_str_0(&str);
 
-                               res = PQexecParams(obj->intern->conn->intern->conn, str.c, count, oids, (const char *const*) params, NULL, NULL, 0);
+                               res = PQexecParams(obj->intern->conn->intern->conn, str.c, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0);
 
                                smart_str_free(&str);
-                               efree(oids);
-                               efree(params);
-                               zend_hash_destroy(&zdtor);
+                               php_pq_params_free(&params);
                        }
 
                        if (!res) {
@@ -295,7 +303,7 @@ static PHP_METHOD(pqtypes, refresh) {
                                                Z_ADDREF_P(row);
 
                                                zend_hash_index_update(&obj->intern->types, oid, (void *) &row, sizeof(zval *), NULL);
-                                               zend_hash_add(&obj->intern->types, name, strlen(name) + 1, (void *) &row, sizeof(zval *), NULL);
+                                               zend_hash_update(&obj->intern->types, name, strlen(name) + 1, (void *) &row, sizeof(zval *), NULL);
                                        }
                                }
 
@@ -312,6 +320,12 @@ static zend_function_entry php_pqtypes_methods[] = {
        {0}
 };
 
+PHP_MSHUTDOWN_FUNCTION(pqtypes)
+{
+       zend_hash_destroy(&php_pqtypes_object_prophandlers);
+       return SUCCESS;
+}
+
 PHP_MINIT_FUNCTION(pqtypes)
 {
        zend_class_entry ce = {0};
index 55398f5..0686e5d 100644 (file)
@@ -32,6 +32,7 @@ zend_class_entry *php_pqtypes_class_entry;
 zend_object_value php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern, php_pqtypes_object_t **ptr TSRMLS_DC);
 
 PHP_MINIT_FUNCTION(pqtypes);
+PHP_MSHUTDOWN_FUNCTION(pqtypes);
 
 #endif
 
index 89c4931..aa754ac 100644 (file)
@@ -16,12 +16,14 @@ $c->execAsync("SELECT pg_sleep(2)");
 
 $x->cancel();
 
+var_dump($c === $x->connection);
 var_dump($c->getResult());
 printf("%s\n", $c->errorMessage);
 ?>
 DONE
 --EXPECTF--
 Test
+bool(true)
 object(pq\Result)#%d (7) {
   ["status"]=>
   int(7)
diff --git a/tests/conv001.phpt b/tests/conv001.phpt
new file mode 100644 (file)
index 0000000..3020945
--- /dev/null
@@ -0,0 +1,180 @@
+--TEST--
+converter
+--SKIPIF--
+<?php
+include "_skipif.inc";
+?>
+--FILE--
+<?php
+echo "Test\n";
+
+include "_setup.inc";
+
+abstract class Converter implements pq\ConverterInterface
+{
+       protected $types;
+       
+       function __construct(\pq\Types $types) {
+               $this->types = $types;
+       }
+}
+
+class HStoreConverter extends Converter
+{
+       function convertTypes() {
+               return [ $this->types["hstore"]->oid ];
+       }
+       
+       function convertFromString($string) {
+               return eval("return [$string];");
+       }
+       
+       function convertToString($data) {
+               $string = "";
+               foreach ($data as $k => $v) {
+                       if (isset($v)) {
+                               $string .= sprintf("\"%s\"=>\"%s\",", addslashes($k), addslashes($v));
+                       } else {
+                               $string .= sprintf("\"%s\"=>NULL,", addslashes($k));
+                       }
+               }
+               return $string;
+       }
+}
+
+class IntVectorConverter extends Converter
+{
+       function convertTypes() {
+               return [ 
+                       $this->types["int2vector"]->oid, 
+                       $this->types["oidvector"]->oid
+               ];
+       }
+       
+       function convertFromString($string) {
+               return array_map("intval", explode(" ", $string));
+       }
+       
+       function convertToString($data) {
+               return implode(" ", $data);
+       }
+}
+
+class JSONConverter extends Converter
+{
+       function convertTypes() {
+               return [ $this->types["json"]->oid ];
+       }
+       
+       function convertFromString($string) {
+               return json_decode($string);
+       }
+       
+       function convertToString($data) {
+               return json_encode($data);
+       }
+}
+
+$c = new pq\Connection(PQ_DSN);
+$c->exec("CREATE EXTENSION IF NOT EXISTS hstore");
+$t = new pq\Types($c);
+
+$c->setConverter(new HStoreConverter($t));
+$c->setConverter(new IntVectorConverter($t));
+$c->setConverter(new JSONConverter($t));
+
+$r = $c->execParams("SELECT \$1 as hs, \$2 as iv, \$3 as oids, \$4 as js",
+       array(
+               // hstore
+               array(
+                       "k1" => "v1",
+                       "k2" => "v2",
+                       "k3" => null
+               ),
+               // vectors
+               array(
+                       1, 3, 5, 7, 9, 11
+               ),
+               array(
+                       2345124, 1431341, 1343423
+               ),
+               // JSON
+               (object) array(
+                       "int" => 123,
+                       "obj" => (object) array(
+                               "a" => 1,
+                               "b" => 2,
+                               "c" => 3,
+                       ),
+                       "str" => "äüö"
+               )
+       ),
+       array(
+               $t["hstore"]->oid,
+               $t["int2vector"]->oid,
+               $t["oidvector"]->oid,
+               $t["json"]->oid
+       )
+);
+
+var_dump($r->fetchAll());
+
+?>
+Done
+--EXPECTF--
+Test
+array(1) {
+  [0]=>
+  array(4) {
+    [0]=>
+    array(3) {
+      ["k1"]=>
+      string(2) "v1"
+      ["k2"]=>
+      string(2) "v2"
+      ["k3"]=>
+      NULL
+    }
+    [1]=>
+    array(6) {
+      [0]=>
+      int(1)
+      [1]=>
+      int(3)
+      [2]=>
+      int(5)
+      [3]=>
+      int(7)
+      [4]=>
+      int(9)
+      [5]=>
+      int(11)
+    }
+    [2]=>
+    array(3) {
+      [0]=>
+      int(2345124)
+      [1]=>
+      int(1431341)
+      [2]=>
+      int(1343423)
+    }
+    [3]=>
+    object(stdClass)#%d (3) {
+      ["int"]=>
+      int(123)
+      ["obj"]=>
+      object(stdClass)#%d (3) {
+        ["a"]=>
+        int(1)
+        ["b"]=>
+        int(2)
+        ["c"]=>
+        int(3)
+      }
+      ["str"]=>
+      string(6) "äüö"
+    }
+  }
+}
+Done
index 4809bb3..e0ecc6e 100644 (file)
@@ -14,12 +14,28 @@ $c->exec("DROP TABLE IF EXISTS copy_test; CREATE TABLE copy_test (id serial, lin
 $file = file(__FILE__);
 
 $in = new pq\COPY($c, "copy_test (line)", pq\COPY::FROM_STDIN, "DELIMITER '\t'");
+
+var_dump(
+       $c === $in->connection,
+       "copy_test (line)" === $in->expression,
+       pq\COPY::FROM_STDIN === $in->direction,
+       "DELIMITER '\t'" === $in->options
+);
+
 foreach ($file as $i => $line) {
        $in->put(addcslashes($line, "\\\t"));
 }
 $in->end();
 
 $out = new pq\COPY($c, "copy_test (line)", pq\COPY::TO_STDOUT, "DELIMITER '\t'");
+
+var_dump(
+       $c === $out->connection,
+       "copy_test (line)" === $out->expression,
+       pq\COPY::TO_STDOUT === $out->direction,
+       "DELIMITER '\t'" === $out->options
+);
+
 while ($out->get($line)) {
        $lines[] = stripcslashes($line);
 }
@@ -39,5 +55,13 @@ DONE
 --EXPECT--
 Test
 bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
 DONE
 
index 951bf1f..d5523da 100644 (file)
@@ -8,7 +8,7 @@ echo "Test\n";
 include "_setup.inc";
 
 $c = new pq\Connection(PQ_DSN);
-$t = new pq\Types($c);
+$t = new pq\Types($c, array("pg_catalog", "public"));
 var_dump($t->connection === $c);
 var_dump(isset($t["int4"]), empty($t["int4"]));
 var_dump(isset($t["whatthahell"]), empty($t["whatthahell"]));