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