6739b459b00876d445f65fe3981b3735435632bf
[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 #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON)
79 smart_str str = {0};
80 #endif
81
82 switch (type) {
83 #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON)
84 # ifdef PHP_PQ_OID_JSONB
85 case PHP_PQ_OID_JSONB:
86 # endif
87 case PHP_PQ_OID_JSON:
88 # if PHP_VERSION_ID >= 70100
89 JSON_G(encode_max_depth) = PHP_JSON_PARSER_DEFAULT_DEPTH;
90 # endif
91 php_json_encode(&str, zobj, PHP_JSON_UNESCAPED_UNICODE);
92 smart_str_0(&str);
93 return str.s;
94 #endif
95
96 case PHP_PQ_OID_DATE:
97 return php_pqdt_to_string(zobj, "Y-m-d");
98
99 case PHP_PQ_OID_ABSTIME:
100 return php_pqdt_to_string(zobj, "Y-m-d H:i:s");
101
102 case PHP_PQ_OID_TIMESTAMP:
103 return php_pqdt_to_string(zobj, "Y-m-d H:i:s.u");
104
105 case PHP_PQ_OID_TIMESTAMPTZ:
106 return php_pqdt_to_string(zobj, "Y-m-d H:i:s.uO");
107 }
108
109 return zval_get_string(zobj);
110 }
111
112 struct apply_to_param_from_array_arg {
113 php_pq_params_t *params;
114 unsigned index;
115 smart_str *buffer;
116 Oid type;
117 char delim;
118 zval *zconv;
119 };
120
121 static int apply_to_param_from_array(zval *zparam, void *arg_ptr)
122 {
123 struct apply_to_param_from_array_arg subarg, *arg = arg_ptr;
124 char *tmp;
125 size_t len;
126 zend_string *str;
127
128 if (arg->index++) {
129 smart_str_appendc(arg->buffer, arg->delim);
130 }
131
132 if (arg->zconv) {
133 zval ztype, rv;
134
135 ZVAL_LONG(&ztype, arg->type);
136 zend_call_method_with_2_params(arg->zconv, NULL, NULL, "converttostring", &rv, zparam, &ztype);
137 str = zval_get_string(&rv);
138 zval_ptr_dtor(&rv);
139 goto append_string;
140
141 } else {
142 again:
143 switch (Z_TYPE_P(zparam)) {
144 case IS_REFERENCE:
145 ZVAL_DEREF(zparam);
146 goto again;
147
148 case IS_NULL:
149 smart_str_appends(arg->buffer, "NULL");
150 break;
151
152 case IS_TRUE:
153 smart_str_appends(arg->buffer, "t");
154 break;
155
156 case IS_FALSE:
157 smart_str_appends(arg->buffer, "f");
158 break;
159
160 case IS_LONG:
161 smart_str_append_long(arg->buffer, Z_LVAL_P(zparam));
162 break;
163
164 case IS_DOUBLE:
165 len = spprintf(&tmp, 0, "%F", Z_DVAL_P(zparam));
166 smart_str_appendl(arg->buffer, tmp, len);
167 efree(tmp);
168 break;
169
170 case IS_ARRAY:
171 subarg = *arg;
172 subarg.index = 0;
173 smart_str_appendc(arg->buffer, '{');
174 zend_hash_apply_with_argument(Z_ARRVAL_P(zparam), apply_to_param_from_array, &subarg);
175 smart_str_appendc(arg->buffer, '}');
176 break;
177
178 case IS_OBJECT:
179 if ((str = object_param_to_string(arg->params, zparam, arg->type))) {
180 goto append_string;
181 }
182 /* no break */
183 default:
184 str = zval_get_string(zparam);
185
186 append_string:
187 str = php_addslashes(str, 1);
188 smart_str_appendc(arg->buffer, '"');
189 smart_str_appendl(arg->buffer, str->val, str->len);
190 smart_str_appendc(arg->buffer, '"');
191 zend_string_release(str);
192 break;
193 }
194 }
195 ++arg->index;
196 return ZEND_HASH_APPLY_KEEP;
197 }
198
199 static zend_string *array_param_to_string(php_pq_params_t *p, zval *zarr, Oid type)
200 {
201 smart_str s = {0};
202 struct apply_to_param_from_array_arg arg = {NULL};
203
204 switch (type) {
205 #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON)
206 # ifdef PHP_PQ_OID_JSONB
207 case PHP_PQ_OID_JSONB:
208 # endif
209 case PHP_PQ_OID_JSON:
210 php_json_encode(&s, zarr, PHP_JSON_UNESCAPED_UNICODE);
211 break;
212 #endif
213
214 default:
215 arg.params = p;
216 arg.buffer = &s;
217 arg.type = PHP_PQ_TYPE_OF_ARRAY(type);
218 arg.delim = PHP_PQ_DELIM_OF_ARRAY(type);
219 arg.zconv = zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type));
220 smart_str_appendc(arg.buffer, '{');
221 SEPARATE_ZVAL(zarr);
222 zend_hash_apply_with_argument(Z_ARRVAL_P(zarr), apply_to_param_from_array, &arg);
223 smart_str_appendc(arg.buffer, '}');
224 break;
225 }
226
227 smart_str_0(&s);
228 return s.s;
229 }
230
231 static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval *zpp)
232 {
233 zval *zconv = NULL;
234 Oid type = p->type.count > index ? p->type.oids[index] : 0;
235
236 if (type && (zconv = zend_hash_index_find(&p->type.conv, type))) {
237 zval ztype, rv;
238
239 ZVAL_NULL(&rv);
240 ZVAL_LONG(&ztype, type);
241 zend_call_method_with_2_params(zconv, NULL, NULL, "converttostring", &rv, zpp, &ztype);
242 convert_to_string(&rv);
243 p->param.strings[index] = Z_STRVAL_P(&rv);
244 zend_hash_next_index_insert(&p->param.dtor, &rv);
245 } else {
246 zval tmp;
247 zend_string *str = NULL;
248 char tmp_str[64];
249 size_t tmp_len = 0;
250
251 again:
252 switch (Z_TYPE_P(zpp)) {
253 case IS_REFERENCE:
254 ZVAL_DEREF(zpp);
255 goto again;
256
257 case IS_NULL:
258 p->param.strings[index] = NULL;
259 return;
260
261 case IS_TRUE:
262 p->param.strings[index] = "t";
263 break;
264
265 case IS_FALSE:
266 p->param.strings[index] = "f";
267 return;
268
269 case IS_DOUBLE:
270 tmp_len = slprintf(tmp_str, sizeof(tmp_str), "%F", Z_DVAL_P(zpp));
271 str = zend_string_init(tmp_str, tmp_len, 0);
272 break;
273
274 case IS_ARRAY:
275 str = array_param_to_string(p, zpp, type);
276 break;
277
278 case IS_OBJECT:
279 if ((str = object_param_to_string(p, zpp, type))) {
280 break;
281 }
282 /* no break */
283 default:
284 str = zval_get_string(zpp);
285 break;
286 }
287
288 if (str) {
289 ZVAL_STR(&tmp, str);
290 p->param.strings[index] = Z_STRVAL(tmp);
291 zend_hash_next_index_insert(&p->param.dtor, &tmp);
292 }
293 }
294 }
295
296 struct apply_to_params_arg {
297 php_pq_params_t *params;
298 unsigned index;
299 };
300
301 static int apply_to_params(zval *zp, void *arg_ptr)
302 {
303 struct apply_to_params_arg *arg = arg_ptr;
304
305 SEPARATE_ZVAL_IF_NOT_REF(zp);
306 php_pq_params_set_param(arg->params, arg->index++, zp);
307 return ZEND_HASH_APPLY_KEEP;
308 }
309
310 unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param)
311 {
312 p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0);
313 php_pq_params_set_param(p, p->param.count-1, param);
314 return p->type.count;
315 }
316
317 unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params)
318 {
319 p->param.count = params ? zend_hash_num_elements(params) : 0;
320
321 if (p->param.strings) {
322 efree(p->param.strings);
323 p->param.strings = NULL;
324 }
325 zend_hash_clean(&p->param.dtor);
326 if (p->param.count) {
327 struct apply_to_params_arg arg = {p, 0};
328 p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings));
329 zend_hash_apply_with_argument(params, apply_to_params, &arg);
330 }
331 return p->param.count;
332 }
333
334 void php_pq_params_free(php_pq_params_t **p)
335 {
336 if (*p) {
337 php_pq_params_set_type_oids(*p, NULL);
338 php_pq_params_set_params(*p, NULL);
339
340 zend_hash_destroy(&(*p)->param.dtor);
341 zend_hash_destroy(&(*p)->type.conv);
342
343 efree(*p);
344 *p = NULL;
345 }
346 }
347
348 php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params)
349 {
350 php_pq_params_t *p = ecalloc(1, sizeof(*p));
351
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 */