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