7208550a999d77fa025a5e23236fd20e7bb001fb
[m6w6/ext-pq] / src / php_pq_params.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_string.h>
19 #include <ext/standard/php_smart_str.h>
20 #include <ext/json/php_json.h>
21
22 #include <Zend/zend_interfaces.h>
23
24 #include <libpq-fe.h>
25
26 #include "php_pq.h"
27 #include "php_pq_params.h"
28 #include "php_pq_misc.h"
29 #undef PHP_PQ_TYPE
30 #include "php_pq_type.h"
31
32 void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv)
33 {
34 zend_hash_clean(&p->type.conv);
35 zend_hash_copy(&p->type.conv, conv, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
36 }
37
38 static int apply_to_oid(void *p, void *arg TSRMLS_DC)
39 {
40 Oid **types = arg;
41 zval **ztype = p;
42
43 if (Z_TYPE_PP(ztype) != IS_LONG) {
44 convert_to_long_ex(ztype);
45 }
46
47 **types = Z_LVAL_PP(ztype);
48 ++*types;
49
50 if (*ztype != *(zval **)p) {
51 zval_ptr_dtor(ztype);
52 }
53 return ZEND_HASH_APPLY_KEEP;
54 }
55
56 unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids)
57 {
58 p->type.count = oids ? zend_hash_num_elements(oids) : 0;
59 TSRMLS_DF(p);
60
61 if (p->type.oids) {
62 efree(p->type.oids);
63 p->type.oids = NULL;
64 }
65 if (p->type.count) {
66 Oid *ptr = ecalloc(p->type.count + 1, sizeof(*p->type.oids));
67 /* +1 for when less types than params are specified */
68 p->type.oids = ptr;
69 zend_hash_apply_with_argument(oids, apply_to_oid, &ptr TSRMLS_CC);
70 }
71 return p->type.count;
72 }
73
74 unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type)
75 {
76 p->type.oids = safe_erealloc(p->type.oids, ++p->type.count, sizeof(*p->type.oids), sizeof(*p->type.oids));
77 p->type.oids[p->type.count] = 0;
78 p->type.oids[p->type.count-1] = type;
79 return p->type.count;
80 }
81
82
83 static zval *object_param_to_string(php_pq_params_t *p, zval *zobj, Oid type TSRMLS_DC)
84 {
85 zval *return_value = NULL;
86 smart_str str = {0};
87
88 switch (type) {
89 #ifdef PHP_PQ_OID_JSON
90 # ifdef PHP_PQ_OID_JSONB
91 case PHP_PQ_OID_JSONB:
92 # endif
93 case PHP_PQ_OID_JSON:
94 php_json_encode(&str, zobj, PHP_JSON_UNESCAPED_UNICODE TSRMLS_CC);
95 smart_str_0(&str);
96 break;
97 #endif
98
99 case PHP_PQ_OID_DATE:
100 php_pqdt_to_string(zobj, "Y-m-d", &str.c, &str.len TSRMLS_CC);
101 break;
102
103 case PHP_PQ_OID_ABSTIME:
104 php_pqdt_to_string(zobj, "Y-m-d H:i:s", &str.c, &str.len TSRMLS_CC);
105 break;
106
107 case PHP_PQ_OID_TIMESTAMP:
108 php_pqdt_to_string(zobj, "Y-m-d H:i:s.u", &str.c, &str.len TSRMLS_CC);
109 break;
110
111 case PHP_PQ_OID_TIMESTAMPTZ:
112 php_pqdt_to_string(zobj, "Y-m-d H:i:s.uO", &str.c, &str.len TSRMLS_CC);
113 break;
114
115 default:
116 SEPARATE_ZVAL(&zobj);
117 convert_to_string(zobj);
118 return_value = zobj;
119 break;
120 }
121
122 if (str.c) {
123 MAKE_STD_ZVAL(return_value);
124 RETVAL_STRINGL(str.c, str.len, 0);
125 }
126
127 return return_value;
128 }
129
130 struct apply_to_param_from_array_arg {
131 php_pq_params_t *params;
132 unsigned index;
133 smart_str *buffer;
134 Oid type;
135 zval **zconv;
136 };
137
138 static int apply_to_param_from_array(void *ptr, void *arg_ptr TSRMLS_DC)
139 {
140 struct apply_to_param_from_array_arg subarg, *arg = arg_ptr;
141 zval *ztmp, **zparam = ptr, *zcopy = *zparam;
142 char *tmp;
143 size_t len;
144 int tmp_len;
145
146 if (arg->index++) {
147 smart_str_appendc(arg->buffer, ',');
148 }
149
150 if (arg->zconv) {
151 zval *ztype, *rv = NULL;
152
153 MAKE_STD_ZVAL(ztype);
154 ZVAL_LONG(ztype, arg->type);
155 zend_call_method_with_2_params(arg->zconv, NULL, NULL, "converttostring", &rv, zcopy, ztype);
156 zval_ptr_dtor(&ztype);
157 convert_to_string(rv);
158 zcopy = rv;
159 goto append_string;
160 } else {
161 switch (Z_TYPE_P(zcopy)) {
162 case IS_NULL:
163 smart_str_appends(arg->buffer, "NULL");
164 break;
165
166 case IS_BOOL:
167 smart_str_appends(arg->buffer, Z_BVAL_P(zcopy) ? "t" : "f");
168 break;
169
170 case IS_LONG:
171 smart_str_append_long(arg->buffer, Z_LVAL_P(zcopy));
172 break;
173
174 case IS_DOUBLE:
175 len = spprintf(&tmp, 0, "%F", Z_DVAL_P(zcopy));
176 smart_str_appendl(arg->buffer, tmp, len);
177 efree(tmp);
178 break;
179
180 case IS_ARRAY:
181 subarg = *arg;
182 subarg.index = 0;
183 smart_str_appendc(arg->buffer, '{');
184 zend_hash_apply_with_argument(Z_ARRVAL_P(zcopy), apply_to_param_from_array, &subarg TSRMLS_CC);
185 smart_str_appendc(arg->buffer, '}');
186 break;
187
188 case IS_OBJECT:
189 if ((ztmp = object_param_to_string(arg->params, zcopy, arg->type TSRMLS_CC))) {
190 zcopy = ztmp;
191 }
192 /* no break */
193 default:
194 SEPARATE_ZVAL(&zcopy);
195 convert_to_string(zcopy);
196
197 append_string:
198 tmp = php_addslashes(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy), &tmp_len, 0 TSRMLS_CC);
199 smart_str_appendc(arg->buffer, '"');
200 smart_str_appendl(arg->buffer, tmp, tmp_len);
201 smart_str_appendc(arg->buffer, '"');
202
203 if (zcopy != *zparam) {
204 zval_ptr_dtor(&zcopy);
205 }
206 efree(tmp);
207 break;
208 }
209 }
210 ++arg->index;
211 return ZEND_HASH_APPLY_KEEP;
212 }
213
214 static zval *array_param_to_string(php_pq_params_t *p, zval *zarr, Oid type TSRMLS_DC)
215 {
216 zval *return_value;
217 smart_str s = {0};
218 struct apply_to_param_from_array_arg arg = {NULL};
219
220 switch (type) {
221 #ifdef PHP_PQ_OID_JSON
222 # ifdef PHP_PQ_OID_JSONB
223 case PHP_PQ_OID_JSONB:
224 # endif
225 case PHP_PQ_OID_JSON:
226 php_json_encode(&s, zarr, PHP_JSON_UNESCAPED_UNICODE TSRMLS_CC);
227 smart_str_0(&s);
228 break;
229 #endif
230
231 default:
232 arg.params = p;
233 arg.buffer = &s;
234 arg.type = PHP_PQ_TYPE_OF_ARRAY(type);
235 zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type), (void *) &arg.zconv);
236 smart_str_appendc(arg.buffer, '{');
237 zend_hash_apply_with_argument(Z_ARRVAL_P(zarr), apply_to_param_from_array, &arg TSRMLS_CC);
238 smart_str_appendc(arg.buffer, '}');
239 smart_str_0(&s);
240 break;
241 }
242
243 /* must not return NULL */
244 MAKE_STD_ZVAL(return_value);
245
246 if (s.c) {
247 RETVAL_STRINGL(s.c, s.len, 0);
248 } else {
249 RETVAL_EMPTY_STRING();
250 }
251
252 return return_value;
253 }
254
255 static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval **zpp)
256 {
257 zval **zconv = NULL;
258 Oid type = p->type.count > index ? p->type.oids[index] : 0;
259 TSRMLS_DF(p);
260
261 if (type && SUCCESS == zend_hash_index_find(&p->type.conv, type, (void *) &zconv)) {
262 zval *ztype, *rv = NULL;
263
264 MAKE_STD_ZVAL(ztype);
265 ZVAL_LONG(ztype, type);
266 zend_call_method_with_2_params(zconv, NULL, NULL, "converttostring", &rv, *zpp, ztype);
267 zval_ptr_dtor(&ztype);
268 convert_to_string(rv);
269 p->param.strings[index] = Z_STRVAL_P(rv);
270 zend_hash_next_index_insert(&p->param.dtor, (void *) &rv, sizeof(zval *), NULL);
271 } else {
272 zval *tmp, *zcopy = *zpp;
273
274 switch (Z_TYPE_P(zcopy)) {
275 case IS_NULL:
276 p->param.strings[index] = NULL;
277 return;
278
279 case IS_BOOL:
280 p->param.strings[index] = Z_BVAL_P(zcopy) ? "t" : "f";
281 return;
282
283 case IS_DOUBLE:
284 SEPARATE_ZVAL(&zcopy);
285 Z_TYPE_P(zcopy) = IS_STRING;
286 Z_STRLEN_P(zcopy) = spprintf(&Z_STRVAL_P(zcopy), 0, "%F", Z_DVAL_PP(zpp));
287 break;
288
289 case IS_ARRAY:
290 zcopy = array_param_to_string(p, zcopy, type TSRMLS_CC);
291 break;
292
293 case IS_OBJECT:
294 if ((tmp = object_param_to_string(p, zcopy, type TSRMLS_CC))) {
295 zcopy = tmp;
296 break;
297 }
298 /* no break */
299
300 default:
301 convert_to_string_ex(&zcopy);
302 break;
303 }
304
305 p->param.strings[index] = Z_STRVAL_P(zcopy);
306
307 if (zcopy != *zpp) {
308 zend_hash_next_index_insert(&p->param.dtor, (void *) &zcopy, sizeof(zval *), NULL);
309 }
310 }
311 }
312
313 static int apply_to_params(void *zp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
314 {
315 php_pq_params_t *p = (php_pq_params_t *) va_arg(argv, php_pq_params_t *);
316 unsigned *index = (unsigned *) va_arg(argv, unsigned *);
317
318 php_pq_params_set_param(p, (*index)++, zp);
319 return ZEND_HASH_APPLY_KEEP;
320 }
321
322 unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param)
323 {
324 p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0);
325 php_pq_params_set_param(p, p->param.count-1, &param);
326 return p->type.count;
327 }
328
329 unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params)
330 {
331 p->param.count = params ? zend_hash_num_elements(params) : 0;
332 TSRMLS_DF(p);
333
334 if (p->param.strings) {
335 efree(p->param.strings);
336 p->param.strings = NULL;
337 }
338 zend_hash_clean(&p->param.dtor);
339 if (p->param.count) {
340 unsigned index = 0;
341 p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings));
342 zend_hash_apply_with_arguments(params TSRMLS_CC, apply_to_params, 2, p, &index);
343 }
344 return p->param.count;
345 }
346
347 void php_pq_params_free(php_pq_params_t **p)
348 {
349 if (*p) {
350 php_pq_params_set_type_oids(*p, NULL);
351 php_pq_params_set_params(*p, NULL);
352
353 zend_hash_destroy(&(*p)->param.dtor);
354 zend_hash_destroy(&(*p)->type.conv);
355
356 efree(*p);
357 *p = NULL;
358 }
359 }
360
361 php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params TSRMLS_DC)
362 {
363 php_pq_params_t *p = ecalloc(1, sizeof(*p));
364
365 TSRMLS_CF(p);
366 zend_hash_init(&p->type.conv, 0, NULL, ZVAL_PTR_DTOR, 0);
367 zend_hash_init(&p->param.dtor, 0, NULL, ZVAL_PTR_DTOR, 0);
368
369 if (conv) {
370 php_pq_params_set_type_conv(p, conv);
371 }
372 if (oids) {
373 php_pq_params_set_type_oids(p, oids);
374 }
375 if (params) {
376 php_pq_params_set_params(p, params);
377 }
378
379 return p;
380 }
381
382 /*
383 * Local variables:
384 * tab-width: 4
385 * c-basic-offset: 4
386 * End:
387 * vim600: noet sw=4 ts=4 fdm=marker
388 * vim<600: noet sw=4 ts=4
389 */