Add deallocate() and prepare() to Statement
[m6w6/ext-pq] / src / php_pqconn.c
index daf2c7947b92821ac0560a3e1b7ae3fdc77fcbe1..f2622d5184b73613019f15bca43f5b56a83c86f9 100644 (file)
@@ -301,6 +301,27 @@ static void php_pqconn_object_read_port(zval *object, void *o, zval *return_valu
        }
 }
 
+#if HAVE_PQCONNINFO
+static void php_pqconn_object_read_params(zval *object, void *o, zval *return_value TSRMLS_DC)
+{
+       php_pqconn_object_t *obj = o;
+       PQconninfoOption *ptr, *params = PQconninfo(obj->intern->conn);
+
+       array_init(return_value);
+
+       if (params) {
+               for (ptr = params; ptr->keyword; ++ptr) {
+                       if (ptr->val) {
+                               add_assoc_string(return_value, ptr->keyword, ptr->val, 1);
+                       } else {
+                               add_assoc_null(return_value, ptr->keyword);
+                       }
+               }
+               PQconninfoFree(params);
+       }
+}
+#endif
+
 static void php_pqconn_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC)
 {
        php_pqconn_object_t *obj = o;
@@ -573,7 +594,7 @@ static inline PGresult *unlisten(PGconn *conn, const char *channel_str, size_t c
 static int apply_unlisten(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
 {
        php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *);
-       PGresult *res = unlisten(obj->intern->conn, key->arKey, key->nKeyLength - 1);
+       PGresult *res = unlisten(obj->intern->conn, key->arKey, key->nKeyLength - 1 TSRMLS_CC);
 
        if (res) {
                PHP_PQclear(res);
@@ -758,7 +779,7 @@ static PHP_METHOD(pqconn, unlisten)
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else if (SUCCESS == zend_hash_del(&obj->intern->listeners, channel_str, channel_len + 1)) {
-                       PGresult *res = unlisten(obj->intern->conn, channel_str, channel_len);
+                       PGresult *res = unlisten(obj->intern->conn, channel_str, channel_len TSRMLS_CC);
 
                        if (res) {
                                php_pqres_success(res TSRMLS_CC);
@@ -1112,8 +1133,10 @@ static PHP_METHOD(pqconn, execAsync) {
                        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));
+#if HAVE_PQSETSINGLEROWMODE
                } 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));
+#endif
                } else {
                        php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC);
                        obj->intern->poller = PQconsumeInput;
@@ -1201,8 +1224,10 @@ static PHP_METHOD(pqconn, execParamsAsync) {
 
                        if (!rc) {
                                throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
+#if HAVE_PQSETSINGLEROWMODE
                        } 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));
+#endif
                        } else {
                                php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC);
                                obj->intern->poller = PQconsumeInput;
