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