ffe5e70ba7b06b6d13fd72134c462c4a456b6c79
[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 static int apply_to_param_from_array(void *ptr TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
131 {
132 php_pq_params_t *p = va_arg(argv, php_pq_params_t *);
133 unsigned j, *i = va_arg(argv, unsigned *);
134 smart_str *s = va_arg(argv, smart_str *);
135 Oid type = va_arg(argv, Oid);
136 zval *ztmp, **zparam = ptr, *zcopy = *zparam, **zconv = va_arg(argv, zval **);
137 char *tmp;
138 size_t len;
139 int tmp_len;
140
141 if ((*i)++) {
142 smart_str_appendc(s, ',');
143 }
144
145 if (zconv) {
146 zval *rv = NULL;
147
148 zend_call_method_with_1_params(zconv, NULL, NULL, "converttostring", &rv, zcopy);
149 convert_to_string(rv);
150 zcopy = rv;
151 goto append_string;
152 } else {
153 switch (Z_TYPE_P(zcopy)) {
154 case IS_NULL:
155 smart_str_appends(s, "NULL");
156 break;
157
158 case IS_BOOL:
159 smart_str_appends(s, Z_BVAL_P(zcopy) ? "t" : "f");
160 break;
161
162 case IS_LONG:
163 smart_str_append_long(s, Z_LVAL_P(zcopy));
164 break;
165
166 case IS_DOUBLE:
167 len = spprintf(&tmp, 0, "%F", Z_DVAL_P(zcopy));
168 smart_str_appendl(s, tmp, len);
169 efree(tmp);
170 break;
171
172 case IS_ARRAY:
173 j = 0;
174 smart_str_appendc(s, '{');
175 zend_hash_apply_with_arguments(Z_ARRVAL_P(zcopy) TSRMLS_CC, apply_to_param_from_array, 5, p, &j, s, type, zconv);
176 smart_str_appendc(s, '}');
177 break;
178
179 case IS_OBJECT:
180 if ((ztmp = object_param_to_string(p, zcopy, type TSRMLS_CC))) {
181 zcopy = ztmp;
182 }
183 /* no break */
184 default:
185 SEPARATE_ZVAL(&zcopy);
186 convert_to_string(zcopy);
187
188 append_string:
189 tmp = php_addslashes(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy), &tmp_len, 0 TSRMLS_CC);
190 smart_str_appendc(s, '"');
191 smart_str_appendl(s, tmp, tmp_len);
192 smart_str_appendc(s, '"');
193
194 if (zcopy != *zparam) {
195 zval_ptr_dtor(&zcopy);
196 }
197 efree(tmp);
198 break;
199 }
200 }
201 ++(*i);
202 return ZEND_HASH_APPLY_KEEP;
203 }
204
205 static zval *array_param_to_string(php_pq_params_t *p, zval *zarr, Oid type TSRMLS_DC)
206 {
207 zval *return_value, **zconv = NULL;
208 smart_str s = {0};
209 unsigned i = 0;
210
211 switch (type) {
212 #ifdef PHP_PQ_OID_JSON
213 # ifdef PHP_PQ_OID_JSONB
214 case PHP_PQ_OID_JSONB:
215 # endif
216 case PHP_PQ_OID_JSON:
217 php_json_encode(&s, zarr, PHP_JSON_UNESCAPED_UNICODE TSRMLS_CC);
218 smart_str_0(&s);
219 break;
220 #endif
221
222 default:
223 zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type), (void *) &zconv);
224
225 smart_str_appendc(&s, '{');
226 zend_hash_apply_with_arguments(Z_ARRVAL_P(zarr) TSRMLS_CC, apply_to_param_from_array, 5, p, &i, &s, (Oid) PHP_PQ_TYPE_OF_ARRAY(type), zconv);
227 smart_str_appendc(&s, '}');
228 smart_str_0(&s);
229 break;
230 }
231
232 /* must not return NULL */
233 MAKE_STD_ZVAL(return_value);
234
235 if (s.c) {
236 RETVAL_STRINGL(s.c, s.len, 0);
237 } else {
238 RETVAL_EMPTY_STRING();
239 }
240
241 return return_value;
242 }
243
244 static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval **zpp)
245 {
246 zval **zconv = NULL;
247 Oid type = p->type.count > index ? p->type.oids[index] : 0;
248 TSRMLS_DF(p);
249
250 if (type && SUCCESS == zend_hash_index_find(&p->type.conv, type, (void *) &zconv)) {
251 zval *rv = NULL;
252
253 zend_call_method_with_1_params(zconv, NULL, NULL, "converttostring", &rv, *zpp);
254 convert_to_string(rv);
255 p->param.strings[index] = Z_STRVAL_P(rv);
256 zend_hash_next_index_insert(&p->param.dtor, (void *) &rv, sizeof(zval *), NULL);
257 } else {
258 zval *tmp, *zcopy = *zpp;
259
260 switch (Z_TYPE_P(zcopy)) {
261 case IS_NULL:
262 p->param.strings[index] = NULL;
263 return;
264
265 case IS_BOOL:
266 p->param.strings[index] = Z_BVAL_P(zcopy) ? "t" : "f";
267 return;
268
269 case IS_DOUBLE:
270 SEPARATE_ZVAL(&zcopy);
271 Z_TYPE_P(zcopy) = IS_STRING;
272 Z_STRLEN_P(zcopy) = spprintf(&Z_STRVAL_P(zcopy), 0, "%F", Z_DVAL_PP(zpp));
273 break;
274
275 case IS_ARRAY:
276 zcopy = array_param_to_string(p, zcopy, type TSRMLS_CC);
277 break;
278
279 case IS_OBJECT:
280 if ((tmp = object_param_to_string(p, zcopy, type TSRMLS_CC))) {
281 zcopy = tmp;
282 break;
283 }
284 /* no break */
285
286 default:
287 convert_to_string_ex(&zcopy);
288 break;
289 }
290
291 p->param.strings[index] = Z_STRVAL_P(zcopy);
292
293 if (zcopy != *zpp) {
294 zend_hash_next_index_insert(&p->param.dtor, (void *) &zcopy, sizeof(zval *), NULL);
295 }
296 }
297 }
298
299 static int apply_to_params(void *zp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
300 {
301 php_pq_params_t *p = (php_pq_params_t *) va_arg(argv, php_pq_params_t *);
302 unsigned *index = (unsigned *) va_arg(argv, unsigned *);
303
304 php_pq_params_set_param(p, (*index)++, zp);
305 return ZEND_HASH_APPLY_KEEP;
306 }
307
308 unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param)
309 {
310 p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0);
311 php_pq_params_set_param(p, p->param.count-1, &param);
312 return p->type.count;
313 }
314
315 unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params)
316 {
317 p->param.count = params ? zend_hash_num_elements(params) : 0;
318 TSRMLS_DF(p);
319
320 if (p->param.strings) {
321 efree(p->param.strings);
322 p->param.strings = NULL;
323 }
324 zend_hash_clean(&p->param.dtor);
325 if (p->param.count) {
326 unsigned index = 0;
327 p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings));
328 zend_hash_apply_with_arguments(params TSRMLS_CC, apply_to_params, 2, p, &index);
329 }
330 return p->param.count;
331 }
332
333 void php_pq_params_free(php_pq_params_t **p)
334 {
335 if (*p) {
336 php_pq_params_set_type_oids(*p, NULL);
337 php_pq_params_set_params(*p, NULL);
338
339 zend_hash_destroy(&(*p)->param.dtor);
340 zend_hash_destroy(&(*p)->type.conv);
341
342 efree(*p);
343 *p = NULL;
344 }
345 }
346
347 php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params TSRMLS_DC)
348 {
349 php_pq_params_t *p = ecalloc(1, sizeof(*p));
350
351 TSRMLS_CF(p);
352 zend_hash_init(&p->type.conv, 0, NULL, ZVAL_PTR_DTOR, 0);
353 zend_hash_init(&p->param.dtor, 0, NULL, ZVAL_PTR_DTOR, 0);
354
355 if (conv) {
356 php_pq_params_set_type_conv(p, conv);
357 }
358 if (oids) {
359 php_pq_params_set_type_oids(p, oids);
360 }
361 if (params) {
362 php_pq_params_set_params(p, params);
363 }
364
365 return p;
366 }
367
368 /*
369 * Local variables:
370 * tab-width: 4
371 * c-basic-offset: 4
372 * End:
373 * vim600: noet sw=4 ts=4 fdm=marker
374 * vim<600: noet sw=4 ts=4
375 */