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