From 88440266c0a9fa8354688b5ed0d2a6cc3bf04db5 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 14 May 2013 13:09:23 +0200 Subject: [PATCH] type converters --- config.m4 | 1 + package.xml | 9 +- src/php_pq_misc.c | 223 ++++-------------------------- src/php_pq_misc.h | 5 +- src/php_pq_module.c | 16 +++ src/php_pq_object.c | 3 +- src/php_pq_params.c | 300 +++++++++++++++++++++++++++++++++++++++++ src/php_pq_params.h | 50 +++++++ src/php_pqcancel.c | 6 + src/php_pqcancel.h | 1 + src/php_pqconn.c | 181 ++++++++++++++----------- src/php_pqconn.h | 7 +- src/php_pqconn_event.c | 2 +- src/php_pqcopy.c | 6 + src/php_pqcopy.h | 1 + src/php_pqlob.c | 6 + src/php_pqlob.h | 1 + src/php_pqres.c | 31 ++++- src/php_pqres.h | 5 +- src/php_pqstm.c | 56 +++----- src/php_pqstm.h | 2 + src/php_pqtxn.c | 6 + src/php_pqtxn.h | 1 + src/php_pqtypes.c | 54 +++++--- src/php_pqtypes.h | 1 + tests/cancel001.phpt | 2 + tests/conv001.phpt | 180 +++++++++++++++++++++++++ tests/copy001.phpt | 24 ++++ tests/types001.phpt | 2 +- 29 files changed, 834 insertions(+), 348 deletions(-) create mode 100644 src/php_pq_params.c create mode 100644 src/php_pq_params.h create mode 100644 tests/conv001.phpt diff --git a/config.m4 b/config.m4 index a6a82b5..79b2f89 100644 --- 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\ diff --git a/package.xml b/package.xml index cba4011..c686d58 100644 --- a/package.xml +++ b/package.xml @@ -34,8 +34,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> 2013-05-03 - 0.3.0 - 0.3.0 + 0.4.0dev + 0.4.0 beta @@ -43,7 +43,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> BSD, revised @@ -72,6 +72,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -93,6 +95,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> + diff --git a/src/php_pq_misc.c b/src/php_pq_misc.c index 0f4c83a..fdc03b2 100644 --- a/src/php_pq_misc.c +++ b/src/php_pq_misc.c @@ -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); diff --git a/src/php_pq_misc.h b/src/php_pq_misc.h index 07f75a3..0ba99a2 100644 --- a/src/php_pq_misc.h +++ b/src/php_pq_misc.h @@ -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); diff --git a/src/php_pq_module.c b/src/php_pq_module.c index 02cd6db..cb7a4e9 100644 --- a/src/php_pq_module.c +++ b/src/php_pq_module.c @@ -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; } diff --git a/src/php_pq_object.c b/src/php_pq_object.c index 704737f..e6640de 100644 --- a/src/php_pq_object.c +++ b/src/php_pq_object.c @@ -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 index 0000000..0c8278e --- /dev/null +++ b/src/php_pq_params.c @@ -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 | + +--------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include + +#include + +#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, ¶m); + 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 index 0000000..00fbad8 --- /dev/null +++ b/src/php_pq_params.h @@ -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 | + +--------------------------------------------------------------------+ +*/ + +#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 + */ diff --git a/src/php_pqcancel.c b/src/php_pqcancel.c index ba5c0e4..0edcdde 100644 --- a/src/php_pqcancel.c +++ b/src/php_pqcancel.c @@ -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}; diff --git a/src/php_pqcancel.h b/src/php_pqcancel.h index 12f6522..2f2d185 100644 --- a/src/php_pqcancel.h +++ b/src/php_pqcancel.h @@ -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 diff --git a/src/php_pqconn.c b/src/php_pqconn.c index 2f0d9f8..79bb570 100644 --- a/src/php_pqconn.c +++ b/src/php_pqconn.c @@ -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), ¶ms, &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(¶ms); 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), ¶ms, &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(¶ms); - 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(¶ms); + } 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(¶ms); + } 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}; diff --git a/src/php_pqconn.h b/src/php_pqconn.h index 1e5c822..b3ef4e8 100644 --- a/src/php_pqconn.h +++ b/src/php_pqconn.h @@ -19,12 +19,14 @@ #include #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 diff --git a/src/php_pqconn_event.c b/src/php_pqconn_event.c index 2c4461a..f509c57 100644 --- a/src/php_pqconn_event.c +++ b/src/php_pqconn_event.c @@ -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)) { diff --git a/src/php_pqcopy.c b/src/php_pqcopy.c index 77644b1..a427f07 100644 --- a/src/php_pqcopy.c +++ b/src/php_pqcopy.c @@ -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}; diff --git a/src/php_pqcopy.h b/src/php_pqcopy.h index 665428e..03bdd5b 100644 --- a/src/php_pqcopy.h +++ b/src/php_pqcopy.h @@ -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 diff --git a/src/php_pqlob.c b/src/php_pqlob.c index 1188907..96b495e 100644 --- a/src/php_pqlob.c +++ b/src/php_pqlob.c @@ -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}; diff --git a/src/php_pqlob.h b/src/php_pqlob.h index aee46bc..2d43718 100644 --- a/src/php_pqlob.h +++ b/src/php_pqlob.h @@ -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 diff --git a/src/php_pqres.c b/src/php_pqres.c index 782589d..addeab3 100644 --- a/src/php_pqres.c +++ b/src/php_pqres.c @@ -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}; diff --git a/src/php_pqres.h b/src/php_pqres.h index 80a3d44..9786249 100644 --- a/src/php_pqres.h +++ b/src/php_pqres.h @@ -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 diff --git a/src/php_pqstm.c b/src/php_pqstm.c index 831663c..a0ae249 100644 --- a/src/php_pqstm.c +++ b/src/php_pqstm.c @@ -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), ¶ms, &zdtor TSRMLS_CC); - } else { - count = php_pq_params_to_array(&obj->intern->bound, ¶ms, &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), ¶ms, &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}; diff --git a/src/php_pqstm.h b/src/php_pqstm.h index 0742859..da8c7d9 100644 --- a/src/php_pqstm.h +++ b/src/php_pqstm.h @@ -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 diff --git a/src/php_pqtxn.c b/src/php_pqtxn.c index 021ece4..7d4b8b7 100644 --- a/src/php_pqtxn.c +++ b/src/php_pqtxn.c @@ -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}; diff --git a/src/php_pqtxn.h b/src/php_pqtxn.h index 36f30b2..4ca8c95 100644 --- a/src/php_pqtxn.h +++ b/src/php_pqtxn.h @@ -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 diff --git a/src/php_pqtypes.c b/src/php_pqtypes.c index 7092c57..e0dadde 100644 --- a/src/php_pqtypes.c +++ b/src/php_pqtypes.c @@ -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, ¶ms, &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(¶ms); } 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}; diff --git a/src/php_pqtypes.h b/src/php_pqtypes.h index 55398f5..0686e5d 100644 --- a/src/php_pqtypes.h +++ b/src/php_pqtypes.h @@ -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 diff --git a/tests/cancel001.phpt b/tests/cancel001.phpt index 89c4931..aa754ac 100644 --- a/tests/cancel001.phpt +++ b/tests/cancel001.phpt @@ -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 index 0000000..3020945 --- /dev/null +++ b/tests/conv001.phpt @@ -0,0 +1,180 @@ +--TEST-- +converter +--SKIPIF-- + +--FILE-- +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 diff --git a/tests/copy001.phpt b/tests/copy001.phpt index 4809bb3..e0ecc6e 100644 --- a/tests/copy001.phpt +++ b/tests/copy001.phpt @@ -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 diff --git a/tests/types001.phpt b/tests/types001.phpt index 951bf1f..d5523da 100644 --- a/tests/types001.phpt +++ b/tests/types001.phpt @@ -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"])); -- 2.30.2