X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-pq;a=blobdiff_plain;f=src%2Fphp_pqres.c;h=2c756558fef2bc1800b6154e8469e7681a113098;hp=782589de85f9dae26427b16ebed55781d05972b0;hb=753359f19c5d29f08faca43ee735c49c217230a7;hpb=8f0022e027a5412e5d404ee62a0c0346481748b4 diff --git a/src/php_pqres.c b/src/php_pqres.c index 782589d..2c75655 100644 --- a/src/php_pqres.c +++ b/src/php_pqres.c @@ -17,6 +17,7 @@ #include #include +#include #include #include "php_pq.h" @@ -24,6 +25,8 @@ #include "php_pq_object.h" #include "php_pqexc.h" #include "php_pqres.h" +#undef PHP_PQ_TYPE +#include "php_pq_type.h" zend_class_entry *php_pqres_class_entry; static zend_object_handlers php_pqres_object_handlers; @@ -38,7 +41,7 @@ static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval iter = ecalloc(1, sizeof(*iter)); iter->zi.funcs = &php_pqres_iterator_funcs; iter->zi.data = object; - Z_ADDREF_P(object); + /* do not addref, because the iterator lives inside this object anyway */ zfetch_type = prop = zend_read_property(ce, object, ZEND_STRL("fetchType"), 0 TSRMLS_CC); if (Z_TYPE_P(zfetch_type) != IS_LONG) { @@ -66,7 +69,6 @@ static void php_pqres_iterator_dtor(zend_object_iterator *i TSRMLS_DC) zval_ptr_dtor(&iter->current_val); iter->current_val = NULL; } - zval_ptr_dtor((zval **) &iter->zi.data); efree(iter); } @@ -89,10 +91,168 @@ static STATUS php_pqres_iterator_valid(zend_object_iterator *i TSRMLS_DC) return SUCCESS; } +zval *php_pqres_typed_zval(php_pqres_t *res, char *val, size_t len, Oid typ TSRMLS_DC) +{ + zval *zv, **zconv; + + MAKE_STD_ZVAL(zv); + + if (SUCCESS == zend_hash_index_find(&res->converters, typ, (void *) &zconv)) { + zval *tmp = NULL; + + ZVAL_STRINGL(zv, val, len, 1); + zend_call_method_with_1_params(zconv, NULL, NULL, "convertfromstring", &tmp, zv); + + if (tmp) { + zval_ptr_dtor(&zv); + zv = tmp; + } + + return zv; + } + + switch (typ) { + case PHP_PQ_OID_BOOL: + if (!(res->auto_convert & PHP_PQRES_CONV_BOOL)) { + goto noconversion; + } + 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: + case PHP_PQ_OID_OID: + if (!(res->auto_convert & PHP_PQRES_CONV_INT)) { + goto noconversion; + } + ZVAL_LONG(zv, zend_atol(val, len)); + break; + + case PHP_PQ_OID_FLOAT4: + case PHP_PQ_OID_FLOAT8: + if (!(res->auto_convert & PHP_PQRES_CONV_FLOAT)) { + goto noconversion; + } + ZVAL_DOUBLE(zv, zend_strtod(val, NULL)); + break; + + case PHP_PQ_OID_DATE: + if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { + goto noconversion; + } + php_pqdt_from_string(val, len, "Y-m-d", zv TSRMLS_CC); + break; + + case PHP_PQ_OID_ABSTIME: + if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { + goto noconversion; + } + php_pqdt_from_string(val, len, "Y-m-d H:i:s", zv TSRMLS_CC); + break; + + case PHP_PQ_OID_TIMESTAMP: + if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { + goto noconversion; + } + php_pqdt_from_string(val, len, "Y-m-d H:i:s.u", zv TSRMLS_CC); + break; + + case PHP_PQ_OID_TIMESTAMPTZ: + if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { + goto noconversion; + } + php_pqdt_from_string(val, len, "Y-m-d H:i:s.uO", zv TSRMLS_CC); + break; + +#ifdef 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; + } + if (PHP_PQ_TYPE_IS_ARRAY(typ) && (Z_ARRVAL_P(zv) = php_pq_parse_array(res, val, len, PHP_PQ_TYPE_OF_ARRAY(typ) TSRMLS_CC))) { + Z_TYPE_P(zv) = IS_ARRAY; + } else { + noconversion: + ZVAL_STRINGL(zv, val, len, 1); + } + break; + } + + 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; int c, cols; + php_pqres_object_t *res_obj = PQresultInstanceData(res, php_pqconn_event); if (data_ptr) { data = *data_ptr; @@ -111,38 +271,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 = php_pq_typed_zval(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); } } @@ -248,15 +377,20 @@ STATUS php_pqres_success(PGresult *res TSRMLS_DC) } } -void php_pqres_init_instance_data(PGresult *res, php_pqres_object_t **ptr TSRMLS_DC) +void php_pqres_init_instance_data(PGresult *res, php_pqconn_object_t *conn_obj, php_pqres_object_t **ptr TSRMLS_DC) { php_pqres_object_t *obj; 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); + zend_hash_init(&r->bound, 0, 0, ZVAL_PTR_DTOR, 0); + zend_hash_init(&r->converters, 0, 0, ZVAL_PTR_DTOR, 0); + zend_hash_copy(&r->converters, &conn_obj->intern->converters, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + r->auto_convert = conn_obj->intern->default_auto_convert; + r->default_fetch_type = conn_obj->intern->default_fetch_type; + php_pqres_create_object_ex(php_pqres_class_entry, r, &obj TSRMLS_CC); PQresultSetInstanceData(res, php_pqconn_event, obj); if (ptr) { @@ -283,6 +417,7 @@ static void php_pqres_object_free(void *o TSRMLS_DC) } zend_hash_destroy(&obj->intern->bound); + zend_hash_destroy(&obj->intern->converters); efree(obj->intern); obj->intern = NULL; @@ -373,7 +508,7 @@ static void php_pqres_object_read_fetch_type(zval *object, void *o, zval *return if (obj->intern->iter) { RETVAL_LONG(obj->intern->iter->fetch_type); } else { - RETVAL_LONG(PHP_PQRES_FETCH_ARRAY); + RETVAL_LONG(obj->intern->default_fetch_type); } } @@ -405,6 +540,37 @@ static void php_pqres_object_write_fetch_type(zval *object, void *o, zval *value } } +static void php_pqres_object_read_auto_conv(zval *object, void *o, zval *return_value TSRMLS_DC) +{ + php_pqres_object_t *obj = o; + + RETVAL_LONG(obj->intern->auto_convert); +} + +static void php_pqres_object_write_auto_conv(zval *object, void *o, zval *value TSRMLS_DC) +{ + php_pqres_object_t *obj = o; + zval *zauto_conv = value; + + if (Z_TYPE_P(value) != IS_LONG) { + if (Z_REFCOUNT_P(value) > 1) { + zval *tmp; + MAKE_STD_ZVAL(tmp); + ZVAL_ZVAL(tmp, zauto_conv, 1, 0); + convert_to_long(tmp); + zauto_conv = tmp; + } else { + convert_to_long_ex(&zauto_conv); + } + } + + obj->intern->auto_convert = Z_LVAL_P(zauto_conv); + + if (zauto_conv != value) { + zval_ptr_dtor(&zauto_conv); + } +} + 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; @@ -440,20 +606,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) { @@ -481,8 +655,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; + + 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 == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z", &zcol, &zref)) { + if (SUCCESS == rv) { php_pqres_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); if (!obj->intern) { @@ -582,7 +762,7 @@ static PHP_METHOD(pqres, fetchRow) { zval **row = NULL; if (fetch_type == -1) { - fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : PHP_PQRES_FETCH_ARRAY; + fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : obj->intern->default_fetch_type; } zend_replace_error_handling(EH_THROW, exce(EX_RUNTIME), &zeh TSRMLS_CC); @@ -614,17 +794,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) { @@ -637,9 +817,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)) { @@ -651,7 +829,7 @@ static PHP_METHOD(pqres, fetchCol) { RETVAL_FALSE; } else { zval_dtor(zref); - ZVAL_ZVAL(zref, *zres, 1, 1); + ZVAL_ZVAL(zref, *zres, 1, 0); RETVAL_TRUE; } } @@ -661,6 +839,40 @@ static PHP_METHOD(pqres, fetchCol) { } } +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); + } + } +} + static int apply_to_col(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key) { zval **c = p; @@ -742,7 +954,7 @@ static PHP_METHOD(pqres, map) { } if (fetch_type == -1) { - fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : PHP_PQRES_FETCH_ARRAY; + fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : obj->intern->default_fetch_type; } if (keys) { @@ -839,7 +1051,7 @@ static PHP_METHOD(pqres, fetchAll) { int r, rows = PQntuples(obj->intern->res); if (fetch_type == -1) { - fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : PHP_PQRES_FETCH_ARRAY; + fetch_type = obj->intern->iter ? obj->intern->iter->fetch_type : obj->intern->default_fetch_type; } array_init(return_value); @@ -864,17 +1076,44 @@ 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()) { + 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 { + int p, params; + + array_init(return_value); + for (p = 0, params = PQnparams(obj->intern->res); p < params; ++p) { + add_next_index_long(return_value, PQparamtype(obj->intern->res, p)); + } + } + } +} + 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, 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) {0} }; +PHP_MSHUTDOWN_FUNCTION(pqres) +{ + zend_hash_destroy(&php_pqres_object_prophandlers); + return SUCCESS; +} + PHP_MINIT_FUNCTION(pqres) { zend_class_entry ce = {0}; @@ -929,6 +1168,12 @@ 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_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); + ph.write = NULL; + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("EMPTY_QUERY"), PGRES_EMPTY_QUERY TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("COMMAND_OK"), PGRES_COMMAND_OK TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("TUPLES_OK"), PGRES_TUPLES_OK TSRMLS_CC); @@ -944,6 +1189,15 @@ PHP_MINIT_FUNCTION(pqres) zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_ASSOC"), PHP_PQRES_FETCH_ASSOC TSRMLS_CC); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("FETCH_OBJECT"), PHP_PQRES_FETCH_OBJECT TSRMLS_CC); + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_BOOL"), PHP_PQRES_CONV_BOOL TSRMLS_CC); + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_INT"), PHP_PQRES_CONV_INT TSRMLS_CC); + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_FLOAT"), PHP_PQRES_CONV_FLOAT TSRMLS_CC); + 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); + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_JSON"), PHP_PQRES_CONV_JSON TSRMLS_CC); + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_ALL"), PHP_PQRES_CONV_ALL TSRMLS_CC); + return SUCCESS; }