X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-pq;a=blobdiff_plain;f=src%2Fphp_pqres.c;h=1ad5f1dd681aa7e636bc276b5a8a61c8dee04b67;hp=8233d6623b718694eb2d900aa277506f46a858a3;hb=7d954192543192f2ef9b0db27f22b7807de31749;hpb=359b3471d6ed843d8ecf9d5fb0b85f06af446800 diff --git a/src/php_pqres.c b/src/php_pqres.c index 8233d66..1ad5f1d 100644 --- a/src/php_pqres.c +++ b/src/php_pqres.c @@ -17,6 +17,10 @@ #include #include +#if PHP_PQ_HAVE_PHP_JSON_H +#include /* we've added the include directory to INCLUDES */ +#endif + #include #include "php_pq.h" @@ -78,7 +82,9 @@ static STATUS php_pqres_iterator_valid(zend_object_iterator *i TSRMLS_DC) switch (PQresultStatus(obj->intern->res)) { case PGRES_TUPLES_OK: +#ifdef HAVE_PGRES_SINGLE_TUPLE case PGRES_SINGLE_TUPLE: +#endif if (PQntuples(obj->intern->res) <= iter->index) { return FAILURE; } @@ -97,10 +103,13 @@ zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRM MAKE_STD_ZVAL(zv); if (SUCCESS == zend_hash_index_find(&res->converters, typ, (void *) &zconv)) { - zval *tmp = NULL; + zval *ztype, *tmp = NULL; + MAKE_STD_ZVAL(ztype); + ZVAL_LONG(ztype, typ); ZVAL_STRINGL(zv, val, len, 1); - zend_call_method_with_1_params(zconv, NULL, NULL, "convertfromstring", &tmp, zv); + zend_call_method_with_2_params(zconv, NULL, NULL, "convertfromstring", &tmp, zv, ztype); + zval_ptr_dtor(&ztype); if (tmp) { zval_ptr_dtor(&zv); @@ -117,10 +126,9 @@ zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRM } ZVAL_BOOL(zv, *val == 't'); break; -#if SIZEOF_LONG >= 8 + case PHP_PQ_OID_INT8: case PHP_PQ_OID_TID: -#endif case PHP_PQ_OID_INT4: case PHP_PQ_OID_INT2: case PHP_PQ_OID_XID: @@ -128,7 +136,21 @@ zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRM if (!(res->auto_convert & PHP_PQRES_CONV_INT)) { goto noconversion; } - ZVAL_LONG(zv, zend_atol(val, len)); + { + long lval; + double dval; + + switch (is_numeric_string(val, len, &lval, &dval, 0)) { + case IS_LONG: + ZVAL_LONG(zv, lval); + break; + case IS_DOUBLE: + ZVAL_DOUBLE(zv, dval); + break; + default: + goto noconversion; + } + } break; case PHP_PQ_OID_FLOAT4: @@ -167,6 +189,18 @@ zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRM php_pqdt_from_string(val, len, "Y-m-d H:i:s.uO", zv TSRMLS_CC); break; +#if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON) +# ifdef PHP_PQ_OID_JSONB + case PHP_PQ_OID_JSONB: +# endif + case PHP_PQ_OID_JSON: + if (!(res->auto_convert & PHP_PQRES_CONV_JSON)) { + goto noconversion; + } + php_json_decode_ex(zv, val, len, 0, 512 /* PHP_JSON_DEFAULT_DEPTH */ TSRMLS_CC); + break; +#endif + default: if (!(res->auto_convert & PHP_PQRES_CONV_ARRAY)) { goto noconversion; @@ -183,6 +217,58 @@ zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRM return zv; } +static inline zval *php_pqres_get_col(php_pqres_t *r, unsigned row, unsigned col TSRMLS_DC) +{ + zval *zv; + + if (PQgetisnull(r->res, row, col)) { + MAKE_STD_ZVAL(zv); + ZVAL_NULL(zv); + } else { + zv = php_pqres_typed_zval(r, PQgetvalue(r->res, row, col), PQgetlength(r->res, row, col), PQftype(r->res, col) TSRMLS_CC); + } + + return zv; +} + +static inline void php_pqres_add_col_to_zval(php_pqres_t *r, unsigned row, unsigned col, php_pqres_fetch_t fetch_type, zval *data TSRMLS_DC) +{ + if (PQgetisnull(r->res, row, col)) { + switch (fetch_type) { + case PHP_PQRES_FETCH_OBJECT: + add_property_null(data, PQfname(r->res, col)); + break; + + case PHP_PQRES_FETCH_ASSOC: + add_assoc_null(data, PQfname(r->res, col)); + break; + + case PHP_PQRES_FETCH_ARRAY: + add_index_null(data, col); + break; + } + } else { + zval *zv; + + zv = php_pqres_typed_zval(r, PQgetvalue(r->res, row, col), PQgetlength(r->res, row, col), PQftype(r->res, col) TSRMLS_CC); + + switch (fetch_type) { + case PHP_PQRES_FETCH_OBJECT: + add_property_zval(data, PQfname(r->res, col), zv); + zval_ptr_dtor(&zv); + break; + + case PHP_PQRES_FETCH_ASSOC: + add_assoc_zval(data, PQfname(r->res, col), zv); + break; + + case PHP_PQRES_FETCH_ARRAY: + add_index_zval(data, col, zv); + break; + } + } +} + zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type, zval **data_ptr TSRMLS_DC) { zval *data = NULL; @@ -206,40 +292,7 @@ zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch if (PQntuples(res) > row) { for (c = 0, cols = PQnfields(res); c < cols; ++c) { - if (PQgetisnull(res, row, c)) { - switch (fetch_type) { - case PHP_PQRES_FETCH_OBJECT: - add_property_null(data, PQfname(res, c)); - break; - - case PHP_PQRES_FETCH_ASSOC: - add_assoc_null(data, PQfname(res, c)); - break; - - case PHP_PQRES_FETCH_ARRAY: - add_index_null(data, c); - break; - } - } else { - zval *zv; - - zv = php_pqres_typed_zval(res_obj->intern, PQgetvalue(res, row, c), PQgetlength(res, row, c), PQftype(res, c) TSRMLS_CC); - - switch (fetch_type) { - case PHP_PQRES_FETCH_OBJECT: - add_property_zval(data, PQfname(res, c), zv); - zval_ptr_dtor(&zv); - break; - - case PHP_PQRES_FETCH_ASSOC: - add_assoc_zval(data, PQfname(res, c), zv); - break; - - case PHP_PQRES_FETCH_ARRAY: - add_index_zval(data, c, zv); - break; - } - } + php_pqres_add_col_to_zval(res_obj->intern, row, c, fetch_type, data TSRMLS_CC); } } @@ -574,20 +627,28 @@ static STATUS column_nn(php_pqres_object_t *obj, zval *zcol, php_pqres_col_t *co long index = -1; char *name = NULL; - switch (Z_TYPE_P(zcol)) { - case IS_LONG: - index = Z_LVAL_P(zcol); - break; - - 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); + if (!zcol) { + index = 0; + } else { + switch (Z_TYPE_P(zcol)) { + case IS_NULL: + index = 0; + break; + + case IS_LONG: + index = Z_LVAL_P(zcol); + break; + + 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; } - break; } if (name) { @@ -615,8 +676,14 @@ ZEND_BEGIN_ARG_INFO_EX(ai_pqres_bind, 0, 0, 2) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, bind) { zval *zcol, *zref; + zend_error_handling zeh; + STATUS rv; - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref)) { + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref); + 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) { @@ -633,7 +700,7 @@ static PHP_METHOD(pqres, bind) { 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); + zend_hash_sort(&obj->intern->bound, zend_qsort, php_pq_compare_index, 0 TSRMLS_CC); RETVAL_TRUE; } } @@ -748,17 +815,17 @@ static zval **column_at(zval *row, int col TSRMLS_DC) return data; } -ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_col, 0, 0, 2) - ZEND_ARG_INFO(0, col) +ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_col, 0, 0, 1) ZEND_ARG_INFO(1, ref) + ZEND_ARG_INFO(0, col) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, fetchCol) { zend_error_handling zeh; - zval *zcol, *zref; + zval *zcol = NULL, *zref; 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", &zcol, &zref); + rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z/!", &zref, &zcol); zend_restore_error_handling(&zeh TSRMLS_CC); if (SUCCESS == rv) { @@ -771,9 +838,7 @@ static PHP_METHOD(pqres, fetchCol) { 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) { - RETVAL_FALSE; - } else { + if (row) { php_pqres_col_t col; if (SUCCESS != column_nn(obj, zcol, &col TSRMLS_CC)) { @@ -795,37 +860,75 @@ static PHP_METHOD(pqres, fetchCol) { } } -static int apply_to_col(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) +ZEND_BEGIN_ARG_INFO_EX(ai_pqres_fetch_all_cols, 0, 0, 0) + ZEND_ARG_INFO(0, col) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqres, fetchAllCols) { + zend_error_handling zeh; + zval *zcol = 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, "|z!", &zcol); + 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 { + php_pqres_col_t col; + + zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); + if (SUCCESS == column_nn(obj, zcol, &col TSRMLS_CC)) { + int r, rows = PQntuples(obj->intern->res); + + array_init(return_value); + for (r = 0; r < rows; ++r) { + add_next_index_zval(return_value, php_pqres_get_col(obj->intern, r, col.num TSRMLS_CC)); + } + } + zend_restore_error_handling(&zeh TSRMLS_CC); + } + } +} + +struct apply_to_col_arg { + php_pqres_object_t *obj; + php_pqres_col_t *cols; + STATUS status; +}; + +static int apply_to_col(void *p, void *a TSRMLS_DC) { 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 *); - - col = *cols; + struct apply_to_col_arg *arg = a; - if (SUCCESS != column_nn(obj, *c, col TSRMLS_CC)) { - *rv = FAILURE; + if (SUCCESS != column_nn(arg->obj, *c, arg->cols TSRMLS_CC)) { + arg->status = FAILURE; return ZEND_HASH_APPLY_STOP; } else { - *rv = SUCCESS; - ++*cols; + arg->status = SUCCESS; + ++arg->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) { - php_pqres_col_t *tmp, *cols = ecalloc(zend_hash_num_elements(ht), sizeof(*cols)); - STATUS rv = SUCCESS; + struct apply_to_col_arg arg = {NULL}; + php_pqres_col_t *tmp; - tmp = cols; - zend_hash_apply_with_arguments(ht TSRMLS_CC, apply_to_col, 2, obj, &tmp, &rv); + arg.obj = obj; + arg.cols = ecalloc(zend_hash_num_elements(ht), sizeof(*tmp)); + tmp = arg.cols; + zend_hash_apply_with_argument(ht, apply_to_col, &arg TSRMLS_CC); - if (SUCCESS == rv) { - return cols; + if (SUCCESS == arg.status) { + return tmp; } else { - efree(cols); + efree(tmp); return NULL; } } @@ -987,7 +1090,14 @@ static PHP_METHOD(pqres, fetchAll) { ZEND_BEGIN_ARG_INFO_EX(ai_pqres_count, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, count) { - 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) { long count; if (SUCCESS != php_pqres_count_elements(getThis(), &count TSRMLS_CC)) { @@ -1001,7 +1111,14 @@ static PHP_METHOD(pqres, count) { ZEND_BEGIN_ARG_INFO_EX(ai_pqres_desc, 0, 0, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, desc) { - 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_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { @@ -1023,6 +1140,7 @@ static zend_function_entry php_pqres_methods[] = { 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, fetchAll, ai_pqres_fetch_all, ZEND_ACC_PUBLIC) + PHP_ME(pqres, fetchAllCols, ai_pqres_fetch_all_cols, ZEND_ACC_PUBLIC) PHP_ME(pqres, count, ai_pqres_count, ZEND_ACC_PUBLIC) PHP_ME(pqres, map, ai_pqres_map, ZEND_ACC_PUBLIC) PHP_ME(pqres, desc, ai_pqres_desc, ZEND_ACC_PUBLIC) @@ -1057,7 +1175,7 @@ PHP_MINIT_FUNCTION(pqres) php_pqres_object_handlers.get_properties = php_pq_object_properties; php_pqres_object_handlers.count_elements = php_pqres_count_elements; - zend_hash_init(&php_pqres_object_prophandlers, 6, NULL, NULL, 1); + zend_hash_init(&php_pqres_object_prophandlers, 8, NULL, NULL, 1); zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("status"), ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_status; @@ -1089,7 +1207,7 @@ PHP_MINIT_FUNCTION(pqres) zend_hash_add(&php_pqres_object_prophandlers, "fetchType", sizeof("fetchType"), (void *) &ph, sizeof(ph), NULL); ph.write = NULL; - zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("autoConvert"), PHP_PQRES_FETCH_ARRAY, ZEND_ACC_PUBLIC TSRMLS_CC); + zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("autoConvert"), PHP_PQRES_CONV_ALL, ZEND_ACC_PUBLIC TSRMLS_CC); ph.read = php_pqres_object_read_auto_conv; ph.write = php_pqres_object_write_auto_conv; zend_hash_add(&php_pqres_object_prophandlers, "autoConvert", sizeof("autoConvert"), (void *) &ph, sizeof(ph), NULL); @@ -1103,8 +1221,12 @@ PHP_MINIT_FUNCTION(pqres) zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("BAD_RESPONSE"), PGRES_BAD_RESPONSE TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("NONFATAL_ERROR"), PGRES_NONFATAL_ERROR TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FATAL_ERROR"), PGRES_FATAL_ERROR TSRMLS_CC); +#ifdef HAVE_PGRES_COPY_BOTH zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COPY_BOTH"), PGRES_COPY_BOTH TSRMLS_CC); +#endif +#ifdef HAVE_PGRES_SINGLE_TUPLE zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("SINGLE_TUPLE"), PGRES_SINGLE_TUPLE TSRMLS_CC); +#endif zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ARRAY"), PHP_PQRES_FETCH_ARRAY TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ASSOC"), PHP_PQRES_FETCH_ASSOC TSRMLS_CC); @@ -1116,6 +1238,9 @@ PHP_MINIT_FUNCTION(pqres) zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_SCALAR"), PHP_PQRES_CONV_SCALAR TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_ARRAY"), PHP_PQRES_CONV_ARRAY TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_DATETIME"), PHP_PQRES_CONV_DATETIME TSRMLS_CC); +#if PHP_PQ_HAVE_PHP_JSON_H + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_JSON"), PHP_PQRES_CONV_JSON TSRMLS_CC); +#endif zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_ALL"), PHP_PQRES_CONV_ALL TSRMLS_CC); return SUCCESS;