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