X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-pq;a=blobdiff_plain;f=src%2Fphp_pqres.c;h=802fcf4a85484b506ed1c87cc58126869b7704df;hp=68645a19c9902d65f94ca52468c632abad36d9bc;hb=HEAD;hpb=7980a8b88b9e01b057ac1ad36c52ae65e2ef4512 diff --git a/src/php_pqres.c b/src/php_pqres.c index 68645a1..802fcf4 100644 --- a/src/php_pqres.c +++ b/src/php_pqres.c @@ -17,9 +17,7 @@ #include #include -#if PHP_PQ_HAVE_PHP_JSON_H -#include /* we've added the include directory to INCLUDES */ -#endif +#include #include @@ -36,18 +34,16 @@ static zend_object_handlers php_pqres_object_handlers; static HashTable php_pqres_object_prophandlers; static zend_object_iterator_funcs php_pqres_iterator_funcs; -static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval *object, int by_ref) +static inline zend_object_iterator *php_pqres_iterator_init_ex(zend_class_entry *ce, zval *object, int by_ref) { php_pqres_iterator_t *iter; zval tmp, *zfetch_type; iter = ecalloc(1, sizeof(*iter)); iter->zi.funcs = &php_pqres_iterator_funcs; - ZVAL_COPY(&iter->zi.data, object); - - zend_iterator_init(&iter->zi); + ZVAL_COPY_VALUE(&iter->zi.data, object); - zfetch_type = zend_read_property(ce, object, ZEND_STRL("fetchType"), 0, &tmp); + zfetch_type = php_pq_read_property(object, "fetchType", &tmp); iter->fetch_type = zval_get_long(zfetch_type); #if DBG_GC fprintf(stderr, "INIT iter(#%d) %p res(#%d) %p\n", iter->zi.std.handle, iter, Z_OBJ_HANDLE_P(object), PHP_PQ_OBJ(object, NULL)); @@ -55,30 +51,50 @@ static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval return (zend_object_iterator *) iter; } +static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval *object, int by_ref) +{ + zend_object_iterator *iter = php_pqres_iterator_init_ex(ce, object, by_ref); + + zend_iterator_init(iter); + Z_ADDREF_P(object); + + return iter; +} static void php_pqres_internal_iterator_init(zval *zobj) { php_pqres_object_t *obj = PHP_PQ_OBJ(zobj, NULL); - obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(zobj), zobj, 0); - /* prevent cyclic dep */ - Z_DELREF_P(zobj); + obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init_ex(Z_OBJCE_P(zobj), zobj, 0); obj->intern->iter->zi.funcs->rewind((zend_object_iterator *) obj->intern->iter); } -static void php_pqres_iterator_dtor(zend_object_iterator *i) +static inline void php_pqres_iterator_dtor_ex(zend_object_iterator *i) { php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i; #if DBG_GC - fprintf(stderr, "FREE iter(#%d) %p\n", iter->zi.std.handle, iter); + fprintf(stderr, "FREE iter(#%d) rc=%d %p\n", iter->zi.std.handle, GC_REFCOUNT(&iter->zi.std), iter); #endif if (!Z_ISUNDEF(iter->current_val)) { zval_ptr_dtor(&iter->current_val); ZVAL_UNDEF(&iter->current_val); } +} + +static void php_pqres_iterator_dtor(zend_object_iterator *i) +{ + php_pqres_iterator_dtor_ex(i); zval_ptr_dtor(&i->data); - zend_iterator_dtor(i); +} + +static void php_pqres_internal_iterator_dtor(php_pqres_object_t *obj) +{ + if (obj->intern && obj->intern->iter) { + php_pqres_iterator_dtor_ex((zend_object_iterator *) obj->intern->iter); + efree(obj->intern->iter); + obj->intern->iter = NULL; + } } static ZEND_RESULT_CODE php_pqres_iterator_valid(zend_object_iterator *i) @@ -116,7 +132,7 @@ zval *php_pqres_typed_zval(php_pqres_t *res, Oid typ, zval *zv) ZVAL_NULL(&rv); ZVAL_LONG(&ztype, typ); - zend_call_method_with_2_params(zconv, NULL, NULL, "convertfromstring", &rv, zv, &ztype); + php_pq_call_method(zconv, "convertfromstring", 2, &rv, zv, &ztype); zval_ptr_dtor(zv); ZVAL_ZVAL(zv, &rv, 0, 0); @@ -175,14 +191,14 @@ zval *php_pqres_typed_zval(php_pqres_t *res, Oid typ, zval *zv) } php_pqdt_from_string(zv, NULL, str->val, str->len, "Y-m-d", NULL); break; - +#ifdef PHP_PQ_OID_ABSTIME case PHP_PQ_OID_ABSTIME: if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { goto noconversion; } php_pqdt_from_string(zv, NULL, str->val, str->len, "Y-m-d H:i:s", NULL); break; - +#endif case PHP_PQ_OID_TIMESTAMP: if (!(res->auto_convert & PHP_PQRES_CONV_DATETIME)) { goto noconversion; @@ -197,10 +213,10 @@ zval *php_pqres_typed_zval(php_pqres_t *res, Oid typ, zval *zv) php_pqdt_from_string(zv, NULL, str->val, str->len, "Y-m-d H:i:s.uO", NULL); break; -#if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON) -# ifdef PHP_PQ_OID_JSONB +#ifdef PHP_PQ_OID_JSON +# ifdef PHP_PQ_OID_JSONB case PHP_PQ_OID_JSONB: -# endif +# endif case PHP_PQ_OID_JSON: if (!(res->auto_convert & PHP_PQRES_CONV_JSON)) { goto noconversion; @@ -209,6 +225,23 @@ zval *php_pqres_typed_zval(php_pqres_t *res, Oid typ, zval *zv) break; #endif + case PHP_PQ_OID_BYTEA: + if (!(res->auto_convert & PHP_PQRES_CONV_BYTEA)) { + goto noconversion; + } else { + size_t to_len; + char *to_str = (char *) PQunescapeBytea((unsigned char *) str->val, &to_len); + + if (!to_str) { + ZVAL_NULL(zv); + php_error_docref(NULL, E_WARNING, "Failed to unsescape BYTEA: '%s'", str->val); + } else { + ZVAL_STRINGL(zv, to_str, to_len); + PQfreemem(to_str); + } + } + break; + default: if (!(res->auto_convert & PHP_PQRES_CONV_ARRAY)) { goto noconversion; @@ -360,30 +393,46 @@ static zend_object_iterator_funcs php_pqres_iterator_funcs = { php_pqres_iterator_rewind, /* invalidate current value/key (optional, may be NULL) */ php_pqres_iterator_invalidate +#if PHP_VERSION_ID >= 80000 + , NULL +#endif }; -static ZEND_RESULT_CODE php_pqres_count_elements(zval *object, long *count) +static inline ZEND_RESULT_CODE php_pqres_count_elements_ex(zend_object *object, zend_long *count) { - php_pqres_object_t *obj = PHP_PQ_OBJ(object, NULL); + php_pqres_object_t *obj = PHP_PQ_OBJ(NULL, object); if (!obj->intern) { return FAILURE; } else { - *count = (long) PQntuples(obj->intern->res); + *count = (zend_long) PQntuples(obj->intern->res); return SUCCESS; } } +#if PHP_VERSION_ID >= 80000 +static ZEND_RESULT_CODE php_pqres_count_elements(zend_object *object, zend_long *count) +{ + return php_pqres_count_elements_ex(object, count); +} +#else +static ZEND_RESULT_CODE php_pqres_count_elements(zval *object, zend_long *count) +{ + return php_pqres_count_elements_ex(Z_OBJ_P(object), count); +} +#endif -ZEND_RESULT_CODE php_pqres_success(PGresult *res TSRMLS_DC) +ZEND_RESULT_CODE php_pqres_success(PGresult *res) { - zval zexc; + zval zexc, zsqlstate; switch (PQresultStatus(res)) { case PGRES_BAD_RESPONSE: case PGRES_NONFATAL_ERROR: case PGRES_FATAL_ERROR: ZVAL_OBJ(&zexc, throw_exce(EX_SQL, "%s", PHP_PQresultErrorMessage(res))); - zend_update_property_string(Z_OBJCE(zexc), &zexc, ZEND_STRL("sqlstate"), PQresultErrorField(res, PG_DIAG_SQLSTATE)); + ZVAL_STRING(&zsqlstate, PQresultErrorField(res, PG_DIAG_SQLSTATE)); + php_pq_update_property(&zexc, "sqlstate", &zsqlstate); + zval_ptr_dtor(&zsqlstate); return FAILURE; default: return SUCCESS; @@ -425,11 +474,7 @@ static void php_pqres_object_free(zend_object *o) obj->intern->res = NULL; } - if (obj->intern->iter) { - ZVAL_NULL(&obj->intern->iter->zi.data); - php_pqres_iterator_dtor((zend_object_iterator *) obj->intern->iter); - obj->intern->iter = NULL; - } + php_pqres_internal_iterator_dtor(obj); zend_hash_destroy(&obj->intern->bound); zend_hash_destroy(&obj->intern->converters); @@ -451,21 +496,21 @@ static zend_object *php_pqres_create_object(zend_class_entry *class_type) return &php_pqres_create_object_ex(class_type, NULL)->zo; } -static void php_pqres_object_read_status(zval *object, void *o, zval *return_value) +static void php_pqres_object_read_status(void *o, zval *return_value) { php_pqres_object_t *obj = o; RETVAL_LONG(PQresultStatus(obj->intern->res)); } -static void php_pqres_object_read_status_message(zval *object, void *o, zval *return_value) +static void php_pqres_object_read_status_message(void *o, zval *return_value) { php_pqres_object_t *obj = o; RETVAL_STRING(PQresStatus(PQresultStatus(obj->intern->res))+sizeof("PGRES")); } -static void php_pqres_object_read_error_message(zval *object, void *o, zval *return_value) +static void php_pqres_object_read_error_message(void *o, zval *return_value) { php_pqres_object_t *obj = o; char *error = PHP_PQresultErrorMessage(obj->intern->res); @@ -477,59 +522,153 @@ static void php_pqres_object_read_error_message(zval *object, void *o, zval *ret } } -static void php_pqres_object_read_num_rows(zval *object, void *o, zval *return_value) +#ifndef PG_DIAG_SEVERITY +# define PG_DIAG_SEVERITY 'S' +#endif +#ifndef PG_DIAG_SQLSTATE +# define PG_DIAG_SQLSTATE 'C' +#endif +#ifndef PG_DIAG_MESSAGE_PRIMARY +# define PG_DIAG_MESSAGE_PRIMARY 'M' +#endif +#ifndef PG_DIAG_MESSAGE_DETAIL +# define PG_DIAG_MESSAGE_DETAIL 'D' +#endif +#ifndef PG_DIAG_MESSAGE_HINT +# define PG_DIAG_MESSAGE_HINT 'H' +#endif +#ifndef PG_DIAG_STATEMENT_POSITION +# define PG_DIAG_STATEMENT_POSITION 'P' +#endif +#ifndef PG_DIAG_INTERNAL_POSITION +# define PG_DIAG_INTERNAL_POSITION 'p' +#endif +#ifndef PG_DIAG_INTERNAL_QUERY +# define PG_DIAG_INTERNAL_QUERY 'q' +#endif +#ifndef PG_DIAG_CONTEXT +# define PG_DIAG_CONTEXT 'W' +#endif +#ifndef PG_DIAG_SCHEMA_NAME +# define PG_DIAG_SCHEMA_NAME 's' +#endif +#ifndef PG_DIAG_TABLE_NAME +# define PG_DIAG_TABLE_NAME 't' +#endif +#ifndef PG_DIAG_COLUMN_NAME +# define PG_DIAG_COLUMN_NAME 'c' +#endif +#ifndef PG_DIAG_DATATYPE_NAME +# define PG_DIAG_DATATYPE_NAME 'd' +#endif +#ifndef PG_DIAG_CONSTRAINT_NAME +# define PG_DIAG_CONSTRAINT_NAME 'n' +#endif +#ifndef PG_DIAG_SOURCE_FILE +# define PG_DIAG_SOURCE_FILE 'F' +#endif +#ifndef PG_DIAG_SOURCE_LINE +# define PG_DIAG_SOURCE_LINE 'L' +#endif +#ifndef PG_DIAG_SOURCE_FUNCTION +# define PG_DIAG_SOURCE_FUNCTION 'R' +#endif + +static void php_pqres_object_read_diag(void *o, zval *return_value) +{ + php_pqres_object_t *obj = o; + int i; + struct { + char code; + const char *const name; + } diag[] = { + {PG_DIAG_SEVERITY, "severity"}, + {PG_DIAG_SQLSTATE, "sqlstate"}, + {PG_DIAG_MESSAGE_PRIMARY, "message_primary"}, + {PG_DIAG_MESSAGE_DETAIL, "message_detail"}, + {PG_DIAG_MESSAGE_HINT, "message_hint"}, + {PG_DIAG_STATEMENT_POSITION,"statement_position"}, + {PG_DIAG_INTERNAL_POSITION, "internal_position"}, + {PG_DIAG_INTERNAL_QUERY, "internal_query"}, + {PG_DIAG_CONTEXT, "context"}, + {PG_DIAG_SCHEMA_NAME, "schema_name"}, + {PG_DIAG_TABLE_NAME, "table_name"}, + {PG_DIAG_COLUMN_NAME, "column_name"}, + {PG_DIAG_DATATYPE_NAME, "datatype_name"}, + {PG_DIAG_CONSTRAINT_NAME, "constraint_name"}, + {PG_DIAG_SOURCE_FILE, "source_file"}, + {PG_DIAG_SOURCE_LINE, "source_line"}, + {PG_DIAG_SOURCE_FUNCTION, "source_function"}, + }; + + array_init_size(return_value, 32); + for (i = 0; i < sizeof(diag)/sizeof(diag[0]); ++i) { + char *value = PQresultErrorField(obj->intern->res, diag[i].code); + + if (value) { + add_assoc_string(return_value, diag[i].name, value); + } else { + add_assoc_null(return_value, diag[i].name); + } + } +} + +static void php_pqres_object_read_num_rows(void *o, zval *return_value) { php_pqres_object_t *obj = o; RETVAL_LONG(PQntuples(obj->intern->res)); } -static void php_pqres_object_read_num_cols(zval *object, void *o, zval *return_value) +static void php_pqres_object_read_num_cols(void *o, zval *return_value) { php_pqres_object_t *obj = o; RETVAL_LONG(PQnfields(obj->intern->res)); } -static void php_pqres_object_read_affected_rows(zval *object, void *o, zval *return_value) +static void php_pqres_object_read_affected_rows(void *o, zval *return_value) { php_pqres_object_t *obj = o; RETVAL_LONG(atoi(PQcmdTuples(obj->intern->res))); } -static void php_pqres_object_read_fetch_type(zval *object, void *o, zval *return_value) +static void php_pqres_object_read_fetch_type(void *o, zval *return_value) { php_pqres_object_t *obj = o; RETVAL_LONG(php_pqres_fetch_type(obj->intern)); } -static void php_pqres_object_write_fetch_type(zval *object, void *o, zval *value) +static void php_pqres_object_write_fetch_type(void *o, zval *value) { php_pqres_object_t *obj = o; if (!obj->intern->iter) { - php_pqres_internal_iterator_init(object); + zval object; + + ZVAL_OBJ(&object, &obj->zo); + php_pqres_internal_iterator_init(&object); } obj->intern->iter->fetch_type = zval_get_long(value); } -static void php_pqres_object_read_auto_conv(zval *object, void *o, zval *return_value) +static void php_pqres_object_read_auto_conv(void *o, zval *return_value) { 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) +static void php_pqres_object_write_auto_conv(void *o, zval *value) { php_pqres_object_t *obj = o; obj->intern->auto_convert = zval_get_long(value); } -static ZEND_RESULT_CODE php_pqres_iteration(zval *zobj, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval *row TSRMLS_DC) +static ZEND_RESULT_CODE php_pqres_iteration(zval *zobj, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval *row) { ZEND_RESULT_CODE rv; php_pqres_fetch_t orig_fetch; @@ -597,7 +736,7 @@ static ZEND_RESULT_CODE column_nn(php_pqres_object_t *obj, zval *zcol, php_pqres } if (!col->name) { - php_error_docref(NULL, E_WARNING, "Failed to find column at index %ld", index); + php_error_docref(NULL, E_WARNING, "Failed to find column at index " ZEND_LONG_FMT, index); return FAILURE; } if (col->num == -1) { @@ -652,7 +791,7 @@ static int apply_bound(zval *zbound, int argc, va_list argv, zend_hash_key *key) ZEND_RESULT_CODE *rv = va_arg(argv, ZEND_RESULT_CODE *); if (!(zvalue = zend_hash_index_find(Z_ARRVAL_P(zrow), key->h))) { - php_error_docref(NULL, E_WARNING, "Failed to find column ad index %lu", key->h); + php_error_docref(NULL, E_WARNING, "Failed to find column ad index " ZEND_ULONG_FMT, key->h); *rv = FAILURE; return ZEND_HASH_APPLY_STOP; } else { @@ -1022,7 +1161,7 @@ static PHP_METHOD(pqres, fetchAll) { } } -ZEND_BEGIN_ARG_INFO_EX(ai_pqres_count, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(ai_pqres_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO(); static PHP_METHOD(pqres, count) { zend_error_handling zeh; @@ -1033,9 +1172,9 @@ static PHP_METHOD(pqres, count) { zend_restore_error_handling(&zeh); if (SUCCESS == rv) { - long count; + zend_long count; - if (SUCCESS != php_pqres_count_elements(getThis(), &count)) { + if (SUCCESS != php_pqres_count_elements_ex(Z_OBJ_P(getThis()), &count)) { throw_exce(EX_UNINITIALIZED, "pq\\Result not initialized"); } else { RETVAL_LONG(count); @@ -1069,6 +1208,30 @@ static PHP_METHOD(pqres, desc) { } } +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(ai_pqres_getIterator, 0, 0, Traversable, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqres, getIterator) +{ + zend_error_handling zeh; + ZEND_RESULT_CODE rv; + + zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh); + rv = zend_parse_parameters_none(); + zend_restore_error_handling(&zeh); + + if (SUCCESS == rv) { + php_pqres_object_t *obj = PHP_PQ_OBJ(getThis(), NULL); + + if (!obj->intern) { + throw_exce(EX_UNINITIALIZED, "pq\\Result not initialized"); + } else { + zend_create_internal_iterator_zval(return_value, getThis()); + } + } +} +#endif + 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) @@ -1079,6 +1242,9 @@ static zend_function_entry php_pqres_methods[] = { 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) +#if PHP_VERSION_ID >= 80000 + PHP_ME(pqres, getIterator, ai_pqres_getIterator, ZEND_ACC_PUBLIC) +#endif {0} }; @@ -1097,7 +1263,11 @@ PHP_MINIT_FUNCTION(pqres) php_pqres_class_entry = zend_register_internal_class_ex(&ce, NULL); php_pqres_class_entry->create_object = php_pqres_create_object; php_pqres_class_entry->get_iterator = php_pqres_iterator_init; - zend_class_implements(php_pqres_class_entry, 2, zend_ce_traversable, spl_ce_Countable); +#if PHP_VERSION_ID >= 80000 + zend_class_implements(php_pqres_class_entry, 2, zend_ce_aggregate, zend_ce_countable); +#else + zend_class_implements(php_pqres_class_entry, 2, zend_ce_traversable, zend_ce_countable); +#endif memcpy(&php_pqres_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); php_pqres_object_handlers.offset = XtOffsetOf(php_pqres_object_t, zo); @@ -1105,13 +1275,13 @@ PHP_MINIT_FUNCTION(pqres) php_pqres_object_handlers.read_property = php_pq_object_read_prop; php_pqres_object_handlers.write_property = php_pq_object_write_prop; php_pqres_object_handlers.clone_obj = NULL; - php_pqres_object_handlers.get_property_ptr_ptr = NULL; + php_pqres_object_handlers.get_property_ptr_ptr = php_pq_object_get_prop_ptr_null; php_pqres_object_handlers.get_gc = php_pq_object_get_gc; php_pqres_object_handlers.get_debug_info = php_pq_object_debug_info; 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, 8, NULL, php_pq_object_prophandler_dtor, 1); + zend_hash_init(&php_pqres_object_prophandlers, 9, NULL, php_pq_object_prophandler_dtor, 1); zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("status"), ZEND_ACC_PUBLIC); ph.read = php_pqres_object_read_status; @@ -1125,6 +1295,10 @@ PHP_MINIT_FUNCTION(pqres) ph.read = php_pqres_object_read_error_message; zend_hash_str_add_mem(&php_pqres_object_prophandlers, "errorMessage", sizeof("errorMessage")-1, (void *) &ph, sizeof(ph)); + zend_declare_property_null(php_pqres_class_entry, ZEND_STRL("diag"), ZEND_ACC_PUBLIC); + ph.read = php_pqres_object_read_diag; + zend_hash_str_add_mem(&php_pqres_object_prophandlers, "diag", sizeof("diag")-1, (void *) &ph, sizeof(ph)); + zend_declare_property_long(php_pqres_class_entry, ZEND_STRL("numRows"), 0, ZEND_ACC_PUBLIC); ph.read = php_pqres_object_read_num_rows; zend_hash_str_add_mem(&php_pqres_object_prophandlers, "numRows", sizeof("numRows")-1, (void *) &ph, sizeof(ph)); @@ -1174,9 +1348,8 @@ PHP_MINIT_FUNCTION(pqres) zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_SCALAR"), PHP_PQRES_CONV_SCALAR); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_ARRAY"), PHP_PQRES_CONV_ARRAY); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_DATETIME"), PHP_PQRES_CONV_DATETIME); -#if PHP_PQ_HAVE_PHP_JSON_H zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_JSON"), PHP_PQRES_CONV_JSON); -#endif + zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_BYTEA"), PHP_PQRES_CONV_BYTEA); zend_declare_class_constant_long(php_pqres_class_entry, ZEND_STRL("CONV_ALL"), PHP_PQRES_CONV_ALL); return SUCCESS;