Merge branch 'v1.1.x'
[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 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_construct, 0, 0, 1)
147 ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
148 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
149 ZEND_END_ARG_INFO();
150 static PHP_METHOD(pqtypes, __construct) {
151 zend_error_handling zeh;
152 zval *zconn, *znsp = NULL;
153 ZEND_RESULT_CODE rv;
154
155 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
156 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "O|a!", &zconn, php_pqconn_class_entry, &znsp);
157 zend_restore_error_handling(&zeh);
158
159 if (SUCCESS == rv) {
160 php_pqconn_object_t *conn_obj = PHP_PQ_OBJ(zconn, NULL);
161
162 if (!conn_obj->intern) {
163 throw_exce(EX_UNINITIALIZED, "pq\\Connection not initialized");
164 } else {
165 php_pqtypes_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
166
167 obj->intern = ecalloc(1, sizeof(*obj->intern));
168 obj->intern->conn = conn_obj;
169 php_pq_object_addref(conn_obj);
170 zend_hash_init(&obj->intern->types, 512, NULL, ZVAL_PTR_DTOR, 0);
171
172 if (znsp) {
173 zend_call_method_with_1_params(getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", NULL, znsp);
174 } else {
175 zend_call_method_with_0_params(getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", NULL);
176 }
177 }
178 }
179 }
180
181 #define PHP_PQ_TYPES_QUERY \
182 "select t.oid, t.* " \
183 "from pg_type t join pg_namespace n on t.typnamespace=n.oid " \
184 "where typisdefined " \
185 "and typrelid=0"
186 #ifndef PHP_PQ_OID_TEXT
187 # define PHP_PQ_OID_TEXT 25
188 #endif
189
190 static int apply_nsp(zval *zp, int argc, va_list argv, zend_hash_key *key)
191 {
192 unsigned pcount, tcount;
193 php_pq_params_t *params = va_arg(argv, php_pq_params_t *);
194 smart_str *str = va_arg(argv, smart_str *);
195
196 tcount = php_pq_params_add_type_oid(params, PHP_PQ_OID_TEXT);
197 pcount = php_pq_params_add_param(params, zp);
198
199 if (tcount != pcount) {
200 php_error_docref(NULL, E_WARNING, "Param/Type count mismatch");
201 return ZEND_HASH_APPLY_STOP;
202 }
203 if (pcount > 1) {
204 smart_str_appendc(str, ',');
205 }
206 smart_str_appendc(str, '$');
207 smart_str_append_unsigned(str, pcount);
208
209 return ZEND_HASH_APPLY_KEEP;
210 }
211
212 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0)
213 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
214 ZEND_END_ARG_INFO();
215 static PHP_METHOD(pqtypes, refresh) {
216 HashTable *nsp = NULL;
217 zend_error_handling zeh;
218 ZEND_RESULT_CODE rv;
219
220 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
221 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "|H/!", &nsp);
222 zend_restore_error_handling(&zeh);
223
224 if (SUCCESS == rv) {
225 php_pqtypes_object_t *obj = PHP_PQ_OBJ(getThis(), NULL);
226
227 if (!obj->intern) {
228 throw_exce(EX_UNINITIALIZED, "pq\\Types not initialized");
229 } else {
230 PGresult *res;
231
232 if (!nsp || !zend_hash_num_elements(nsp)) {
233 res = php_pq_exec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY " and nspname in ('public', 'pg_catalog')");
234 } else {
235 smart_str str = {0};
236 php_pq_params_t *params = php_pq_params_init(&obj->intern->conn->intern->converters, NULL, NULL);
237
238 smart_str_appends(&str, PHP_PQ_TYPES_QUERY " and nspname in(");
239 zend_hash_apply_with_arguments(nsp, apply_nsp, 2, params, &str);
240 smart_str_appendc(&str, ')');
241 smart_str_0(&str);
242
243 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);
244
245 smart_str_free(&str);
246 php_pq_params_free(&params);
247 }
248
249 if (!res) {
250 throw_exce(EX_RUNTIME, "Failed to fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
251 } else {
252 if (SUCCESS == php_pqres_success(res)) {
253 int r, rows;
254
255 for (r = 0, rows = PQntuples(res); r < rows; ++r) {
256 zval tmp, *row;
257
258 ZVAL_NULL(&tmp);
259 row = php_pqres_row_to_zval(res, r, PHP_PQRES_FETCH_OBJECT, &tmp);
260 Z_ADDREF_P(row);
261
262 zend_hash_index_update(&obj->intern->types, atol(PQgetvalue(res, r, 0 )), row);
263 zend_hash_str_update(&obj->intern->types, PQgetvalue(res, r, 1), PQgetlength(res, r, 1), row);
264 }
265 }
266
267 php_pqres_clear(res);
268 php_pqconn_notify_listeners(obj->intern->conn);
269 }
270 }
271 }
272 }
273
274 static zend_function_entry php_pqtypes_methods[] = {
275 PHP_ME(pqtypes, __construct, ai_pqtypes_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
276 PHP_ME(pqtypes, refresh, ai_pqtypes_refresh, ZEND_ACC_PUBLIC)
277 {0}
278 };
279
280 PHP_MSHUTDOWN_FUNCTION(pqtypes)
281 {
282 zend_hash_destroy(&php_pqtypes_object_prophandlers);
283 return SUCCESS;
284 }
285
286 PHP_MINIT_FUNCTION(pqtypes)
287 {
288 zend_class_entry ce = {0};
289 php_pq_object_prophandler_t ph = {0};
290
291 INIT_NS_CLASS_ENTRY(ce, "pq", "Types", php_pqtypes_methods);
292 php_pqtypes_class_entry = zend_register_internal_class_ex(&ce, NULL);
293 php_pqtypes_class_entry->create_object = php_pqtypes_create_object;
294
295 /*
296 zend_class_implements(php_pqtypes_class_entry, 1, zend_ce_arrayaccess);
297 */
298
299 memcpy(&php_pqtypes_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
300 php_pqtypes_object_handlers.offset = XtOffsetOf(php_pqtypes_object_t, zo);
301 php_pqtypes_object_handlers.free_obj = php_pqtypes_object_free;
302 php_pqtypes_object_handlers.read_property = php_pq_object_read_prop;
303 php_pqtypes_object_handlers.write_property = php_pq_object_write_prop;
304 php_pqtypes_object_handlers.clone_obj = NULL;
305 php_pqtypes_object_handlers.get_property_ptr_ptr = NULL;
306 php_pqtypes_object_handlers.get_gc = php_pq_object_get_gc;
307 php_pqtypes_object_handlers.get_properties = php_pq_object_properties;
308 php_pqtypes_object_handlers.get_debug_info = php_pq_object_debug_info;
309 php_pqtypes_object_handlers.has_dimension = php_pqtypes_object_has_dimension;
310 php_pqtypes_object_handlers.read_dimension = php_pqtypes_object_read_dimension;
311 php_pqtypes_object_handlers.unset_dimension = NULL;
312 php_pqtypes_object_handlers.write_dimension = NULL;
313
314 zend_hash_init(&php_pqtypes_object_prophandlers, 1, NULL, php_pq_object_prophandler_dtor, 1);
315
316 zend_declare_property_null(php_pqtypes_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC);
317 ph.read = php_pqtypes_object_read_connection;
318 ph.gc = php_pqtypes_object_gc_connection;
319 zend_hash_str_add_mem(&php_pqtypes_object_prophandlers, "connection", sizeof("connection")-1, (void *) &ph, sizeof(ph));
320 ph.gc = NULL;
321
322 # undef PHP_PQ_TYPE
323 # define PHP_PQ_TYPE(name, oid) zend_declare_class_constant_long(php_pqtypes_class_entry, ZEND_STRL(name), oid);
324 # include "php_pq_type.h"
325
326 return SUCCESS;
327 }
328
329 /*
330 * Local variables:
331 * tab-width: 4
332 * c-basic-offset: 4
333 * End:
334 * vim600: noet sw=4 ts=4 fdm=marker
335 * vim<600: noet sw=4 ts=4
336 */