prepare v2.2.3
[m6w6/ext-pq] / src / php_pq_misc.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/date/php_date.h>
19 #include <ext/standard/php_string.h>
20
21 #include <Zend/zend_interfaces.h>
22
23 #include <libpq/libpq-fs.h>
24
25 #include "php_pq.h"
26 #include "php_pqexc.h"
27 #include "php_pq_misc.h"
28 #include "php_pqconn_event.h"
29 #undef PHP_PQ_TYPE
30 #include "php_pq_type.h"
31
32 /* convert version to string */
33 extern void php_pq_version_to_string(int version, char *buffer, int len) {
34 if (version < 100000) {
35 slprintf(buffer, len, "%d.%d.%d", version/10000, version/100%100, version%100);
36 } else { /* since version 10 */
37 slprintf(buffer, len, "%d.%d", version/10000, version%100);
38 }
39 }
40
41 /* clear result object associated with a result handle */
42 void php_pqres_clear(PGresult *r) {
43 php_pq_object_t *o = PQresultInstanceData(r, php_pqconn_event);
44
45 if (o) {
46 php_pq_object_delref(o);
47 } else {
48 PQclear(r);
49 }
50 }
51
52 /* clear any asynchronous results */
53 void php_pqconn_clear(PGconn *conn) {
54 PGresult *r;
55 php_pqconn_event_data_t *evdata = PQinstanceData(conn, php_pqconn_event);
56
57 while ((r = PQgetResult(conn))) {
58 php_pqres_clear(r);
59 }
60
61 if (evdata && evdata->obj) {
62 if (php_pq_callback_is_enabled(&evdata->obj->intern->onevent)) {
63 if (php_pq_callback_is_locked(&evdata->obj->intern->onevent)) {
64 php_pq_callback_disable(&evdata->obj->intern->onevent);
65 } else {
66 php_pq_callback_dtor(&evdata->obj->intern->onevent);
67 }
68 }
69 }
70 }
71
72 /* safe wrappers to clear any asynchronous wrappers before querying synchronously */
73 PGresult *php_pq_exec(PGconn *conn, const char *query) {
74 php_pqconn_clear(conn);
75 return PQexec(conn, query);
76 }
77 PGresult *php_pq_exec_params(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const * paramValues, const int *paramLengths, const int *paramFormats, int resultFormat) {
78 php_pqconn_clear(conn);
79 return PQexecParams(conn, command, nParams, paramTypes, paramValues, paramLengths, paramFormats, resultFormat);
80 }
81 PGresult *php_pq_prepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes) {
82 php_pqconn_clear(conn);
83 return PQprepare(conn, stmtName, query, nParams, paramTypes);
84 }
85 PGresult *php_pq_exec_prepared(PGconn *conn, const char *stmtName, int nParams, const char *const * paramValues, const int *paramLengths, const int *paramFormats, int resultFormat) {
86 php_pqconn_clear(conn);
87 return PQexecPrepared(conn, stmtName, nParams, paramValues, paramLengths, paramFormats, resultFormat);
88 }
89
90 char *php_pq_rtrim(char *e)
91 {
92 size_t l = strlen(e);
93
94 while (l-- > 0 && e[l] == '\n') {
95 e[l] = '\0';
96 }
97 return e;
98 }
99
100 const char *php_pq_strmode(long mode)
101 {
102 switch (mode & (INV_READ|INV_WRITE)) {
103 case INV_READ|INV_WRITE:
104 return "rw";
105 case INV_READ:
106 return "r";
107 case INV_WRITE:
108 return "w";
109 default:
110 return "-";
111 }
112 }
113
114 static inline int compare_index(zend_ulong l, zend_ulong r)
115 {
116 if (l < r) {
117 return -1;
118 }
119 if (l > r) {
120 return 1;
121 }
122 return 0;
123 }
124 #if PHP_VERSION_ID >= 80000
125 int php_pq_compare_index(Bucket *lptr, Bucket *rptr)
126 {
127 return compare_index(lptr->h, rptr->h);
128 }
129 #else
130 int php_pq_compare_index(const void *lptr, const void *rptr) {
131 return compare_index(((const Bucket *) lptr)->h, ((const Bucket *) rptr)->h);
132 }
133 #endif
134
135 void php_pq_hash_ptr_dtor(zval *p)
136 {
137 efree(Z_PTR_P(p));
138 }
139
140 zend_class_entry *php_pqdt_class_entry;
141
142 #if PHP_VERSION_ID >= 80100
143 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(ai_pqdt_jsonserialize, 0, 0, IS_MIXED, 0)
144 ZEND_END_ARG_INFO()
145 #else
146 #define ai_pqdt_jsonserialize ai_pqdt_to_string
147 #endif
148
149 #if PHP_VERSION_ID >= 80200
150 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(ai_pqdt_to_string, 0, 0, IS_STRING, 0)
151 #else
152 ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_to_string, 0, 0, 0)
153 #endif
154 ZEND_END_ARG_INFO();
155 static PHP_METHOD(pqdt, __toString)
156 {
157 zval rv, tmp;
158
159 ZVAL_NULL(&rv);
160 php_pq_call_method(getThis(), "format", 1, &rv, php_pq_read_property(getThis(), "format", &tmp));
161 RETVAL_ZVAL(&rv, 1, 1);
162 }
163
164 #if PHP_VERSION_ID >= 80100
165 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(ai_pqdt_create_from_format, 0, 2, DateTime, MAY_BE_FALSE)
166 #else
167 ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_create_from_format, 0, 0, 2)
168 #endif
169 ZEND_ARG_INFO(0, format)
170 ZEND_ARG_INFO(0, datetime)
171 #if PHP_VERSION_ID >= 70200
172 ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 1)
173 #else
174 ZEND_ARG_INFO(0, timezone)
175 #endif
176 ZEND_END_ARG_INFO();
177 static PHP_METHOD(pqdt, createFromFormat)
178 {
179 zend_error_handling zeh;
180 char *fmt_str, *dt_str;
181 size_t fmt_len, dt_len;
182 zval *ztz = NULL;
183 ZEND_RESULT_CODE rv;
184
185 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
186 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O", &fmt_str, &fmt_len, &dt_str, &dt_len, &ztz, php_date_get_timezone_ce());
187 zend_restore_error_handling(&zeh);
188
189 if (SUCCESS == rv) {
190 php_pqdt_from_string(return_value, fmt_str, dt_str, dt_len, "Y-m-d H:i:s.uO", ztz);
191 }
192 }
193
194 static zend_function_entry php_pqdt_methods[] = {
195 PHP_ME(pqdt, createFromFormat, ai_pqdt_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
196 PHP_ME(pqdt, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC)
197 PHP_MALIAS(pqdt, jsonSerialize, __toString, ai_pqdt_jsonserialize, ZEND_ACC_PUBLIC)
198 {0}
199 };
200
201 zval *php_pqdt_from_string(zval *zv, char *input_fmt, char *dt_str, size_t dt_len, const char *output_fmt, zval *ztimezone)
202 {
203 php_date_obj *dobj;
204
205 php_date_instantiate(php_pqdt_class_entry, zv);
206 dobj = php_date_obj_from_obj(Z_OBJ_P(zv));
207 if (!php_date_initialize(dobj, dt_str, dt_len, input_fmt, ztimezone, 1)) {
208 zval_dtor(zv);
209 ZVAL_NULL(zv);
210 } else if (output_fmt) {
211 zval fmt;
212 ZVAL_STRING(&fmt, output_fmt);
213 php_pq_update_property(zv, "format", &fmt);
214 zval_ptr_dtor(&fmt);
215 }
216
217 return zv;
218 }
219
220 zend_string *php_pqdt_to_string(zval *zdt, const char *format)
221 {
222 zval rv;
223
224 ZVAL_NULL(&rv);
225
226 if (php_pq_cast_object(zdt, IS_STRING, &rv)) {
227 return Z_STR(rv);
228 } else if (instanceof_function(Z_OBJCE_P(zdt), php_date_get_date_ce())) {
229 zval rv, zfmt;
230
231 ZVAL_NULL(&rv);
232 ZVAL_STRING(&zfmt, format);
233 php_pq_call_method(zdt, "format", 1, &rv, &zfmt);
234 zval_ptr_dtor(&zfmt);
235
236 if (Z_TYPE(rv) == IS_STRING) {
237 return Z_STR(rv);
238 }
239 zval_ptr_dtor(&rv);
240 }
241
242 return NULL;
243 }
244
245 zend_class_entry *php_pqconv_class_entry;
246
247 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_types, 0, 0, 0)
248 ZEND_END_ARG_INFO();
249
250 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_from_string, 0, 0, 2)
251 ZEND_ARG_INFO(0, data)
252 ZEND_ARG_INFO(0, type)
253 ZEND_END_ARG_INFO();
254
255 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_to_string, 0, 0, 2)
256 ZEND_ARG_INFO(0, data)
257 ZEND_ARG_INFO(0, type)
258 ZEND_END_ARG_INFO();
259
260 zend_function_entry php_pqconv_methods[] = {
261 PHP_ABSTRACT_ME(pqconv, convertTypes, ai_pqconv_convert_types)
262 PHP_ABSTRACT_ME(pqconv, convertFromString, ai_pqconv_convert_from_string)
263 PHP_ABSTRACT_ME(pqconv, convertToString, ai_pqconv_convert_to_string)
264 {0}
265 };
266
267
268 PHP_MINIT_FUNCTION(pq_misc)
269 {
270 zend_class_entry *json, ce = {0};
271
272 INIT_NS_CLASS_ENTRY(ce, "pq", "Converter", php_pqconv_methods);
273 php_pqconv_class_entry = zend_register_internal_interface(&ce);
274
275 memset(&ce, 0, sizeof(ce));
276 INIT_NS_CLASS_ENTRY(ce ,"pq", "DateTime", php_pqdt_methods);
277 php_pqdt_class_entry = zend_register_internal_class_ex(&ce, php_date_get_date_ce());
278
279 zend_declare_property_stringl(php_pqdt_class_entry, ZEND_STRL("format"), ZEND_STRL("Y-m-d H:i:s.uO"), ZEND_ACC_PUBLIC);
280
281 /* stop reading this file right here! */
282 if ((json = zend_hash_str_find_ptr(CG(class_table), ZEND_STRL("jsonserializable")))) {
283 zend_class_implements(php_pqdt_class_entry, 1, json);
284 }
285
286 return SUCCESS;
287 }
288
289 typedef struct _HashTableList {
290 zval arr;
291 struct _HashTableList *parent;
292 } HashTableList;
293
294 typedef struct _ArrayParserState {
295 const char *ptr, *end;
296 HashTableList *list;
297 php_pqres_t *res;
298 Oid typ;
299 unsigned quotes:1;
300 unsigned escaped:1;
301 } ArrayParserState;
302
303 static char caa(ArrayParserState *a, const char *any, unsigned advance)
304 {
305 const char *p = any;
306
307 do {
308 if (*p == *a->ptr) {
309 a->ptr += advance;
310 return *p;
311 }
312 } while (*++p);
313
314 php_error_docref(NULL, E_WARNING, "Failed to parse array: expected one of '%s', got '%c'", any, *a->ptr); \
315 return 0;
316 }
317
318 static ZEND_RESULT_CODE add_element(ArrayParserState *a, const char *start)
319 {
320 zval zelem;
321 zend_string *zstr = zend_string_init(start, a->ptr - start, 0);
322
323 if (a->quotes) {
324 php_stripslashes(zstr);
325 ZVAL_STR(&zelem, zstr);
326 } else if (!zend_string_equals_literal(zstr, "NULL")) {
327 ZVAL_STR(&zelem, zstr);
328 } else {
329 zend_string_release(zstr);
330 ZVAL_NULL(&zelem);
331 }
332
333 if (!ZVAL_IS_NULL(&zelem)) {
334 php_pqres_typed_zval(a->res, a->typ, &zelem);
335 }
336
337 add_next_index_zval(&a->list->arr, &zelem);
338 return SUCCESS;
339 }
340
341 static ZEND_RESULT_CODE parse_array(ArrayParserState *a);
342
343 static ZEND_RESULT_CODE parse_element(ArrayParserState *a, char delim)
344 {
345 const char *el;
346
347 switch (*a->ptr) {
348 case '{':
349 return parse_array(a);
350
351 case '}':
352 return SUCCESS;
353
354 case '"':
355 a->quotes = 1;
356 ++a->ptr;
357 break;
358 }
359
360 for (el = a->ptr; a->ptr < a->end; ++a->ptr) {
361 switch (*a->ptr) {
362 case '\\':
363 a->escaped = !a->escaped;
364 break;
365
366 case '"':
367 if (a->escaped) {
368 a->escaped = 0;
369 } else if (a->quotes) {
370 if (SUCCESS != add_element(a, el)) {
371 return FAILURE;
372 }
373 a->quotes = 0;
374 ++a->ptr;
375 return SUCCESS;
376 } else {
377 php_error_docref(NULL, E_WARNING, "Failed to parse element, unexpected quote: '%.*s'", (int) (a->ptr - el), el);
378 return FAILURE;
379 }
380 break;
381
382 default:
383 if (delim != *a->ptr) {
384 a->escaped = 0;
385 break;
386 }
387 /* no break */
388 case '}':
389 if (!a->quotes) {
390 return add_element(a, el);
391 }
392 break;
393
394 }
395 }
396
397 php_error_docref(NULL, E_WARNING, "Failed to parse element, reached end of input");
398 return FAILURE;
399 }
400
401 static ZEND_RESULT_CODE parse_elements(ArrayParserState *a)
402 {
403 char delims[] = {'}', (char) PHP_PQ_DELIM_OF_ARRAY(a->typ), 0};
404
405 while (SUCCESS == parse_element(a, delims[1])) {
406 switch (caa(a, delims, 0)) {
407 case 0:
408 return FAILURE;
409
410 case '}':
411 return SUCCESS;
412
413 default:
414 if (!*++a->ptr) {
415 php_error_docref(NULL, E_WARNING, "Failed to parse elements, reached end of input");
416 return FAILURE;
417 }
418 break;
419 }
420 }
421
422 return FAILURE;
423 }
424
425 static ZEND_RESULT_CODE parse_array(ArrayParserState *a)
426 {
427 HashTableList *list;
428
429 if (!caa(a, "{", 1)) {
430 return FAILURE;
431 }
432
433 list = ecalloc(1, sizeof(*list));
434 array_init(&list->arr);
435
436 if (a->list) {
437 add_next_index_zval(&a->list->arr, &list->arr);
438 list->parent = a->list;
439 }
440 a->list = list;
441
442 if (SUCCESS != parse_elements(a)) {
443 return FAILURE;
444 }
445
446 if (!caa(a, "}", 1)) {
447 return FAILURE;
448 }
449
450 /* step one level back up */
451 if (a->list->parent) {
452 HashTableList *l = a->list->parent;
453
454 efree(a->list);
455 a->list = l;
456 }
457
458 return SUCCESS;
459 }
460
461 HashTable *php_pq_parse_array(php_pqres_t *res, const char *val_str, size_t val_len, Oid typ)
462 {
463 HashTable *ht = NULL;
464 ArrayParserState a = {0};
465
466 a.typ = typ;
467 a.ptr = val_str;
468 a.end = val_str + val_len;
469 a.res = res;
470
471 if (SUCCESS != parse_array(&a)) {
472 while (a.list) {
473 HashTableList *l = a.list->parent;
474
475 zval_dtor(&a.list->arr);
476 efree(a.list);
477 a.list = l;
478 }
479 return ht;
480 }
481
482 if (*a.ptr) {
483 php_error_docref(NULL, E_NOTICE, "Trailing input: '%s'", a.ptr);
484 }
485
486 while (a.list) {
487 HashTableList *l = a.list->parent;
488
489 ht = Z_ARRVAL(a.list->arr);
490 efree(a.list);
491 a.list = l;
492 }
493
494 return ht;
495 }
496
497
498 /*
499 * Local variables:
500 * tab-width: 4
501 * c-basic-offset: 4
502 * End:
503 * vim600: noet sw=4 ts=4 fdm=marker
504 * vim<600: noet sw=4 ts=4
505 */