type converters
[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 <ext/standard/php_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(void *o TSRMLS_DC)
32 {
33 php_pqtypes_object_t *obj = o;
34 #if DBG_GC
35 fprintf(stderr, "FREE types(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.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 TSRMLS_CC);
40 efree(obj->intern);
41 obj->intern = NULL;
42 }
43 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
44 efree(obj);
45 }
46
47 zend_object_value php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern, php_pqtypes_object_t **ptr TSRMLS_DC)
48 {
49 php_pqtypes_object_t *o;
50
51 o = ecalloc(1, sizeof(*o));
52 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
53 object_properties_init((zend_object *) o, ce);
54 o->prophandler = &php_pqtypes_object_prophandlers;
55
56 if (ptr) {
57 *ptr = o;
58 }
59
60 if (intern) {
61 o->intern = intern;
62 }
63
64 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqtypes_object_free, NULL TSRMLS_CC);
65 o->zv.handlers = &php_pqtypes_object_handlers;
66
67 return o->zv;
68 }
69
70 static zend_object_value php_pqtypes_create_object(zend_class_entry *class_type TSRMLS_DC)
71 {
72 return php_pqtypes_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
73 }
74
75 static void php_pqtypes_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
76 {
77 php_pqtypes_object_t *obj = o;
78
79 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
80 }
81
82 static int has_dimension(HashTable *ht, zval *member, char **key_str, int *key_len, ulong *index TSRMLS_DC)
83 {
84 long lval = 0;
85 zval *tmp = member;
86
87 switch (Z_TYPE_P(member)) {
88 default:
89 convert_to_string_ex(&tmp);
90 /* no break */
91 case IS_STRING:
92 if (!is_numeric_string(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &lval, NULL, 0)) {
93 int exists = zend_hash_exists(ht, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1);
94
95 if (key_str) {
96 *key_str = estrndup(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
97 if (key_len) {
98 *key_len = Z_STRLEN_P(tmp) + 1;
99 }
100 }
101 if (member != tmp) {
102 zval_ptr_dtor(&tmp);
103 }
104
105 return exists;
106 }
107 break;
108 case IS_LONG:
109 lval = Z_LVAL_P(member);
110 break;
111 }
112
113 if (member != tmp) {
114 zval_ptr_dtor(&tmp);
115 }
116 if (index) {
117 *index = lval;
118 }
119 return zend_hash_index_exists(ht, lval);
120 }
121
122 static int php_pqtypes_object_has_dimension(zval *object, zval *member, int check_empty TSRMLS_DC)
123 {
124 php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
125 char *key_str = NULL;
126 int key_len = 0;
127 ulong index = 0;
128
129 if (check_empty) {
130 if (has_dimension(&obj->intern->types, member, &key_str, &key_len, &index TSRMLS_CC)) {
131 zval **data;
132
133 if (key_str && key_len) {
134 if (SUCCESS == zend_hash_find(&obj->intern->types, key_str, key_len, (void *) &data)) {
135 efree(key_str);
136 return Z_TYPE_PP(data) != IS_NULL;
137 }
138 efree(key_str);
139 } else {
140 if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) &data)) {
141 return Z_TYPE_PP(data) != IS_NULL;
142 }
143 }
144 }
145 if (key_str) {
146 efree(key_str);
147 }
148 } else {
149 return has_dimension(&obj->intern->types, member, NULL, NULL, NULL TSRMLS_CC);
150 }
151
152 return 0;
153 }
154
155 static zval *php_pqtypes_object_read_dimension(zval *object, zval *member, int type TSRMLS_DC)
156 {
157 ulong index = 0;
158 char *key_str = NULL;
159 int key_len = 0;
160 php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
161
162 if (has_dimension(&obj->intern->types, member, &key_str, &key_len, &index TSRMLS_CC)) {
163 zval **data;
164
165 if (key_str && key_len) {
166 if (SUCCESS == zend_hash_find(&obj->intern->types, key_str, key_len, (void *) &data)) {
167 efree(key_str);
168 return *data;
169 }
170 } else {
171 if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) &data)) {
172 return *data;
173 }
174 }
175 if (key_str) {
176 efree(key_str);
177 }
178 }
179
180 return NULL;
181 }
182
183 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_construct, 0, 0, 1)
184 ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
185 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
186 ZEND_END_ARG_INFO();
187 static PHP_METHOD(pqtypes, __construct) {
188 zend_error_handling zeh;
189 zval *zconn, *znsp = NULL;
190 STATUS rv;
191
192 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
193 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|a!", &zconn, php_pqconn_class_entry, &znsp);
194 zend_restore_error_handling(&zeh TSRMLS_CC);
195
196 if (SUCCESS == rv) {
197 php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
198
199 if (!conn_obj->intern) {
200 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
201 } else {
202 php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
203 zval *retval = NULL;
204
205 obj->intern = ecalloc(1, sizeof(*obj->intern));
206 obj->intern->conn = conn_obj;
207 php_pq_object_addref(conn_obj TSRMLS_CC);
208 zend_hash_init(&obj->intern->types, 300, NULL, ZVAL_PTR_DTOR, 0);
209
210 if (znsp) {
211 zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval, znsp);
212 } else {
213 zend_call_method_with_0_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval);
214 }
215
216 if (retval) {
217 zval_ptr_dtor(&retval);
218 }
219 }
220 }
221 }
222
223 #define PHP_PQ_TYPES_QUERY \
224 "select t.oid, t.* " \
225 "from pg_type t join pg_namespace n on t.typnamespace=n.oid " \
226 "where typisdefined " \
227 "and typrelid=0"
228 #ifndef PHP_PQ_OID_TEXT
229 # define PHP_PQ_OID_TEXT 25
230 #endif
231
232 static int apply_nsp(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
233 {
234 zval **zp = p;
235 unsigned pcount, tcount;
236 php_pq_params_t *params = va_arg(argv, php_pq_params_t *);
237 smart_str *str = va_arg(argv, smart_str *);
238
239 tcount = php_pq_params_add_type_oid(params, PHP_PQ_OID_TEXT);
240 pcount = php_pq_params_add_param(params, *zp);
241
242 if (tcount != pcount) {
243 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Param/Type count mismatch");
244 return ZEND_HASH_APPLY_STOP;
245 }
246 if (pcount > 1) {
247 smart_str_appendc(str, ',');
248 }
249 smart_str_appendc(str, '$');
250 smart_str_append_unsigned(str, pcount);
251
252 return ZEND_HASH_APPLY_KEEP;
253 }
254
255 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0)
256 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
257 ZEND_END_ARG_INFO();
258 static PHP_METHOD(pqtypes, refresh) {
259 HashTable *nsp = NULL;
260 zend_error_handling zeh;
261 STATUS rv;
262
263 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
264 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H/!", &nsp);
265 zend_restore_error_handling(&zeh TSRMLS_CC);
266
267 if (SUCCESS == rv) {
268 php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
269
270 if (!obj->intern) {
271 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Types not initialized");
272 } else {
273 PGresult *res;
274
275 if (!nsp || !zend_hash_num_elements(nsp)) {
276 res = PQexec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY " and nspname in ('public', 'pg_catalog')");
277 } else {
278 smart_str str = {0};
279 php_pq_params_t *params = php_pq_params_init(&obj->intern->conn->intern->converters, NULL, NULL TSRMLS_CC);
280
281 smart_str_appends(&str, PHP_PQ_TYPES_QUERY " and nspname in(");
282 zend_hash_apply_with_arguments(nsp TSRMLS_CC, apply_nsp, 2, params, &str);
283 smart_str_appendc(&str, ')');
284 smart_str_0(&str);
285
286 res = PQexecParams(obj->intern->conn->intern->conn, str.c, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0);
287
288 smart_str_free(&str);
289 php_pq_params_free(&params);
290 }
291
292 if (!res) {
293 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
294 } else {
295 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
296 int r, rows;
297
298 for (r = 0, rows = PQntuples(res); r < rows; ++r) {
299 zval *row = php_pqres_row_to_zval(res, r, PHP_PQRES_FETCH_OBJECT, NULL TSRMLS_CC);
300 long oid = atol(PQgetvalue(res, r, 0 ));
301 char *name = PQgetvalue(res, r, 1);
302
303 Z_ADDREF_P(row);
304
305 zend_hash_index_update(&obj->intern->types, oid, (void *) &row, sizeof(zval *), NULL);
306 zend_hash_update(&obj->intern->types, name, strlen(name) + 1, (void *) &row, sizeof(zval *), NULL);
307 }
308 }
309
310 PHP_PQclear(res);
311 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
312 }
313 }
314 }
315 }
316
317 static zend_function_entry php_pqtypes_methods[] = {
318 PHP_ME(pqtypes, __construct, ai_pqtypes_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
319 PHP_ME(pqtypes, refresh, ai_pqtypes_refresh, ZEND_ACC_PUBLIC)
320 {0}
321 };
322
323 PHP_MSHUTDOWN_FUNCTION(pqtypes)
324 {
325 zend_hash_destroy(&php_pqtypes_object_prophandlers);
326 return SUCCESS;
327 }
328
329 PHP_MINIT_FUNCTION(pqtypes)
330 {
331 zend_class_entry ce = {0};
332 php_pq_object_prophandler_t ph = {0};
333
334 INIT_NS_CLASS_ENTRY(ce, "pq", "Types", php_pqtypes_methods);
335 php_pqtypes_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
336 php_pqtypes_class_entry->create_object = php_pqtypes_create_object;
337
338 memcpy(&php_pqtypes_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
339 php_pqtypes_object_handlers.read_property = php_pq_object_read_prop;
340 php_pqtypes_object_handlers.write_property = php_pq_object_write_prop;
341 php_pqtypes_object_handlers.clone_obj = NULL;
342 php_pqtypes_object_handlers.get_property_ptr_ptr = NULL;
343 php_pqtypes_object_handlers.get_gc = NULL;
344 php_pqtypes_object_handlers.get_properties = php_pq_object_properties;
345 php_pqtypes_object_handlers.get_debug_info = php_pq_object_debug_info;
346 php_pqtypes_object_handlers.has_dimension = php_pqtypes_object_has_dimension;
347 php_pqtypes_object_handlers.read_dimension = php_pqtypes_object_read_dimension;
348 php_pqtypes_object_handlers.unset_dimension = NULL;
349 php_pqtypes_object_handlers.write_dimension = NULL;
350
351 zend_hash_init(&php_pqtypes_object_prophandlers, 1, NULL, NULL, 1);
352
353 zend_declare_property_null(php_pqtypes_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC);
354 ph.read = php_pqtypes_object_read_connection;
355 zend_hash_add(&php_pqtypes_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
356
357 #ifdef HAVE_PHP_PQ_TYPE_H
358 # undef PHP_PQ_TYPE
359 # define PHP_PQ_TYPE(name, oid) zend_declare_class_constant_long(php_pqtypes_class_entry, ZEND_STRL(name), oid TSRMLS_CC);
360 # include "php_pq_type.h"
361 zend_declare_class_constant_bool(php_pqtypes_class_entry, ZEND_STRL("DEFINED"), 1 TSRMLS_CC);
362 #else
363 zend_declare_class_constant_bool(php_pqtypes_class_entry, ZEND_STRL("DEFINED"), 0 TSRMLS_CC);
364 #endif
365
366 return SUCCESS;
367 }
368
369 /*
370 * Local variables:
371 * tab-width: 4
372 * c-basic-offset: 4
373 * End:
374 * vim600: noet sw=4 ts=4 fdm=marker
375 * vim<600: noet sw=4 ts=4
376 */