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