@@ -1263,13 +1288,7 @@ static PHP_METHOD(pqconn, prepare) {
                        if (SUCCESS != php_pqconn_prepare(getThis(), obj, name_str, query_str, params TSRMLS_CC)) {
                                php_pq_params_free(&params);
                        } else {
-                               php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
-
-                               php_pq_object_addref(obj TSRMLS_CC);
-                               stm->conn = obj;
-                               stm->name = estrdup(name_str);
-                               stm->params = params;
-                               ZEND_INIT_SYMTABLE(&stm->bound);
+                               php_pqstm_t *stm = php_pqstm_init(obj, name_str, query_str, params TSRMLS_CC);
 
                                return_value->type = IS_OBJECT;
                                return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
@@ -1289,9 +1308,6 @@ STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const ch
        if (!PQsendPrepare(obj->intern->conn, name, query, params->type.count, params->type.oids)) {
                rv = FAILURE;
                throw_exce(EX_IO TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn));
-       } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
-               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;
@@ -1328,13 +1344,7 @@ static PHP_METHOD(pqconn, prepareAsync) {
                        if (SUCCESS != php_pqconn_prepare_async(getThis(), obj, name_str, query_str, params TSRMLS_CC)) {
                                php_pq_params_free(&params);
                        } else {
-                               php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
-
-                               php_pq_object_addref(obj TSRMLS_CC);
-                               stm->conn = obj;
-                               stm->name = estrdup(name_str);
-                               stm->params = params;
-                               ZEND_INIT_SYMTABLE(&stm->bound);
+                               php_pqstm_t *stm = php_pqstm_init(obj, name_str, query_str, params TSRMLS_CC);
 
                                return_value->type = IS_OBJECT;
                                return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
@@ -1343,24 +1353,6 @@ static PHP_METHOD(pqconn, prepareAsync) {
        }
 }
 
-static inline char *declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len)
-{
-       size_t decl_len = name_len + query_len + sizeof("DECLARE BINARY INSENSITIVE NO SCROLL  CURSOR WITHOUT HOLD FOR ");
-       char *decl_str;
-
-       decl_str = emalloc(decl_len);
-       decl_len = slprintf(decl_str, decl_len, "DECLARE %s %s %s %s CURSOR %s FOR %s",
-                       name_str,
-                       (flags & PHP_PQ_DECLARE_BINARY) ? "BINARY" : "",
-                       (flags & PHP_PQ_DECLARE_INSENSITIVE) ? "INSENSITIVE" : "",
-                       (flags & PHP_PQ_DECLARE_NO_SCROLL) ? "NO SCROLL" :
-                                       (flags & PHP_PQ_DECLARE_SCROLL) ? "SCROLL" : "",
-                       (flags & PHP_PQ_DECLARE_WITH_HOLD) ? "WITH HOLD" : "",
-                       query_str
-       );
-       return decl_str;
-}
-
 STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
 {
        PGresult *res;
@@ -1406,7 +1398,7 @@ static PHP_METHOD(pqconn, declare) {
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
-                       char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
+                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len);
 
                        if (SUCCESS != php_pqconn_declare(getThis(), obj, decl TSRMLS_CC)) {
                                efree(decl);
@@ -1437,9 +1429,6 @@ STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const ch
        if (!PQsendQuery(obj->intern->conn, decl)) {
                rv = FAILURE;
                throw_exce(EX_IO TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn));
-       } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
-               rv = FAILURE;
-               throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
        } else {
                rv = SUCCESS;
                obj->intern->poller = PQconsumeInput;
@@ -1471,7 +1460,7 @@ static PHP_METHOD(pqconn, declareAsync) {
                if (!obj->intern) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
-                       char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
+                       char *decl = php_pqcur_declare_str(name_str, name_len, flags, query_str, query_len);
 
                        if (SUCCESS != php_pqconn_declare_async(getThis(), obj, decl TSRMLS_CC)) {
                                efree(decl);
@@ -1610,7 +1599,7 @@ STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj,
        } else {
                PGresult *res;
                smart_str cmd = {0};
-               const char *il = isolation_level(&isolation);
+               const char *il = php_pq_isolation_level(&isolation);
 
                smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL ");
                smart_str_appends(&cmd, il);
@@ -1649,7 +1638,7 @@ STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn
                throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
        } else {
                smart_str cmd = {0};
-               const char *il = isolation_level(&isolation);
+               const char *il = php_pq_isolation_level(&isolation);
 
                smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL ");
                smart_str_appends(&cmd, il);
@@ -1735,6 +1724,7 @@ static PHP_METHOD(pqconn, startTransactionAsync) {
 
                        php_pq_object_addref(obj TSRMLS_CC);
                        txn->conn = obj;
+                       txn->open = 1;
                        txn->isolation = isolation;
                        txn->readonly = readonly;
                        txn->deferrable = deferrable;
@@ -1830,20 +1820,25 @@ static PHP_METHOD(pqconn, on) {
        }
 }
 
-static int apply_set_converter(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
+struct apply_set_converter_arg {
+       HashTable *ht;
+       zval **zconv;
+       unsigned add:1;
+};
+
+static int apply_set_converter(void *p, void *a TSRMLS_DC)
 {
-       zval *tmp, **zoid = p, **zcnv = va_arg(argv, zval **);
-       HashTable *converters = va_arg(argv, HashTable *);
-       int add = va_arg(argv, int);
+       zval *tmp, **zoid = p;
+       struct apply_set_converter_arg *arg = a;
 
        tmp = *zoid;
        Z_ADDREF_P(tmp);
        convert_to_long_ex(&tmp);
-       if (add) {
-               Z_ADDREF_PP(zcnv);
-               zend_hash_index_update(converters, Z_LVAL_P(tmp), zcnv, sizeof(zval *), NULL);
+       if (arg->add) {
+               Z_ADDREF_PP(arg->zconv);
+               zend_hash_index_update(arg->ht, Z_LVAL_P(tmp), arg->zconv, sizeof(zval *), NULL);
        } else {
-               zend_hash_index_del(converters, Z_LVAL_P(tmp));
+               zend_hash_index_del(arg->ht, Z_LVAL_P(tmp));
        }
        zval_ptr_dtor(&tmp);
 
@@ -1869,12 +1864,19 @@ static PHP_METHOD(pqconn, setConverter) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
                        zval *tmp, *zoids = NULL;
+                       struct apply_set_converter_arg arg = {NULL};
 
                        zend_call_method_with_0_params(&zcnv, NULL, NULL, "converttypes", &zoids);
                        tmp = zoids;
                        Z_ADDREF_P(tmp);
                        convert_to_array_ex(&tmp);
-                       zend_hash_apply_with_arguments(Z_ARRVAL_P(tmp) TSRMLS_CC, apply_set_converter, 3, &zcnv, &obj->intern->converters, 1);
+
+                       arg.ht = &obj->intern->converters;
+                       arg.zconv = &zcnv;
+                       arg.add = 1;
+
+                       zend_hash_apply_with_argument(Z_ARRVAL_P(tmp), apply_set_converter, &arg TSRMLS_CC);
+
                        zval_ptr_dtor(&tmp);
                        zval_ptr_dtor(&zoids);
                }
@@ -1900,12 +1902,19 @@ static PHP_METHOD(pqconn, unsetConverter) {
                        throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
                } else {
                        zval *tmp, *zoids = NULL;
+                       struct apply_set_converter_arg arg = {NULL};
 
                        zend_call_method_with_0_params(&zcnv, NULL, NULL, "converttypes", &zoids);
                        tmp = zoids;
                        Z_ADDREF_P(tmp);
                        convert_to_array_ex(&tmp);
-                       zend_hash_apply_with_arguments(Z_ARRVAL_P(tmp) TSRMLS_CC, apply_set_converter, 3, &zcnv, &obj->intern->converters, 0);
+
+                       arg.ht = &obj->intern->converters;
+                       arg.zconv = &zcnv;
+                       arg.add = 0;
+
+                       zend_hash_apply_with_argument(Z_ARRVAL_P(tmp), apply_set_converter, &arg TSRMLS_CC);
+
                        zval_ptr_dtor(&tmp);
                        zval_ptr_dtor(&zoids);
                }
@@ -1970,7 +1979,7 @@ PHP_MINIT_FUNCTION(pqconn)
        php_pqconn_object_handlers.get_properties = php_pq_object_properties;
        php_pqconn_object_handlers.get_debug_info = php_pq_object_debug_info;
 
-       zend_hash_init(&php_pqconn_object_prophandlers, 14, NULL, NULL, 1);
+       zend_hash_init(&php_pqconn_object_prophandlers, 20, 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;
@@ -2024,6 +2033,12 @@ PHP_MINIT_FUNCTION(pqconn)
        ph.read = php_pqconn_object_read_port;
        zend_hash_add(&php_pqconn_object_prophandlers, "port", sizeof("port"), (void *) &ph, sizeof(ph), NULL);
 
+#if HAVE_PQCONNINFO
+       zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("params"), ZEND_ACC_PUBLIC TSRMLS_CC);
+       ph.read = php_pqconn_object_read_params;
+       zend_hash_add(&php_pqconn_object_prophandlers, "params", sizeof("params"), (void *) &ph, sizeof(ph), NULL);
+#endif
+
        zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("options"), ZEND_ACC_PUBLIC TSRMLS_CC);
        ph.read = php_pqconn_object_read_options;
        zend_hash_add(&php_pqconn_object_prophandlers, "options", sizeof("options"), (void *) &ph, sizeof(ph), NULL);