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