better checks for json support
[m6w6/ext-pq] / src / php_pqconn.c
index 4378404eb98ba3a9fcd6d8788f672473ff5bc7ba..dd08ced5170743dddc3d608dccf68f9f949887a6 100644 (file)
@@ -219,7 +219,7 @@ static void php_pqconn_object_write_encoding(zval *object, void *o, zval *value
        }
 
        if (0 > PQsetClientEncoding(obj->intern->conn, Z_STRVAL_P(zenc))) {
-               zend_error(E_NOTICE, "Unrecognized encoding '%s'", Z_STRVAL_P(zenc));
+               php_error(E_NOTICE, "Unrecognized encoding '%s'", Z_STRVAL_P(zenc));
        }
 
        if (zenc != value) {
@@ -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;
@@ -372,7 +393,7 @@ static void php_pqconn_object_write_def_fetch_type(zval *object, void *o, zval *
                }
        }
 
-       obj->intern->default_fetch_type = Z_LVAL_P(zft) & 0x2; /* two bits only */
+       obj->intern->default_fetch_type = Z_LVAL_P(zft) & 0x3; /* two bits only */
 
        if (zft != value) {
                zval_ptr_dtor(&zft);
@@ -402,7 +423,7 @@ static void php_pqconn_object_write_def_txn_isolation(zval *object, void *o, zva
                }
        }
 
-       obj->intern->default_txn_isolation = Z_LVAL_P(zti) & 0x2; /* two bits only */
+       obj->intern->default_txn_isolation = Z_LVAL_P(zti) & 0x3; /* two bits only */
 
        if (zti != value) {
                zval_ptr_dtor(&zti);
@@ -435,6 +456,36 @@ static void php_pqconn_object_write_def_txn_deferrable(zval *object, void *o, zv
        obj->intern->default_txn_deferrable = zend_is_true(value);
 }
 
+static void php_pqconn_object_read_def_auto_conv(zval *object, void *o, zval *return_value TSRMLS_DC)
+{
+       php_pqconn_object_t *obj = o;
+
+       RETVAL_LONG(obj->intern->default_auto_convert);
+}
+static void php_pqconn_object_write_def_auto_conv(zval*object, void *o, zval *value TSRMLS_DC)
+{
+       php_pqconn_object_t *obj = o;
+       zval *zac = value;
+
+       if (Z_TYPE_P(zac) != IS_LONG) {
+               if (Z_REFCOUNT_P(zac) > 1) {
+                       zval *tmp;
+                       MAKE_STD_ZVAL(tmp);
+                       ZVAL_ZVAL(tmp, zac, 1, 0);
+                       convert_to_long(tmp);
+                       zac = tmp;
+               } else {
+                       convert_to_long_ex(&zac);
+               }
+       }
+
+       obj->intern->default_auto_convert = Z_LVAL_P(zac) & PHP_PQRES_CONV_ALL;
+
+       if (zac != value) {
+               zval_ptr_dtor(&zac);
+       }
+}
+
 static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
 {
        zval *zsocket, zmember;
@@ -511,7 +562,12 @@ php_resource_factory_ops_t *php_pqconn_get_resource_factory_ops(void)
 
 static void php_pqconn_wakeup(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC)
 {
-       // FIXME: ping server
+       PGresult *res = PQexec(*handle, "");
+       PHP_PQclear(res);
+
+       if (CONNECTION_OK != PQstatus(*handle)) {
+               PQreset(*handle);
+       }
 }
 
 static inline PGresult *unlisten(PGconn *conn, const char *channel_str, size_t channel_len TSRMLS_DC)
@@ -538,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);
@@ -590,7 +646,6 @@ static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle
                zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj);
 
                /* release instance data */
-               //memset(evdata, 0, sizeof(*evdata));
                efree(evdata);
        }
 }
@@ -621,6 +676,8 @@ static PHP_METHOD(pqconn, __construct) {
 
                        obj->intern = ecalloc(1, sizeof(*obj->intern));
 
+                       obj->intern->default_auto_convert = PHP_PQRES_CONV_ALL;
+
                        zend_hash_init(&obj->intern->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
                        zend_hash_init(&obj->intern->converters, 0, NULL, ZVAL_PTR_DTOR, 0);
                        zend_hash_init(&obj->intern->eventhandlers, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
@@ -722,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);
@@ -1076,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;
@@ -1165,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;
@@ -1253,9 +1314,11 @@ 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));
+#if HAVE_PQSETSINGLEROWMODE
        } 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));
+#endif
        } else {
                rv = SUCCESS;
                obj->intern->poller = PQconsumeInput;
@@ -1307,24 +1370,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;
@@ -1370,7 +1415,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);
@@ -1401,9 +1446,11 @@ 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));
+#if HAVE_PQSETSINGLEROWMODE
        } 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));
+#endif
        } else {
                rv = SUCCESS;
                obj->intern->poller = PQconsumeInput;
@@ -1435,7 +1482,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);
@@ -1794,20 +1841,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);
 
@@ -1815,7 +1867,7 @@ static int apply_set_converter(void *p TSRMLS_DC, int argc, va_list argv, zend_h
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_set_converter, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, converter, pq\\ConverterInterface, 0)
+       ZEND_ARG_OBJ_INFO(0, converter, pq\\Converter, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(pqconn, setConverter) {
        STATUS rv;
@@ -1833,12 +1885,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);
                }
@@ -1846,7 +1905,7 @@ static PHP_METHOD(pqconn, setConverter) {
 }
 
 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unset_converter, 0, 0, 1)
-       ZEND_ARG_OBJ_INFO(0, converter, pq\\ConverterInterface, 0)
+       ZEND_ARG_OBJ_INFO(0, converter, pq\\Converter, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(pqconn, unsetConverter) {
        STATUS rv;
@@ -1864,12 +1923,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);
                }
@@ -1934,7 +2000,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;
@@ -1988,6 +2054,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);
@@ -2020,6 +2092,12 @@ PHP_MINIT_FUNCTION(pqconn)
        zend_hash_add(&php_pqconn_object_prophandlers, "defaultTransactionDeferrable", sizeof("defaultTransactionDeferrable"), (void *) &ph, sizeof(ph), NULL);
        ph.write = NULL;
 
+       zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("defaultAutoConvert"), PHP_PQRES_CONV_ALL, ZEND_ACC_PUBLIC TSRMLS_CC);
+       ph.read = php_pqconn_object_read_def_auto_conv;
+       ph.write = php_pqconn_object_write_def_auto_conv;
+       zend_hash_add(&php_pqconn_object_prophandlers, "defaultAutoConvert", sizeof("defaultAutoConvert"), (void *) &ph, sizeof(ph), NULL);
+       ph.write = NULL;
+
        zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("OK"), CONNECTION_OK TSRMLS_CC);
        zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("BAD"), CONNECTION_BAD TSRMLS_CC);
        zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("STARTED"), CONNECTION_STARTED TSRMLS_CC);