X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-pq;a=blobdiff_plain;f=src%2Fphp_pq.c;h=a0d18a510023f7aa5944ad106f447ded84392ea6;hp=0a0dea60f120995f4fcf6bb6e19e35de7939d22a;hb=3024b0b5a903a2bcb082b01e1787410dc9508a9d;hpb=81211142f5c20a21a64a6c3e4963a6853ba3d741 diff --git a/src/php_pq.c b/src/php_pq.c index 0a0dea6..a0d18a5 100644 --- a/src/php_pq.c +++ b/src/php_pq.c @@ -6,7 +6,7 @@ | modification, are permitted provided that the conditions mentioned | | in the accompanying LICENSE file are met. | +--------------------------------------------------------------------+ - | Copyright (c) 2013, Michael Wallner | + | Copyright (c) 2013, Michael Wallner | +--------------------------------------------------------------------+ */ @@ -14,10 +14,16 @@ # include "config.h" #endif +#define SMART_STR_PREALLOC 256 + #include #include +#include #include +#include #include +#include +#include #include #include @@ -43,15 +49,18 @@ static int php_pqconn_event(PGEventId id, void *e, void *data); #define PHP_PQclear(_r) \ do { \ - zval *_resinszv = PQresultInstanceData((_r), php_pqconn_event); \ - if (!_resinszv) PQclear((_r)); \ + php_pqres_object_t *_o = PQresultInstanceData((_r), php_pqconn_event); \ + if (_o) { \ + php_pq_object_delref(_o TSRMLS_CC); \ + } else { \ + PQclear(_r); \ + } \ } while (0) /* ZEND_DECLARE_MODULE_GLOBALS(pq) */ - /* {{{ PHP_INI */ /* Remove comments and fill if you need to have entries in php.ini @@ -83,6 +92,60 @@ static zend_class_entry *php_pqevent_class_entry; static zend_class_entry *php_pqlob_class_entry; static zend_class_entry *php_pqcopy_class_entry; +typedef enum php_pqexc_type { + EX_INVALID_ARGUMENT, + EX_RUNTIME, + EX_CONNECTION_FAILED, + EX_IO, + EX_ESCAPE, + EX_BAD_METHODCALL, + EX_UNINITIALIZED, + EX_DOMAIN, + EX_SQL +} php_pqexc_type_t; + +static zend_class_entry *php_pqexc_interface_class_entry; +static zend_class_entry *php_pqexc_invalid_argument_class_entry; +static zend_class_entry *php_pqexc_runtime_class_entry; +static zend_class_entry *php_pqexc_bad_methodcall_class_entry; +static zend_class_entry *php_pqexc_domain_class_entry; + +static zend_class_entry *exce(php_pqexc_type_t type) +{ + switch (type) { + default: + case EX_INVALID_ARGUMENT: + return php_pqexc_invalid_argument_class_entry; + case EX_RUNTIME: + case EX_CONNECTION_FAILED: + case EX_IO: + case EX_ESCAPE: + return php_pqexc_runtime_class_entry; + case EX_UNINITIALIZED: + case EX_BAD_METHODCALL: + return php_pqexc_bad_methodcall_class_entry; + case EX_DOMAIN: + case EX_SQL: + return php_pqexc_domain_class_entry; + } +} + +static zval *throw_exce(php_pqexc_type_t type TSRMLS_DC, const char *fmt, ...) +{ + char *msg; + zval *zexc; + va_list argv; + + va_start(argv, fmt); + vspprintf(&msg, 0, fmt, argv); + va_end(argv); + + zexc = zend_throw_exception(exce(type), msg, type TSRMLS_CC); + efree(msg); + + return zexc; +} + static zend_object_handlers php_pqconn_object_handlers; static zend_object_handlers php_pqtypes_object_handlers; static zend_object_handlers php_pqres_object_handlers; @@ -106,9 +169,13 @@ typedef struct php_pq_object { void *intern; } php_pq_object_t; +#define PHP_PQCONN_ASYNC 0x01 +#define PHP_PQCONN_PERSISTENT 0x02 + typedef struct php_pqconn { PGconn *conn; int (*poller)(PGconn *); + php_resource_factory_t factory; HashTable listeners; HashTable eventhandlers; php_pq_callback_t onevent; @@ -157,6 +224,7 @@ typedef struct php_pqres_iterator { typedef struct php_pqres { PGresult *res; php_pqres_iterator_t *iter; + HashTable bound; } php_pqres_t; typedef struct php_pqres_object { @@ -189,6 +257,7 @@ typedef struct php_pqtxn { php_pqconn_object_t *conn; php_pqtxn_isolation_t isolation; unsigned savepoint; + unsigned open:1; unsigned readonly:1; unsigned deferrable:1; } php_pqtxn_t; @@ -214,8 +283,8 @@ typedef struct php_pqcancel_object { typedef struct php_pqevent { php_pq_callback_t cb; - php_pqconn_object_t *conn; char *type; + ulong h; } php_pqevent_t; typedef struct php_pqevent_object { @@ -327,10 +396,14 @@ static STATUS php_pqres_iterator_valid(zend_object_iterator *i TSRMLS_DC) php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC); - if (PQresultStatus(obj->intern->res) != PGRES_TUPLES_OK) { - return FAILURE; - } - if (PQntuples(obj->intern->res) <= iter->index) { + switch (PQresultStatus(obj->intern->res)) { + case PGRES_TUPLES_OK: + case PGRES_SINGLE_TUPLE: + if (PQntuples(obj->intern->res) <= iter->index) { + return FAILURE; + } + break; + default: return FAILURE; } @@ -450,27 +523,64 @@ static int php_pqres_count_elements(zval *object, long *count TSRMLS_DC) { php_pqres_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + return FAILURE; + } else { *count = (long) PQntuples(obj->intern->res); return SUCCESS; - } else { - return FAILURE; } } static STATUS php_pqres_success(PGresult *res TSRMLS_DC) { + zval *zexc; + switch (PQresultStatus(res)) { case PGRES_BAD_RESPONSE: case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", PHP_PQresultErrorMessage(res)); + zexc = throw_exce(EX_SQL TSRMLS_CC, "%s", PHP_PQresultErrorMessage(res)); + zend_update_property_string(php_pqexc_domain_class_entry, zexc, ZEND_STRL("sqlstate"), PQresultErrorField(res, PG_DIAG_SQLSTATE) TSRMLS_CC); return FAILURE; default: return SUCCESS; } } +/* +static void php_pqconn_del_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, ulong id TSRMLS_DC) +{ + zval **evhs; + + if (SUCCESS == zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) { + zend_hash_index_del(Z_ARRVAL_PP(evhs), id); + } +} +*/ + +static ulong php_pqconn_add_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, zval *zevent TSRMLS_DC) +{ + zval **evhs; + ulong h; + + if (SUCCESS == zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) { + Z_ADDREF_P(zevent); + h = zend_hash_next_free_element(Z_ARRVAL_PP(evhs)); + add_next_index_zval(*evhs, zevent); + } else { + zval *evh; + + MAKE_STD_ZVAL(evh); + array_init(evh); + Z_ADDREF_P(zevent); + h = zend_hash_next_free_element(Z_ARRVAL_P(evh)); + add_next_index_zval(evh, zevent); + zend_hash_add(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evh, sizeof(zval *), NULL); + } + + return h; +} + static void php_pq_callback_dtor(php_pq_callback_t *cb) { if (cb->fci.size > 0) { zend_fcall_info_args_clear(&cb->fci, 1); @@ -504,6 +614,20 @@ static void php_pq_object_to_zval(void *o, zval **zv TSRMLS_DC) (*zv)->value.obj = obj->zv; } +static void php_pq_object_to_zval_no_addref(void *o, zval **zv TSRMLS_DC) +{ + php_pq_object_t *obj = o; + + if (!*zv) { + MAKE_STD_ZVAL(*zv); + } + + /* no add ref */ + + (*zv)->type = IS_OBJECT; + (*zv)->value.obj = obj->zv; +} + static void php_pq_object_addref(void *o TSRMLS_DC) { php_pq_object_t *obj = o; @@ -519,9 +643,12 @@ static void php_pq_object_delref(void *o TSRMLS_DC) static void php_pqconn_object_free(void *o TSRMLS_DC) { php_pqconn_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj); +#endif if (obj->intern) { - PQfinish(obj->intern->conn); + 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->eventhandlers); @@ -535,7 +662,9 @@ static void php_pqconn_object_free(void *o TSRMLS_DC) static void php_pqtypes_object_free(void *o TSRMLS_DC) { php_pqtypes_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE types(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); +#endif if (obj->intern) { zend_hash_destroy(&obj->intern->types); php_pq_object_delref(obj->intern->conn TSRMLS_CC); @@ -549,19 +678,14 @@ static void php_pqtypes_object_free(void *o TSRMLS_DC) static void php_pqres_object_free(void *o TSRMLS_DC) { php_pqres_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE res(#%d) %p\n", obj->zv.handle, obj); +#endif if (obj->intern) { if (obj->intern->res) { - zval *res = PQresultInstanceData(obj->intern->res, php_pqconn_event); - if (res) { - if (1 == Z_REFCOUNT_P(res)) { - PQresultSetInstanceData(obj->intern->res, php_pqconn_event, NULL); - } - zval_ptr_dtor(&res); - } else { - PQclear(obj->intern->res); - obj->intern->res = NULL; - } + PQresultSetInstanceData(obj->intern->res, php_pqconn_event, NULL); + PQclear(obj->intern->res); + obj->intern->res = NULL; } if (obj->intern->iter) { @@ -569,6 +693,8 @@ static void php_pqres_object_free(void *o TSRMLS_DC) obj->intern->iter = NULL; } + zend_hash_destroy(&obj->intern->bound); + efree(obj->intern); obj->intern = NULL; } @@ -579,8 +705,29 @@ static void php_pqres_object_free(void *o TSRMLS_DC) static void php_pqstm_object_free(void *o TSRMLS_DC) { php_pqstm_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE stm(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); +#endif if (obj->intern) { + char *quoted_name = PQescapeIdentifier(obj->intern->conn->intern->conn, obj->intern->name, strlen(obj->intern->name)); + + php_pq_callback_dtor(&obj->intern->conn->intern->onevent); + + if (quoted_name) { + PGresult *res; + smart_str cmd = {0}; + + smart_str_appends(&cmd, "DEALLOCATE "); + smart_str_appends(&cmd, quoted_name); + smart_str_0(&cmd); + PQfreemem(quoted_name); + + if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) { + PHP_PQclear(res); + } + smart_str_free(&cmd); + } + php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern->name); zend_hash_destroy(&obj->intern->bound); @@ -594,8 +741,17 @@ static void php_pqstm_object_free(void *o TSRMLS_DC) static void php_pqtxn_object_free(void *o TSRMLS_DC) { php_pqtxn_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE txn(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); +#endif if (obj->intern) { + if (obj->intern->open) { + PGresult *res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK"); + + if (res) { + PHP_PQclear(res); + } + } php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern); obj->intern = NULL; @@ -607,7 +763,9 @@ static void php_pqtxn_object_free(void *o TSRMLS_DC) static void php_pqcancel_object_free(void *o TSRMLS_DC) { php_pqcancel_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE cancel(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); +#endif if (obj->intern) { PQfreeCancel(obj->intern->cancel); php_pq_object_delref(obj->intern->conn TSRMLS_CC); @@ -621,10 +779,11 @@ static void php_pqcancel_object_free(void *o TSRMLS_DC) static void php_pqevent_object_free(void *o TSRMLS_DC) { php_pqevent_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE event(#%d) %p\n", obj->zv.handle, obj); +#endif if (obj->intern) { php_pq_callback_dtor(&obj->intern->cb); - php_pq_object_delref(obj->intern->conn TSRMLS_CC); efree(obj->intern->type); efree(obj->intern); obj->intern = NULL; @@ -636,7 +795,9 @@ static void php_pqevent_object_free(void *o TSRMLS_DC) static void php_pqlob_object_free(void *o TSRMLS_DC) { php_pqlob_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE lob(#%d) %p (txn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->txn->zv.handle, obj->intern->txn); +#endif if (obj->intern) { if (obj->intern->lofd) { lo_close(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd); @@ -652,7 +813,9 @@ static void php_pqlob_object_free(void *o TSRMLS_DC) static void php_pqcopy_object_free(void *o TSRMLS_DC) { php_pqcopy_object_t *obj = o; - +#if DBG_GC + fprintf(stderr, "FREE copy(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn); +#endif if (obj->intern) { efree(obj->intern->expression); efree(obj->intern->options); @@ -916,36 +1079,16 @@ static zend_object_value php_pqcopy_create_object(zend_class_entry *class_type T return php_pqcopy_create_object_ex(class_type, NULL, NULL TSRMLS_CC); } -static int apply_ph_to_debug(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) -{ - php_pq_object_prophandler_t *ph = p; - HashTable *ht = va_arg(argv, HashTable *); - zval **return_value, *object = va_arg(argv, zval *); - php_pq_object_t *obj = va_arg(argv, php_pq_object_t *); - - if (SUCCESS == zend_hash_find(ht, key->arKey, key->nKeyLength, (void *) &return_value)) { - - if (ph->read) { - zval_ptr_dtor(return_value); - MAKE_STD_ZVAL(*return_value); - ZVAL_NULL(*return_value); - - ph->read(object, obj, *return_value TSRMLS_CC); - } - } - - return ZEND_HASH_APPLY_KEEP; -} - -static int apply_pi_to_debug(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) +static int apply_pi_to_ht(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { zend_property_info *pi = p; HashTable *ht = va_arg(argv, HashTable *); zval *object = va_arg(argv, zval *); php_pq_object_t *obj = va_arg(argv, php_pq_object_t *); + int addref = va_arg(argv, int); zval *property = zend_read_property(obj->zo.ce, object, pi->name, pi->name_length, 0 TSRMLS_CC); - if (1||!Z_REFCOUNT_P(property)) { + if (addref) { Z_ADDREF_P(property); } zend_hash_add(ht, pi->name, pi->name_length + 1, (void *) &property, sizeof(zval *), NULL); @@ -962,8 +1105,7 @@ static HashTable *php_pq_object_debug_info(zval *object, int *temp TSRMLS_DC) ALLOC_HASHTABLE(ht); ZEND_INIT_SYMTABLE(ht); - zend_hash_apply_with_arguments(&obj->zo.ce->properties_info TSRMLS_CC, apply_pi_to_debug, 3, ht, object, obj); - zend_hash_apply_with_arguments(obj->prophandler TSRMLS_CC, apply_ph_to_debug, 3, ht, object, obj); + zend_hash_apply_with_arguments(&obj->zo.ce->properties_info TSRMLS_CC, apply_pi_to_ht, 4, ht, object, obj, 1); return ht; } @@ -1059,7 +1201,15 @@ static void php_pqconn_object_write_encoding(zval *object, void *o, zval *value zval *zenc = value; if (Z_TYPE_P(value) != IS_STRING) { - convert_to_string_ex(&zenc); + if (Z_REFCOUNT_P(value) > 1) { + zval *tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_ZVAL(tmp, zenc, 1, 0); + convert_to_string(tmp); + zenc = tmp; + } else { + convert_to_string_ex(&zenc); + } } if (0 > PQsetClientEncoding(obj->intern->conn, Z_STRVAL_P(zenc))) { @@ -1085,7 +1235,7 @@ static void php_pqconn_object_write_unbuffered(zval *object, void *o, zval *valu obj->intern->unbuffered = zend_is_true(value); } -static void php_pqconn_object_read_db(zval *objec, void *o, zval *return_value TSRMLS_DC) +static void php_pqconn_object_read_db(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *db = PQdb(obj->intern->conn); @@ -1097,7 +1247,7 @@ static void php_pqconn_object_read_db(zval *objec, void *o, zval *return_value T } } -static void php_pqconn_object_read_user(zval *objec, void *o, zval *return_value TSRMLS_DC) +static void php_pqconn_object_read_user(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *user = PQuser(obj->intern->conn); @@ -1109,7 +1259,7 @@ static void php_pqconn_object_read_user(zval *objec, void *o, zval *return_value } } -static void php_pqconn_object_read_pass(zval *objec, void *o, zval *return_value TSRMLS_DC) +static void php_pqconn_object_read_pass(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *pass = PQpass(obj->intern->conn); @@ -1121,7 +1271,7 @@ static void php_pqconn_object_read_pass(zval *objec, void *o, zval *return_value } } -static void php_pqconn_object_read_host(zval *objec, void *o, zval *return_value TSRMLS_DC) +static void php_pqconn_object_read_host(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *host = PQhost(obj->intern->conn); @@ -1133,7 +1283,7 @@ static void php_pqconn_object_read_host(zval *objec, void *o, zval *return_value } } -static void php_pqconn_object_read_port(zval *objec, void *o, zval *return_value TSRMLS_DC) +static void php_pqconn_object_read_port(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *port = PQport(obj->intern->conn); @@ -1145,7 +1295,7 @@ static void php_pqconn_object_read_port(zval *objec, void *o, zval *return_value } } -static void php_pqconn_object_read_options(zval *objec, void *o, zval *return_value TSRMLS_DC) +static void php_pqconn_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqconn_object_t *obj = o; char *options = PQoptions(obj->intern->conn); @@ -1157,6 +1307,14 @@ static void php_pqconn_object_read_options(zval *objec, void *o, zval *return_va } } +static void php_pqconn_object_read_event_handlers(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqconn_object_t *obj = o; + + array_init(return_value); + zend_hash_copy(Z_ARRVAL_P(return_value), &obj->intern->eventhandlers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); +} + static void php_pqtypes_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqtypes_object_t *obj = o; @@ -1164,7 +1322,7 @@ static void php_pqtypes_object_read_connection(zval *object, void *o, zval *retu php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } -static int has_dimension(HashTable *ht, zval *member, char **key_str, int *key_len, long *index TSRMLS_DC) +static int has_dimension(HashTable *ht, zval *member, char **key_str, int *key_len, ulong *index TSRMLS_DC) { long lval = 0; zval *tmp = member; @@ -1175,18 +1333,21 @@ static int has_dimension(HashTable *ht, zval *member, char **key_str, int *key_l /* no break */ case IS_STRING: if (!is_numeric_string(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &lval, NULL, 0)) { - if (member != tmp) { - zval_ptr_dtor(&tmp); - } + int exists = zend_hash_exists(ht, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1); + if (key_str) { *key_str = estrndup(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); if (key_len) { *key_len = Z_STRLEN_P(tmp) + 1; } } - return zend_hash_exists(ht, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1); + if (member != tmp) { + zval_ptr_dtor(&tmp); + } + + return exists; } - /* no break */ + break; case IS_LONG: lval = Z_LVAL_P(member); break; @@ -1206,7 +1367,7 @@ static int php_pqtypes_object_has_dimension(zval *object, zval *member, int chec php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); char *key_str = NULL; int key_len = 0; - long index = 0; + ulong index = 0; if (check_empty) { if (has_dimension(&obj->intern->types, member, &key_str, &key_len, &index TSRMLS_CC)) { @@ -1219,11 +1380,14 @@ static int php_pqtypes_object_has_dimension(zval *object, zval *member, int chec } efree(key_str); } else { - if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) data)) { + if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) &data)) { return Z_TYPE_PP(data) != IS_NULL; } } } + if (key_str) { + efree(key_str); + } } else { return has_dimension(&obj->intern->types, member, NULL, NULL, NULL TSRMLS_CC); } @@ -1233,7 +1397,7 @@ static int php_pqtypes_object_has_dimension(zval *object, zval *member, int chec static zval *php_pqtypes_object_read_dimension(zval *object, zval *member, int type TSRMLS_DC) { - long index = 0; + ulong index = 0; char *key_str = NULL; int key_len = 0; php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); @@ -1251,6 +1415,9 @@ static zval *php_pqtypes_object_read_dimension(zval *object, zval *member, int t return *data; } } + if (key_str) { + efree(key_str); + } } return NULL; @@ -1263,6 +1430,13 @@ static void php_pqres_object_read_status(zval *object, void *o, zval *return_val RETVAL_LONG(PQresultStatus(obj->intern->res)); } +static void php_pqres_object_read_status_message(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqres_object_t *obj = o; + + RETVAL_STRING(PQresStatus(PQresultStatus(obj->intern->res))+sizeof("PGRES"), 1); +} + static void php_pqres_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqres_object_t *obj = o; @@ -1312,8 +1486,16 @@ static void php_pqres_object_write_fetch_type(zval *object, void *o, zval *value php_pqres_object_t *obj = o; zval *zfetch_type = value; - if (Z_TYPE_P(zfetch_type) != IS_LONG) { - convert_to_long_ex(&zfetch_type); + if (Z_TYPE_P(value) != IS_LONG) { + if (Z_REFCOUNT_P(value) > 1) { + zval *tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_ZVAL(tmp, zfetch_type, 1, 0); + convert_to_long(tmp); + zfetch_type = tmp; + } else { + convert_to_long_ex(&zfetch_type); + } } if (!obj->intern->iter) { @@ -1359,14 +1541,14 @@ static void php_pqtxn_object_read_readonly(zval *object, void *o, zval *return_v { php_pqtxn_object_t *obj = o; - RETVAL_LONG(obj->intern->readonly); + RETVAL_BOOL(obj->intern->readonly); } static void php_pqtxn_object_read_deferrable(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqtxn_object_t *obj = o; - RETVAL_LONG(obj->intern->deferrable); + RETVAL_BOOL(obj->intern->deferrable); } static void php_pqtxn_object_write_isolation(zval *object, void *o, zval *value TSRMLS_DC) @@ -1377,18 +1559,26 @@ static void php_pqtxn_object_write_isolation(zval *object, void *o, zval *value PGresult *res; if (Z_TYPE_P(zisolation) != IS_LONG) { - convert_to_long_ex(&zisolation); + if (Z_REFCOUNT_P(value) > 1) { + zval *tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_ZVAL(tmp, zisolation, 1, 0); + convert_to_long(tmp); + zisolation = tmp; + } else { + convert_to_long_ex(&zisolation); + } } switch ((obj->intern->isolation = Z_LVAL_P(zisolation))) { case PHP_PQTXN_READ_COMMITTED: - res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION READ COMMITED"); + res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL READ COMMITED"); break; case PHP_PQTXN_REPEATABLE_READ: - res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION REPEATABLE READ"); + res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ"); break; case PHP_PQTXN_SERIALIZABLE: - res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION SERIALIZABLE"); + res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); break; default: obj->intern->isolation = orig; @@ -1447,13 +1637,6 @@ static void php_pqcancel_object_read_connection(zval *object, void *o, zval *ret php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); } -static void php_pqevent_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC) -{ - php_pqevent_object_t *obj = o; - - php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC); -} - static void php_pqevent_object_read_type(zval *object, void *o, zval *return_value TSRMLS_DC) { php_pqevent_object_t *obj = o; @@ -1568,6 +1751,7 @@ static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj if ((CONNECTION_BAD != PQstatus(obj->intern->conn)) && (-1 < (socket = PQsocket(obj->intern->conn))) && (stream = php_stream_fopen_from_fd(socket, "r+b", NULL))) { + stream->flags |= PHP_STREAM_FLAG_NO_CLOSE; php_stream_to_zval(stream, zsocket); retval = SUCCESS; } else { @@ -1588,17 +1772,6 @@ static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj # define TSRMLS_CF(d) #endif -static void php_pqconn_event_register(PGEventRegister *event, php_pqconn_event_data_t *data) -{ - PQsetInstanceData(event->conn, php_pqconn_event, data); -} - -static void php_pqconn_event_conndestroy(PGEventConnDestroy *event, php_pqconn_event_data_t *data) -{ - PQsetInstanceData(event->conn, php_pqconn_event, NULL); - efree(data); -} - static int apply_event(void *p, void *a TSRMLS_DC) { zval **evh = p; @@ -1613,84 +1786,101 @@ static int apply_event(void *p, void *a TSRMLS_DC) return ZEND_HASH_APPLY_KEEP; } -static void php_pqconn_event_connreset(PGEventConnReset *event, php_pqconn_event_data_t *data) +static void php_pqconn_event_connreset(PGEventConnReset *event) { - zval **evhs; - TSRMLS_DF(data); + php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event); - if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("reset"), (void *) &evhs)) { - zval *args, *connection = NULL; + if (data) { + zval **evhs; + TSRMLS_DF(data); - MAKE_STD_ZVAL(args); - array_init(args); - php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); - add_next_index_zval(args, connection); - zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC); - zval_ptr_dtor(&args); + if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("reset"), (void *) &evhs)) { + zval *args, *connection = NULL; + + MAKE_STD_ZVAL(args); + array_init(args); + php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); + add_next_index_zval(args, connection); + zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC); + zval_ptr_dtor(&args); + } } } -static zval *result_instance_zval(PGresult *res TSRMLS_DC) +static void php_pqres_init_instance_data(PGresult *res, php_pqres_object_t **ptr TSRMLS_DC) { - zval *rid = PQresultInstanceData(res, php_pqconn_event); + php_pqres_object_t *obj; + php_pqres_t *r = ecalloc(1, sizeof(*r)); - if (!rid) { - php_pqres_t *r = ecalloc(1, sizeof(*r)); + r->res = res; + ZEND_INIT_SYMTABLE(&r->bound); + php_pqres_create_object_ex(php_pqres_class_entry, r, &obj TSRMLS_CC); - MAKE_STD_ZVAL(rid); - r->res = res; - rid->type = IS_OBJECT; - rid->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); + PQresultSetInstanceData(res, php_pqconn_event, obj); - PQresultSetInstanceData(res, php_pqconn_event, rid); + if (ptr) { + *ptr = obj; } - - Z_ADDREF_P(rid); - return rid; } -static void php_pqconn_event_resultcreate(PGEventResultCreate *event, php_pqconn_event_data_t *data) +static void php_pqconn_event_resultcreate(PGEventResultCreate *event) { - zval **evhs; - TSRMLS_DF(data); + php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event); + + if (data) { + php_pqres_object_t *obj; + zval **evhs; + TSRMLS_DF(data); + + php_pqres_init_instance_data(event->result, &obj TSRMLS_CC); + + /* event listener */ + if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("result"), (void *) &evhs)) { + zval *args, *connection = NULL, *res = NULL; + + MAKE_STD_ZVAL(args); + array_init(args); + php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); + add_next_index_zval(args, connection); + php_pq_object_to_zval(obj, &res TSRMLS_CC); + add_next_index_zval(args, res); + zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC); + zval_ptr_dtor(&args); + } - /* event listener */ - if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("result"), (void *) &evhs)) { - zval *args, *connection = NULL, *res = result_instance_zval(event->result TSRMLS_CC); + /* async callback */ + if (data->obj->intern->onevent.fci.size > 0) { + zval *res = NULL; + + php_pq_object_to_zval(obj, &res TSRMLS_CC); + zend_fcall_info_argn(&data->obj->intern->onevent.fci TSRMLS_CC, 1, &res); + zend_fcall_info_call(&data->obj->intern->onevent.fci, &data->obj->intern->onevent.fcc, NULL, NULL TSRMLS_CC); + zval_ptr_dtor(&res); + } - MAKE_STD_ZVAL(args); - array_init(args); - php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); - add_next_index_zval(args, connection); - add_next_index_zval(args, res); - zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC); - zval_ptr_dtor(&args); } +} - /* async callback */ - if (data->obj->intern->onevent.fci.size > 0) { - zval *res = result_instance_zval(event->result TSRMLS_CC); +static void php_pqconn_event_resultdestroy(PGEventResultDestroy *event) +{ + php_pqres_object_t *obj = PQresultInstanceData(event->result, php_pqconn_event); - zend_fcall_info_argn(&data->obj->intern->onevent.fci TSRMLS_CC, 1, &res); - zend_fcall_info_call(&data->obj->intern->onevent.fci, &data->obj->intern->onevent.fcc, NULL, NULL TSRMLS_CC); - zval_ptr_dtor(&res); + if (obj) { + obj->intern->res = NULL; } } static int php_pqconn_event(PGEventId id, void *e, void *data) { switch (id) { - case PGEVT_REGISTER: - php_pqconn_event_register(e, data); - break; - case PGEVT_CONNDESTROY: - php_pqconn_event_conndestroy(e, data); - break; case PGEVT_CONNRESET: - php_pqconn_event_connreset(e, data); + php_pqconn_event_connreset(e); break; case PGEVT_RESULTCREATE: - php_pqconn_event_resultcreate(e, data); + php_pqconn_event_resultcreate(e); + break; + case PGEVT_RESULTDESTROY: + php_pqconn_event_resultdestroy(e); break; default: break; @@ -1712,161 +1902,364 @@ static php_pqconn_event_data_t *php_pqconn_event_data_init(php_pqconn_object_t * static void php_pqconn_notice_recv(void *p, const PGresult *res) { php_pqconn_event_data_t *data = p; - zval **evhs; - TSRMLS_DF(data); - if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("notice"), (void *) &evhs)) { - zval *args, *connection = NULL; + if (data) { + zval **evhs; + TSRMLS_DF(data); - MAKE_STD_ZVAL(args); - array_init(args); - php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); - add_next_index_zval(args, connection); - add_next_index_string(args, PHP_PQresultErrorMessage(res), 1); - zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC); - zval_ptr_dtor(&args); + if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("notice"), (void *) &evhs)) { + zval *args, *connection = NULL; + + MAKE_STD_ZVAL(args); + array_init(args); + php_pq_object_to_zval(data->obj, &connection TSRMLS_CC); + add_next_index_zval(args, connection); + add_next_index_string(args, PHP_PQresultErrorMessage(res), 1); + zend_hash_apply_with_argument(Z_ARRVAL_PP(evhs), apply_event, args TSRMLS_CC); + zval_ptr_dtor(&args); + } } } -ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_construct, 0, 0, 1) - ZEND_ARG_INFO(0, dsn) - ZEND_ARG_INFO(0, async) -ZEND_END_ARG_INFO(); -static PHP_METHOD(pqconn, __construct) { - zend_error_handling zeh; - char *dsn_str = ""; - int dsn_len = 0; - zend_bool async = 0; - - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sb", &dsn_str, &dsn_len, &async)) { - php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - php_pqconn_event_data_t *data = php_pqconn_event_data_init(obj TSRMLS_CC); - - obj->intern = ecalloc(1, sizeof(*obj->intern)); +typedef struct php_pqconn_resource_factory_data { + char *dsn; + long flags; +} php_pqconn_resource_factory_data_t; - zend_hash_init(&obj->intern->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0); - zend_hash_init(&obj->intern->eventhandlers, 0, NULL, ZVAL_PTR_DTOR, 0); - - - if (async) { - obj->intern->conn = PQconnectStart(dsn_str); - obj->intern->poller = (int (*)(PGconn*)) PQconnectPoll; - } else { - obj->intern->conn = PQconnectdb(dsn_str); - } +static void *php_pqconn_resource_factory_ctor(void *data, void *init_arg TSRMLS_DC) +{ + php_pqconn_resource_factory_data_t *o = init_arg; + PGconn *conn = NULL;; - PQsetNoticeReceiver(obj->intern->conn, php_pqconn_notice_recv, data); - PQregisterEventProc(obj->intern->conn, php_pqconn_event, "ext-pq", data); + if (o->flags & PHP_PQCONN_ASYNC) { + conn = PQconnectStart(o->dsn); + } else { + conn = PQconnectdb(o->dsn); + } - if (SUCCESS != php_pqconn_update_socket(getThis(), obj TSRMLS_CC)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection failed (%s)", PHP_PQerrorMessage(obj->intern->conn)); - } + if (conn) { + PQregisterEventProc(conn, php_pqconn_event, "ext-pq", NULL); } - zend_restore_error_handling(&zeh TSRMLS_CC); -} -ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset, 0, 0, 0) -ZEND_END_ARG_INFO(); -static PHP_METHOD(pqconn, reset) { - if (SUCCESS == zend_parse_parameters_none()) { - php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + return conn; +} - if (obj->intern) { - PQreset(obj->intern->conn); +static void php_pqconn_resource_factory_dtor(void *opaque, void *handle TSRMLS_DC) +{ + php_pqconn_event_data_t *evdata = PQinstanceData(handle, php_pqconn_event); - if (CONNECTION_OK == PQstatus(obj->intern->conn)) { - RETURN_TRUE; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Connection reset failed: (%s)", PHP_PQerrorMessage(obj->intern->conn)); - } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - } - RETURN_FALSE; + /* we don't care for anything, except free'ing evdata */ + if (evdata) { + PQsetInstanceData(handle, php_pqconn_event, NULL); + memset(evdata, 0, sizeof(*evdata)); + efree(evdata); } + + PQfinish(handle); } -ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset_async, 0, 0, 0) -ZEND_END_ARG_INFO(); -static PHP_METHOD(pqconn, resetAsync) { - if (SUCCESS == zend_parse_parameters_none()) { - php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); +static php_resource_factory_ops_t php_pqconn_resource_factory_ops = { + php_pqconn_resource_factory_ctor, + NULL, + php_pqconn_resource_factory_dtor +}; - if (obj->intern) { - if (PQresetStart(obj->intern->conn)) { - obj->intern->poller = (int (*)(PGconn*)) PQresetPoll; - RETURN_TRUE; - } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - } - RETURN_FALSE; - } +static void php_pqconn_wakeup(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC) +{ + // FIXME: ping server } -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) +static int apply_unlisten(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { - HashTable ht, *existing_listeners; + php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *); + char *quoted_channel = PQescapeIdentifier(obj->intern->conn, key->arKey, key->nKeyLength - 1); - php_pq_callback_addref(listener); + if (quoted_channel) { + PGresult *res; + char *cmd; - if (SUCCESS == zend_hash_find(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &existing_listeners)) { - zend_hash_next_index_insert(existing_listeners, (void *) listener, sizeof(*listener), NULL); - } else { - zend_hash_init(&ht, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0); - zend_hash_next_index_insert(&ht, (void *) listener, sizeof(*listener), NULL); - zend_hash_add(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &ht, sizeof(HashTable), NULL); + spprintf(&cmd, 0, "UNLISTEN %s", quoted_channel); + if ((res = PQexec(obj->intern->conn, cmd))) { + PHP_PQclear(res); + } + + efree(cmd); + PQfreemem(quoted_channel); } -} -ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 0) - ZEND_ARG_INFO(0, channel) - ZEND_ARG_INFO(0, callable) -ZEND_END_ARG_INFO(); -static PHP_METHOD(pqconn, listen) { - char *channel_str = NULL; - int channel_len = 0; - php_pq_callback_t listener; + return ZEND_HASH_APPLY_REMOVE; +} - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc)) { - php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); +static void php_pqconn_notice_ignore(void *p, const PGresult *res) +{ +} - obj->intern->poller = PQconsumeInput; +static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC) +{ + php_pqconn_event_data_t *evdata = PQinstanceData(*handle, php_pqconn_event); + PGcancel *cancel; + PGresult *res; - if (obj->intern) { - char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len); + /* go away */ + PQsetInstanceData(*handle, php_pqconn_event, NULL); - if (quoted_channel) { - PGresult *res; - char *cmd; + /* ignore notices */ + PQsetNoticeReceiver(*handle, php_pqconn_notice_ignore, NULL); - spprintf(&cmd, 0, "LISTEN %s", channel_str); - res = PQexec(obj->intern->conn, cmd); + /* cancel async queries */ + if (PQisBusy(*handle) && (cancel = PQgetCancel(*handle))) { + char err[256] = {0}; - efree(cmd); + PQcancel(cancel, err, sizeof(err)); + PQfreeCancel(cancel); + } + /* clean up async results */ + while ((res = PQgetResult(*handle))) { + PHP_PQclear(res); + } + + /* clean up transaction & session */ + switch (PQtransactionStatus(*handle)) { + case PQTRANS_IDLE: + res = PQexec(*handle, "RESET ALL"); + break; + default: + res = PQexec(*handle, "ROLLBACK; RESET ALL"); + break; + } + + if (res) { + PHP_PQclear(res); + } + + if (evdata) { + /* clean up notify listeners */ + 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); + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_construct, 0, 0, 1) + ZEND_ARG_INFO(0, dsn) + ZEND_ARG_INFO(0, async) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, __construct) { + zend_error_handling zeh; + char *dsn_str = ""; + int dsn_len = 0; + long flags = 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, "|sl", &dsn_str, &dsn_len, &flags); + 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_BAD_METHODCALL TSRMLS_CC, "pq\\Connection already initialized"); + } else { + php_pqconn_event_data_t *evdata = php_pqconn_event_data_init(obj TSRMLS_CC); + php_pqconn_resource_factory_data_t rfdata = {dsn_str, flags}; + + 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->eventhandlers, 0, NULL, ZVAL_PTR_DTOR, 0); + + if (flags & PHP_PQCONN_PERSISTENT) { + php_persistent_handle_factory_t *phf = php_persistent_handle_concede(NULL, ZEND_STRL("pq\\Connection"), dsn_str, dsn_len, php_pqconn_wakeup, php_pqconn_retire TSRMLS_CC); + php_resource_factory_init(&obj->intern->factory, php_persistent_handle_get_resource_factory_ops(), phf, (void (*)(void*)) php_persistent_handle_abandon); + } else { + php_resource_factory_init(&obj->intern->factory, &php_pqconn_resource_factory_ops, NULL, NULL); + } + + if (flags & PHP_PQCONN_ASYNC) { + obj->intern->poller = (int (*)(PGconn*)) PQconnectPoll; + } + + obj->intern->conn = php_resource_factory_handle_ctor(&obj->intern->factory, &rfdata TSRMLS_CC); + + PQsetInstanceData(obj->intern->conn, php_pqconn_event, evdata); + PQsetNoticeReceiver(obj->intern->conn, php_pqconn_notice_recv, evdata); + + if (SUCCESS != php_pqconn_update_socket(getThis(), obj TSRMLS_CC)) { + throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection failed (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } + } + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, reset) { + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + 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 { + PQreset(obj->intern->conn); + + if (CONNECTION_OK != PQstatus(obj->intern->conn)) { + throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection reset failed: (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } + + php_pqconn_notify_listeners(obj TSRMLS_CC); + } + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset_async, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, resetAsync) { + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + 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 (!PQresetStart(obj->intern->conn)) { + throw_exce(EX_IO TSRMLS_CC, "Failed to start connection reset (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { + obj->intern->poller = (int (*)(PGconn*)) PQresetPoll; + } + + 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; + + php_pq_callback_addref(listener); + + if (SUCCESS == zend_hash_find(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &existing_listeners)) { + zend_hash_next_index_insert(existing_listeners, (void *) listener, sizeof(*listener), NULL); + } else { + zend_hash_init(&ht, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0); + zend_hash_next_index_insert(&ht, (void *) listener, sizeof(*listener), NULL); + zend_hash_add(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &ht, sizeof(HashTable), NULL); + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 0) + ZEND_ARG_INFO(0, channel) + ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, listen) { + zend_error_handling zeh; + char *channel_str = NULL; + int channel_len = 0; + php_pq_callback_t listener; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc); + 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 { + PGresult *res; + smart_str cmd = {0}; + + smart_str_appends(&cmd, "LISTEN "); + smart_str_appends(&cmd, quoted_channel); + smart_str_0(&cmd); + + res = PQexec(obj->intern->conn, cmd.c); + + smart_str_free(&cmd); PQfreemem(quoted_channel); - if (res) { + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to install listener (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { + obj->intern->poller = PQconsumeInput; php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC); - RETVAL_TRUE; - } else { - RETVAL_FALSE; } PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not install listener (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; } php_pqconn_notify_listeners(obj TSRMLS_CC); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn)); } + } + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen_async, 0, 0, 0) + ZEND_ARG_INFO(0, channel) + ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, listenAsync) { + zend_error_handling zeh; + char *channel_str = NULL; + int channel_len = 0; + php_pq_callback_t listener; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc); + 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 { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; + 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, "LISTEN "); + 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 install listener (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { + obj->intern->poller = PQconsumeInput; + php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC); + } + + smart_str_free(&cmd); + PQfreemem(quoted_channel); + php_pqconn_notify_listeners(obj TSRMLS_CC); + } } } } @@ -1876,35 +2269,67 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify, 0, 0, 2) ZEND_ARG_INFO(0, message) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, notify) { + zend_error_handling zeh; char *channel_str, *message_str; int channel_len, message_len; + STATUS rv; - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_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) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { PGresult *res; char *params[2] = {channel_str, message_str}; res = PQexecParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0); - if (res) { - if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { - RETVAL_TRUE; - } else { - RETVAL_FALSE; - } - PHP_PQclear(res); + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; + php_pqres_success(res TSRMLS_CC); + PHP_PQclear(res); } php_pqconn_notify_listeners(obj TSRMLS_CC); + } + } +} +ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify_async, 0, 0, 2) + ZEND_ARG_INFO(0, channel) + ZEND_ARG_INFO(0, message) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqconn, notifyAsync) { + zend_error_handling zeh; + char *channel_str, *message_str; + int channel_len, message_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, "ss", &channel_str, &channel_len, &message_str, &message_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 { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; + char *params[2] = {channel_str, message_str}; + + if (!PQsendQueryParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0)) { + throw_exce(EX_IO TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { + obj->intern->poller = PQconsumeInput; + } + + php_pqconn_notify_listeners(obj TSRMLS_CC); } } } @@ -1912,25 +2337,28 @@ static PHP_METHOD(pqconn, notify) { ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_poll, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, poll) { - if (SUCCESS == zend_parse_parameters_none()) { + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + 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) { - if (obj->intern->poller) { - if (obj->intern->poller == PQconsumeInput) { - RETVAL_LONG(obj->intern->poller(obj->intern->conn) * PGRES_POLLING_OK); - php_pqconn_notify_listeners(obj TSRMLS_CC); - return; - } else { - RETURN_LONG(obj->intern->poller(obj->intern->conn)); - } + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else if (!obj->intern->poller) { + throw_exce(EX_RUNTIME TSRMLS_CC, "No asynchronous operation active"); + } else { + if (obj->intern->poller == PQconsumeInput) { + RETVAL_LONG(obj->intern->poller(obj->intern->conn) * PGRES_POLLING_OK); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "No asynchronous operation active"); + RETVAL_LONG(obj->intern->poller(obj->intern->conn)); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); + php_pqconn_notify_listeners(obj TSRMLS_CC); } - RETURN_FALSE; } } @@ -1941,55 +2369,58 @@ static PHP_METHOD(pqconn, exec) { zend_error_handling zeh; char *query_str; int query_len; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query_str, &query_len)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &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) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { PGresult *res = PQexec(obj->intern->conn, query_str); - php_pqconn_notify_listeners(obj TSRMLS_CC); - - if (res) { - if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { - php_pqres_t *r = ecalloc(1, sizeof(*r)); - - r->res = res; - return_value->type = IS_OBJECT; - return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); - } + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { + php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); + PHP_PQclear(res); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); + + php_pqconn_notify_listeners(obj TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_get_result, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqconn, getResult) { - if (SUCCESS == zend_parse_parameters_none()) { + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + 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) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connectio not initialized"); + } else { PGresult *res = PQgetResult(obj->intern->conn); - if (res) { - php_pqres_t *r = ecalloc(1, sizeof(*r)); - - r->res = res; - return_value->type = IS_OBJECT; - return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); - } else { + if (!res) { RETVAL_NULL(); + } else { + php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; + + php_pqconn_notify_listeners(obj TSRMLS_CC); } } } @@ -2003,37 +2434,31 @@ static PHP_METHOD(pqconn, execAsync) { php_pq_callback_t resolver = {{0}}; char *query_str; int query_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|f", &query_str, &query_len, &resolver.fci, &resolver.fcc); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f", &query_str, &query_len, &resolver.fci, &resolver.fcc)) { + if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else if (!PQsendQuery(obj->intern->conn, query_str)) { + 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 { + 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); } - - obj->intern->poller = PQconsumeInput; - - if (PQsendQuery(obj->intern->conn, query_str)) { - if (obj->intern->unbuffered) { - if (!PQsetSingleRowMode(obj->intern->conn)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn)); - } - } - RETVAL_TRUE; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; - } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; + php_pqconn_notify_listeners(obj TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } static int apply_to_oid(void *p, void *arg TSRMLS_DC) @@ -2091,7 +2516,7 @@ static int php_pq_types_to_array(HashTable *ht, Oid **types TSRMLS_DC) Oid *tmp; /* +1 for when less types than params are specified */ - *types = tmp = ecalloc(count + 1, sizeof(Oid)); + *types = tmp = ecalloc(count + 1, sizeof(**types)); zend_hash_apply_with_argument(ht, apply_to_oid, &tmp TSRMLS_CC); } @@ -2113,7 +2538,26 @@ static int php_pq_params_to_array(HashTable *ht, char ***params, HashTable *zdto return count; } +/* +static 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_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params, 0, 0, 2) ZEND_ARG_INFO(0, query) ZEND_ARG_ARRAY_INFO(0, params, 0) @@ -2125,12 +2569,18 @@ static PHP_METHOD(pqconn, execParams) { int query_len; zval *zparams; zval *ztypes = NULL; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!", &query_str, &query_len, &zparams, &ztypes)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!", &query_str, &query_len, &zparams, &ztypes); + 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) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { PGresult *res; int count; Oid *types = NULL; @@ -2154,26 +2604,19 @@ static PHP_METHOD(pqconn, execParams) { efree(params); } - php_pqconn_notify_listeners(obj TSRMLS_CC); - - if (res) { + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { - php_pqres_t *r = ecalloc(1, sizeof(*r)); - - r->res = res; - return_value->type = IS_OBJECT; - return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); + php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); + } else { + PHP_PQclear(res); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; + + php_pqconn_notify_listeners(obj TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params_async, 0, 0, 2) @@ -2189,12 +2632,18 @@ static PHP_METHOD(pqconn, execParamsAsync) { int query_len; zval *zparams; zval *ztypes = NULL; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!f", &query_str, &query_len, &zparams, &ztypes, &resolver.fci, &resolver.fcc); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!f", &query_str, &query_len, &zparams, &ztypes, &resolver.fci, &resolver.fcc)) { + if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { int count; Oid *types = NULL; char **params = NULL; @@ -2207,24 +2656,18 @@ static PHP_METHOD(pqconn, execParamsAsync) { php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC); } - php_pq_callback_dtor(&obj->intern->onevent); - if (resolver.fci.size > 0) { - obj->intern->onevent = resolver; - php_pq_callback_addref(&obj->intern->onevent); - } - - obj->intern->poller = PQconsumeInput; - - if (PQsendQueryParams(obj->intern->conn, query_str, count, types, (const char *const*) params, NULL, NULL, 0)) { - if (obj->intern->unbuffered) { - if (!PQsetSingleRowMode(obj->intern->conn)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn)); - } - } - RETVAL_TRUE; + if (!PQsendQueryParams(obj->intern->conn, query_str, count, types, (const char *const*) params, NULL, NULL, 0)) { + 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_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute query (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; + 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); @@ -2234,12 +2677,6 @@ static PHP_METHOD(pqconn, execParamsAsync) { if (params) { efree(params); } - - php_pqconn_notify_listeners(obj TSRMLS_CC); - - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; } } zend_restore_error_handling(&zeh TSRMLS_CC); @@ -2267,12 +2704,13 @@ static STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const c efree(types); } - if (res) { + if (!res) { + rv = FAILURE; + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn)); + } else { rv = php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); - } else { - rv = FAILURE; - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn)); + php_pqconn_notify_listeners(obj TSRMLS_CC); } return rv; @@ -2288,29 +2726,29 @@ static PHP_METHOD(pqconn, prepare) { zval *ztypes = NULL; char *name_str, *query_str; int name_len, *query_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, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes)) { + if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - 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)); + 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)); - php_pq_object_addref(obj TSRMLS_CC); - stm->conn = obj; - stm->name = estrdup(name_str); - ZEND_INIT_SYMTABLE(&stm->bound); + php_pq_object_addref(obj TSRMLS_CC); + stm->conn = obj; + stm->name = estrdup(name_str); + 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); - } - php_pqconn_notify_listeners(obj TSRMLS_CC); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); + return_value->type = IS_OBJECT; + return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } static STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, HashTable *typest TSRMLS_DC) @@ -2327,16 +2765,16 @@ static STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, c count = php_pq_types_to_array(typest, &types TSRMLS_CC); } - if (PQsendPrepare(obj->intern->conn, name, query, count, types)) { - if (obj->intern->unbuffered) { - if (!PQsetSingleRowMode(obj->intern->conn)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn)); - } - } - rv = SUCCESS; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn)); + if (!PQsendPrepare(obj->intern->conn, name, query, count, types)) { + 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)) { 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); } if (types) { @@ -2356,30 +2794,29 @@ static PHP_METHOD(pqconn, prepareAsync) { zval *ztypes = NULL; char *name_str, *query_str; int name_len, *query_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, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes)) { + if (SUCCESS == rv) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - obj->intern->poller = PQconsumeInput; - 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)); + 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)); - php_pq_object_addref(obj TSRMLS_CC); - stm->conn = obj; - stm->name = estrdup(name_str); - ZEND_INIT_SYMTABLE(&stm->bound); + php_pq_object_addref(obj TSRMLS_CC); + stm->conn = obj; + stm->name = estrdup(name_str); + 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); - } - php_pqconn_notify_listeners(obj TSRMLS_CC); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); + return_value->type = IS_OBJECT; + return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote, 0, 0, 1) @@ -2392,19 +2829,18 @@ static PHP_METHOD(pqconn, quote) { if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { char *quoted = PQescapeLiteral(obj->intern->conn, str, len); - if (quoted) { + if (!quoted) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote string (%s)", PHP_PQerrorMessage(obj->intern->conn)); + RETVAL_FALSE; + } else { RETVAL_STRING(quoted, 1); PQfreemem(quoted); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not quote string (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; } } } @@ -2419,19 +2855,18 @@ static PHP_METHOD(pqconn, quoteName) { if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { char *quoted = PQescapeIdentifier(obj->intern->conn, str, len); - if (quoted) { + if (!quoted) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote name (%s)", PHP_PQerrorMessage(obj->intern->conn)); + RETVAL_FALSE; + } else { RETVAL_STRING(quoted, 1); PQfreemem(quoted); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not quote name (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; } } } @@ -2446,20 +2881,19 @@ static PHP_METHOD(pqconn, escapeBytea) { if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { size_t escaped_len; char *escaped_str = (char *) PQescapeByteaConn(obj->intern->conn, (unsigned char *) str, len, &escaped_len); - if (escaped_str) { + if (!escaped_str) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to escape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn)); + RETVAL_FALSE; + } else { RETVAL_STRINGL(escaped_str, escaped_len - 1, 1); PQfreemem(escaped_str); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not escape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; } } } @@ -2474,20 +2908,19 @@ static PHP_METHOD(pqconn, unescapeBytea) { if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { size_t unescaped_len; char *unescaped_str = (char *) PQunescapeBytea((unsigned char *)str, &unescaped_len); - if (unescaped_str) { + if (!unescaped_str) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to unescape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn)); + RETVAL_FALSE; + } else { RETVAL_STRINGL(unescaped_str, unescaped_len, 1); PQfreemem(unescaped_str); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not unescape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn)); - RETVAL_FALSE; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; } } } @@ -2508,61 +2941,79 @@ static const char *isolation_level(long *isolation) { static STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC) { + STATUS rv = FAILURE; + if (!conn_obj) { conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); } - if (conn_obj->intern) { + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { PGresult *res; - char *cmd; - - spprintf(&cmd, 0, "START TRANSACTION ISOLATION LEVEL %s, READ %s, %s DEFERRABLE", - isolation_level(&isolation), readonly ? "ONLY" : "WRITE", deferrable ? "": "NOT"); - - res = PQexec(conn_obj->intern->conn, cmd); - - efree(cmd); - - if (res) { - STATUS rv = php_pqres_success(res TSRMLS_CC); - - PHP_PQclear(res); - return rv; + smart_str cmd = {0}; + const char *il = isolation_level(&isolation); + + smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL "); + smart_str_appends(&cmd, il); + smart_str_appends(&cmd, ", READ "); + smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE"); + smart_str_appends(&cmd, ","); + smart_str_appends(&cmd, deferrable ? "" : " NOT"); + smart_str_appends(&cmd, " DEFERRABLE"); + smart_str_0(&cmd); + + res = PQexec(conn_obj->intern->conn, cmd.c); + + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); - return FAILURE; + rv = php_pqres_success(res TSRMLS_CC); + PHP_PQclear(res); + php_pqconn_notify_listeners(conn_obj TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - return FAILURE; + + smart_str_free(&cmd); } + + return rv; } static STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC) { + STATUS rv = FAILURE; + if (!conn_obj) { conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); } - if (conn_obj->intern->conn) { - char *cmd; - - spprintf(&cmd, 0, "START TRANSACTION ISOLATION LEVEL %s, READ %s, %s DEFERRABLE", - isolation_level(&isolation), readonly ? "ONLY" : "WRITE", deferrable ? "": "NOT"); - - if (PQsendQuery(conn_obj->intern->conn, cmd)) { - conn_obj->intern->poller = PQconsumeInput; - efree(cmd); - return SUCCESS; + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { + smart_str cmd = {0}; + const char *il = isolation_level(&isolation); + + smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL "); + smart_str_appends(&cmd, il); + smart_str_appends(&cmd, ", READ "); + smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE"); + smart_str_appends(&cmd, ","); + smart_str_appends(&cmd, deferrable ? "" : "NOT "); + smart_str_appends(&cmd, " DEFERRABLE"); + smart_str_0(&cmd); + + if (!PQsendQuery(conn_obj->intern->conn, cmd.c)) { + throw_exce(EX_IO TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); } else { - efree(cmd); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); - return FAILURE; + rv = SUCCESS; + conn_obj->intern->poller = PQconsumeInput; + php_pqconn_notify_listeners(conn_obj TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - return FAILURE; + + smart_str_free(&cmd); } + + return rv; } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction, 0, 0, 0) @@ -2574,10 +3025,13 @@ static PHP_METHOD(pqconn, startTransaction) { zend_error_handling zeh; long isolation = PHP_PQTXN_READ_COMMITTED; zend_bool readonly = 0, 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); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable)) { - STATUS rv; + 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); @@ -2587,6 +3041,7 @@ static PHP_METHOD(pqconn, startTransaction) { php_pq_object_addref(obj TSRMLS_CC); txn->conn = obj; + txn->open = 1; txn->isolation = isolation; txn->readonly = readonly; txn->deferrable = deferrable; @@ -2595,10 +3050,8 @@ static PHP_METHOD(pqconn, startTransaction) { return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } - ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction_async, 0, 0, 0) ZEND_ARG_INFO(0, isolation) ZEND_ARG_INFO(0, readonly) @@ -2608,10 +3061,12 @@ static PHP_METHOD(pqconn, startTransactionAsync) { zend_error_handling zeh; long isolation = PHP_PQTXN_READ_COMMITTED; zend_bool readonly = 0, deferrable = 0; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable)) { - 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); rv = php_pqconn_start_transaction_async(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC); @@ -2629,7 +3084,6 @@ static PHP_METHOD(pqconn, startTransactionAsync) { return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_trace, 0, 0, 0) @@ -2640,27 +3094,27 @@ static PHP_METHOD(pqconn, trace) { if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream)) { php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - if (zstream) { + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { + if (!zstream) { + PQuntrace(obj->intern->conn); + RETVAL_TRUE; + } else { FILE *fp; php_stream *stream = NULL; php_stream_from_zval(stream, &zstream); - if (SUCCESS == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) { + if (SUCCESS != php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) { + RETVAL_FALSE; + } else { stream->flags |= PHP_STREAM_FLAG_NO_CLOSE; PQtrace(obj->intern->conn, fp); RETVAL_TRUE; - } else { - RETVAL_FALSE; } - } else { - PQuntrace(obj->intern->conn); - RETVAL_TRUE; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; } } } @@ -2677,7 +3131,9 @@ static zend_function_entry php_pqconn_methods[] = { PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC) PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_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) + PHP_ME(pqconn, notifyAsync, ai_pqconn_notify_async, ZEND_ACC_PUBLIC) PHP_ME(pqconn, getResult, ai_pqconn_get_result, ZEND_ACC_PUBLIC) PHP_ME(pqconn, quote, ai_pqconn_quote, ZEND_ACC_PUBLIC) PHP_ME(pqconn, quoteName, ai_pqconn_quote_name, ZEND_ACC_PUBLIC) @@ -2691,55 +3147,107 @@ static zend_function_entry php_pqconn_methods[] = { ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_construct, 0, 0, 1) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) + ZEND_ARG_ARRAY_INFO(0, namespaces, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtypes, __construct) { zend_error_handling zeh; - zval *zconn; + zval *zconn, *znsp = NULL; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|a!", &zconn, php_pqconn_class_entry, &znsp); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zconn, php_pqconn_class_entry)) { + if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); - if (conn_obj->intern) { - zval *retval = NULL; + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + zval *retval = NULL; obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->conn = conn_obj; php_pq_object_addref(conn_obj TSRMLS_CC); zend_hash_init(&obj->intern->types, 300, NULL, ZVAL_PTR_DTOR, 0); - zend_call_method_with_0_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval); + if (znsp) { + zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval, znsp); + } else { + zend_call_method_with_0_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval); + } + if (retval) { zval_ptr_dtor(&retval); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } #define PHP_PQ_TYPES_QUERY \ "select t.oid, t.* " \ "from pg_type t join pg_namespace n on t.typnamespace=n.oid " \ "where typisdefined " \ - "and typrelid=0 " \ - "and nspname in ('public', 'pg_catalog')" + "and typrelid=0" +#define PHP_PQ_OID_TEXT 25 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0) + ZEND_ARG_ARRAY_INFO(0, namespaces, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtypes, refresh) { - if (SUCCESS == zend_parse_parameters_none()) { + HashTable *nsp = NULL; + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H/!", &nsp); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - PGresult *res = PQexec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY); + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Types not initialized"); + } else { + PGresult *res; - php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); + 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}; + + 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); + } + smart_str_appendc(&str, ')'); + smart_str_0(&str); - if (res) { - if (PGRES_TUPLES_OK == PQresultStatus(res)) { + res = PQexecParams(obj->intern->conn->intern->conn, str.c, count, oids, (const char *const*) params, NULL, NULL, 0); + + smart_str_free(&str); + efree(oids); + efree(params); + zend_hash_destroy(&zdtor); + } + + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { + if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { int r, rows; for (r = 0, rows = PQntuples(res); r < rows; ++r) { @@ -2752,16 +3260,11 @@ static PHP_METHOD(pqtypes, refresh) { 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); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch types (%s)", PHP_PQresultErrorMessage(res)); } + PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } - - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Types not initialized"); } } } @@ -2772,8 +3275,9 @@ static zend_function_entry php_pqtypes_methods[] = { {0} }; -static zval *php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval ***row TSRMLS_DC) +static STATUS php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval ***row TSRMLS_DC) { + STATUS rv; php_pqres_fetch_t orig_fetch; if (!obj) { @@ -2786,13 +3290,159 @@ static zval *php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pq } orig_fetch = obj->intern->iter->fetch_type; obj->intern->iter->fetch_type = fetch_type; - if (SUCCESS == obj->intern->iter->zi.funcs->valid((zend_object_iterator *) obj->intern->iter TSRMLS_CC)) { + if (SUCCESS == (rv = obj->intern->iter->zi.funcs->valid((zend_object_iterator *) obj->intern->iter TSRMLS_CC))) { obj->intern->iter->zi.funcs->get_current_data((zend_object_iterator *) obj->intern->iter, row TSRMLS_CC); obj->intern->iter->zi.funcs->move_forward((zend_object_iterator *) obj->intern->iter TSRMLS_CC); } obj->intern->iter->fetch_type = orig_fetch; - return *row ? **row : NULL; + return rv; +} + +typedef struct php_pqres_col { + char *name; + int num; +} php_pqres_col_t; + +static STATUS column_nn(php_pqres_object_t *obj, zval *zcol, php_pqres_col_t *col TSRMLS_DC) +{ + long index = -1; + char *name = NULL; + + switch (Z_TYPE_P(zcol)) { + default: + convert_to_string(zcol); + /* no break */ + + case IS_STRING: + if (!is_numeric_string(Z_STRVAL_P(zcol), Z_STRLEN_P(zcol), &index, NULL, 0)) { + name = Z_STRVAL_P(zcol); + } + break; + + case IS_LONG: + index = Z_LVAL_P(zcol); + break; + } + + if (name) { + col->name = name; + col->num = PQfnumber(obj->intern->res, name); + } else { + col->name = PQfname(obj->intern->res, index); + col->num = index; + } + + if (!col->name) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column at index %ld", index); + return FAILURE; + } + if (col->num == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column with name '%s'", name); + return FAILURE; + } + return SUCCESS; +} + +static int compare_index(const void *lptr, const void *rptr TSRMLS_DC) +{ + const Bucket *l = *(const Bucket **) lptr; + const Bucket *r = *(const Bucket **) rptr; + + if (l->h < r->h) { + return -1; + } + if (l->h > r->h) { + return 1; + } + return 0; +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqres_bind, 0, 0, 2) + ZEND_ARG_INFO(0, col) + ZEND_ARG_INFO(1, ref) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqres, bind) { + zval *zcol, *zref; + + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref)) { + php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); + } else { + php_pqres_col_t col; + + if (SUCCESS != column_nn(obj, zcol, &col TSRMLS_CC)) { + RETVAL_FALSE; + } else { + Z_ADDREF_P(zref); + + if (SUCCESS != zend_hash_index_update(&obj->intern->bound, col.num, (void *) &zref, sizeof(zval *), NULL)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to bind column %s@%d", col.name, col.num); + RETVAL_FALSE; + } else { + zend_hash_sort(&obj->intern->bound, zend_qsort, compare_index, 0 TSRMLS_CC); + RETVAL_TRUE; + } + } + } + } +} + +static int apply_bound(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) +{ + zval **zvalue, **zbound = p; + zval **zrow = va_arg(argv, zval **); + STATUS *rv = va_arg(argv, STATUS *); + + if (SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(zrow), key->h, (void *) &zvalue)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column ad index %lu", key->h); + *rv = FAILURE; + return ZEND_HASH_APPLY_STOP; + } else { + zval_dtor(*zbound); + ZVAL_COPY_VALUE(*zbound, *zvalue); + ZVAL_NULL(*zvalue); + zval_ptr_dtor(zvalue); + Z_ADDREF_P(*zbound); + *zvalue = *zbound; + *rv = SUCCESS; + return ZEND_HASH_APPLY_KEEP; + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_bound, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqres, fetchBound) { + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); + } else { + zval **row = NULL; + + if (SUCCESS == php_pqres_iteration(getThis(), obj, PHP_PQRES_FETCH_ARRAY, &row TSRMLS_CC) && row) { + zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); + zend_hash_apply_with_arguments(&obj->intern->bound TSRMLS_CC, apply_bound, 2, row, &rv); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS != rv) { + zval_ptr_dtor(row); + } else { + RETVAL_ZVAL(*row, 1, 0); + } + } + } + } } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_row, 0, 0, 0) @@ -2802,25 +3452,31 @@ static PHP_METHOD(pqres, fetchRow) { zend_error_handling zeh; php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); long fetch_type = -1; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_type)) { - if (obj->intern) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_type); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); + } else { zval **row = NULL; if (fetch_type == -1) { fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : PHP_PQRES_FETCH_ARRAY; } + + zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); php_pqres_iteration(getThis(), obj, fetch_type, &row TSRMLS_CC); + zend_restore_error_handling(&zeh TSRMLS_CC); if (row) { RETVAL_ZVAL(*row, 1, 0); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Result not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } static zval **column_at(zval *row, int col TSRMLS_DC) @@ -2829,14 +3485,14 @@ static zval **column_at(zval *row, int col TSRMLS_DC) HashTable *ht = HASH_OF(row); int count = zend_hash_num_elements(ht); - if (col < count) { + if (col >= count) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Column index %d exceeds column count %d", col, count); + } else { zend_hash_internal_pointer_reset(ht); while (col-- > 0) { zend_hash_move_forward(ht); } zend_hash_get_current_data(ht, (void *) &data); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Column index %d exceeds column count %d", col, count); } return data; } @@ -2847,16 +3503,22 @@ ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, fetchCol) { zend_error_handling zeh; long fetch_col = 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, "|l", &fetch_col); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &fetch_col)) { + if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); + } else { zval **row = NULL; + zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); php_pqres_iteration(getThis(), obj, obj->intern->iter ? obj->intern->iter->fetch_type : 0, &row TSRMLS_CC); - if (row) { zval **col = column_at(*row, fetch_col TSRMLS_CC); @@ -2864,63 +3526,28 @@ static PHP_METHOD(pqres, fetchCol) { RETVAL_ZVAL(*col, 1, 0); } } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Result not initialized"); + zend_restore_error_handling(&zeh TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } -typedef struct php_pqres_col { - char *name; - int num; -} php_pqres_col_t; - static int apply_to_col(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { zval **c = p; php_pqres_object_t *obj = va_arg(argv, php_pqres_object_t *); php_pqres_col_t *col, **cols = va_arg(argv, php_pqres_col_t **); STATUS *rv = va_arg(argv, STATUS *); - long index = -1; - char *name = NULL; - - switch (Z_TYPE_PP(c)) { - default: - convert_to_string(*c); - /* no break */ - case IS_STRING: - if (!is_numeric_string(Z_STRVAL_PP(c), Z_STRLEN_PP(c), &index, NULL, 0)) { - name = Z_STRVAL_PP(c); - } - break; - case IS_LONG: - index = Z_LVAL_PP(c); - break; - } col = *cols; - if (name) { - col->name = name; - col->num = PQfnumber(obj->intern->res, name); - } else { - col->name = PQfname(obj->intern->res, index); - col->num = index; - } - if (!col->name) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column at index %ld", index); - *rv = FAILURE; - return ZEND_HASH_APPLY_STOP; - } - if (col->num == -1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to find column with name '%s'", name); + if (SUCCESS != column_nn(obj, *c, col TSRMLS_CC)) { *rv = FAILURE; return ZEND_HASH_APPLY_STOP; + } else { + *rv = SUCCESS; + ++*cols; + return ZEND_HASH_APPLY_KEEP; } - - ++*cols; - return ZEND_HASH_APPLY_KEEP; } static php_pqres_col_t *php_pqres_convert_to_cols(php_pqres_object_t *obj, HashTable *ht TSRMLS_DC) @@ -2948,13 +3575,18 @@ static PHP_METHOD(pqres, map) { zend_error_handling zeh; zval *zkeys = 0, *zvals = 0; long fetch_type = -1; + STATUS rv; + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/!z/!l", &zkeys, &zvals, &fetch_type); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/!z/!l", &zkeys, &zvals, &fetch_type)) { + if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); + } else { int ks = 0, vs = 0; php_pqres_col_t def = {PQfname(obj->intern->res, 0), 0}, *keys = NULL, *vals = NULL; @@ -3018,7 +3650,7 @@ static PHP_METHOD(pqres, map) { break; } if (SUCCESS != zend_symtable_update(HASH_OF(*cur), key, len + 1, (void *) &tmp, sizeof(zval *), (void *) &cur)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to create map"); + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create map"); goto err; } } @@ -3053,11 +3685,8 @@ static PHP_METHOD(pqres, map) { if (vals) { efree(vals); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Result not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqres_count, 0, 0, 0) @@ -3066,15 +3695,17 @@ static PHP_METHOD(pqres, count) { if (SUCCESS == zend_parse_parameters_none()) { long count; - if (SUCCESS == php_pqres_count_elements(getThis(), &count TSRMLS_CC)) { - RETVAL_LONG(count); + if (SUCCESS != php_pqres_count_elements(getThis(), &count TSRMLS_CC)) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Result not initialized"); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Result not initialized"); + RETVAL_LONG(count); } } } static zend_function_entry php_pqres_methods[] = { + PHP_ME(pqres, bind, ai_pqres_bind, ZEND_ACC_PUBLIC) + PHP_ME(pqres, fetchBound, ai_pqres_fetch_bound, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchRow, ai_pqres_fetch_row, ZEND_ACC_PUBLIC) PHP_ME(pqres, fetchCol, ai_pqres_fetch_col, ZEND_ACC_PUBLIC) PHP_ME(pqres, count, ai_pqres_count, ZEND_ACC_PUBLIC) @@ -3095,20 +3726,23 @@ static PHP_METHOD(pqstm, __construct) { char *name_str, *query_str; int name_len, *query_len; zend_bool async = 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, "Oss|a/!b", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes, &async); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oss|a/!b", &zconn, php_pqconn_class_entry, &name_str, &name_len, &query_str, &query_len, &ztypes, &async)) { + if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); - if (conn_obj->intern) { - STATUS rv; + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { if (async) { - conn_obj->intern->poller = PQconsumeInput; rv = php_pqconn_prepare_async(zconn, conn_obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC); } else { rv = php_pqconn_prepare(zconn, conn_obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC); - php_pqconn_notify_listeners(conn_obj TSRMLS_CC); } if (SUCCESS == rv) { @@ -3120,13 +3754,9 @@ static PHP_METHOD(pqstm, __construct) { ZEND_INIT_SYMTABLE(&stm->bound); obj->intern = stm; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } - ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_bind, 0, 0, 2) ZEND_ARG_INFO(0, param_no) ZEND_ARG_INFO(1, param_ref) @@ -3138,11 +3768,12 @@ static PHP_METHOD(pqstm, bind) { if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", ¶m_no, ¶m_ref)) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); + } else { Z_ADDREF_P(param_ref); zend_hash_index_update(&obj->intern->bound, param_no, (void *) ¶m_ref, sizeof(zval *), NULL); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Statement not initialized"); + zend_hash_sort(&obj->intern->bound, zend_qsort, compare_index, 0 TSRMLS_CC); } } } @@ -3153,54 +3784,46 @@ ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, exec) { zend_error_handling zeh; zval *zparams = NULL; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &zparams)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!", &zparams); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - if (obj->intern->conn->intern) { - int count = 0; - char **params = NULL; - HashTable zdtor; - PGresult *res; + 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); + 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); - } + 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); + 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); + if (params) { + efree(params); + } + zend_hash_destroy(&zdtor); + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { + php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC); php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); - - if (res) { - if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { - php_pqres_t *r = ecalloc(1, sizeof(*r)); - - r->res = res; - return_value->type = IS_OBJECT; - return_value->value.obj = php_pqres_create_object_ex(php_pqres_class_entry, r, NULL TSRMLS_CC); - } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Statement not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_exec_async, 0, 0, 0) @@ -3211,98 +3834,110 @@ static PHP_METHOD(pqstm, execAsync) { zend_error_handling zeh; zval *zparams = NULL; php_pq_callback_t resolver = {{0}}; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!f", &zparams, &resolver.fci, &resolver.fcc)) { - php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - - if (obj->intern) { - if (obj->intern->conn->intern) { - int count; - char **params = NULL; - HashTable zdtor; - - if (zparams) { - ZEND_INIT_SYMTABLE(&zdtor); - count = php_pq_params_to_array(Z_ARRVAL_P(zparams), ¶ms, &zdtor TSRMLS_CC); - } - - php_pq_callback_dtor(&obj->intern->conn->intern->onevent); - if (resolver.fci.size > 0) { - obj->intern->conn->intern->onevent = resolver; - php_pq_callback_addref(&obj->intern->conn->intern->onevent); - } - - obj->intern->conn->intern->poller = PQconsumeInput; + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a/!f", &zparams, &resolver.fci, &resolver.fcc); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (PQsendQueryPrepared(obj->intern->conn->intern->conn, obj->intern->name, count, (const char *const*) params, NULL, NULL, 0)) { - if (obj->intern->conn->intern->unbuffered) { - if (!PQsetSingleRowMode(obj->intern->conn->intern->conn)) { - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Could not enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } - } - RETVAL_TRUE; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not execute statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - RETVAL_FALSE; - } + if (SUCCESS == rv) { + php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (params) { - efree(params); - } - if (zparams) { - zend_hash_destroy(&zdtor); - } + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); + } else { + int count; + char **params = NULL; + HashTable zdtor; - php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); + if (zparams) { + ZEND_INIT_SYMTABLE(&zdtor); + count = php_pq_params_to_array(Z_ARRVAL_P(zparams), ¶ms, &zdtor TSRMLS_CC); + } + if (!PQsendQueryPrepared(obj->intern->conn->intern->conn, obj->intern->name, count, (const char *const*) params, NULL, NULL, 0)) { + 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)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); - RETVAL_FALSE; + php_pq_callback_dtor(&obj->intern->conn->intern->onevent); + if (resolver.fci.size > 0) { + obj->intern->conn->intern->onevent = resolver; + php_pq_callback_addref(&obj->intern->conn->intern->onevent); + } + obj->intern->conn->intern->poller = PQconsumeInput; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Statement not initialized"); - RETVAL_FALSE; + + if (params) { + efree(params); + } + if (zparams) { + zend_hash_destroy(&zdtor); + } + + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqstm, desc) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { - php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (obj->intern) { - if (obj->intern->conn->intern) { - PGresult *res = PQdescribePrepared(obj->intern->conn->intern->conn, obj->intern->name); + if (SUCCESS == rv) { + php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); + } else { + PGresult *res = PQdescribePrepared(obj->intern->conn->intern->conn, obj->intern->name); - if (res) { - if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { - int p, params; + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to describe statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { + if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { + int p, params; - array_init(return_value); - for (p = 0, params = PQnparams(res); p < params; ++p) { - add_next_index_long(return_value, PQparamtype(res, p)); - } + array_init(return_value); + for (p = 0, params = PQnparams(res); p < params; ++p) { + add_next_index_long(return_value, PQparamtype(res, p)); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not describe statement (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); + PHP_PQclear(res); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Statement not initialized"); } } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_pqstm_desc_async, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqstm, descAsync) { + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { + php_pqstm_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Statement not initialized"); + } else if (!PQsendDescribePrepared(obj->intern->conn->intern->conn, obj->intern->name)) { + throw_exce(EX_IO TSRMLS_CC, "Failed to describe statement: %s", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { + obj->intern->conn->intern->poller = PQconsumeInput; + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); + } + } } static zend_function_entry php_pqstm_methods[] = { @@ -3311,6 +3946,7 @@ static zend_function_entry php_pqstm_methods[] = { PHP_ME(pqstm, exec, ai_pqstm_exec, ZEND_ACC_PUBLIC) PHP_ME(pqstm, desc, ai_pqstm_desc, ZEND_ACC_PUBLIC) PHP_ME(pqstm, execAsync, ai_pqstm_exec_async, ZEND_ACC_PUBLIC) + PHP_ME(pqstm, descAsync, ai_pqstm_desc_async, ZEND_ACC_PUBLIC) {0} }; @@ -3326,14 +3962,18 @@ static PHP_METHOD(pqtxn, __construct) { zval *zconn; long isolation = PHP_PQTXN_READ_COMMITTED; zend_bool async = 0, readonly = 0, deferrable = 0; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|blbb", &zconn, php_pqconn_class_entry, &async, &isolation, &readonly, &deferrable)) { - STATUS rv; - php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|blbb", &zconn, php_pqconn_class_entry, &async, &isolation, &readonly, &deferrable); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); - if (conn_obj->intern) { + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { if (async) { rv = php_pqconn_start_transaction_async(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC); } else { @@ -3341,277 +3981,342 @@ static PHP_METHOD(pqtxn, __construct) { } if (SUCCESS == rv) { + php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + obj->intern = ecalloc(1, sizeof(*obj->intern)); php_pq_object_addref(conn_obj TSRMLS_CC); obj->intern->conn = conn_obj; + obj->intern->open = 1; obj->intern->isolation = isolation; obj->intern->readonly = readonly; obj->intern->deferrable = deferrable; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, savepoint) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - char *cmd; + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (!obj->intern->open) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); + } else { PGresult *res; + smart_str cmd = {0}; - spprintf(&cmd, 0, "SAVEPOINT \"%u\"", ++obj->intern->savepoint); - res = PQexec(obj->intern->conn->intern->conn, cmd); + smart_str_appends(&cmd, "SAVEPOINT \""); + smart_str_append_unsigned(&cmd, ++obj->intern->savepoint); + smart_str_appends(&cmd, "\""); + smart_str_0(&cmd); - if (res) { + res = PQexec(obj->intern->conn->intern->conn, cmd.c); + + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to create %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } - efree(cmd); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + smart_str_free(&cmd); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, savepointAsync) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - char *cmd; + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (!obj->intern->open) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); + } else { + smart_str cmd = {0}; - spprintf(&cmd, 0, "SAVEPOINT \"%u\"", ++obj->intern->savepoint); - if (!PQsendQuery(obj->intern->conn->intern->conn, cmd)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to create %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + smart_str_appends(&cmd, "SAVEPOINT \""); + smart_str_append_unsigned(&cmd, ++obj->intern->savepoint); + smart_str_appends(&cmd, "\""); + smart_str_0(&cmd); + + if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) { + throw_exce(EX_IO TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } - efree(cmd); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + smart_str_free(&cmd); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, commit) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transacation not initialized"); + } else if (!obj->intern->open) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transacation already closed"); + } else { PGresult *res; + smart_str cmd = {0}; - if (obj->intern->savepoint) { - char *cmd; - - spprintf(&cmd, 0, "RELEASE SAVEPOINT \"%u\"", obj->intern->savepoint--); - res = PQexec(obj->intern->conn->intern->conn, cmd); + if (!obj->intern->savepoint) { + res = PQexec(obj->intern->conn->intern->conn, "COMMIT"); + } else { + smart_str_appends(&cmd, "RELEASE SAVEPOINT \""); + smart_str_append_unsigned(&cmd, obj->intern->savepoint--); + smart_str_appends(&cmd, "\""); + smart_str_0(&cmd); - if (res) { - php_pqres_success(res TSRMLS_CC); - PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } + res = PQexec(obj->intern->conn->intern->conn, cmd.c); + } - efree(cmd); + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { - res = PQexec(obj->intern->conn->intern->conn, "COMMIT"); - - if (res) { - php_pqres_success(res TSRMLS_CC); - PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to commit transaction (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { + if (!cmd.c) { + obj->intern->open = 0; + } } + PHP_PQclear(res); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + smart_str_free(&cmd); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, commitAsync) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - obj->intern->conn->intern->poller = PQconsumeInput; + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (!obj->intern->open) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); + } else { + int rc; + smart_str cmd = {0}; - if (obj->intern->savepoint) { - char *cmd; + if (!obj->intern->savepoint) { + rc = PQsendQuery(obj->intern->conn->intern->conn, "COMMIT"); + } else { + smart_str_appends(&cmd, "RELEASE SAVEPOINT \""); + smart_str_append_unsigned(&cmd, obj->intern->savepoint--); + smart_str_appends(&cmd, "\""); + smart_str_0(&cmd); - spprintf(&cmd, 0, "RELEASE SAVEPOINT \"%u\"", obj->intern->savepoint--); - if (!PQsendQuery(obj->intern->conn->intern->conn, cmd)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } + rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c); + } - efree(cmd); + if (!rc) { + throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commmit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { - if (!PQsendQuery(obj->intern->conn->intern->conn, "COMMIT")) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to commit transaction (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + if (!cmd.c) { + obj->intern->open = 0; } + obj->intern->conn->intern->poller = PQconsumeInput; + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + smart_str_free(&cmd); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, rollback) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (!obj->intern->open) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); + } else { PGresult *res; + smart_str cmd = {0}; - if (obj->intern->savepoint) { - char *cmd; - - spprintf(&cmd, 0, "ROLLBACK TO SAVEPOINT \"%u\"", obj->intern->savepoint--); - res = PQexec(obj->intern->conn->intern->conn, cmd); + if (!obj->intern->savepoint) { + res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK"); + } else { + smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \""); + smart_str_append_unsigned(&cmd, obj->intern->savepoint--); + smart_str_appends(&cmd, "\""); + smart_str_0(&cmd); - if (res) { - php_pqres_success(res TSRMLS_CC); - PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } + res = PQexec(obj->intern->conn->intern->conn, cmd.c); + } - efree(cmd); + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { - res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK"); - - if (res) { - php_pqres_success(res TSRMLS_CC); - PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to rollback transaction (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { + if (!cmd.c) { + obj->intern->open = 0; + } } + PHP_PQclear(res); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + smart_str_free(&cmd); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, rollbackAsync) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - obj->intern->conn->intern->poller = PQconsumeInput; + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (!obj->intern->open) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed"); + } else { + int rc; + smart_str cmd = {0}; - if (obj->intern->savepoint) { - char *cmd; + if (!obj->intern->savepoint) { + rc = PQsendQuery(obj->intern->conn->intern->conn, "ROLLBACK"); + } else { + smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \""); + smart_str_append_unsigned(&cmd, obj->intern->savepoint--); + smart_str_appends(&cmd, "\""); + smart_str_0(&cmd); - spprintf(&cmd, 0, "ROLLBACK TO SAVEPOINT \"%u\"", obj->intern->savepoint--); - if (!PQsendQuery(obj->intern->conn->intern->conn, cmd)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } + rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c); + } - efree(cmd); + if (!rc) { + throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { - if (!PQsendQuery(obj->intern->conn->intern->conn, "ROLLBACK")) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not rollback transaction (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + if (!cmd.c) { + obj->intern->open = 0; } + obj->intern->conn->intern->poller = PQconsumeInput; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + smart_str_free(&cmd); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, exportSnapshot) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else { PGresult *res = PQexec(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()"); - if (res) { + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { RETVAL_STRING(PQgetvalue(res, 0, 0), 1); } PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot_async, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, exportSnapshotAsync) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { - php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (obj->intern) { - obj->intern->conn->intern->poller = PQconsumeInput; + if (SUCCESS == rv) { + php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (!PQsendQuery(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()")) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (!PQsendQuery(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()")) { + throw_exce(EX_IO TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + obj->intern->conn->intern->poller = PQconsumeInput; + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot, 0, 0, 1) @@ -3621,33 +4326,46 @@ static PHP_METHOD(pqtxn, importSnapshot) { zend_error_handling zeh; char *snapshot_str; int snapshot_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", &snapshot_str, &snapshot_len); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len)) { + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - if (obj->intern->isolation >= PHP_PQTXN_REPEATABLE_READ) { - char *cmd, *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len); + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot"); + } else { + char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len); + + if (!sid) { + throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { PGresult *res; + smart_str cmd = {0}; + + smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT "); + smart_str_appends(&cmd, sid); + smart_str_0(&cmd); - spprintf(&cmd, 0, "SET TRANSACTION SNAPSHOT %s", sid); - res = PQexec(obj->intern->conn->intern->conn, cmd); + res = PQexec(obj->intern->conn->intern->conn, cmd.c); - if (res) { + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { php_pqres_success(res TSRMLS_CC); PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "A transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot"); + + smart_str_free(&cmd); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot_async, 0, 0, 1) @@ -3657,35 +4375,56 @@ static PHP_METHOD(pqtxn, importSnapshotAsync) { zend_error_handling zeh; char *snapshot_str; int snapshot_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", &snapshot_str, &snapshot_len); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len)) { + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - if (obj->intern->isolation >= PHP_PQTXN_REPEATABLE_READ) { - char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len); + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot"); + } else { + char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len); - if (sid) { - char *cmd; - obj->intern->conn->intern->poller = PQconsumeInput; + if (!sid) { + throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { + smart_str cmd = {0}; - spprintf(&cmd, 0, "SET TRANSACTION SNAPSHOT %s", sid); - if (!PQsendQuery(obj->intern->conn->intern->conn, cmd)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to %s (%s)", cmd, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } - efree(cmd); + smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT "); + smart_str_appends(&cmd, sid); + smart_str_0(&cmd); + + if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) { + throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + obj->intern->conn->intern->poller = PQconsumeInput; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "A transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot"); + + smart_str_free(&cmd); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); +} + +static const char *strmode(long mode) +{ + switch (mode & (INV_READ|INV_WRITE)) { + case INV_READ|INV_WRITE: + return "rw"; + case INV_READ: + return "r"; + case INV_WRITE: + return "w"; + default: + return "-"; + } } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_open_lob, 0, 0, 1) @@ -3695,15 +4434,23 @@ ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, openLOB) { zend_error_handling zeh; long mode = INV_WRITE|INV_READ, loid; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &loid, &mode); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &loid, &mode)) { + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else { int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode); - if (lofd >= 0) { + if (lofd < 0) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%ld with mode '%s' (%s)", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { php_pqlob_t *lob = ecalloc(1, sizeof(*lob)); lob->lofd = lofd; @@ -3713,19 +4460,11 @@ static PHP_METHOD(pqtxn, openLOB) { return_value->type = IS_OBJECT; return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to open large object with mode '%s' (%s)", - (mode & (INV_READ|INV_WRITE) ? "rw" : - (mode & INV_READ ? "r" : - (mode & INV_WRITE ? "w" : "-"))), - PHP_PQerrorMessage(obj->intern->conn->intern->conn) - ); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_create_lob, 0, 0, 0) @@ -3734,19 +4473,30 @@ ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, createLOB) { zend_error_handling zeh; long mode = INV_WRITE|INV_READ; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mode); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mode)) { + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else { Oid loid = lo_creat(obj->intern->conn->intern->conn, mode); - if (loid != InvalidOid) { + if (loid == InvalidOid) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create large object with mode '%s' (%s)", strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode); - if (lofd >= 0) { + if (lofd < 0) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%ld with mode '%s': %s", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { php_pqlob_t *lob = ecalloc(1, sizeof(*lob)); + lob->lofd = lofd; lob->loid = loid; php_pq_object_addref(obj TSRMLS_CC); @@ -3754,48 +4504,39 @@ static PHP_METHOD(pqtxn, createLOB) { return_value->type = IS_OBJECT; return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to open large object with mode '%s': %s", - (mode & (INV_READ|INV_WRITE) ? "rw" : - (mode & INV_READ ? "r" : - (mode & INV_WRITE ? "w" : "-"))), - PHP_PQerrorMessage(obj->intern->conn->intern->conn) - ); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to create large object with mode '%s': %s", - (mode & (INV_READ|INV_WRITE) ? "rw" : - (mode & INV_READ ? "r" : - (mode & INV_WRITE ? "w" : "-"))), - PHP_PQerrorMessage(obj->intern->conn->intern->conn) - ); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_unlink_lob, 0, 0, 1) ZEND_ARG_INFO(0, oid) ZEND_END_ARG_INFO(); static PHP_METHOD(pqtxn, unlinkLOB) { + zend_error_handling zeh; long loid; + STATUS rv; - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &loid)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &loid); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - if (1 == lo_unlink(obj->intern->conn->intern->conn, loid)) { - RETVAL_TRUE; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to unlink LOB (oid=%ld): %s", loid, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - RETVAL_FALSE; - } + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); - RETVAL_FALSE; + int rc = lo_unlink(obj->intern->conn->intern->conn, loid); + + if (rc != 1) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to unlink LOB (oid=%ld): %s", loid, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } + + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } } @@ -3824,51 +4565,57 @@ ZEND_END_ARG_INFO(); static PHP_METHOD(pqcancel, __construct) { zend_error_handling zeh; zval *zconn; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zconn, php_pqconn_class_entry); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zconn, php_pqconn_class_entry)) { + if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); - if (conn_obj->intern) { + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { PGcancel *cancel = PQgetCancel(conn_obj->intern->conn); - if (cancel) { + if (!cancel) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to acquire cancel (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); + } else { php_pqcancel_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->cancel = cancel; php_pq_object_addref(conn_obj TSRMLS_CC); obj->intern->conn = conn_obj; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not acquire cancel (%s)", PHP_PQerrorMessage(conn_obj->intern->conn)); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcancel_cancel, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqcancel, cancel) { zend_error_handling zeh; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters_none()) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqcancel_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - char err[256]; + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Cancel not initialized"); + } else { + char err[256] = {0}; if (!PQcancel(obj->intern->cancel, err, sizeof(err))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not request cancellation: %s", err); + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to request cancellation (%s)", err); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Cancel not initialized"); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } static zend_function_entry php_pqcancel_methods[] = { @@ -3877,24 +4624,6 @@ static zend_function_entry php_pqcancel_methods[] = { {0} }; -static void php_pqconn_add_eventhandler(zval *zconn, php_pqconn_object_t *conn_obj, const char *type_str, size_t type_len, zval *zevent TSRMLS_DC) -{ - zval **evhs; - - if (SUCCESS == zend_hash_find(&conn_obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) { - Z_ADDREF_P(zevent); - add_next_index_zval(*evhs, zevent); - } else { - zval *evh; - - MAKE_STD_ZVAL(evh); - array_init(evh); - Z_ADDREF_P(zevent); - add_next_index_zval(evh, zevent); - zend_hash_add(&conn_obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evh, sizeof(zval *), NULL); - } -} - ZEND_BEGIN_ARG_INFO_EX(ai_pqevent_construct, 0, 0, 3) ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0) ZEND_ARG_INFO(0, type) @@ -3906,52 +4635,58 @@ static PHP_METHOD(pqevent, __construct) { char *type_str; int type_len; php_pq_callback_t cb; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osf", &zconn, php_pqconn_class_entry, &type_str, &type_len, &cb.fci, &cb.fcc); + zend_restore_error_handling(&zeh TSRMLS_CC); - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osf", &zconn, php_pqconn_class_entry, &type_str, &type_len, &cb.fci, &cb.fcc)) { + if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); - if (conn_obj->intern) { + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { php_pqevent_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); obj->intern = ecalloc(1, sizeof(*obj->intern)); php_pq_callback_addref(&cb); obj->intern->cb = cb; - php_pq_object_addref(conn_obj TSRMLS_CC); - obj->intern->conn = conn_obj; obj->intern->type = estrdup(type_str); - - php_pqconn_add_eventhandler(zconn, conn_obj, type_str, type_len, getThis() TSRMLS_CC); - - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); + obj->intern->h = php_pqconn_add_eventhandler(conn_obj, type_str, type_len, getThis() TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqevent_trigger, 0, 0, 1) ZEND_ARG_ARRAY_INFO(0, args, 1) ZEND_END_ARG_INFO(); static PHP_METHOD(pqevent, trigger) { + zend_error_handling zeh; zval *args; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &args); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &args)) { + if (SUCCESS == rv) { php_pqevent_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Event not initialized"); + } else { zval *rv = NULL; - if (SUCCESS == zend_fcall_info_call(&obj->intern->cb.fci, &obj->intern->cb.fcc, &rv, args TSRMLS_CC)) { + if (SUCCESS != zend_fcall_info_call(&obj->intern->cb.fci, &obj->intern->cb.fcc, &rv, args TSRMLS_CC)) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to trigger event"); + } else { if (rv) { RETVAL_ZVAL(rv, 0, 1); } else { RETVAL_TRUE; } } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Event not initialized"); - RETVAL_FALSE; } } } @@ -3971,21 +4706,32 @@ static PHP_METHOD(pqlob, __construct) { zend_error_handling zeh; zval *ztxn; long mode = INV_WRITE|INV_READ, loid = InvalidOid; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|ll", &ztxn, php_pqtxn_class_entry, &loid, &mode)) { - php_pqtxn_object_t *txn_obj = zend_object_store_get_object(ztxn TSRMLS_CC); + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|ll", &ztxn, php_pqtxn_class_entry, &loid, &mode); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (txn_obj->intern) { + if (SUCCESS == rv) { + php_pqtxn_object_t *txn_obj = zend_object_store_get_object(ztxn TSRMLS_CC); + if (!txn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized"); + } else if (!txn_obj->intern->open) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transation already closed"); + } else { if (loid == InvalidOid) { loid = lo_creat(txn_obj->intern->conn->intern->conn, mode); } - if (loid != InvalidOid) { + if (loid == InvalidOid) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create large object with mode '%s' (%s)", strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn)); + } else { int lofd = lo_open(txn_obj->intern->conn->intern->conn, loid, mode); - if (lofd >= 0) { + if (lofd < 0) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%ld with mode '%s' (%s)", loid, strmode(mode), PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn)); + } else { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); obj->intern = ecalloc(1, sizeof(*obj->intern)); @@ -3993,52 +4739,42 @@ static PHP_METHOD(pqlob, __construct) { obj->intern->loid = loid; php_pq_object_addref(txn_obj TSRMLS_CC); obj->intern->txn = txn_obj; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to open large object with mode '%s' (%s)", - (mode & (INV_READ|INV_WRITE) ? "rw" : - (mode & INV_READ ? "r" : - (mode & INV_WRITE ? "w" : "-"))), - PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn) - ); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to create large object with mode '%s' (%s)", - (mode & (INV_READ|INV_WRITE) ? "rw" : - (mode & INV_READ ? "r" : - (mode & INV_WRITE ? "w" : "-"))), - PHP_PQerrorMessage(txn_obj->intern->conn->intern->conn) - ); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Transaction not initialized"); + + php_pqconn_notify_listeners(txn_obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_write, 0, 0, 1) ZEND_ARG_INFO(0, data) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, write) { + zend_error_handling zeh; char *data_str; int data_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", &data_str, &data_len); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len)) { + if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); + } else { int written = lo_write(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, data_str, data_len); - if (written >= 0) { - RETVAL_LONG(written); + if (written < 0) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to write to LOB with oid=%ld (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to write to LOB, oid=%d (%s)", obj->intern->loid, - PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); - RETVAL_FALSE; + RETVAL_LONG(written); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\LOB not initialized"); - RETVAL_FALSE; + + php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } @@ -4048,33 +4784,37 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_read, 0, 0, 0) ZEND_ARG_INFO(1, read) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, read) { + zend_error_handling zeh; long length = 0x1000; zval *zread = NULL; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lz!", &length, &zread); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lz!", &length, &zread)) { + if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); + } else { char *buffer = emalloc(length + 1); int read = lo_read(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, buffer, length); - if (read >= 0) { + if (read < 0) { + efree(buffer); + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to read from LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); + } else { if (zread) { zval_dtor(zread); ZVAL_LONG(zread, read); } buffer[read] = '\0'; RETVAL_STRINGL(buffer, read, 0); - } else { - efree(buffer); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to read from LOB, oid=%d (%s)", obj->intern->loid, - PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); - RETVAL_FALSE; } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\LOB not initialized"); - RETVAL_FALSE; + php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } @@ -4084,24 +4824,29 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_seek, 0, 0, 1) ZEND_ARG_INFO(0, whence) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, seek) { + zend_error_handling zeh; long offset, whence = SEEK_SET; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &offset, &whence); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &offset, &whence)) { + if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); + } else { int position = lo_lseek(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, offset, whence); - if (position >= 0) { - RETVAL_LONG(position); + if (position < 0) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to seek offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to seek offset in LOB, oid=%d (%s)", obj->intern->loid, - PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); - RETVAL_FALSE; + RETVAL_LONG(position); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\LOB not initialized"); - RETVAL_FALSE; + + php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } @@ -4109,22 +4854,28 @@ static PHP_METHOD(pqlob, seek) { ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_tell, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, tell) { - if (SUCCESS == zend_parse_parameters_none()) { + zend_error_handling zeh; + STATUS rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); + } else { int position = lo_tell(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd); - if (position >= 0) { - RETVAL_LONG(position); + if (position < 0) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to tell offset in LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to tell offset in LOB (oid=%d): %s", obj->intern->loid, - PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); - RETVAL_FALSE; + RETVAL_LONG(position); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\LOB not initialized"); - RETVAL_FALSE; + + php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } @@ -4133,22 +4884,27 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqlob_truncate, 0, 0, 0) ZEND_ARG_INFO(0, length) ZEND_END_ARG_INFO(); static PHP_METHOD(pqlob, truncate) { + zend_error_handling zeh; long length = 0; + STATUS rv; - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &length)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &length); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqlob_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - if (0 == lo_truncate(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, length)) { - RETVAL_TRUE; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to truncate LOB (oid=%d): %s", obj->intern->loid, - PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); - RETVAL_FALSE; - } + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\LOB not initialized"); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\LOB not initialized"); - RETVAL_FALSE; + int rc = lo_truncate(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd, length); + + if (rc != 0) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to truncate LOB with oid=%d (%s)", obj->intern->loid, PHP_PQerrorMessage(obj->intern->txn->intern->conn->intern->conn)); + } + + php_pqconn_notify_listeners(obj->intern->txn->intern->conn TSRMLS_CC); } } } @@ -4175,35 +4931,45 @@ static PHP_METHOD(pqcopy, __construct) { char *expr_str, *opt_str = ""; int expr_len, opt_len = 0; long direction; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osl|s", &zconn, php_pqconn_class_entry, &expr_str, &expr_len, &direction, &opt_str, &opt_len)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Osl|s", &zconn, php_pqconn_class_entry, &expr_str, &expr_len, &direction, &opt_str, &opt_len); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC); - if (conn_obj->intern) { - char *copy = NULL; + if (!conn_obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized"); + } else { + php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + smart_str cmd = {0}; + PGresult *res; + + smart_str_appends(&cmd, "COPY "); + smart_str_appendl(&cmd, expr_str, expr_len); switch (direction) { case PHP_PQCOPY_FROM_STDIN: - spprintf(©, 0, "COPY %s %s %s", expr_str, "FROM STDIN", opt_str); + smart_str_appends(&cmd, " FROM STDIN "); break; - case PHP_PQCOPY_TO_STDOUT: - spprintf(©, 0, "COPY %s %s %s", expr_str, "TO STDOUT", opt_str); + smart_str_appends(&cmd, " TO STDOUT "); break; - default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid COPY direction, expected one of FROM_STDIN (%d) TO_STDOUT (%d), got %ld", - PHP_PQCOPY_FROM_STDIN, PHP_PQCOPY_TO_STDOUT, direction); - break; + throw_exce(EX_RUNTIME TSRMLS_CC, "Invalid COPY direction, expected one of FROM_STDIN (%d) TO_STDOUT (%d), got %ld", PHP_PQCOPY_FROM_STDIN, PHP_PQCOPY_TO_STDOUT, direction); + smart_str_free(&cmd); + return; } + smart_str_appendl(&cmd, opt_str, opt_len); + smart_str_0(&cmd); - if (copy) { - php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - PGresult *res = PQexec(conn_obj->intern->conn, copy); - - efree(copy); + res = PQexec(conn_obj->intern->conn, cmd.c); + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to start %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { obj->intern = ecalloc(1, sizeof(*obj->intern)); obj->intern->direction = direction; @@ -4215,11 +4981,11 @@ static PHP_METHOD(pqcopy, __construct) { PHP_PQclear(res); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\Connection not initialized"); + + smart_str_free(&cmd); + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_put, 0, 0, 1) @@ -4229,26 +4995,26 @@ static PHP_METHOD(pqcopy, put) { zend_error_handling zeh; char *data_str; int data_len; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data_str, &data_len); + zend_restore_error_handling(&zeh TSRMLS_CC); + + if (SUCCESS == rv) { php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (obj->intern) { - if (PHP_PQCOPY_FROM_STDIN == obj->intern->direction) { - if (1 == PQputCopyData(obj->intern->conn->intern->conn, data_str, data_len)) { - RETVAL_TRUE; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\COPY was not initialized with FROM_STDIN"); - } + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not initialized"); + } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\COPY was not initialized with FROM_STDIN"); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\COPY not initialized"); + if (1 != PQputCopyData(obj->intern->conn->intern->conn, data_str, data_len)) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to put COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_end, 0, 0, 0) @@ -4258,36 +5024,36 @@ static PHP_METHOD(pqcopy, end) { zend_error_handling zeh; char *error_str = NULL; int error_len = 0; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &error_str, &error_len)) { - php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &error_str, &error_len); + zend_restore_error_handling(&zeh TSRMLS_CC); - if (obj->intern) { - if (PHP_PQCOPY_FROM_STDIN == obj->intern->direction) { - if (1 == PQputCopyEnd(obj->intern->conn->intern->conn, error_str)) { - PGresult *res = PQgetResult(obj->intern->conn->intern->conn); + if (SUCCESS == rv) { + php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (res) { - if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { - RETVAL_TRUE; - } + if (!obj->intern) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\COPY not intitialized"); + } else if (obj->intern->direction != PHP_PQCOPY_FROM_STDIN) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\COPY was not intitialized with FROM_STDIN"); + } else { + if (1 != PQputCopyEnd(obj->intern->conn->intern->conn, error_str)) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to end COPY (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { + PGresult *res = PQgetResult(obj->intern->conn->intern->conn); - PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to end COPY (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + php_pqres_success(res TSRMLS_CC); + PHP_PQclear(res); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\COPY was not initialized with FROM_STDIN"); } - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "pq\\COPY not initialized"); + + php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC); } } - zend_restore_error_handling(&zeh TSRMLS_CC); } ZEND_BEGIN_ARG_INFO_EX(ai_pqcopy_get, 0, 0, 1) @@ -4296,54 +5062,57 @@ ZEND_END_ARG_INFO(); static PHP_METHOD(pqcopy, get) { zend_error_handling zeh; zval *zdata; + STATUS rv; - zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC); - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata)) { - php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - char *buffer = NULL; - - if (obj->intern) { - if (PHP_PQCOPY_TO_STDOUT == obj->intern->direction) { - PGresult *res; - int bytes = PQgetCopyData(obj->intern->conn->intern->conn, &buffer, 0); + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdata); + zend_restore_error_handling(&zeh TSRMLS_CC); - switch (bytes) { - case -1: - res = PQgetResult(obj->intern->conn->intern->conn); + if (SUCCESS == rv) { + php_pqcopy_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - if (res) { - if (SUCCESS == php_pqres_success(res TSRMLS_CC)) { - RETVAL_FALSE; - } + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\COPY not initialized"); + } else if (obj->intern->direction != PHP_PQCOPY_TO_STDOUT) { + throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\COPY was not intialized with TO_STDOUT"); + } else { + PGresult *res; + char *buffer = NULL; + int bytes = PQgetCopyData(obj->intern->conn->intern->conn, &buffer, 0); - PHP_PQclear(res); - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - } - break; + switch (bytes) { + case -2: + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + break; - case -2: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get COPY data (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); - break; + case -1: + res = PQgetResult(obj->intern->conn->intern->conn); - default: - zval_dtor(zdata); - if (buffer) { - ZVAL_STRINGL(zdata, buffer, bytes, 1); - } else { - ZVAL_EMPTY_STRING(zdata); - } - RETVAL_TRUE; - break; + if (!res) { + throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch COPY result (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn)); + } else { + php_pqres_success(res TSRMLS_CC); + PHP_PQclear(res); + RETVAL_FALSE; } + break; + default: + zval_dtor(zdata); if (buffer) { - PQfreemem(buffer); + ZVAL_STRINGL(zdata, buffer, bytes, 1); + } else { + ZVAL_EMPTY_STRING(zdata); } + RETVAL_TRUE; + break; + } + + if (buffer) { + PQfreemem(buffer); } } } - zend_restore_error_handling(&zeh TSRMLS_CC); } static zend_function_entry php_pqcopy_methods[] = { @@ -4354,6 +5123,10 @@ static zend_function_entry php_pqcopy_methods[] = { {0} }; +static zend_function_entry php_pqexc_methods[] = { + {0} +}; + /* {{{ PHP_MINIT_FUNCTION */ static PHP_MINIT_FUNCTION(pq) @@ -4361,6 +5134,41 @@ static PHP_MINIT_FUNCTION(pq) zend_class_entry ce = {0}; php_pq_object_prophandler_t ph = {0}; + INIT_NS_CLASS_ENTRY(ce, "pq", "Exception", php_pqexc_methods); + php_pqexc_interface_class_entry = zend_register_internal_interface(&ce TSRMLS_CC); + + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("INVALID_ARGUMENT"), EX_INVALID_ARGUMENT TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("RUNTIME"), EX_RUNTIME TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("CONNECTION_FAILED"), EX_CONNECTION_FAILED TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("IO"), EX_IO TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("ESCAPE"), EX_ESCAPE TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("BAD_METHODCALL"), EX_BAD_METHODCALL TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("UNINITIALIZED"), EX_UNINITIALIZED TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("DOMAIN"), EX_DOMAIN TSRMLS_CC); + zend_declare_class_constant_long(php_pqexc_interface_class_entry, ZEND_STRL("SQL"), EX_SQL TSRMLS_CC); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "InvalidArgumentException", php_pqexc_methods); + php_pqexc_invalid_argument_class_entry = zend_register_internal_class_ex(&ce, spl_ce_InvalidArgumentException, "InvalidArgumentException" TSRMLS_CC); + zend_class_implements(php_pqexc_invalid_argument_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "RuntimeException", php_pqexc_methods); + php_pqexc_runtime_class_entry = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException, "RuntimeException" TSRMLS_CC); + zend_class_implements(php_pqexc_runtime_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "BadMethodCallException", php_pqexc_methods); + php_pqexc_bad_methodcall_class_entry = zend_register_internal_class_ex(&ce, spl_ce_BadMethodCallException, "BadMethodCallException" TSRMLS_CC); + zend_class_implements(php_pqexc_bad_methodcall_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); + + memset(&ce, 0, sizeof(ce)); + INIT_NS_CLASS_ENTRY(ce, "pq\\Exception", "DomainException", php_pqexc_methods); + php_pqexc_domain_class_entry = zend_register_internal_class_ex(&ce, spl_ce_DomainException, "DomainException" TSRMLS_CC); + zend_class_implements(php_pqexc_domain_class_entry TSRMLS_CC, 1, php_pqexc_interface_class_entry); + zend_declare_property_null(php_pqexc_domain_class_entry, ZEND_STRL("sqlstate"), ZEND_ACC_PUBLIC TSRMLS_CC); + + memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce, "pq", "Connection", php_pqconn_methods); php_pqconn_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); php_pqconn_class_entry->create_object = php_pqconn_create_object; @@ -4372,7 +5180,7 @@ static PHP_MINIT_FUNCTION(pq) php_pqconn_object_handlers.get_property_ptr_ptr = NULL; php_pqconn_object_handlers.get_debug_info = php_pq_object_debug_info; - zend_hash_init(&php_pqconn_object_prophandlers, 13, NULL, NULL, 1); + zend_hash_init(&php_pqconn_object_prophandlers, 14, NULL, NULL, 1); zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("status"), CONNECTION_BAD, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqconn_object_read_status; @@ -4430,6 +5238,10 @@ static PHP_MINIT_FUNCTION(pq) ph.read = php_pqconn_object_read_options; zend_hash_add(&php_pqconn_object_prophandlers, "options", sizeof("options"), (void *) &ph, sizeof(ph), NULL); + zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("eventHandlers"), ZEND_ACC_PUBLIC TSRMLS_CC); + 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_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); @@ -4450,6 +5262,8 @@ static PHP_MINIT_FUNCTION(pq) zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_WRITING"), PGRES_POLLING_WRITING TSRMLS_CC); zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_OK"), PGRES_POLLING_OK TSRMLS_CC); + zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("ASYNC"), 0x1 TSRMLS_CC); + zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("PERSISTENT"), 0x2 TSRMLS_CC); memset(&ce, 0, sizeof(ce)); INIT_NS_CLASS_ENTRY(ce, "pq", "Types", php_pqtypes_methods); php_pqtypes_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC); @@ -4494,6 +5308,10 @@ static PHP_MINIT_FUNCTION(pq) ph.read = php_pqres_object_read_status; zend_hash_add(&php_pqres_object_prophandlers, "status", sizeof("status"), (void *) &ph, sizeof(ph), NULL); + zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("statusMessage"), ZEND_ACC_PUBLIC TSRMLS_CC); + ph.read = php_pqres_object_read_status_message; + zend_hash_add(&php_pqres_object_prophandlers, "statusMessage", sizeof("statusMessage"), (void *) &ph, sizeof(ph), NULL); + zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("errorMessage"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_error_message; zend_hash_add(&php_pqres_object_prophandlers, "errorMessage", sizeof("errorMessage"), (void *) &ph, sizeof(ph), NULL); @@ -4576,19 +5394,19 @@ static PHP_MINIT_FUNCTION(pq) ph.write = php_pqtxn_object_write_isolation; zend_hash_add(&php_pqtxn_object_prophandlers, "isolation", sizeof("isolation"), (void *) &ph, sizeof(ph), NULL); - zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("readonly"), ZEND_ACC_PUBLIC TSRMLS_CC); + zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("readonly"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqtxn_object_read_readonly; ph.write = php_pqtxn_object_write_readonly; zend_hash_add(&php_pqtxn_object_prophandlers, "readonly", sizeof("readonly"), (void *) &ph, sizeof(ph), NULL); - zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("deferrable"), ZEND_ACC_PUBLIC TSRMLS_CC); + zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("deferrable"), 0, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqtxn_object_read_deferrable; ph.write = php_pqtxn_object_write_deferrable; zend_hash_add(&php_pqtxn_object_prophandlers, "deferrable", sizeof("deferrable"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("READ_COMMITTED"), PHP_PQTXN_READ_COMMITTED TSRMLS_CC); - zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("REPEATABLE READ"), PHP_PQTXN_REPEATABLE_READ TSRMLS_CC); + zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("REPEATABLE_READ"), PHP_PQTXN_REPEATABLE_READ TSRMLS_CC); zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("SERIALIZABLE"), PHP_PQTXN_SERIALIZABLE TSRMLS_CC); memset(&ce, 0, sizeof(ce)); @@ -4621,11 +5439,7 @@ static PHP_MINIT_FUNCTION(pq) php_pqevent_object_handlers.get_property_ptr_ptr = NULL; php_pqevent_object_handlers.get_debug_info = php_pq_object_debug_info; - zend_hash_init(&php_pqevent_object_prophandlers, 2, NULL, NULL, 1); - - zend_declare_property_null(php_pqevent_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC); - ph.read = php_pqevent_object_read_connection; - zend_hash_add(&php_pqevent_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL); + zend_hash_init(&php_pqevent_object_prophandlers, 1, NULL, NULL, 1); zend_declare_property_null(php_pqevent_class_entry, ZEND_STRL("type"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqevent_object_read_type; @@ -4695,6 +5509,8 @@ static PHP_MINIT_FUNCTION(pq) zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("FROM_STDIN"), PHP_PQCOPY_FROM_STDIN TSRMLS_CC); zend_declare_class_constant_long(php_pqcopy_class_entry, ZEND_STRL("TO_STDOUT"), PHP_PQCOPY_TO_STDOUT TSRMLS_CC); + php_persistent_handle_provide(ZEND_STRL("pq\\Connection"), &php_pqconn_resource_factory_ops, NULL, NULL TSRMLS_CC); + /* REGISTER_INI_ENTRIES(); */ @@ -4709,6 +5525,7 @@ static PHP_MSHUTDOWN_FUNCTION(pq) /* uncomment this line if you have INI entries UNREGISTER_INI_ENTRIES(); */ + php_persistent_handle_cleanup(ZEND_STRL("pq\\Connection"), NULL, 0 TSRMLS_CC); return SUCCESS; } /* }}} */ @@ -4717,7 +5534,9 @@ static PHP_MSHUTDOWN_FUNCTION(pq) */ static PHP_MINFO_FUNCTION(pq) { +#ifdef HAVE_PQLIBVERSION int libpq_v; +#endif char libpq_version[10] = "pre-9.1"; php_info_print_table_start(); @@ -4744,10 +5563,18 @@ const zend_function_entry pq_functions[] = { {0} }; +static zend_module_dep pq_module_deps[] = { + ZEND_MOD_REQUIRED("raphf") + ZEND_MOD_REQUIRED("spl") + ZEND_MOD_END +}; + /* {{{ pq_module_entry */ zend_module_entry pq_module_entry = { - STANDARD_MODULE_HEADER, + STANDARD_MODULE_HEADER_EX, + NULL, + pq_module_deps, "pq", pq_functions, PHP_MINIT(pq),