Merge pull request #33 from m6w6/fix-19
[m6w6/ext-pq] / src / php_pqtypes.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: pq |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <php.h>
18 #include <Zend/zend_smart_str.h>
19
20 #include "php_pq.h"
21 #include "php_pq_misc.h"
22 #include "php_pq_object.h"
23 #include "php_pqexc.h"
24 #include "php_pqres.h"
25 #include "php_pqtypes.h"
26
27 zend_class_entry *php_pqtypes_class_entry;
28 static zend_object_handlers php_pqtypes_object_handlers;
29 static HashTable php_pqtypes_object_prophandlers;
30
31 static void php_pqtypes_object_free(zend_object *o)
32 {
33 php_pqtypes_object_t *obj = PHP_PQ_OBJ(NULL, o);
34 #if DBG_GC
35 fprintf(stderr, "FREE types(#%d) %p (conn(#%d): %p)\n", obj->zo.handle, obj, obj->intern->conn->zo.handle, obj->intern->conn);
36 #endif
37 if (obj->intern) {
38 zend_hash_destroy(&obj->intern->types);
39 php_pq_object_delref(obj->intern->conn);
40 efree(obj->intern);
41 obj->intern = NULL;
42 }
43 php_pq_object_dtor(o);
44 }
45
46 php_pqtypes_object_t *php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern)
47 {
48 return php_pq_object_create(ce, intern, sizeof(php_pqtypes_object_t),
49 &php_pqtypes_object_handlers, &php_pqtypes_object_prophandlers);
50 }
51
52 static zend_object *php_pqtypes_create_object(zend_class_entry *class_type)
53 {
54 return &php_pqtypes_create_object_ex(class_type, NULL)->zo;
55 }
56
57 static void php_pqtypes_object_read_connection(zval *object, void *o, zval *return_value)
58 {
59 php_pqtypes_object_t *obj = o;
60
61 php_pq_object_to_zval(obj->intern->conn, return_value);
62 }
63 static void php_pqtypes_object_gc_connection(zval *object, void *o, zval *return_value)
64 {
65 php_pqtypes_object_t *obj = o;
66 zval zconn;
67
68 php_pq_object_to_zval_no_addref(obj->intern->conn, &zconn);
69 add_next_index_zval(return_value, &zconn);
70 }
71
72 static int has_dimension(HashTable *ht, zval *member, zend_string **key, zend_long *index)
73 {
74 if (Z_TYPE_P(member) == IS_LONG) {
75 *index = Z_LVAL_P(member);
76
77 check_index:
78 return zend_hash_index_exists(ht, *index);
79 } else {
80 zend_string *str = zval_get_string(member);
81
82 if (is_numeric_str_function(str, index, NULL)) {
83 zend_string_release(str);
84 goto check_index;
85 }
86
87 if (zend_hash_exists(ht, str)) {
88 *key = str;
89 return 1;
90 }
91
92 zend_string_release(str);
93 return 0;
94 }
95 }
96
97 static int php_pqtypes_object_has_dimension(zval *object, zval *member, int check_empty)
98 {
99 php_pqtypes_object_t *obj = PHP_PQ_OBJ(object, NULL);
100 zend_string *key = NULL;
101 zend_long index = 0;
102
103 if (has_dimension(&obj->intern->types, member, &key, &index)) {
104 if (check_empty) {
105 zval *data;
106
107 if (key) {
108 if ((data = zend_hash_find(&obj->intern->types, key))) {
109 zend_string_release(key);
110 return Z_TYPE_P(data) != IS_NULL;
111 }
112 zend_string_release(key);
113 } else if ((data = zend_hash_index_find(&obj->intern->types, index))) {
114 return Z_TYPE_P(data) != IS_NULL;
115 }
116 } else {
117 if (key) {
118 zend_string_release(key);
119 }
120 return 1;
121 }
122 }
123
124 return 0;
125 }
126
127 static zval *php_pqtypes_object_read_dimension(zval *object, zval *member, int type, zval *rv)
128 {
129 php_pqtypes_object_t *obj = PHP_PQ_OBJ(object, NULL);
130 zend_string *key = NULL;
131 zend_long index = 0;
132 zval *data = NULL;
133
134 if (has_dimension(&obj->intern->types, member, &key, &index)) {
135 if (key) {
136 data = zend_hash_find(&obj->intern->types, key);
137 zend_string_release(key);
138 } else {
139 data = zend_hash_index_find(&obj->intern->types, index);
140 }
141 }
142
143 return data;
144 }
145
146 static void php_pqtypes_object_write_dimension(zval *object, zval *offset, zval *value)
147 {
148 throw_exce(EX_RUNTIME, "pq\\Types object must not be modified");
149 }
150
151 static void php_pqtypes_object_unset_dimension(zval *object, zval *offset)
152 {
153 throw_exce(EX_RUNTIME, "pq\\Types object must not be modified");
154 }
155
156 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_construct, 0, 0, 1)
157 ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
158 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
159 ZEND_END_ARG_INFO();
160 static PHP_METHOD(pqtypes, __construct) {
161 zend_error_handling zeh;
162 zval *zconn, *znsp = NULL;
163 ZEND_RESULT_CODE rv;
164
165 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
166 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "O|a!", &zconn, php_pqconn_class_entry, &znsp);
167 zend_restore_error_handling(&zeh);
168
169 if (SUCCESS == rv) {
170 php_pqconn_object_t *conn_obj = PHP_PQ_OBJ(zconn, NULL);
171
172 if (!conn_obj->intern) {
173 throw_exce(EX_UNINITIALIZED, "pq\\Connection not initialized");
174 } else {
175 php_pqtypes_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
176
177 obj->intern = ecalloc(1, sizeof(*obj->intern));
178 obj->intern->conn = conn_obj;
179 php_pq_object_addref(conn_obj);
180 zend_hash_init(&obj->intern->types, 512, NULL, ZVAL_PTR_DTOR, 0);
181
182 if (znsp) {
183 zend_call_method_with_1_params(getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", NULL, znsp);
184 } else {
185 zend_call_method_with_0_params(getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", NULL);
186 }
187 }
188 }
189 }
190
191 #define PHP_PQ_TYPES_QUERY \
192 "select t.oid, t.* " \
193 "from pg_type t join pg_namespace n on t.typnamespace=n.oid " \
194 "where typisdefined"
195 #ifndef PHP_PQ_OID_TEXT
196 # define PHP_PQ_OID_TEXT 25
197 #endif
198
199 static int apply_nsp(zval *zp, int argc, va_list argv, zend_hash_key *key)
200 {
201 unsigned pcount, tcount;
202 php_pq_params_t *params = va_arg(argv, php_pq_params_t *);
203 smart_str *str = va_arg(argv, smart_str *);
204
205 tcount = php_pq_params_add_type_oid(params, PHP_PQ_OID_TEXT);
206 pcount = php_pq_params_add_param(params, zp);
207
208 if (tcount != pcount) {
209 php_error_docref(NULL, E_WARNING, "Param/Type count mismatch");
210 return ZEND_HASH_APPLY_STOP;
211 }
212 if (pcount > 1) {
213 smart_str_appendc(str, ',');
214 }
215 smart_str_appendc(str, '$');
216 smart_str_append_unsigned(str, pcount);
217
218 return ZEND_HASH_APPLY_KEEP;
219 }
220
221 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0)
222 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
223 ZEND_END_ARG_INFO();
224 static PHP_METHOD(pqtypes, refresh) {
225 HashTable *nsp = NULL;
226 zend_error_handling zeh;
227 ZEND_RESULT_CODE rv;
228
229 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
230 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|H/!", &nsp);
231 zend_restore_error_handling(&zeh);
232
233 if (SUCCESS == rv) {
234 php_pqtypes_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
235
236 if (!obj->intern) {
237 throw_exce(EX_UNINITIALIZED, "pq\\Types not initialized");
238 } else {
239 PGresult *res;
240
241 if (!nsp || !zend_hash_num_elements(nsp)) {
242 res = php_pq_exec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY " and nspname in ('public', 'pg_catalog')");
243 } else {
244 smart_str str = {0};
245 php_pq_params_t *params = php_pq_params_init(&obj->intern->conn->intern->converters, NULL, NULL);
246
247 smart_str_appends(&str, PHP_PQ_TYPES_QUERY " and nspname in(");
248 zend_hash_apply_with_arguments(nsp, apply_nsp, 2, params, &str);
249 smart_str_appendc(&str, ')');
250 smart_str_0(&str);
251
252 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);
253
254 smart_str_free(&str);
255 php_pq_params_free(&params);
256 }
257
258 if (!res) {
259 throw_exce(EX_RUNTIME, "Failed to fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
260 } else {
261 if (SUCCESS == php_pqres_success(res)) {
262 int r, rows;
263
264 for (r = 0, rows = PQntuples(res); r < rows; ++r) {
265 zval tmp, *row;
266
267 ZVAL_NULL(&tmp);
268 row = php_pqres_row_to_zval(res, r, PHP_PQRES_FETCH_OBJECT, &tmp);
269 Z_ADDREF_P(row);
270
271 zend_hash_index_update(&obj->intern->types, atol(PQgetvalue(res, r, 0 )), row);
272 zend_hash_str_update(&obj->intern->types, PQgetvalue(res, r, 1), PQgetlength(res, r, 1), row);
273 }
274 }
275
276 php_pqres_clear(res);
277 php_pqconn_notify_listeners(obj->intern->conn);
278 }
279 }
280 }
281 }
282
283 static zend_function_entry php_pqtypes_methods[] = {
284 PHP_ME(pqtypes, __construct, ai_pqtypes_construct, ZEND_ACC_PUBLIC)
285 PHP_ME(pqtypes, refresh, ai_pqtypes_refresh, ZEND_ACC_PUBLIC)
286 {0}
287 };
288
289 PHP_MSHUTDOWN_FUNCTION(pqtypes)
290 {
291 zend_hash_destroy(&php_pqtypes_object_prophandlers);
292 return SUCCESS;
293 }
294
295 PHP_MINIT_FUNCTION(pqtypes)
296 {
297 zend_class_entry ce = {0};
298 php_pq_object_prophandler_t ph = {0};
299
300 INIT_NS_CLASS_ENTRY(ce, "pq", "Types", php_pqtypes_methods);
301 php_pqtypes_class_entry = zend_register_internal_class_ex(&ce, NULL);
302 php_pqtypes_class_entry->create_object = php_pqtypes_create_object;
303
304 /*
305 zend_class_implements(php_pqtypes_class_entry, 1, zend_ce_arrayaccess);
306 */
307
308 memcpy(&php_pqtypes_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
309 php_pqtypes_object_handlers.offset = XtOffsetOf(php_pqtypes_object_t, zo);
310 php_pqtypes_object_handlers.free_obj = php_pqtypes_object_free;
311 php_pqtypes_object_handlers.read_property = php_pq_object_read_prop;
312 php_pqtypes_object_handlers.write_property = php_pq_object_write_prop;
313 php_pqtypes_object_handlers.clone_obj = NULL;
314 php_pqtypes_object_handlers.get_property_ptr_ptr = php_pq_object_get_prop_ptr_null;
315 php_pqtypes_object_handlers.get_gc = php_pq_object_get_gc;
316 php_pqtypes_object_handlers.get_properties = php_pq_object_properties;
317 php_pqtypes_object_handlers.get_debug_info = php_pq_object_debug_info;
318 php_pqtypes_object_handlers.has_dimension = php_pqtypes_object_has_dimension;
319 php_pqtypes_object_handlers.read_dimension = php_pqtypes_object_read_dimension;
320 php_pqtypes_object_handlers.unset_dimension = php_pqtypes_object_unset_dimension;
321 php_pqtypes_object_handlers.write_dimension = php_pqtypes_object_write_dimension;
322
323 zend_hash_init(&php_pqtypes_object_prophandlers, 1, NULL, php_pq_object_prophandler_dtor, 1);
324
325 zend_declare_property_null(php_pqtypes_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC);
326 ph.read = php_pqtypes_object_read_connection;
327 ph.gc = php_pqtypes_object_gc_connection;
328 zend_hash_str_add_mem(&php_pqtypes_object_prophandlers, "connection", sizeof("connection")-1, (void *) &ph, sizeof(ph));
329 ph.gc = NULL;
330
331 # undef PHP_PQ_TYPE
332 # define PHP_PQ_TYPE(name, oid) zend_declare_class_constant_long(php_pqtypes_class_entry, ZEND_STRL(name), oid);
333 # include "php_pq_type.h"
334
335 return SUCCESS;
336 }
337
338 /*
339 * Local variables:
340 * tab-width: 4
341 * c-basic-offset: 4
342 * End:
343 * vim600: noet sw=4 ts=4 fdm=marker
344 * vim<600: noet sw=4 ts=4
345 */