1
0
mirror of https://github.com/danog/ext-pq.git synced 2025-01-22 13:51:20 +01:00
ext-pq/src/php_pqtypes.c
Michael Wallner ddb4297550
fix gh issue #30
lift restriction on non-composite types of pq\Types
2018-06-26 09:10:28 +02:00

336 lines
10 KiB
C

/*
+--------------------------------------------------------------------+
| PECL :: pq |
+--------------------------------------------------------------------+
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the conditions mentioned |
| in the accompanying LICENSE file are met. |
+--------------------------------------------------------------------+
| Copyright (c) 2013, Michael Wallner <mike@php.net> |
+--------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <php.h>
#include <Zend/zend_smart_str.h>
#include "php_pq.h"
#include "php_pq_misc.h"
#include "php_pq_object.h"
#include "php_pqexc.h"
#include "php_pqres.h"
#include "php_pqtypes.h"
zend_class_entry *php_pqtypes_class_entry;
static zend_object_handlers php_pqtypes_object_handlers;
static HashTable php_pqtypes_object_prophandlers;
static void php_pqtypes_object_free(zend_object *o)
{
php_pqtypes_object_t *obj = PHP_PQ_OBJ(NULL, o);
#if DBG_GC
fprintf(stderr, "FREE types(#%d) %p (conn(#%d): %p)\n", obj->zo.handle, obj, obj->intern->conn->zo.handle, obj->intern->conn);
#endif
if (obj->intern) {
zend_hash_destroy(&obj->intern->types);
php_pq_object_delref(obj->intern->conn);
efree(obj->intern);
obj->intern = NULL;
}
php_pq_object_dtor(o);
}
php_pqtypes_object_t *php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern)
{
return php_pq_object_create(ce, intern, sizeof(php_pqtypes_object_t),
&php_pqtypes_object_handlers, &php_pqtypes_object_prophandlers);
}
static zend_object *php_pqtypes_create_object(zend_class_entry *class_type)
{
return &php_pqtypes_create_object_ex(class_type, NULL)->zo;
}
static void php_pqtypes_object_read_connection(zval *object, void *o, zval *return_value)
{
php_pqtypes_object_t *obj = o;
php_pq_object_to_zval(obj->intern->conn, return_value);
}
static void php_pqtypes_object_gc_connection(zval *object, void *o, zval *return_value)
{
php_pqtypes_object_t *obj = o;
zval zconn;
php_pq_object_to_zval_no_addref(obj->intern->conn, &zconn);
add_next_index_zval(return_value, &zconn);
}
static int has_dimension(HashTable *ht, zval *member, zend_string **key, zend_long *index)
{
if (Z_TYPE_P(member) == IS_LONG) {
*index = Z_LVAL_P(member);
check_index:
return zend_hash_index_exists(ht, *index);
} else {
zend_string *str = zval_get_string(member);
if (is_numeric_str_function(str, index, NULL)) {
zend_string_release(str);
goto check_index;
}
if (zend_hash_exists(ht, str)) {
*key = str;
return 1;
}
zend_string_release(str);
return 0;
}
}
static int php_pqtypes_object_has_dimension(zval *object, zval *member, int check_empty)
{
php_pqtypes_object_t *obj = PHP_PQ_OBJ(object, NULL);
zend_string *key = NULL;
zend_long index = 0;
if (has_dimension(&obj->intern->types, member, &key, &index)) {
if (check_empty) {
zval *data;
if (key) {
if ((data = zend_hash_find(&obj->intern->types, key))) {
zend_string_release(key);
return Z_TYPE_P(data) != IS_NULL;
}
zend_string_release(key);
} else if ((data = zend_hash_index_find(&obj->intern->types, index))) {
return Z_TYPE_P(data) != IS_NULL;
}
} else {
if (key) {
zend_string_release(key);
}
return 1;
}
}
return 0;
}
static zval *php_pqtypes_object_read_dimension(zval *object, zval *member, int type, zval *rv)
{
php_pqtypes_object_t *obj = PHP_PQ_OBJ(object, NULL);
zend_string *key = NULL;
zend_long index = 0;
zval *data = NULL;
if (has_dimension(&obj->intern->types, member, &key, &index)) {
if (key) {
data = zend_hash_find(&obj->intern->types, key);
zend_string_release(key);
} else {
data = zend_hash_index_find(&obj->intern->types, index);
}
}
return data;
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_construct, 0, 0, 1)
ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqtypes, __construct) {
zend_error_handling zeh;
zval *zconn, *znsp = NULL;
ZEND_RESULT_CODE rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
rv = zend_parse_parameters(ZEND_NUM_ARGS(), "O|a!", &zconn, php_pqconn_class_entry, &znsp);
zend_restore_error_handling(&zeh);
if (SUCCESS == rv) {
php_pqconn_object_t *conn_obj = PHP_PQ_OBJ(zconn, NULL);
if (!conn_obj->intern) {
throw_exce(EX_UNINITIALIZED, "pq\\Connection not initialized");
} else {
php_pqtypes_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
obj->intern = ecalloc(1, sizeof(*obj->intern));
obj->intern->conn = conn_obj;
php_pq_object_addref(conn_obj);
zend_hash_init(&obj->intern->types, 512, NULL, ZVAL_PTR_DTOR, 0);
if (znsp) {
zend_call_method_with_1_params(getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", NULL, znsp);
} else {
zend_call_method_with_0_params(getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", NULL);
}
}
}
}
#define PHP_PQ_TYPES_QUERY \
"select t.oid, t.* " \
"from pg_type t join pg_namespace n on t.typnamespace=n.oid " \
"where typisdefined"
#ifndef PHP_PQ_OID_TEXT
# define PHP_PQ_OID_TEXT 25
#endif
static int apply_nsp(zval *zp, int argc, va_list argv, zend_hash_key *key)
{
unsigned pcount, tcount;
php_pq_params_t *params = va_arg(argv, php_pq_params_t *);
smart_str *str = va_arg(argv, smart_str *);
tcount = php_pq_params_add_type_oid(params, PHP_PQ_OID_TEXT);
pcount = php_pq_params_add_param(params, zp);
if (tcount != pcount) {
php_error_docref(NULL, E_WARNING, "Param/Type count mismatch");
return ZEND_HASH_APPLY_STOP;
}
if (pcount > 1) {
smart_str_appendc(str, ',');
}
smart_str_appendc(str, '$');
smart_str_append_unsigned(str, pcount);
return ZEND_HASH_APPLY_KEEP;
}
ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0)
ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
ZEND_END_ARG_INFO();
static PHP_METHOD(pqtypes, refresh) {
HashTable *nsp = NULL;
zend_error_handling zeh;
ZEND_RESULT_CODE rv;
zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|H/!", &nsp);
zend_restore_error_handling(&zeh);
if (SUCCESS == rv) {
php_pqtypes_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
if (!obj->intern) {
throw_exce(EX_UNINITIALIZED, "pq\\Types not initialized");
} else {
PGresult *res;
if (!nsp || !zend_hash_num_elements(nsp)) {
res = php_pq_exec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY " and nspname in ('public', 'pg_catalog')");
} else {
smart_str str = {0};
php_pq_params_t *params = php_pq_params_init(&obj->intern->conn->intern->converters, NULL, NULL);
smart_str_appends(&str, PHP_PQ_TYPES_QUERY " and nspname in(");
zend_hash_apply_with_arguments(nsp, apply_nsp, 2, params, &str);
smart_str_appendc(&str, ')');
smart_str_0(&str);
res = php_pq_exec_params(obj->intern->conn->intern->conn, smart_str_v(&str), params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0);
smart_str_free(&str);
php_pq_params_free(&params);
}
if (!res) {
throw_exce(EX_RUNTIME, "Failed to fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
} else {
if (SUCCESS == php_pqres_success(res)) {
int r, rows;
for (r = 0, rows = PQntuples(res); r < rows; ++r) {
zval tmp, *row;
ZVAL_NULL(&tmp);
row = php_pqres_row_to_zval(res, r, PHP_PQRES_FETCH_OBJECT, &tmp);
Z_ADDREF_P(row);
zend_hash_index_update(&obj->intern->types, atol(PQgetvalue(res, r, 0 )), row);
zend_hash_str_update(&obj->intern->types, PQgetvalue(res, r, 1), PQgetlength(res, r, 1), row);
}
}
php_pqres_clear(res);
php_pqconn_notify_listeners(obj->intern->conn);
}
}
}
}
static zend_function_entry php_pqtypes_methods[] = {
PHP_ME(pqtypes, __construct, ai_pqtypes_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(pqtypes, refresh, ai_pqtypes_refresh, ZEND_ACC_PUBLIC)
{0}
};
PHP_MSHUTDOWN_FUNCTION(pqtypes)
{
zend_hash_destroy(&php_pqtypes_object_prophandlers);
return SUCCESS;
}
PHP_MINIT_FUNCTION(pqtypes)
{
zend_class_entry ce = {0};
php_pq_object_prophandler_t ph = {0};
INIT_NS_CLASS_ENTRY(ce, "pq", "Types", php_pqtypes_methods);
php_pqtypes_class_entry = zend_register_internal_class_ex(&ce, NULL);
php_pqtypes_class_entry->create_object = php_pqtypes_create_object;
/*
zend_class_implements(php_pqtypes_class_entry, 1, zend_ce_arrayaccess);
*/
memcpy(&php_pqtypes_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
php_pqtypes_object_handlers.offset = XtOffsetOf(php_pqtypes_object_t, zo);
php_pqtypes_object_handlers.free_obj = php_pqtypes_object_free;
php_pqtypes_object_handlers.read_property = php_pq_object_read_prop;
php_pqtypes_object_handlers.write_property = php_pq_object_write_prop;
php_pqtypes_object_handlers.clone_obj = NULL;
php_pqtypes_object_handlers.get_property_ptr_ptr = NULL;
php_pqtypes_object_handlers.get_gc = php_pq_object_get_gc;
php_pqtypes_object_handlers.get_properties = php_pq_object_properties;
php_pqtypes_object_handlers.get_debug_info = php_pq_object_debug_info;
php_pqtypes_object_handlers.has_dimension = php_pqtypes_object_has_dimension;
php_pqtypes_object_handlers.read_dimension = php_pqtypes_object_read_dimension;
php_pqtypes_object_handlers.unset_dimension = NULL;
php_pqtypes_object_handlers.write_dimension = NULL;
zend_hash_init(&php_pqtypes_object_prophandlers, 1, NULL, php_pq_object_prophandler_dtor, 1);
zend_declare_property_null(php_pqtypes_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC);
ph.read = php_pqtypes_object_read_connection;
ph.gc = php_pqtypes_object_gc_connection;
zend_hash_str_add_mem(&php_pqtypes_object_prophandlers, "connection", sizeof("connection")-1, (void *) &ph, sizeof(ph));
ph.gc = NULL;
# undef PHP_PQ_TYPE
# define PHP_PQ_TYPE(name, oid) zend_declare_class_constant_long(php_pqtypes_class_entry, ZEND_STRL(name), oid);
# include "php_pq_type.h"
return SUCCESS;
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/