X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-pq;a=blobdiff_plain;f=src%2Fphp_pqconn.c;h=daf2c7947b92821ac0560a3e1b7ae3fdc77fcbe1;hp=2f0d9f88bcf6658bce26b414db9cc5fc7cae2f48;hb=753359f19c5d29f08faca43ee735c49c217230a7;hpb=5aa292d5bc46cd8fe266d682397dba19dc1e1d36 diff --git a/src/php_pqconn.c b/src/php_pqconn.c index 2f0d9f8..daf2c79 100644 --- a/src/php_pqconn.c +++ b/src/php_pqconn.c @@ -31,6 +31,7 @@ #include "php_pqres.h" #include "php_pqstm.h" #include "php_pqtxn.h" +#include "php_pqcur.h" zend_class_entry *php_pqconn_class_entry; static zend_object_handlers php_pqconn_object_handlers; @@ -73,10 +74,11 @@ static void php_pqconn_object_free(void *o TSRMLS_DC) fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj); #endif if (obj->intern) { + php_pq_callback_dtor(&obj->intern->onevent); php_resource_factory_handle_dtor(&obj->intern->factory, obj->intern->conn TSRMLS_CC); php_resource_factory_dtor(&obj->intern->factory); - php_pq_callback_dtor(&obj->intern->onevent); zend_hash_destroy(&obj->intern->listeners); + zend_hash_destroy(&obj->intern->converters); zend_hash_destroy(&obj->intern->eventhandlers); efree(obj->intern); obj->intern = NULL; @@ -217,7 +219,7 @@ static void php_pqconn_object_write_encoding(zval *object, void *o, zval *value } if (0 > PQsetClientEncoding(obj->intern->conn, Z_STRVAL_P(zenc))) { - zend_error(E_NOTICE, "Unrecognized encoding '%s'", Z_STRVAL_P(zenc)); + php_error(E_NOTICE, "Unrecognized encoding '%s'", Z_STRVAL_P(zenc)); } if (zenc != value) { @@ -236,7 +238,7 @@ static void php_pqconn_object_write_unbuffered(zval *object, void *o, zval *valu { php_pqconn_object_t *obj = o; - obj->intern->unbuffered = zend_is_true(value); + obj->intern->unbuffered = z_is_true(value); } static void php_pqconn_object_read_db(zval *object, void *o, zval *return_value TSRMLS_DC) @@ -347,6 +349,122 @@ static void php_pqconn_object_read_event_handlers(zval *object, void *o, zval *r zend_hash_apply_with_arguments(&obj->intern->eventhandlers TSRMLS_CC, apply_read_event_handlers, 1, Z_ARRVAL_P(return_value) TSRMLS_CC); } +static void php_pqconn_object_read_def_fetch_type(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + RETVAL_LONG(obj->intern->default_fetch_type); +} +static void php_pqconn_object_write_def_fetch_type(zval *object, void *o, zval *value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + zval *zft = value; + + if (Z_TYPE_P(zft) != IS_LONG) { + if (Z_REFCOUNT_P(zft) > 1) { + zval *tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_ZVAL(tmp, zft, 1, 0); + convert_to_long(tmp); + zft = tmp; + } else { + convert_to_long_ex(&zft); + } + } + + obj->intern->default_fetch_type = Z_LVAL_P(zft) & 0x3; /* two bits only */ + + if (zft != value) { + zval_ptr_dtor(&zft); + } +} + +static void php_pqconn_object_read_def_txn_isolation(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + RETVAL_LONG(obj->intern->default_txn_isolation); +} +static void php_pqconn_object_write_def_txn_isolation(zval *object, void *o, zval *value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + zval *zti = value; + + if (Z_TYPE_P(zti) != IS_LONG) { + if (Z_REFCOUNT_P(zti) > 1) { + zval *tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_ZVAL(tmp, zti, 1, 0); + convert_to_long(tmp); + zti = tmp; + } else { + convert_to_long_ex(&zti); + } + } + + obj->intern->default_txn_isolation = Z_LVAL_P(zti) & 0x3; /* two bits only */ + + if (zti != value) { + zval_ptr_dtor(&zti); + } +} + +static void php_pqconn_object_read_def_txn_readonly(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + RETVAL_BOOL(obj->intern->default_txn_readonly); +} +static void php_pqconn_object_write_def_txn_readonly(zval *object, void *o, zval *value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + obj->intern->default_txn_readonly = zend_is_true(value); +} + +static void php_pqconn_object_read_def_txn_deferrable(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + RETVAL_BOOL(obj->intern->default_txn_deferrable); +} +static void php_pqconn_object_write_def_txn_deferrable(zval *object, void *o, zval *value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + obj->intern->default_txn_deferrable = zend_is_true(value); +} + +static void php_pqconn_object_read_def_auto_conv(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + RETVAL_LONG(obj->intern->default_auto_convert); +} +static void php_pqconn_object_write_def_auto_conv(zval*object, void *o, zval *value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + zval *zac = value; + + if (Z_TYPE_P(zac) != IS_LONG) { + if (Z_REFCOUNT_P(zac) > 1) { + zval *tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_ZVAL(tmp, zac, 1, 0); + convert_to_long(tmp); + zac = tmp; + } else { + convert_to_long_ex(&zac); + } + } + + obj->intern->default_auto_convert = Z_LVAL_P(zac) & PHP_PQRES_CONV_ALL; + + if (zac != value) { + zval_ptr_dtor(&zac); + } +} + static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC) { zval *zsocket, zmember; @@ -423,27 +541,44 @@ php_resource_factory_ops_t *php_pqconn_get_resource_factory_ops(void) static void php_pqconn_wakeup(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC) { - // FIXME: ping server + PGresult *res = PQexec(*handle, ""); + PHP_PQclear(res); + + if (CONNECTION_OK != PQstatus(*handle)) { + PQreset(*handle); + } } -static int apply_unlisten(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) +static inline PGresult *unlisten(PGconn *conn, const char *channel_str, size_t channel_len TSRMLS_DC) { - php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *); - char *quoted_channel = PQescapeIdentifier(obj->intern->conn, key->arKey, key->nKeyLength - 1); + char *quoted_channel = PQescapeIdentifier(conn, channel_str, channel_len); + PGresult *res = NULL; if (quoted_channel) { - PGresult *res; - char *cmd; + smart_str cmd = {0}; - spprintf(&cmd, 0, "UNLISTEN %s", quoted_channel); - if ((res = PQexec(obj->intern->conn, cmd))) { - PHP_PQclear(res); - } + smart_str_appends(&cmd, "UNLISTEN "); + smart_str_appends(&cmd, quoted_channel); + smart_str_0(&cmd); + + res = PQexec(conn, cmd.c); - efree(cmd); + smart_str_free(&cmd); PQfreemem(quoted_channel); } + return res; +} + +static int apply_unlisten(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) +{ + php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *); + PGresult *res = unlisten(obj->intern->conn, key->arKey, key->nKeyLength - 1); + + if (res) { + PHP_PQclear(res); + } + return ZEND_HASH_APPLY_REMOVE; } @@ -490,7 +625,6 @@ static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj); /* release instance data */ - memset(evdata, 0, sizeof(*evdata)); efree(evdata); } } @@ -521,7 +655,10 @@ static PHP_METHOD(pqconn, __construct) { obj->intern = ecalloc(1, sizeof(*obj->intern)); + obj->intern->default_auto_convert = PHP_PQRES_CONV_ALL; + 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) { @@ -601,6 +738,81 @@ static PHP_METHOD(pqconn, resetAsync) { } } +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unlisten, 0, 0, 1) + ZEND_ARG_INFO(0, channel) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, unlisten) +{ + zend_error_handling zeh; + char *channel_str; + int channel_len; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else if (SUCCESS == zend_hash_del(&obj->intern->listeners, channel_str, channel_len + 1)) { + PGresult *res = unlisten(obj->intern->conn, channel_str, channel_len); + + if (res) { + php_pqres_success(res TSRMLS_CC); + PHP_PQclear(res); + } + } + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unlisten_async, 0, 0, 1) + ZEND_ARG_INFO(0, channel) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, unlistenAsync) { + zend_error_handling zeh; + char *channel_str; + int channel_len; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { + char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len); + + if (!quoted_channel) { + throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { + smart_str cmd = {0}; + + smart_str_appends(&cmd, "UNLISTEN "); + smart_str_appends(&cmd, quoted_channel); + smart_str_0(&cmd); + + if (!PQsendQuery(obj->intern->conn, cmd.c)) { + throw_exce(EX_IO TSRMLS_CC, "Failed to uninstall listener (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { + obj->intern->poller = PQconsumeInput; + zend_hash_del(&obj->intern->listeners, channel_str, channel_len + 1); + } + + smart_str_free(&cmd); + PQfreemem(quoted_channel); + php_pqconn_notify_listeners(obj TSRMLS_CC); + } + } + } +} + static void php_pqconn_add_listener(php_pqconn_object_t *obj, const char *channel_str, size_t channel_len, php_pq_callback_t *listener TSRMLS_DC) { HashTable ht, *existing_listeners; @@ -616,7 +828,7 @@ static void php_pqconn_add_listener(php_pqconn_object_t *obj, const char *channe } } -ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 0) +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 2) ZEND_ARG_INFO(0, channel) ZEND_ARG_INFO(0, callable) ZEND_END_ARG_INFO(); @@ -624,7 +836,7 @@ static PHP_METHOD(pqconn, listen) { zend_error_handling zeh; char *channel_str = NULL; int channel_len = 0; - php_pq_callback_t listener; + php_pq_callback_t listener = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); @@ -678,7 +890,7 @@ static PHP_METHOD(pqconn, listenAsync) { zend_error_handling zeh; char *channel_str = NULL; int channel_len = 0; - php_pq_callback_t listener; + php_pq_callback_t listener = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); @@ -903,12 +1115,8 @@ static PHP_METHOD(pqconn, execAsync) { } 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)); } else { + php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC); obj->intern->poller = PQconsumeInput; - php_pq_callback_dtor(&obj->intern->onevent); - if (resolver.fci.size > 0) { - obj->intern->onevent = resolver; - php_pq_callback_addref(&obj->intern->onevent); - } php_pqconn_notify_listeners(obj TSRMLS_CC); } } @@ -938,27 +1146,11 @@ static PHP_METHOD(pqconn, execParams) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); } else { PGresult *res; - int count; - Oid *types = NULL; - char **params = NULL; - HashTable zdtor; - - ZEND_INIT_SYMTABLE(&zdtor); - count = php_pq_params_to_array(Z_ARRVAL_P(zparams), ¶ms, &zdtor TSRMLS_CC); - - if (ztypes) { - php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC); - } + php_pq_params_t *params; - 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,48 +1192,29 @@ 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); + 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 (ztypes) { - php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC); - } - - 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)); } else { + php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC); obj->intern->poller = PQconsumeInput; - php_pq_callback_dtor(&obj->intern->onevent); - if (resolver.fci.size > 0) { - obj->intern->onevent = resolver; - php_pq_callback_addref(&obj->intern->onevent); - } 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 +1222,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; @@ -1073,7 +1237,7 @@ STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *na } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2) - ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_END_ARG_INFO(); @@ -1093,35 +1257,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,15 +1298,11 @@ STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const ch php_pqconn_notify_listeners(obj TSRMLS_CC); } - if (types) { - efree(types); - } - return rv; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2) -ZEND_ARG_INFO(0, type) + ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, types, 1) ZEND_END_ARG_INFO(); @@ -1161,16 +1322,171 @@ static PHP_METHOD(pqconn, prepareAsync) { if (!obj->intern) { throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); - } else if (SUCCESS == php_pqconn_prepare_async(getThis(), obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) { - php_pqstm_t *stm = ecalloc(1, sizeof(*stm)); + } else { + php_pq_params_t *params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC); - php_pq_object_addref(obj TSRMLS_CC); - stm->conn = obj; - stm->name = estrdup(name_str); - ZEND_INIT_SYMTABLE(&stm->bound); + if (SUCCESS != php_pqconn_prepare_async(getThis(), obj, name_str, query_str, params TSRMLS_CC)) { + php_pq_params_free(¶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); + } + } + } +} + +static inline char *declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len) +{ + size_t decl_len = name_len + query_len + sizeof("DECLARE BINARY INSENSITIVE NO SCROLL CURSOR WITHOUT HOLD FOR "); + char *decl_str; + + decl_str = emalloc(decl_len); + decl_len = slprintf(decl_str, decl_len, "DECLARE %s %s %s %s CURSOR %s FOR %s", + name_str, + (flags & PHP_PQ_DECLARE_BINARY) ? "BINARY" : "", + (flags & PHP_PQ_DECLARE_INSENSITIVE) ? "INSENSITIVE" : "", + (flags & PHP_PQ_DECLARE_NO_SCROLL) ? "NO SCROLL" : + (flags & PHP_PQ_DECLARE_SCROLL) ? "SCROLL" : "", + (flags & PHP_PQ_DECLARE_WITH_HOLD) ? "WITH HOLD" : "", + query_str + ); + return decl_str; +} + +STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC) +{ + PGresult *res; + STATUS rv; + + if (!obj) { + obj = zend_object_store_get_object(object TSRMLS_CC); + } + + res = PQexec(obj->intern->conn, decl); + + if (!res) { + rv = FAILURE; + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { + rv = php_pqres_success(res TSRMLS_CC); + PHP_PQclear(res); + php_pqconn_notify_listeners(obj TSRMLS_CC); + } + + return rv; +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare, 0, 0, 3) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, query) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, declare) { + zend_error_handling zeh; + char *name_str, *query_str; + int name_len, query_len; + long flags; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { + char *decl = declare_str(name_str, name_len, flags, query_str, query_len); + + if (SUCCESS != php_pqconn_declare(getThis(), obj, decl TSRMLS_CC)) { + efree(decl); + } else { + php_pqcur_t *cur = ecalloc(1, sizeof(*cur)); + + php_pq_object_addref(obj TSRMLS_CC); + cur->conn = obj; + cur->open = 1; + cur->name = estrdup(name_str); + cur->decl = decl; + + return_value->type = IS_OBJECT; + return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC); + } + } + } +} + +STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC) +{ + STATUS rv; + + if (!obj) { + obj = zend_object_store_get_object(object TSRMLS_CC); + } + + if (!PQsendQuery(obj->intern->conn, decl)) { + rv = FAILURE; + throw_exce(EX_IO TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) { + rv = FAILURE; + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { + rv = SUCCESS; + obj->intern->poller = PQconsumeInput; + php_pqconn_notify_listeners(obj TSRMLS_CC); + } + + return rv; +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare_async, 0, 0, 2) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, query) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, declareAsync) { + zend_error_handling zeh; + char *name_str, *query_str; + int name_len, query_len; + long flags; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { + char *decl = declare_str(name_str, name_len, flags, query_str, query_len); + + if (SUCCESS != php_pqconn_declare_async(getThis(), obj, decl TSRMLS_CC)) { + efree(decl); + } else { + php_pqcur_t *cur = ecalloc(1, sizeof(*cur)); + + php_pq_object_addref(obj TSRMLS_CC); + cur->conn = obj; + cur->open = 1; + cur->name = estrdup(name_str); + cur->decl = decl; + + return_value->type = IS_OBJECT; + return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC); + } } } } @@ -1365,8 +1681,10 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, startTransaction) { zend_error_handling zeh; - long isolation = PHP_PQTXN_READ_COMMITTED; - zend_bool readonly = 0, deferrable = 0; + php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + long isolation = obj->intern ? obj->intern->default_txn_isolation : PHP_PQTXN_READ_COMMITTED; + zend_bool readonly = obj->intern ? obj->intern->default_txn_readonly : 0; + zend_bool deferrable = obj->intern ? obj->intern->default_txn_deferrable : 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); @@ -1374,8 +1692,6 @@ static PHP_METHOD(pqconn, startTransaction) { zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { - php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - rv = php_pqconn_start_transaction(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC); if (SUCCESS == rv) { @@ -1401,16 +1717,17 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, startTransactionAsync) { zend_error_handling zeh; - long isolation = PHP_PQTXN_READ_COMMITTED; - zend_bool readonly = 0, deferrable = 0; + php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + long isolation = obj->intern ? obj->intern->default_txn_isolation : PHP_PQTXN_READ_COMMITTED; + zend_bool readonly = obj->intern ? obj->intern->default_txn_readonly : 0; + zend_bool deferrable = obj->intern ? obj->intern->default_txn_deferrable : 0; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable); zend_restore_error_handling(&zeh TSRMLS_CC); - if (SUCCESS == rv) { - php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + if (SUCCESS == rv) { rv = php_pqconn_start_transaction_async(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC); if (SUCCESS == rv) { @@ -1461,6 +1778,30 @@ static PHP_METHOD(pqconn, trace) { } } +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_off, 0, 0, 1) + ZEND_ARG_INFO(0, type) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, off) { + zend_error_handling zeh; + char *type_str; + int type_len; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &type_str, &type_len); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { + RETURN_BOOL(SUCCESS == zend_hash_del(&obj->intern->eventhandlers, type_str, type_len + 1)); + } + } +} + ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_on, 0, 0, 2) ZEND_ARG_INFO(0, type) ZEND_ARG_INFO(0, callable) @@ -1469,7 +1810,7 @@ static PHP_METHOD(pqconn, on) { zend_error_handling zeh; char *type_str; int type_len; - php_pq_callback_t cb; + php_pq_callback_t cb = {{0}}; STATUS rv; zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); @@ -1489,6 +1830,88 @@ 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 *); + int add = va_arg(argv, int); + + tmp = *zoid; + Z_ADDREF_P(tmp); + convert_to_long_ex(&tmp); + if (add) { + Z_ADDREF_PP(zcnv); + zend_hash_index_update(converters, Z_LVAL_P(tmp), zcnv, sizeof(zval *), NULL); + } else { + zend_hash_index_del(converters, Z_LVAL_P(tmp)); + } + 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\\Converter, 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; + Z_ADDREF_P(tmp); + convert_to_array_ex(&tmp); + zend_hash_apply_with_arguments(Z_ARRVAL_P(tmp) TSRMLS_CC, apply_set_converter, 3, &zcnv, &obj->intern->converters, 1); + zval_ptr_dtor(&tmp); + zval_ptr_dtor(&zoids); + } + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unset_converter, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, converter, pq\\Converter, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, unsetConverter) { + 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; + Z_ADDREF_P(tmp); + convert_to_array_ex(&tmp); + zend_hash_apply_with_arguments(Z_ARRVAL_P(tmp) TSRMLS_CC, apply_set_converter, 3, &zcnv, &obj->intern->converters, 0); + 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) @@ -1500,6 +1923,10 @@ static zend_function_entry php_pqconn_methods[] = { PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC) PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC) + PHP_ME(pqconn, declare, ai_pqconn_declare, ZEND_ACC_PUBLIC) + PHP_ME(pqconn, declareAsync, ai_pqconn_declare_async, ZEND_ACC_PUBLIC) + PHP_ME(pqconn, unlisten, ai_pqconn_unlisten, ZEND_ACC_PUBLIC) + PHP_ME(pqconn, unlistenAsync, ai_pqconn_unlisten_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC) PHP_ME(pqconn, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC) @@ -1512,10 +1939,19 @@ static zend_function_entry php_pqconn_methods[] = { PHP_ME(pqconn, startTransaction, ai_pqconn_start_transaction, ZEND_ACC_PUBLIC) 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, off, ai_pqconn_off, ZEND_ACC_PUBLIC) PHP_ME(pqconn, on, ai_pqconn_on, ZEND_ACC_PUBLIC) + PHP_ME(pqconn, setConverter, ai_pqconn_set_converter, ZEND_ACC_PUBLIC) + PHP_ME(pqconn, unsetConverter, ai_pqconn_unset_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}; @@ -1596,6 +2032,36 @@ PHP_MINIT_FUNCTION(pqconn) ph.read = php_pqconn_object_read_event_handlers; zend_hash_add(&php_pqconn_object_prophandlers, "eventHandlers", sizeof("eventHandlers"), (void *) &ph, sizeof(ph), NULL); + zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("defaultFetchType"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); + ph.read = php_pqconn_object_read_def_fetch_type; + ph.write = php_pqconn_object_write_def_fetch_type; + zend_hash_add(&php_pqconn_object_prophandlers, "defaultFetchType", sizeof("defaultFetchType"), (void *) &ph, sizeof(ph), NULL); + ph.write = NULL; + + zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("defaultTransactionIsolation"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); + ph.read = php_pqconn_object_read_def_txn_isolation; + ph.write = php_pqconn_object_write_def_txn_isolation; + zend_hash_add(&php_pqconn_object_prophandlers, "defaultTransactionIsolation", sizeof("defaultTransactionIsolation"), (void *) &ph, sizeof(ph), NULL); + ph.write = NULL; + + zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("defaultTransactionReadonly"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); + ph.read = php_pqconn_object_read_def_txn_readonly; + ph.write = php_pqconn_object_write_def_txn_readonly; + zend_hash_add(&php_pqconn_object_prophandlers, "defaultTransactionReadonly", sizeof("defaultTransactionReadonly"), (void *) &ph, sizeof(ph), NULL); + ph.write = NULL; + + zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("defaultTransactionDeferrable"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); + ph.read = php_pqconn_object_read_def_txn_deferrable; + ph.write = php_pqconn_object_write_def_txn_deferrable; + zend_hash_add(&php_pqconn_object_prophandlers, "defaultTransactionDeferrable", sizeof("defaultTransactionDeferrable"), (void *) &ph, sizeof(ph), NULL); + ph.write = NULL; + + zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("defaultAutoConvert"), PHP_PQRES_CONV_ALL, ZEND_ACC_PUBLIC TSRMLS_CC); + ph.read = php_pqconn_object_read_def_auto_conv; + ph.write = php_pqconn_object_write_def_auto_conv; + zend_hash_add(&php_pqconn_object_prophandlers, "defaultAutoConvert", sizeof("defaultAutoConvert"), (void *) &ph, sizeof(ph), NULL); + ph.write = NULL; + zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("OK"), CONNECTION_OK TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("BAD"), CONNECTION_BAD TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("STARTED"), CONNECTION_STARTED TSRMLS_CC);