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