remove pq\Event
[m6w6/ext-pq] / php_pq.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 #define SMART_STR_PREALLOC 256
18
19 #include <php.h>
20 #include <Zend/zend_interfaces.h>
21 #include <Zend/zend_exceptions.h>
22 #include <ext/standard/info.h>
23 #include <ext/standard/php_smart_str.h>
24 #include <ext/spl/spl_array.h>
25 #include <ext/spl/spl_exceptions.h>
26 #include <ext/raphf/php_raphf.h>
27
28 #include <libpq-events.h>
29 #include <libpq/libpq-fs.h>
30 #include <fnmatch.h>
31
32 #include "php_pq.h"
33
34 typedef int STATUS; /* SUCCESS/FAILURE */
35
36 static char *rtrim(char *e) {
37 size_t l = strlen(e);
38
39 while (l-- > 0 && e[l] == '\n') {
40 e[l] = '\0';
41 }
42 return e;
43 }
44
45 #define PHP_PQerrorMessage(c) rtrim(PQerrorMessage((c)))
46 #define PHP_PQresultErrorMessage(r) rtrim(PQresultErrorMessage((r)))
47
48 static int php_pqconn_event(PGEventId id, void *e, void *data);
49
50 #define PHP_PQclear(_r) \
51 do { \
52 php_pqres_object_t *_o = PQresultInstanceData((_r), php_pqconn_event); \
53 if (_o) { \
54 php_pq_object_delref(_o TSRMLS_CC); \
55 } else { \
56 PQclear(_r); \
57 } \
58 } while (0)
59
60 /*
61 ZEND_DECLARE_MODULE_GLOBALS(pq)
62 */
63
64 /* {{{ PHP_INI
65 */
66 /* Remove comments and fill if you need to have entries in php.ini
67 PHP_INI_BEGIN()
68 STD_PHP_INI_ENTRY("pq.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_pq_globals, pq_globals)
69 STD_PHP_INI_ENTRY("pq.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_pq_globals, pq_globals)
70 PHP_INI_END()
71 */
72 /* }}} */
73
74 /* {{{ php_pq_init_globals
75 */
76 /* Uncomment this function if you have INI entries
77 static void php_pq_init_globals(zend_pq_globals *pq_globals)
78 {
79 pq_globals->global_value = 0;
80 pq_globals->global_string = NULL;
81 }
82 */
83 /* }}} */
84
85 static zend_class_entry *php_pqconn_class_entry;
86 static zend_class_entry *php_pqtypes_class_entry;
87 static zend_class_entry *php_pqres_class_entry;
88 static zend_class_entry *php_pqstm_class_entry;
89 static zend_class_entry *php_pqtxn_class_entry;
90 static zend_class_entry *php_pqcancel_class_entry;
91 static zend_class_entry *php_pqlob_class_entry;
92 static zend_class_entry *php_pqcopy_class_entry;
93
94 typedef enum php_pqexc_type {
95 EX_INVALID_ARGUMENT,
96 EX_RUNTIME,
97 EX_CONNECTION_FAILED,
98 EX_IO,
99 EX_ESCAPE,
100 EX_BAD_METHODCALL,
101 EX_UNINITIALIZED,
102 EX_DOMAIN,
103 EX_SQL
104 } php_pqexc_type_t;
105
106 static zend_class_entry *php_pqexc_interface_class_entry;
107 static zend_class_entry *php_pqexc_invalid_argument_class_entry;
108 static zend_class_entry *php_pqexc_runtime_class_entry;
109 static zend_class_entry *php_pqexc_bad_methodcall_class_entry;
110 static zend_class_entry *php_pqexc_domain_class_entry;
111
112 static zend_class_entry *exce(php_pqexc_type_t type)
113 {
114 switch (type) {
115 default:
116 case EX_INVALID_ARGUMENT:
117 return php_pqexc_invalid_argument_class_entry;
118 case EX_RUNTIME:
119 case EX_CONNECTION_FAILED:
120 case EX_IO:
121 case EX_ESCAPE:
122 return php_pqexc_runtime_class_entry;
123 case EX_UNINITIALIZED:
124 case EX_BAD_METHODCALL:
125 return php_pqexc_bad_methodcall_class_entry;
126 case EX_DOMAIN:
127 case EX_SQL:
128 return php_pqexc_domain_class_entry;
129 }
130 }
131
132 static zval *throw_exce(php_pqexc_type_t type TSRMLS_DC, const char *fmt, ...)
133 {
134 char *msg;
135 zval *zexc;
136 va_list argv;
137
138 va_start(argv, fmt);
139 vspprintf(&msg, 0, fmt, argv);
140 va_end(argv);
141
142 zexc = zend_throw_exception(exce(type), msg, type TSRMLS_CC);
143 efree(msg);
144
145 return zexc;
146 }
147
148 static zend_object_handlers php_pqconn_object_handlers;
149 static zend_object_handlers php_pqtypes_object_handlers;
150 static zend_object_handlers php_pqres_object_handlers;
151 static zend_object_handlers php_pqstm_object_handlers;
152 static zend_object_handlers php_pqtxn_object_handlers;
153 static zend_object_handlers php_pqcancel_object_handlers;
154 static zend_object_handlers php_pqlob_object_handlers;
155 static zend_object_handlers php_pqcopy_object_handlers;
156
157 typedef struct php_pq_callback {
158 zend_fcall_info fci;
159 zend_fcall_info_cache fcc;
160 void *data;
161 } php_pq_callback_t;
162
163 typedef struct php_pq_object {
164 zend_object zo;
165 zend_object_value zv;
166 HashTable *prophandler;
167 void *intern;
168 } php_pq_object_t;
169
170 #define PHP_PQCONN_ASYNC 0x01
171 #define PHP_PQCONN_PERSISTENT 0x02
172
173 typedef struct php_pqconn {
174 PGconn *conn;
175 int (*poller)(PGconn *);
176 php_resource_factory_t factory;
177 HashTable listeners;
178 HashTable eventhandlers;
179 php_pq_callback_t onevent;
180 unsigned unbuffered:1;
181 } php_pqconn_t;
182
183 typedef struct php_pqconn_object {
184 zend_object zo;
185 zend_object_value zv;
186 HashTable *prophandler;
187 php_pqconn_t *intern;
188 } php_pqconn_object_t;
189
190 typedef struct php_pqtypes {
191 HashTable types;
192 php_pqconn_object_t *conn;
193 } php_pqtypes_t;
194
195 typedef struct php_pqtypes_object {
196 zend_object zo;
197 zend_object_value zv;
198 HashTable *prophandler;
199 php_pqtypes_t *intern;
200 } php_pqtypes_object_t;
201
202 typedef struct php_pqconn_event_data {
203 php_pqconn_object_t *obj;
204 #ifdef ZTS
205 void ***ts;
206 #endif
207 } php_pqconn_event_data_t;
208
209 typedef enum php_pqres_fetch {
210 PHP_PQRES_FETCH_ARRAY,
211 PHP_PQRES_FETCH_ASSOC,
212 PHP_PQRES_FETCH_OBJECT
213 } php_pqres_fetch_t;
214
215 typedef struct php_pqres_iterator {
216 zend_object_iterator zi;
217 zval *current_val;
218 unsigned index;
219 php_pqres_fetch_t fetch_type;
220 } php_pqres_iterator_t;
221
222 typedef struct php_pqres {
223 PGresult *res;
224 php_pqres_iterator_t *iter;
225 HashTable bound;
226 } php_pqres_t;
227
228 typedef struct php_pqres_object {
229 zend_object zo;
230 zend_object_value zv;
231 HashTable *prophandler;
232 php_pqres_t *intern;
233 } php_pqres_object_t;
234
235 typedef struct php_pqstm {
236 php_pqconn_object_t *conn;
237 char *name;
238 HashTable bound;
239 } php_pqstm_t;
240
241 typedef struct php_pqstm_object {
242 zend_object zo;
243 zend_object_value zv;
244 HashTable *prophandler;
245 php_pqstm_t *intern;
246 } php_pqstm_object_t;
247
248 typedef enum php_pqtxn_isolation {
249 PHP_PQTXN_READ_COMMITTED,
250 PHP_PQTXN_REPEATABLE_READ,
251 PHP_PQTXN_SERIALIZABLE,
252 } php_pqtxn_isolation_t;
253
254 typedef struct php_pqtxn {
255 php_pqconn_object_t *conn;
256 php_pqtxn_isolation_t isolation;
257 unsigned savepoint;
258 unsigned open:1;
259 unsigned readonly:1;
260 unsigned deferrable:1;
261 } php_pqtxn_t;
262
263 typedef struct php_pqtxn_object {
264 zend_object zo;
265 zend_object_value zv;
266 HashTable *prophandler;
267 php_pqtxn_t *intern;
268 } php_pqtxn_object_t;
269
270 typedef struct php_pqcancel {
271 PGcancel *cancel;
272 php_pqconn_object_t *conn;
273 } php_pqcancel_t;
274
275 typedef struct php_pqcancel_object {
276 zend_object zo;
277 zend_object_value zv;
278 HashTable *prophandler;
279 php_pqcancel_t *intern;
280 } php_pqcancel_object_t;
281
282 typedef struct php_pqevent {
283 php_pq_callback_t cb;
284 char *type;
285 ulong h;
286 } php_pqevent_t;
287
288 typedef struct php_pqevent_object {
289 zend_object zo;
290 zend_object_value zv;
291 HashTable *prophandler;
292 php_pqevent_t *intern;
293 } php_pqevent_object_t;
294
295 typedef struct php_pqlob {
296 int lofd;
297 Oid loid;
298 php_pqtxn_object_t *txn;
299 } php_pqlob_t;
300
301 typedef struct php_pqlob_object {
302 zend_object zo;
303 zend_object_value zv;
304 HashTable *prophandler;
305 php_pqlob_t *intern;
306 } php_pqlob_object_t;
307
308 typedef enum php_pqcopy_direction {
309 PHP_PQCOPY_FROM_STDIN,
310 PHP_PQCOPY_TO_STDOUT
311 } php_pqcopy_direction_t;
312
313 typedef enum php_pqcopy_status {
314 PHP_PQCOPY_FAIL,
315 PHP_PQCOPY_CONT,
316 PHP_PQCOPY_DONE
317 } php_pqcopy_status_t;
318
319 typedef struct php_pqcopy {
320 php_pqcopy_direction_t direction;
321 char *expression;
322 char *options;
323 php_pqconn_object_t *conn;
324 } php_pqcopy_t;
325
326 typedef struct php_pqcopy_object {
327 zend_object zo;
328 zend_object_value zv;
329 HashTable *prophandler;
330 php_pqcopy_t *intern;
331 } php_pqcopy_object_t;
332
333 static HashTable php_pqconn_object_prophandlers;
334 static HashTable php_pqtypes_object_prophandlers;
335 static HashTable php_pqres_object_prophandlers;
336 static HashTable php_pqstm_object_prophandlers;
337 static HashTable php_pqtxn_object_prophandlers;
338 static HashTable php_pqcancel_object_prophandlers;
339 static HashTable php_pqlob_object_prophandlers;
340 static HashTable php_pqcopy_object_prophandlers;
341
342 typedef void (*php_pq_object_prophandler_func_t)(zval *object, void *o, zval *return_value TSRMLS_DC);
343
344 typedef struct php_pq_object_prophandler {
345 php_pq_object_prophandler_func_t read;
346 php_pq_object_prophandler_func_t write;
347 } php_pq_object_prophandler_t;
348
349 static zend_object_iterator_funcs php_pqres_iterator_funcs;
350
351 static zend_object_iterator *php_pqres_iterator_init(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
352 {
353 php_pqres_iterator_t *iter;
354 zval *prop, *zfetch_type;
355
356 iter = ecalloc(1, sizeof(*iter));
357 iter->zi.funcs = &php_pqres_iterator_funcs;
358 iter->zi.data = object;
359 Z_ADDREF_P(object);
360
361 zfetch_type = prop = zend_read_property(ce, object, ZEND_STRL("fetchType"), 0 TSRMLS_CC);
362 if (Z_TYPE_P(zfetch_type) != IS_LONG) {
363 convert_to_long_ex(&zfetch_type);
364 }
365 iter->fetch_type = Z_LVAL_P(zfetch_type);
366 if (zfetch_type != prop) {
367 zval_ptr_dtor(&zfetch_type);
368 }
369 if (Z_REFCOUNT_P(prop)) {
370 zval_ptr_dtor(&prop);
371 } else {
372 zval_dtor(prop);
373 FREE_ZVAL(prop);
374 }
375
376 return (zend_object_iterator *) iter;
377 }
378
379 static void php_pqres_iterator_dtor(zend_object_iterator *i TSRMLS_DC)
380 {
381 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
382
383 if (iter->current_val) {
384 zval_ptr_dtor(&iter->current_val);
385 iter->current_val = NULL;
386 }
387 zval_ptr_dtor((zval **) &iter->zi.data);
388 efree(iter);
389 }
390
391 static STATUS php_pqres_iterator_valid(zend_object_iterator *i TSRMLS_DC)
392 {
393 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
394 php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC);
395
396 switch (PQresultStatus(obj->intern->res)) {
397 case PGRES_TUPLES_OK:
398 case PGRES_SINGLE_TUPLE:
399 if (PQntuples(obj->intern->res) <= iter->index) {
400 return FAILURE;
401 }
402 break;
403 default:
404 return FAILURE;
405 }
406
407 return SUCCESS;
408 }
409
410 static zval *php_pqres_row_to_zval(PGresult *res, unsigned row, php_pqres_fetch_t fetch_type, zval **data_ptr TSRMLS_DC)
411 {
412 zval *data = NULL;
413 int c, cols;
414
415 if (data_ptr) {
416 data = *data_ptr;
417 }
418 if (!data) {
419 MAKE_STD_ZVAL(data);
420 if (PHP_PQRES_FETCH_OBJECT == fetch_type) {
421 object_init(data);
422 } else {
423 array_init(data);
424 }
425 if (data_ptr) {
426 *data_ptr = data;
427 }
428 }
429
430 for (c = 0, cols = PQnfields(res); c < cols; ++c) {
431 if (PQgetisnull(res, row, c)) {
432 switch (fetch_type) {
433 case PHP_PQRES_FETCH_OBJECT:
434 add_property_null(data, PQfname(res, c));
435 break;
436
437 case PHP_PQRES_FETCH_ASSOC:
438 add_assoc_null(data, PQfname(res, c));
439 break;
440
441 case PHP_PQRES_FETCH_ARRAY:
442 add_index_null(data, c);
443 break;
444 }
445 } else {
446 char *val = PQgetvalue(res, row, c);
447 int len = PQgetlength(res, row, c);
448
449 switch (fetch_type) {
450 case PHP_PQRES_FETCH_OBJECT:
451 add_property_stringl(data, PQfname(res, c), val, len, 1);
452 break;
453
454 case PHP_PQRES_FETCH_ASSOC:
455 add_assoc_stringl(data, PQfname(res, c), val, len, 1);
456 break;
457
458 case PHP_PQRES_FETCH_ARRAY:
459 add_index_stringl(data, c, val, len ,1);
460 break;
461 }
462 }
463 }
464
465 return data;
466 }
467
468 static void php_pqres_iterator_current(zend_object_iterator *i, zval ***data_ptr TSRMLS_DC)
469 {
470 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
471 php_pqres_object_t *obj = zend_object_store_get_object(iter->zi.data TSRMLS_CC);
472
473 if (iter->current_val) {
474 zval_ptr_dtor(&iter->current_val);
475 }
476 iter->current_val = php_pqres_row_to_zval(obj->intern->res, iter->index, iter->fetch_type, NULL TSRMLS_CC);
477 *data_ptr = &iter->current_val;
478 }
479
480 static int php_pqres_iterator_key(zend_object_iterator *i, char **key_str, uint *key_len, ulong *key_num TSRMLS_DC)
481 {
482 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
483
484 *key_num = (ulong) iter->index;
485
486 return HASH_KEY_IS_LONG;
487 }
488
489 static void php_pqres_iterator_next(zend_object_iterator *i TSRMLS_DC)
490 {
491 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
492
493 ++iter->index;
494 }
495
496 static void php_pqres_iterator_rewind(zend_object_iterator *i TSRMLS_DC)
497 {
498 php_pqres_iterator_t *iter = (php_pqres_iterator_t *) i;
499
500 iter->index = 0;
501 }
502
503 static zend_object_iterator_funcs php_pqres_iterator_funcs = {
504 php_pqres_iterator_dtor,
505 /* check for end of iteration (FAILURE or SUCCESS if data is valid) */
506 php_pqres_iterator_valid,
507 /* fetch the item data for the current element */
508 php_pqres_iterator_current,
509 /* fetch the key for the current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) (optional, may be NULL) */
510 php_pqres_iterator_key,
511 /* step forwards to next element */
512 php_pqres_iterator_next,
513 /* rewind to start of data (optional, may be NULL) */
514 php_pqres_iterator_rewind,
515 /* invalidate current value/key (optional, may be NULL) */
516 NULL
517 };
518
519 static int php_pqres_count_elements(zval *object, long *count TSRMLS_DC)
520 {
521 php_pqres_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
522
523 if (!obj->intern) {
524 return FAILURE;
525 } else {
526 *count = (long) PQntuples(obj->intern->res);
527 return SUCCESS;
528 }
529 }
530
531 static STATUS php_pqres_success(PGresult *res TSRMLS_DC)
532 {
533 zval *zexc;
534
535 switch (PQresultStatus(res)) {
536 case PGRES_BAD_RESPONSE:
537 case PGRES_NONFATAL_ERROR:
538 case PGRES_FATAL_ERROR:
539 zexc = throw_exce(EX_SQL TSRMLS_CC, "%s", PHP_PQresultErrorMessage(res));
540 zend_update_property_string(php_pqexc_domain_class_entry, zexc, ZEND_STRL("sqlstate"), PQresultErrorField(res, PG_DIAG_SQLSTATE) TSRMLS_CC);
541 return FAILURE;
542 default:
543 return SUCCESS;
544 }
545 }
546
547 static void php_pq_callback_dtor(php_pq_callback_t *cb) {
548 if (cb->fci.size > 0) {
549 zend_fcall_info_args_clear(&cb->fci, 1);
550 zval_ptr_dtor(&cb->fci.function_name);
551 if (cb->fci.object_ptr) {
552 zval_ptr_dtor(&cb->fci.object_ptr);
553 }
554 }
555 cb->fci.size = 0;
556 }
557
558 static void php_pq_callback_addref(php_pq_callback_t *cb)
559 {
560 Z_ADDREF_P(cb->fci.function_name);
561 if (cb->fci.object_ptr) {
562 Z_ADDREF_P(cb->fci.object_ptr);
563 }
564 }
565
566 /*
567 static void php_pqconn_del_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, ulong id TSRMLS_DC)
568 {
569 zval **evhs;
570
571 if (SUCCESS == zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) {
572 zend_hash_index_del(Z_ARRVAL_PP(evhs), id);
573 }
574 }
575 */
576
577 static ulong php_pqconn_add_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, php_pq_callback_t *cb TSRMLS_DC)
578 {
579 ulong h;
580 HashTable *evhs;
581
582 if (SUCCESS != zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) {
583 HashTable evh;
584
585 zend_hash_init(&evh, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0);
586 zend_hash_add(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evh, sizeof(evh), (void *) &evhs);
587 }
588
589 php_pq_callback_addref(cb);
590 h = zend_hash_next_free_element(evhs);
591 zend_hash_index_update(evhs, h, (void *) cb, sizeof(*cb), NULL);
592
593 return h;
594 }
595
596 static void php_pq_object_to_zval(void *o, zval **zv TSRMLS_DC)
597 {
598 php_pq_object_t *obj = o;
599
600 if (!*zv) {
601 MAKE_STD_ZVAL(*zv);
602 }
603
604 zend_objects_store_add_ref_by_handle(obj->zv.handle TSRMLS_CC);
605
606 (*zv)->type = IS_OBJECT;
607 (*zv)->value.obj = obj->zv;
608 }
609
610 static void php_pq_object_to_zval_no_addref(void *o, zval **zv TSRMLS_DC)
611 {
612 php_pq_object_t *obj = o;
613
614 if (!*zv) {
615 MAKE_STD_ZVAL(*zv);
616 }
617
618 /* no add ref */
619
620 (*zv)->type = IS_OBJECT;
621 (*zv)->value.obj = obj->zv;
622 }
623
624 static void php_pq_object_addref(void *o TSRMLS_DC)
625 {
626 php_pq_object_t *obj = o;
627 zend_objects_store_add_ref_by_handle(obj->zv.handle TSRMLS_CC);
628 }
629
630 static void php_pq_object_delref(void *o TSRMLS_DC)
631 {
632 php_pq_object_t *obj = o;
633 zend_objects_store_del_ref_by_handle_ex(obj->zv.handle, obj->zv.handlers TSRMLS_CC);
634 }
635
636 static void php_pqconn_object_free(void *o TSRMLS_DC)
637 {
638 php_pqconn_object_t *obj = o;
639 #if DBG_GC
640 fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj);
641 #endif
642 if (obj->intern) {
643 php_resource_factory_handle_dtor(&obj->intern->factory, obj->intern->conn TSRMLS_CC);
644 php_resource_factory_dtor(&obj->intern->factory);
645 php_pq_callback_dtor(&obj->intern->onevent);
646 zend_hash_destroy(&obj->intern->listeners);
647 zend_hash_destroy(&obj->intern->eventhandlers);
648 efree(obj->intern);
649 obj->intern = NULL;
650 }
651 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
652 efree(obj);
653 }
654
655 static void php_pqtypes_object_free(void *o TSRMLS_DC)
656 {
657 php_pqtypes_object_t *obj = o;
658 #if DBG_GC
659 fprintf(stderr, "FREE types(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
660 #endif
661 if (obj->intern) {
662 zend_hash_destroy(&obj->intern->types);
663 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
664 efree(obj->intern);
665 obj->intern = NULL;
666 }
667 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
668 efree(obj);
669 }
670
671 static void php_pqres_object_free(void *o TSRMLS_DC)
672 {
673 php_pqres_object_t *obj = o;
674 #if DBG_GC
675 fprintf(stderr, "FREE res(#%d) %p\n", obj->zv.handle, obj);
676 #endif
677 if (obj->intern) {
678 if (obj->intern->res) {
679 PQresultSetInstanceData(obj->intern->res, php_pqconn_event, NULL);
680 PQclear(obj->intern->res);
681 obj->intern->res = NULL;
682 }
683
684 if (obj->intern->iter) {
685 php_pqres_iterator_dtor((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
686 obj->intern->iter = NULL;
687 }
688
689 zend_hash_destroy(&obj->intern->bound);
690
691 efree(obj->intern);
692 obj->intern = NULL;
693 }
694 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
695 efree(obj);
696 }
697
698 static void php_pqstm_object_free(void *o TSRMLS_DC)
699 {
700 php_pqstm_object_t *obj = o;
701 #if DBG_GC
702 fprintf(stderr, "FREE stm(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
703 #endif
704 if (obj->intern) {
705 char *quoted_name = PQescapeIdentifier(obj->intern->conn->intern->conn, obj->intern->name, strlen(obj->intern->name));
706
707 php_pq_callback_dtor(&obj->intern->conn->intern->onevent);
708
709 if (quoted_name) {
710 PGresult *res;
711 smart_str cmd = {0};
712
713 smart_str_appends(&cmd, "DEALLOCATE ");
714 smart_str_appends(&cmd, quoted_name);
715 smart_str_0(&cmd);
716 PQfreemem(quoted_name);
717
718 if ((res = PQexec(obj->intern->conn->intern->conn, cmd.c))) {
719 PHP_PQclear(res);
720 }
721 smart_str_free(&cmd);
722 }
723
724 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
725 efree(obj->intern->name);
726 zend_hash_destroy(&obj->intern->bound);
727 efree(obj->intern);
728 obj->intern = NULL;
729 }
730 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
731 efree(obj);
732 }
733
734 static void php_pqtxn_object_free(void *o TSRMLS_DC)
735 {
736 php_pqtxn_object_t *obj = o;
737 #if DBG_GC
738 fprintf(stderr, "FREE txn(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
739 #endif
740 if (obj->intern) {
741 if (obj->intern->open) {
742 PGresult *res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK");
743
744 if (res) {
745 PHP_PQclear(res);
746 }
747 }
748 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
749 efree(obj->intern);
750 obj->intern = NULL;
751 }
752 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
753 efree(obj);
754 }
755
756 static void php_pqcancel_object_free(void *o TSRMLS_DC)
757 {
758 php_pqcancel_object_t *obj = o;
759 #if DBG_GC
760 fprintf(stderr, "FREE cancel(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
761 #endif
762 if (obj->intern) {
763 PQfreeCancel(obj->intern->cancel);
764 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
765 efree(obj->intern);
766 obj->intern = NULL;
767 }
768 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
769 efree(obj);
770 }
771
772 static void php_pqlob_object_free(void *o TSRMLS_DC)
773 {
774 php_pqlob_object_t *obj = o;
775 #if DBG_GC
776 fprintf(stderr, "FREE lob(#%d) %p (txn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->txn->zv.handle, obj->intern->txn);
777 #endif
778 if (obj->intern) {
779 if (obj->intern->lofd) {
780 lo_close(obj->intern->txn->intern->conn->intern->conn, obj->intern->lofd);
781 }
782 php_pq_object_delref(obj->intern->txn TSRMLS_CC);
783 efree(obj->intern);
784 obj->intern = NULL;
785 }
786 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
787 efree(obj);
788 }
789
790 static void php_pqcopy_object_free(void *o TSRMLS_DC)
791 {
792 php_pqcopy_object_t *obj = o;
793 #if DBG_GC
794 fprintf(stderr, "FREE copy(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
795 #endif
796 if (obj->intern) {
797 efree(obj->intern->expression);
798 efree(obj->intern->options);
799 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
800 efree(obj->intern);
801 obj->intern = NULL;
802 }
803 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
804 efree(obj);
805 }
806
807 static zend_object_value php_pqconn_create_object_ex(zend_class_entry *ce, php_pqconn_t *intern, php_pqconn_object_t **ptr TSRMLS_DC)
808 {
809 php_pqconn_object_t *o;
810
811 o = ecalloc(1, sizeof(*o));
812 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
813 object_properties_init((zend_object *) o, ce);
814 o->prophandler = &php_pqconn_object_prophandlers;
815
816 if (ptr) {
817 *ptr = o;
818 }
819
820 if (intern) {
821 o->intern = intern;
822 }
823
824 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqconn_object_free, NULL TSRMLS_CC);
825 o->zv.handlers = &php_pqconn_object_handlers;
826
827 return o->zv;
828 }
829
830 static zend_object_value php_pqtypes_create_object_ex(zend_class_entry *ce, php_pqtypes_t *intern, php_pqtypes_object_t **ptr TSRMLS_DC)
831 {
832 php_pqtypes_object_t *o;
833
834 o = ecalloc(1, sizeof(*o));
835 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
836 object_properties_init((zend_object *) o, ce);
837 o->prophandler = &php_pqtypes_object_prophandlers;
838
839 if (ptr) {
840 *ptr = o;
841 }
842
843 if (intern) {
844 o->intern = intern;
845 }
846
847 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqtypes_object_free, NULL TSRMLS_CC);
848 o->zv.handlers = &php_pqtypes_object_handlers;
849
850 return o->zv;
851 }
852
853 static zend_object_value php_pqres_create_object_ex(zend_class_entry *ce, php_pqres_t *intern, php_pqres_object_t **ptr TSRMLS_DC)
854 {
855 php_pqres_object_t *o;
856
857 o = ecalloc(1, sizeof(*o));
858 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
859 object_properties_init((zend_object *) o, ce);
860 o->prophandler = &php_pqres_object_prophandlers;
861
862 if (ptr) {
863 *ptr = o;
864 }
865
866 if (intern) {
867 o->intern = intern;
868 }
869
870 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqres_object_free, NULL TSRMLS_CC);
871 o->zv.handlers = &php_pqres_object_handlers;
872
873 return o->zv;
874 }
875
876 static zend_object_value php_pqstm_create_object_ex(zend_class_entry *ce, php_pqstm_t *intern, php_pqstm_object_t **ptr TSRMLS_DC)
877 {
878 php_pqstm_object_t *o;
879
880 o = ecalloc(1, sizeof(*o));
881 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
882 object_properties_init((zend_object *) o, ce);
883 o->prophandler = &php_pqstm_object_prophandlers;
884
885 if (ptr) {
886 *ptr = o;
887 }
888
889 if (intern) {
890 o->intern = intern;
891 }
892
893 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqstm_object_free, NULL TSRMLS_CC);
894 o->zv.handlers = &php_pqstm_object_handlers;
895
896 return o->zv;
897 }
898
899 static zend_object_value php_pqtxn_create_object_ex(zend_class_entry *ce, php_pqtxn_t *intern, php_pqtxn_object_t **ptr TSRMLS_DC)
900 {
901 php_pqtxn_object_t *o;
902
903 o = ecalloc(1, sizeof(*o));
904 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
905 object_properties_init((zend_object *) o, ce);
906 o->prophandler = &php_pqtxn_object_prophandlers;
907
908 if (ptr) {
909 *ptr = o;
910 }
911
912 if (intern) {
913 o->intern = intern;
914 }
915
916 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqtxn_object_free, NULL TSRMLS_CC);
917 o->zv.handlers = &php_pqtxn_object_handlers;
918
919 return o->zv;
920 }
921
922 static zend_object_value php_pqcancel_create_object_ex(zend_class_entry *ce, php_pqcancel_t *intern, php_pqcancel_object_t **ptr TSRMLS_DC)
923 {
924 php_pqcancel_object_t *o;
925
926 o = ecalloc(1, sizeof(*o));
927 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
928 object_properties_init((zend_object *) o, ce);
929 o->prophandler = &php_pqcancel_object_prophandlers;
930
931 if (ptr) {
932 *ptr = o;
933 }
934
935 if (intern) {
936 o->intern = intern;
937 }
938
939 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqcancel_object_free, NULL TSRMLS_CC);
940 o->zv.handlers = &php_pqcancel_object_handlers;
941
942 return o->zv;
943 }
944
945 static zend_object_value php_pqlob_create_object_ex(zend_class_entry *ce, php_pqlob_t *intern, php_pqlob_object_t **ptr TSRMLS_DC)
946 {
947 php_pqlob_object_t *o;
948
949 o = ecalloc(1, sizeof(*o));
950 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
951 object_properties_init((zend_object *) o, ce);
952 o->prophandler = &php_pqlob_object_prophandlers;
953
954 if (ptr) {
955 *ptr = o;
956 }
957
958 if (intern) {
959 o->intern = intern;
960 }
961
962 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqlob_object_free, NULL TSRMLS_CC);
963 o->zv.handlers = &php_pqlob_object_handlers;
964
965 return o->zv;
966 }
967
968 static zend_object_value php_pqcopy_create_object_ex(zend_class_entry *ce, php_pqcopy_t *intern, php_pqcopy_object_t **ptr TSRMLS_DC)
969 {
970 php_pqcopy_object_t *o;
971
972 o = ecalloc(1, sizeof(*o));
973 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
974 object_properties_init((zend_object *) o, ce);
975 o->prophandler = &php_pqcopy_object_prophandlers;
976
977 if (ptr) {
978 *ptr = o;
979 }
980
981 if (intern) {
982 o->intern = intern;
983 }
984
985 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqcopy_object_free, NULL TSRMLS_CC);
986 o->zv.handlers = &php_pqcopy_object_handlers;
987
988 return o->zv;
989 }
990
991 static zend_object_value php_pqconn_create_object(zend_class_entry *class_type TSRMLS_DC)
992 {
993 return php_pqconn_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
994 }
995
996 static zend_object_value php_pqtypes_create_object(zend_class_entry *class_type TSRMLS_DC)
997 {
998 return php_pqtypes_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
999 }
1000
1001 static zend_object_value php_pqres_create_object(zend_class_entry *class_type TSRMLS_DC)
1002 {
1003 return php_pqres_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
1004 }
1005
1006 static zend_object_value php_pqstm_create_object(zend_class_entry *class_type TSRMLS_DC)
1007 {
1008 return php_pqstm_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
1009 }
1010
1011 static zend_object_value php_pqtxn_create_object(zend_class_entry *class_type TSRMLS_DC)
1012 {
1013 return php_pqtxn_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
1014 }
1015
1016 static zend_object_value php_pqcancel_create_object(zend_class_entry *class_type TSRMLS_DC)
1017 {
1018 return php_pqcancel_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
1019 }
1020
1021 static zend_object_value php_pqlob_create_object(zend_class_entry *class_type TSRMLS_DC)
1022 {
1023 return php_pqlob_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
1024 }
1025
1026 static zend_object_value php_pqcopy_create_object(zend_class_entry *class_type TSRMLS_DC)
1027 {
1028 return php_pqcopy_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
1029 }
1030
1031 static int apply_pi_to_ht(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
1032 {
1033 zend_property_info *pi = p;
1034 HashTable *ht = va_arg(argv, HashTable *);
1035 zval *object = va_arg(argv, zval *);
1036 php_pq_object_t *obj = va_arg(argv, php_pq_object_t *);
1037 int addref = va_arg(argv, int);
1038 zval *property = zend_read_property(obj->zo.ce, object, pi->name, pi->name_length, 0 TSRMLS_CC);
1039
1040 if (addref) {
1041 Z_ADDREF_P(property);
1042 }
1043 zend_hash_add(ht, pi->name, pi->name_length + 1, (void *) &property, sizeof(zval *), NULL);
1044
1045 return ZEND_HASH_APPLY_KEEP;
1046 }
1047
1048 static HashTable *php_pq_object_debug_info(zval *object, int *temp TSRMLS_DC)
1049 {
1050 HashTable *ht;
1051 php_pq_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
1052
1053 *temp = 1;
1054 ALLOC_HASHTABLE(ht);
1055 ZEND_INIT_SYMTABLE(ht);
1056
1057 zend_hash_apply_with_arguments(&obj->zo.ce->properties_info TSRMLS_CC, apply_pi_to_ht, 4, ht, object, obj, 1);
1058
1059 return ht;
1060 }
1061
1062 static void php_pqconn_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC)
1063 {
1064 php_pqconn_object_t *obj = o;
1065
1066 RETVAL_LONG(PQstatus(obj->intern->conn));
1067 }
1068
1069 static void php_pqconn_object_read_transaction_status(zval *object, void *o, zval *return_value TSRMLS_DC)
1070 {
1071 php_pqconn_object_t *obj = o;
1072
1073 RETVAL_LONG(PQtransactionStatus(obj->intern->conn));
1074 }
1075
1076 static void php_pqconn_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC)
1077 {
1078 php_pqconn_object_t *obj = o;
1079 char *error = PHP_PQerrorMessage(obj->intern->conn);
1080
1081 if (error) {
1082 RETVAL_STRING(error, 1);
1083 } else {
1084 RETVAL_NULL();
1085 }
1086 }
1087
1088 static int apply_notify_listener(void *p, void *arg TSRMLS_DC)
1089 {
1090 php_pq_callback_t *listener = p;
1091 PGnotify *nfy = arg;
1092 zval *zpid, *zchannel, *zmessage;
1093
1094 MAKE_STD_ZVAL(zpid);
1095 ZVAL_LONG(zpid, nfy->be_pid);
1096 MAKE_STD_ZVAL(zchannel);
1097 ZVAL_STRING(zchannel, nfy->relname, 1);
1098 MAKE_STD_ZVAL(zmessage);
1099 ZVAL_STRING(zmessage, nfy->extra, 1);
1100
1101 zend_fcall_info_argn(&listener->fci TSRMLS_CC, 3, &zchannel, &zmessage, &zpid);
1102 zend_fcall_info_call(&listener->fci, &listener->fcc, NULL, NULL TSRMLS_CC);
1103
1104 zval_ptr_dtor(&zchannel);
1105 zval_ptr_dtor(&zmessage);
1106 zval_ptr_dtor(&zpid);
1107
1108 return ZEND_HASH_APPLY_KEEP;
1109 }
1110
1111 static int apply_notify_listeners(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
1112 {
1113 HashTable *listeners = p;
1114 PGnotify *nfy = va_arg(argv, PGnotify *);
1115
1116 if (0 == fnmatch(key->arKey, nfy->relname, 0)) {
1117 zend_hash_apply_with_argument(listeners, apply_notify_listener, nfy TSRMLS_CC);
1118 }
1119
1120 return ZEND_HASH_APPLY_KEEP;
1121 }
1122
1123 static void php_pqconn_notify_listeners(php_pqconn_object_t *obj TSRMLS_DC)
1124 {
1125 PGnotify *nfy;
1126
1127 while ((nfy = PQnotifies(obj->intern->conn))) {
1128 zend_hash_apply_with_arguments(&obj->intern->listeners TSRMLS_CC, apply_notify_listeners, 1, nfy);
1129 PQfreemem(nfy);
1130 }
1131 }
1132
1133 static void php_pqconn_object_read_busy(zval *object, void *o, zval *return_value TSRMLS_DC)
1134 {
1135 php_pqconn_object_t *obj = o;
1136
1137 RETVAL_BOOL(PQisBusy(obj->intern->conn));
1138 }
1139
1140 static void php_pqconn_object_read_encoding(zval *object, void *o, zval *return_value TSRMLS_DC)
1141 {
1142 php_pqconn_object_t *obj = o;
1143
1144 RETVAL_STRING(pg_encoding_to_char(PQclientEncoding(obj->intern->conn)), 1);
1145 }
1146
1147 static void php_pqconn_object_write_encoding(zval *object, void *o, zval *value TSRMLS_DC)
1148 {
1149 php_pqconn_object_t *obj = o;
1150 zval *zenc = value;
1151
1152 if (Z_TYPE_P(value) != IS_STRING) {
1153 if (Z_REFCOUNT_P(value) > 1) {
1154 zval *tmp;
1155 MAKE_STD_ZVAL(tmp);
1156 ZVAL_ZVAL(tmp, zenc, 1, 0);
1157 convert_to_string(tmp);
1158 zenc = tmp;
1159 } else {
1160 convert_to_string_ex(&zenc);
1161 }
1162 }
1163
1164 if (0 > PQsetClientEncoding(obj->intern->conn, Z_STRVAL_P(zenc))) {
1165 zend_error(E_NOTICE, "Unrecognized encoding '%s'", Z_STRVAL_P(zenc));
1166 }
1167
1168 if (zenc != value) {
1169 zval_ptr_dtor(&zenc);
1170 }
1171 }
1172
1173 static void php_pqconn_object_read_unbuffered(zval *object, void *o, zval *return_value TSRMLS_DC)
1174 {
1175 php_pqconn_object_t *obj = o;
1176
1177 RETVAL_BOOL(obj->intern->unbuffered);
1178 }
1179
1180 static void php_pqconn_object_write_unbuffered(zval *object, void *o, zval *value TSRMLS_DC)
1181 {
1182 php_pqconn_object_t *obj = o;
1183
1184 obj->intern->unbuffered = zend_is_true(value);
1185 }
1186
1187 static void php_pqconn_object_read_db(zval *object, void *o, zval *return_value TSRMLS_DC)
1188 {
1189 php_pqconn_object_t *obj = o;
1190 char *db = PQdb(obj->intern->conn);
1191
1192 if (db) {
1193 RETVAL_STRING(db, 1);
1194 } else {
1195 RETVAL_EMPTY_STRING();
1196 }
1197 }
1198
1199 static void php_pqconn_object_read_user(zval *object, void *o, zval *return_value TSRMLS_DC)
1200 {
1201 php_pqconn_object_t *obj = o;
1202 char *user = PQuser(obj->intern->conn);
1203
1204 if (user) {
1205 RETVAL_STRING(user, 1);
1206 } else {
1207 RETVAL_EMPTY_STRING();
1208 }
1209 }
1210
1211 static void php_pqconn_object_read_pass(zval *object, void *o, zval *return_value TSRMLS_DC)
1212 {
1213 php_pqconn_object_t *obj = o;
1214 char *pass = PQpass(obj->intern->conn);
1215
1216 if (pass) {
1217 RETVAL_STRING(pass, 1);
1218 } else {
1219 RETVAL_EMPTY_STRING();
1220 }
1221 }
1222
1223 static void php_pqconn_object_read_host(zval *object, void *o, zval *return_value TSRMLS_DC)
1224 {
1225 php_pqconn_object_t *obj = o;
1226 char *host = PQhost(obj->intern->conn);
1227
1228 if (host) {
1229 RETVAL_STRING(host, 1);
1230 } else {
1231 RETVAL_EMPTY_STRING();
1232 }
1233 }
1234
1235 static void php_pqconn_object_read_port(zval *object, void *o, zval *return_value TSRMLS_DC)
1236 {
1237 php_pqconn_object_t *obj = o;
1238 char *port = PQport(obj->intern->conn);
1239
1240 if (port) {
1241 RETVAL_STRING(port, 1);
1242 } else {
1243 RETVAL_EMPTY_STRING();
1244 }
1245 }
1246
1247 static void php_pqconn_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC)
1248 {
1249 php_pqconn_object_t *obj = o;
1250 char *options = PQoptions(obj->intern->conn);
1251
1252 if (options) {
1253 RETVAL_STRING(options, 1);
1254 } else {
1255 RETVAL_EMPTY_STRING();
1256 }
1257 }
1258
1259 static void php_pqconn_object_read_event_handlers(zval *object, void *o, zval *return_value TSRMLS_DC)
1260 {
1261 php_pqconn_object_t *obj = o;
1262
1263 array_init(return_value);
1264 zend_hash_copy(Z_ARRVAL_P(return_value), &obj->intern->eventhandlers, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
1265 }
1266
1267 static void php_pqtypes_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
1268 {
1269 php_pqtypes_object_t *obj = o;
1270
1271 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
1272 }
1273
1274 static int has_dimension(HashTable *ht, zval *member, char **key_str, int *key_len, ulong *index TSRMLS_DC)
1275 {
1276 long lval = 0;
1277 zval *tmp = member;
1278
1279 switch (Z_TYPE_P(member)) {
1280 default:
1281 convert_to_string_ex(&tmp);
1282 /* no break */
1283 case IS_STRING:
1284 if (!is_numeric_string(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp), &lval, NULL, 0)) {
1285 int exists = zend_hash_exists(ht, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp) + 1);
1286
1287 if (key_str) {
1288 *key_str = estrndup(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp));
1289 if (key_len) {
1290 *key_len = Z_STRLEN_P(tmp) + 1;
1291 }
1292 }
1293 if (member != tmp) {
1294 zval_ptr_dtor(&tmp);
1295 }
1296
1297 return exists;
1298 }
1299 break;
1300 case IS_LONG:
1301 lval = Z_LVAL_P(member);
1302 break;
1303 }
1304
1305 if (member != tmp) {
1306 zval_ptr_dtor(&tmp);
1307 }
1308 if (index) {
1309 *index = lval;
1310 }
1311 return zend_hash_index_exists(ht, lval);
1312 }
1313
1314 static int php_pqtypes_object_has_dimension(zval *object, zval *member, int check_empty TSRMLS_DC)
1315 {
1316 php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
1317 char *key_str = NULL;
1318 int key_len = 0;
1319 ulong index = 0;
1320
1321 if (check_empty) {
1322 if (has_dimension(&obj->intern->types, member, &key_str, &key_len, &index TSRMLS_CC)) {
1323 zval **data;
1324
1325 if (key_str && key_len) {
1326 if (SUCCESS == zend_hash_find(&obj->intern->types, key_str, key_len, (void *) &data)) {
1327 efree(key_str);
1328 return Z_TYPE_PP(data) != IS_NULL;
1329 }
1330 efree(key_str);
1331 } else {
1332 if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) &data)) {
1333 return Z_TYPE_PP(data) != IS_NULL;
1334 }
1335 }
1336 }
1337 if (key_str) {
1338 efree(key_str);
1339 }
1340 } else {
1341 return has_dimension(&obj->intern->types, member, NULL, NULL, NULL TSRMLS_CC);
1342 }
1343
1344 return 0;
1345 }
1346
1347 static zval *php_pqtypes_object_read_dimension(zval *object, zval *member, int type TSRMLS_DC)
1348 {
1349 ulong index = 0;
1350 char *key_str = NULL;
1351 int key_len = 0;
1352 php_pqtypes_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
1353
1354 if (has_dimension(&obj->intern->types, member, &key_str, &key_len, &index TSRMLS_CC)) {
1355 zval **data;
1356
1357 if (key_str && key_len) {
1358 if (SUCCESS == zend_hash_find(&obj->intern->types, key_str, key_len, (void *) &data)) {
1359 efree(key_str);
1360 return *data;
1361 }
1362 } else {
1363 if (SUCCESS == zend_hash_index_find(&obj->intern->types, index, (void *) &data)) {
1364 return *data;
1365 }
1366 }
1367 if (key_str) {
1368 efree(key_str);
1369 }
1370 }
1371
1372 return NULL;
1373 }
1374
1375 static void php_pqres_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC)
1376 {
1377 php_pqres_object_t *obj = o;
1378
1379 RETVAL_LONG(PQresultStatus(obj->intern->res));
1380 }
1381
1382 static void php_pqres_object_read_status_message(zval *object, void *o, zval *return_value TSRMLS_DC)
1383 {
1384 php_pqres_object_t *obj = o;
1385
1386 RETVAL_STRING(PQresStatus(PQresultStatus(obj->intern->res))+sizeof("PGRES"), 1);
1387 }
1388
1389 static void php_pqres_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC)
1390 {
1391 php_pqres_object_t *obj = o;
1392 char *error = PHP_PQresultErrorMessage(obj->intern->res);
1393
1394 if (error) {
1395 RETVAL_STRING(error, 1);
1396 } else {
1397 RETVAL_NULL();
1398 }
1399 }
1400
1401 static void php_pqres_object_read_num_rows(zval *object, void *o, zval *return_value TSRMLS_DC)
1402 {
1403 php_pqres_object_t *obj = o;
1404
1405 RETVAL_LONG(PQntuples(obj->intern->res));
1406 }
1407
1408 static void php_pqres_object_read_num_cols(zval *object, void *o, zval *return_value TSRMLS_DC)
1409 {
1410 php_pqres_object_t *obj = o;
1411
1412 RETVAL_LONG(PQnfields(obj->intern->res));
1413 }
1414
1415 static void php_pqres_object_read_affected_rows(zval *object, void *o, zval *return_value TSRMLS_DC)
1416 {
1417 php_pqres_object_t *obj = o;
1418
1419 RETVAL_LONG(atoi(PQcmdTuples(obj->intern->res)));
1420 }
1421
1422 static void php_pqres_object_read_fetch_type(zval *object, void *o, zval *return_value TSRMLS_DC)
1423 {
1424 php_pqres_object_t *obj = o;
1425
1426 if (obj->intern->iter) {
1427 RETVAL_LONG(obj->intern->iter->fetch_type);
1428 } else {
1429 RETVAL_LONG(PHP_PQRES_FETCH_ARRAY);
1430 }
1431 }
1432
1433 static void php_pqres_object_write_fetch_type(zval *object, void *o, zval *value TSRMLS_DC)
1434 {
1435 php_pqres_object_t *obj = o;
1436 zval *zfetch_type = value;
1437
1438 if (Z_TYPE_P(value) != IS_LONG) {
1439 if (Z_REFCOUNT_P(value) > 1) {
1440 zval *tmp;
1441 MAKE_STD_ZVAL(tmp);
1442 ZVAL_ZVAL(tmp, zfetch_type, 1, 0);
1443 convert_to_long(tmp);
1444 zfetch_type = tmp;
1445 } else {
1446 convert_to_long_ex(&zfetch_type);
1447 }
1448 }
1449
1450 if (!obj->intern->iter) {
1451 obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(object), object, 0 TSRMLS_CC);
1452 obj->intern->iter->zi.funcs->rewind((zend_object_iterator *) obj->intern->iter TSRMLS_CC);
1453 }
1454 obj->intern->iter->fetch_type = Z_LVAL_P(zfetch_type);
1455
1456 if (zfetch_type != value) {
1457 zval_ptr_dtor(&zfetch_type);
1458 }
1459 }
1460
1461 static void php_pqstm_object_read_name(zval *object, void *o, zval *return_value TSRMLS_DC)
1462 {
1463 php_pqstm_object_t *obj = o;
1464
1465 RETVAL_STRING(obj->intern->name, 1);
1466 }
1467
1468 static void php_pqstm_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
1469 {
1470 php_pqstm_object_t *obj = o;
1471
1472 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
1473 }
1474
1475 static void php_pqtxn_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
1476 {
1477 php_pqtxn_object_t *obj = o;
1478
1479 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
1480 }
1481
1482 static void php_pqtxn_object_read_isolation(zval *object, void *o, zval *return_value TSRMLS_DC)
1483 {
1484 php_pqtxn_object_t *obj = o;
1485
1486 RETVAL_LONG(obj->intern->isolation);
1487 }
1488
1489 static void php_pqtxn_object_read_readonly(zval *object, void *o, zval *return_value TSRMLS_DC)
1490 {
1491 php_pqtxn_object_t *obj = o;
1492
1493 RETVAL_BOOL(obj->intern->readonly);
1494 }
1495
1496 static void php_pqtxn_object_read_deferrable(zval *object, void *o, zval *return_value TSRMLS_DC)
1497 {
1498 php_pqtxn_object_t *obj = o;
1499
1500 RETVAL_BOOL(obj->intern->deferrable);
1501 }
1502
1503 static void php_pqtxn_object_write_isolation(zval *object, void *o, zval *value TSRMLS_DC)
1504 {
1505 php_pqtxn_object_t *obj = o;
1506 php_pqtxn_isolation_t orig = obj->intern->isolation;
1507 zval *zisolation = value;
1508 PGresult *res;
1509
1510 if (Z_TYPE_P(zisolation) != IS_LONG) {
1511 if (Z_REFCOUNT_P(value) > 1) {
1512 zval *tmp;
1513 MAKE_STD_ZVAL(tmp);
1514 ZVAL_ZVAL(tmp, zisolation, 1, 0);
1515 convert_to_long(tmp);
1516 zisolation = tmp;
1517 } else {
1518 convert_to_long_ex(&zisolation);
1519 }
1520 }
1521
1522 switch ((obj->intern->isolation = Z_LVAL_P(zisolation))) {
1523 case PHP_PQTXN_READ_COMMITTED:
1524 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL READ COMMITED");
1525 break;
1526 case PHP_PQTXN_REPEATABLE_READ:
1527 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
1528 break;
1529 case PHP_PQTXN_SERIALIZABLE:
1530 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
1531 break;
1532 default:
1533 obj->intern->isolation = orig;
1534 res = NULL;
1535 break;
1536 }
1537
1538 if (zisolation != value) {
1539 zval_ptr_dtor(&zisolation);
1540 }
1541
1542 if (res) {
1543 php_pqres_success(res TSRMLS_CC);
1544 PHP_PQclear(res);
1545 }
1546 }
1547
1548 static void php_pqtxn_object_write_readonly(zval *object, void *o, zval *value TSRMLS_DC)
1549 {
1550 php_pqtxn_object_t *obj = o;
1551 PGresult *res;
1552
1553 if ((obj->intern->readonly = zend_is_true(value))) {
1554 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION READ ONLY");
1555 } else {
1556 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION READ WRITE");
1557 }
1558
1559 if (res) {
1560 php_pqres_success(res TSRMLS_CC);
1561 PHP_PQclear(res);
1562 }
1563 }
1564
1565 static void php_pqtxn_object_write_deferrable(zval *object, void *o, zval *value TSRMLS_DC)
1566 {
1567 php_pqtxn_object_t *obj = o;
1568 PGresult *res;
1569
1570 if ((obj->intern->deferrable = zend_is_true(value))) {
1571 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION DEFERRABLE");
1572 } else {
1573 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION NOT DEFERRABLE");
1574 }
1575
1576 if (res) {
1577 php_pqres_success(res TSRMLS_CC);
1578 PHP_PQclear(res);
1579 }
1580 }
1581
1582 static void php_pqcancel_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
1583 {
1584 php_pqcancel_object_t *obj = o;
1585
1586 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
1587 }
1588
1589 static void php_pqlob_object_read_transaction(zval *object, void *o, zval *return_value TSRMLS_DC)
1590 {
1591 php_pqlob_object_t *obj = o;
1592
1593 php_pq_object_to_zval(obj->intern->txn, &return_value TSRMLS_CC);
1594 }
1595
1596 static void php_pqlob_object_read_oid(zval *object, void *o, zval *return_value TSRMLS_DC)
1597 {
1598 php_pqlob_object_t *obj = o;
1599
1600 RETVAL_LONG(obj->intern->loid);
1601 }
1602
1603 static void php_pqcopy_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
1604 {
1605 php_pqcopy_object_t *obj = o;
1606
1607 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
1608 }
1609
1610 static void php_pqcopy_object_read_direction(zval *object, void *o, zval *return_value TSRMLS_DC)
1611 {
1612 php_pqcopy_object_t *obj = o;
1613
1614 RETVAL_LONG(obj->intern->direction);
1615 }
1616
1617 static void php_pqcopy_object_read_expression(zval *object, void *o, zval *return_value TSRMLS_DC)
1618 {
1619 php_pqcopy_object_t *obj = o;
1620
1621 RETURN_STRING(obj->intern->expression, 1);
1622 }
1623
1624 static void php_pqcopy_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC)
1625 {
1626 php_pqcopy_object_t *obj = o;
1627
1628 RETURN_STRING(obj->intern->options, 1);
1629 }
1630
1631 static zend_class_entry *ancestor(zend_class_entry *ce) {
1632 while (ce->parent) {
1633 ce = ce->parent;
1634 }
1635 return ce;
1636 }
1637
1638 static zval *php_pq_object_read_prop(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
1639 {
1640 php_pq_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
1641 php_pq_object_prophandler_t *handler;
1642 zval *return_value;
1643
1644 if (!obj->intern) {
1645 zend_error(E_WARNING, "%s not initialized", ancestor(obj->zo.ce)->name);
1646 } else if ((SUCCESS == zend_hash_find(obj->prophandler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) && handler->read) {
1647 if (type == BP_VAR_R) {
1648 ALLOC_ZVAL(return_value);
1649 Z_SET_REFCOUNT_P(return_value, 0);
1650 Z_UNSET_ISREF_P(return_value);
1651
1652 handler->read(object, obj, return_value TSRMLS_CC);
1653 } else {
1654 zend_error(E_ERROR, "Cannot access %s properties by reference or array key/index", ancestor(obj->zo.ce)->name);
1655 return_value = NULL;
1656 }
1657 } else {
1658 return_value = zend_get_std_object_handlers()->read_property(object, member, type, key TSRMLS_CC);
1659 }
1660
1661 return return_value;
1662 }
1663
1664 static void php_pq_object_write_prop(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
1665 {
1666 php_pq_object_t *obj = zend_object_store_get_object(object TSRMLS_CC);
1667 php_pq_object_prophandler_t *handler;
1668
1669 if (SUCCESS == zend_hash_find(obj->prophandler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void *) &handler)) {
1670 if (handler->write) {
1671 handler->write(object, obj, value TSRMLS_CC);
1672 }
1673 } else {
1674 zend_get_std_object_handlers()->write_property(object, member, value, key TSRMLS_CC);
1675 }
1676 }
1677
1678 static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
1679 {
1680 zval *zsocket, zmember;
1681 php_stream *stream;
1682 STATUS retval;
1683 int socket;
1684
1685 if (!obj) {
1686 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1687 }
1688
1689 INIT_PZVAL(&zmember);
1690 ZVAL_STRINGL(&zmember, "socket", sizeof("socket")-1, 0);
1691 MAKE_STD_ZVAL(zsocket);
1692
1693 if ((CONNECTION_BAD != PQstatus(obj->intern->conn))
1694 && (-1 < (socket = PQsocket(obj->intern->conn)))
1695 && (stream = php_stream_fopen_from_fd(socket, "r+b", NULL))) {
1696 stream->flags |= PHP_STREAM_FLAG_NO_CLOSE;
1697 php_stream_to_zval(stream, zsocket);
1698 retval = SUCCESS;
1699 } else {
1700 ZVAL_NULL(zsocket);
1701 retval = FAILURE;
1702 }
1703 zend_get_std_object_handlers()->write_property(getThis(), &zmember, zsocket, NULL TSRMLS_CC);
1704 zval_ptr_dtor(&zsocket);
1705
1706 return retval;
1707 }
1708
1709 #ifdef ZTS
1710 # define TSRMLS_DF(d) TSRMLS_D = (d)->ts
1711 # define TSRMLS_CF(d) (d)->ts = TSRMLS_C
1712 #else
1713 # define TSRMLS_DF(d)
1714 # define TSRMLS_CF(d)
1715 #endif
1716
1717 static int apply_event(void *p, void *a TSRMLS_DC)
1718 {
1719 php_pq_callback_t *cb = p;
1720 zval *args = a;
1721 zval *retval = NULL;
1722
1723 zend_fcall_info_args(&cb->fci, args TSRMLS_CC);
1724 zend_fcall_info_call(&cb->fci, &cb->fcc, &retval, NULL TSRMLS_CC);
1725 if (retval) {
1726 zval_ptr_dtor(&retval);
1727 }
1728
1729 return ZEND_HASH_APPLY_KEEP;
1730 }
1731
1732 static void php_pqconn_event_connreset(PGEventConnReset *event)
1733 {
1734 php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event);
1735
1736 if (data) {
1737 HashTable *evhs;
1738 TSRMLS_DF(data);
1739
1740 if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("reset"), (void *) &evhs)) {
1741 zval *args, *connection = NULL;
1742
1743 MAKE_STD_ZVAL(args);
1744 array_init(args);
1745 php_pq_object_to_zval(data->obj, &connection TSRMLS_CC);
1746 add_next_index_zval(args, connection);
1747 zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC);
1748 zval_ptr_dtor(&args);
1749 }
1750 }
1751 }
1752
1753 static void php_pqres_init_instance_data(PGresult *res, php_pqres_object_t **ptr TSRMLS_DC)
1754 {
1755 php_pqres_object_t *obj;
1756 php_pqres_t *r = ecalloc(1, sizeof(*r));
1757
1758 r->res = res;
1759 ZEND_INIT_SYMTABLE(&r->bound);
1760 php_pqres_create_object_ex(php_pqres_class_entry, r, &obj TSRMLS_CC);
1761
1762 PQresultSetInstanceData(res, php_pqconn_event, obj);
1763
1764 if (ptr) {
1765 *ptr = obj;
1766 }
1767 }
1768
1769 static void php_pqconn_event_resultcreate(PGEventResultCreate *event)
1770 {
1771 php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event);
1772
1773 if (data) {
1774 php_pqres_object_t *obj;
1775 HashTable *evhs;
1776 TSRMLS_DF(data);
1777
1778 php_pqres_init_instance_data(event->result, &obj TSRMLS_CC);
1779
1780 /* event listener */
1781 if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("result"), (void *) &evhs)) {
1782 zval *args, *connection = NULL, *res = NULL;
1783
1784 MAKE_STD_ZVAL(args);
1785 array_init(args);
1786 php_pq_object_to_zval(data->obj, &connection TSRMLS_CC);
1787 add_next_index_zval(args, connection);
1788 php_pq_object_to_zval(obj, &res TSRMLS_CC);
1789 add_next_index_zval(args, res);
1790 zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC);
1791 zval_ptr_dtor(&args);
1792 }
1793
1794 /* async callback */
1795 if (data->obj->intern->onevent.fci.size > 0) {
1796 zval *res = NULL;
1797
1798 php_pq_object_to_zval(obj, &res TSRMLS_CC);
1799 zend_fcall_info_argn(&data->obj->intern->onevent.fci TSRMLS_CC, 1, &res);
1800 zend_fcall_info_call(&data->obj->intern->onevent.fci, &data->obj->intern->onevent.fcc, NULL, NULL TSRMLS_CC);
1801 zval_ptr_dtor(&res);
1802 }
1803
1804 }
1805 }
1806
1807 static void php_pqconn_event_resultdestroy(PGEventResultDestroy *event)
1808 {
1809 php_pqres_object_t *obj = PQresultInstanceData(event->result, php_pqconn_event);
1810
1811 if (obj) {
1812 obj->intern->res = NULL;
1813 }
1814 }
1815
1816 static int php_pqconn_event(PGEventId id, void *e, void *data)
1817 {
1818 switch (id) {
1819 case PGEVT_CONNRESET:
1820 php_pqconn_event_connreset(e);
1821 break;
1822 case PGEVT_RESULTCREATE:
1823 php_pqconn_event_resultcreate(e);
1824 break;
1825 case PGEVT_RESULTDESTROY:
1826 php_pqconn_event_resultdestroy(e);
1827 break;
1828 default:
1829 break;
1830 }
1831
1832 return 1;
1833 }
1834
1835 static php_pqconn_event_data_t *php_pqconn_event_data_init(php_pqconn_object_t *obj TSRMLS_DC)
1836 {
1837 php_pqconn_event_data_t *data = emalloc(sizeof(*data));
1838
1839 data->obj = obj;
1840 TSRMLS_CF(data);
1841
1842 return data;
1843 }
1844
1845 static void php_pqconn_notice_recv(void *p, const PGresult *res)
1846 {
1847 php_pqconn_event_data_t *data = p;
1848
1849 if (data) {
1850 HashTable *evhs;
1851 TSRMLS_DF(data);
1852
1853 if (SUCCESS == zend_hash_find(&data->obj->intern->eventhandlers, ZEND_STRS("notice"), (void *) &evhs)) {
1854 zval *args, *connection = NULL;
1855
1856 MAKE_STD_ZVAL(args);
1857 array_init(args);
1858 php_pq_object_to_zval(data->obj, &connection TSRMLS_CC);
1859 add_next_index_zval(args, connection);
1860 add_next_index_string(args, PHP_PQresultErrorMessage(res), 1);
1861 zend_hash_apply_with_argument(evhs, apply_event, args TSRMLS_CC);
1862 zval_ptr_dtor(&args);
1863 }
1864 }
1865 }
1866
1867 typedef struct php_pqconn_resource_factory_data {
1868 char *dsn;
1869 long flags;
1870 } php_pqconn_resource_factory_data_t;
1871
1872 static void *php_pqconn_resource_factory_ctor(void *data, void *init_arg TSRMLS_DC)
1873 {
1874 php_pqconn_resource_factory_data_t *o = init_arg;
1875 PGconn *conn = NULL;;
1876
1877 if (o->flags & PHP_PQCONN_ASYNC) {
1878 conn = PQconnectStart(o->dsn);
1879 } else {
1880 conn = PQconnectdb(o->dsn);
1881 }
1882
1883 if (conn) {
1884 PQregisterEventProc(conn, php_pqconn_event, "ext-pq", NULL);
1885 }
1886
1887 return conn;
1888 }
1889
1890 static void php_pqconn_resource_factory_dtor(void *opaque, void *handle TSRMLS_DC)
1891 {
1892 php_pqconn_event_data_t *evdata = PQinstanceData(handle, php_pqconn_event);
1893
1894 /* we don't care for anything, except free'ing evdata */
1895 if (evdata) {
1896 PQsetInstanceData(handle, php_pqconn_event, NULL);
1897 memset(evdata, 0, sizeof(*evdata));
1898 efree(evdata);
1899 }
1900
1901 PQfinish(handle);
1902 }
1903
1904 static php_resource_factory_ops_t php_pqconn_resource_factory_ops = {
1905 php_pqconn_resource_factory_ctor,
1906 NULL,
1907 php_pqconn_resource_factory_dtor
1908 };
1909
1910 static void php_pqconn_wakeup(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC)
1911 {
1912 // FIXME: ping server
1913 }
1914
1915 static int apply_unlisten(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
1916 {
1917 php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *);
1918 char *quoted_channel = PQescapeIdentifier(obj->intern->conn, key->arKey, key->nKeyLength - 1);
1919
1920 if (quoted_channel) {
1921 PGresult *res;
1922 char *cmd;
1923
1924 spprintf(&cmd, 0, "UNLISTEN %s", quoted_channel);
1925 if ((res = PQexec(obj->intern->conn, cmd))) {
1926 PHP_PQclear(res);
1927 }
1928
1929 efree(cmd);
1930 PQfreemem(quoted_channel);
1931 }
1932
1933 return ZEND_HASH_APPLY_REMOVE;
1934 }
1935
1936 static void php_pqconn_notice_ignore(void *p, const PGresult *res)
1937 {
1938 }
1939
1940 static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC)
1941 {
1942 php_pqconn_event_data_t *evdata = PQinstanceData(*handle, php_pqconn_event);
1943 PGcancel *cancel;
1944 PGresult *res;
1945
1946 /* go away */
1947 PQsetInstanceData(*handle, php_pqconn_event, NULL);
1948
1949 /* ignore notices */
1950 PQsetNoticeReceiver(*handle, php_pqconn_notice_ignore, NULL);
1951
1952 /* cancel async queries */
1953 if (PQisBusy(*handle) && (cancel = PQgetCancel(*handle))) {
1954 char err[256] = {0};
1955
1956 PQcancel(cancel, err, sizeof(err));
1957 PQfreeCancel(cancel);
1958 }
1959 /* clean up async results */
1960 while ((res = PQgetResult(*handle))) {
1961 PHP_PQclear(res);
1962 }
1963
1964 /* clean up transaction & session */
1965 switch (PQtransactionStatus(*handle)) {
1966 case PQTRANS_IDLE:
1967 res = PQexec(*handle, "RESET ALL");
1968 break;
1969 default:
1970 res = PQexec(*handle, "ROLLBACK; RESET ALL");
1971 break;
1972 }
1973
1974 if (res) {
1975 PHP_PQclear(res);
1976 }
1977
1978 if (evdata) {
1979 /* clean up notify listeners */
1980 zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj);
1981
1982 /* release instance data */
1983 memset(evdata, 0, sizeof(*evdata));
1984 efree(evdata);
1985 }
1986 }
1987
1988 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_construct, 0, 0, 1)
1989 ZEND_ARG_INFO(0, dsn)
1990 ZEND_ARG_INFO(0, async)
1991 ZEND_END_ARG_INFO();
1992 static PHP_METHOD(pqconn, __construct) {
1993 zend_error_handling zeh;
1994 char *dsn_str = "";
1995 int dsn_len = 0;
1996 long flags = 0;
1997 STATUS rv;
1998
1999 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2000 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &dsn_str, &dsn_len, &flags);
2001 zend_restore_error_handling(&zeh TSRMLS_CC);
2002
2003 if (SUCCESS == rv) {
2004 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2005
2006 if (obj->intern) {
2007 throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\Connection already initialized");
2008 } else {
2009 php_pqconn_event_data_t *evdata = php_pqconn_event_data_init(obj TSRMLS_CC);
2010 php_pqconn_resource_factory_data_t rfdata = {dsn_str, flags};
2011
2012 obj->intern = ecalloc(1, sizeof(*obj->intern));
2013
2014 zend_hash_init(&obj->intern->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
2015 zend_hash_init(&obj->intern->eventhandlers, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
2016
2017 if (flags & PHP_PQCONN_PERSISTENT) {
2018 php_persistent_handle_factory_t *phf = php_persistent_handle_concede(NULL, ZEND_STRL("pq\\Connection"), dsn_str, dsn_len, php_pqconn_wakeup, php_pqconn_retire TSRMLS_CC);
2019 php_resource_factory_init(&obj->intern->factory, php_persistent_handle_get_resource_factory_ops(), phf, (void (*)(void*)) php_persistent_handle_abandon);
2020 } else {
2021 php_resource_factory_init(&obj->intern->factory, &php_pqconn_resource_factory_ops, NULL, NULL);
2022 }
2023
2024 if (flags & PHP_PQCONN_ASYNC) {
2025 obj->intern->poller = (int (*)(PGconn*)) PQconnectPoll;
2026 }
2027
2028 obj->intern->conn = php_resource_factory_handle_ctor(&obj->intern->factory, &rfdata TSRMLS_CC);
2029
2030 PQsetInstanceData(obj->intern->conn, php_pqconn_event, evdata);
2031 PQsetNoticeReceiver(obj->intern->conn, php_pqconn_notice_recv, evdata);
2032
2033 if (SUCCESS != php_pqconn_update_socket(getThis(), obj TSRMLS_CC)) {
2034 throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection failed (%s)", PHP_PQerrorMessage(obj->intern->conn));
2035 }
2036 }
2037 }
2038 }
2039
2040 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset, 0, 0, 0)
2041 ZEND_END_ARG_INFO();
2042 static PHP_METHOD(pqconn, reset) {
2043 zend_error_handling zeh;
2044 STATUS rv;
2045
2046 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2047 rv = zend_parse_parameters_none();
2048 zend_restore_error_handling(&zeh TSRMLS_CC);
2049
2050 if (SUCCESS == rv) {
2051 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2052
2053 if (!obj->intern) {
2054 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2055 } else {
2056 PQreset(obj->intern->conn);
2057
2058 if (CONNECTION_OK != PQstatus(obj->intern->conn)) {
2059 throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection reset failed: (%s)", PHP_PQerrorMessage(obj->intern->conn));
2060 }
2061
2062 php_pqconn_notify_listeners(obj TSRMLS_CC);
2063 }
2064 }
2065 }
2066
2067 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset_async, 0, 0, 0)
2068 ZEND_END_ARG_INFO();
2069 static PHP_METHOD(pqconn, resetAsync) {
2070 zend_error_handling zeh;
2071 STATUS rv;
2072
2073 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2074 rv = zend_parse_parameters_none();
2075 zend_restore_error_handling(&zeh TSRMLS_CC);
2076
2077 if (SUCCESS == rv) {
2078 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2079
2080 if (!obj->intern) {
2081 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2082 } else {
2083 if (!PQresetStart(obj->intern->conn)) {
2084 throw_exce(EX_IO TSRMLS_CC, "Failed to start connection reset (%s)", PHP_PQerrorMessage(obj->intern->conn));
2085 } else {
2086 obj->intern->poller = (int (*)(PGconn*)) PQresetPoll;
2087 }
2088
2089 php_pqconn_notify_listeners(obj TSRMLS_CC);
2090 }
2091 }
2092 }
2093
2094 static void php_pqconn_add_listener(php_pqconn_object_t *obj, const char *channel_str, size_t channel_len, php_pq_callback_t *listener TSRMLS_DC)
2095 {
2096 HashTable ht, *existing_listeners;
2097
2098 php_pq_callback_addref(listener);
2099
2100 if (SUCCESS == zend_hash_find(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &existing_listeners)) {
2101 zend_hash_next_index_insert(existing_listeners, (void *) listener, sizeof(*listener), NULL);
2102 } else {
2103 zend_hash_init(&ht, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0);
2104 zend_hash_next_index_insert(&ht, (void *) listener, sizeof(*listener), NULL);
2105 zend_hash_add(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &ht, sizeof(HashTable), NULL);
2106 }
2107 }
2108
2109 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 0)
2110 ZEND_ARG_INFO(0, channel)
2111 ZEND_ARG_INFO(0, callable)
2112 ZEND_END_ARG_INFO();
2113 static PHP_METHOD(pqconn, listen) {
2114 zend_error_handling zeh;
2115 char *channel_str = NULL;
2116 int channel_len = 0;
2117 php_pq_callback_t listener;
2118 STATUS rv;
2119
2120 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2121 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc);
2122 zend_restore_error_handling(&zeh TSRMLS_CC);
2123
2124 if (SUCCESS == rv) {
2125 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2126
2127 if (!obj->intern) {
2128 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2129 } else {
2130 char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len);
2131
2132 if (!quoted_channel) {
2133 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn));
2134 } else {
2135 PGresult *res;
2136 smart_str cmd = {0};
2137
2138 smart_str_appends(&cmd, "LISTEN ");
2139 smart_str_appends(&cmd, quoted_channel);
2140 smart_str_0(&cmd);
2141
2142 res = PQexec(obj->intern->conn, cmd.c);
2143
2144 smart_str_free(&cmd);
2145 PQfreemem(quoted_channel);
2146
2147 if (!res) {
2148 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to install listener (%s)", PHP_PQerrorMessage(obj->intern->conn));
2149 } else {
2150 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
2151 obj->intern->poller = PQconsumeInput;
2152 php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC);
2153 }
2154 PHP_PQclear(res);
2155 }
2156
2157 php_pqconn_notify_listeners(obj TSRMLS_CC);
2158 }
2159 }
2160 }
2161 }
2162
2163 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen_async, 0, 0, 0)
2164 ZEND_ARG_INFO(0, channel)
2165 ZEND_ARG_INFO(0, callable)
2166 ZEND_END_ARG_INFO();
2167 static PHP_METHOD(pqconn, listenAsync) {
2168 zend_error_handling zeh;
2169 char *channel_str = NULL;
2170 int channel_len = 0;
2171 php_pq_callback_t listener;
2172 STATUS rv;
2173
2174 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2175 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc);
2176 zend_restore_error_handling(&zeh TSRMLS_CC);
2177
2178 if (SUCCESS == rv) {
2179 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2180
2181 if (!obj->intern) {
2182 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2183 } else {
2184 char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len);
2185
2186 if (!quoted_channel) {
2187 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn));
2188 } else {
2189 smart_str cmd = {0};
2190
2191 smart_str_appends(&cmd, "LISTEN ");
2192 smart_str_appends(&cmd, quoted_channel);
2193 smart_str_0(&cmd);
2194
2195 if (!PQsendQuery(obj->intern->conn, cmd.c)) {
2196 throw_exce(EX_IO TSRMLS_CC, "Failed to install listener (%s)", PHP_PQerrorMessage(obj->intern->conn));
2197 } else {
2198 obj->intern->poller = PQconsumeInput;
2199 php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC);
2200 }
2201
2202 smart_str_free(&cmd);
2203 PQfreemem(quoted_channel);
2204 php_pqconn_notify_listeners(obj TSRMLS_CC);
2205 }
2206 }
2207 }
2208 }
2209
2210 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify, 0, 0, 2)
2211 ZEND_ARG_INFO(0, channel)
2212 ZEND_ARG_INFO(0, message)
2213 ZEND_END_ARG_INFO();
2214 static PHP_METHOD(pqconn, notify) {
2215 zend_error_handling zeh;
2216 char *channel_str, *message_str;
2217 int channel_len, message_len;
2218 STATUS rv;
2219
2220 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2221 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len);
2222 zend_restore_error_handling(&zeh TSRMLS_CC);
2223
2224 if (SUCCESS == rv) {
2225 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2226
2227 if (!obj->intern) {
2228 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2229 } else {
2230 PGresult *res;
2231 char *params[2] = {channel_str, message_str};
2232
2233 res = PQexecParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0);
2234
2235 if (!res) {
2236 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn));
2237 } else {
2238 php_pqres_success(res TSRMLS_CC);
2239 PHP_PQclear(res);
2240 }
2241
2242 php_pqconn_notify_listeners(obj TSRMLS_CC);
2243 }
2244 }
2245 }
2246
2247 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify_async, 0, 0, 2)
2248 ZEND_ARG_INFO(0, channel)
2249 ZEND_ARG_INFO(0, message)
2250 ZEND_END_ARG_INFO();
2251 static PHP_METHOD(pqconn, notifyAsync) {
2252 zend_error_handling zeh;
2253 char *channel_str, *message_str;
2254 int channel_len, message_len;
2255 STATUS rv;
2256
2257 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2258 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len);
2259 zend_restore_error_handling(&zeh TSRMLS_CC);
2260
2261 if (SUCCESS == rv) {
2262 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2263
2264 if (!obj->intern) {
2265 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2266 } else {
2267 char *params[2] = {channel_str, message_str};
2268
2269 if (!PQsendQueryParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0)) {
2270 throw_exce(EX_IO TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn));
2271 } else {
2272 obj->intern->poller = PQconsumeInput;
2273 }
2274
2275 php_pqconn_notify_listeners(obj TSRMLS_CC);
2276 }
2277 }
2278 }
2279
2280 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_poll, 0, 0, 0)
2281 ZEND_END_ARG_INFO();
2282 static PHP_METHOD(pqconn, poll) {
2283 zend_error_handling zeh;
2284 STATUS rv;
2285
2286 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2287 rv = zend_parse_parameters_none();
2288 zend_restore_error_handling(&zeh TSRMLS_CC);
2289
2290 if (SUCCESS == rv) {
2291 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2292
2293 if (!obj->intern) {
2294 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2295 } else if (!obj->intern->poller) {
2296 throw_exce(EX_RUNTIME TSRMLS_CC, "No asynchronous operation active");
2297 } else {
2298 if (obj->intern->poller == PQconsumeInput) {
2299 RETVAL_LONG(obj->intern->poller(obj->intern->conn) * PGRES_POLLING_OK);
2300 } else {
2301 RETVAL_LONG(obj->intern->poller(obj->intern->conn));
2302 }
2303 php_pqconn_notify_listeners(obj TSRMLS_CC);
2304 }
2305 }
2306 }
2307
2308 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec, 0, 0, 1)
2309 ZEND_ARG_INFO(0, query)
2310 ZEND_END_ARG_INFO();
2311 static PHP_METHOD(pqconn, exec) {
2312 zend_error_handling zeh;
2313 char *query_str;
2314 int query_len;
2315 STATUS rv;
2316
2317 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2318 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query_str, &query_len);
2319 zend_restore_error_handling(&zeh TSRMLS_CC);
2320
2321 if (SUCCESS == rv) {
2322 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2323
2324 if (!obj->intern) {
2325 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2326 } else {
2327 PGresult *res = PQexec(obj->intern->conn, query_str);
2328
2329 if (!res) {
2330 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
2331 } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
2332 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
2333 } else {
2334 PHP_PQclear(res);
2335 }
2336
2337 php_pqconn_notify_listeners(obj TSRMLS_CC);
2338 }
2339 }
2340 }
2341
2342 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_get_result, 0, 0, 0)
2343 ZEND_END_ARG_INFO();
2344 static PHP_METHOD(pqconn, getResult) {
2345 zend_error_handling zeh;
2346 STATUS rv;
2347
2348 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2349 rv = zend_parse_parameters_none();
2350 zend_restore_error_handling(&zeh TSRMLS_CC);
2351
2352 if (SUCCESS == rv) {
2353 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2354
2355 if (!obj->intern) {
2356 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connectio not initialized");
2357 } else {
2358 PGresult *res = PQgetResult(obj->intern->conn);
2359
2360 if (!res) {
2361 RETVAL_NULL();
2362 } else {
2363 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
2364 }
2365
2366 php_pqconn_notify_listeners(obj TSRMLS_CC);
2367 }
2368 }
2369 }
2370
2371 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_async, 0, 0, 1)
2372 ZEND_ARG_INFO(0, query)
2373 ZEND_ARG_INFO(0, callable)
2374 ZEND_END_ARG_INFO();
2375 static PHP_METHOD(pqconn, execAsync) {
2376 zend_error_handling zeh;
2377 php_pq_callback_t resolver = {{0}};
2378 char *query_str;
2379 int query_len;
2380 STATUS rv;
2381
2382 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2383 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f", &query_str, &query_len, &resolver.fci, &resolver.fcc);
2384 zend_restore_error_handling(&zeh TSRMLS_CC);
2385
2386 if (SUCCESS == rv) {
2387 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2388
2389 if (!obj->intern) {
2390 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2391 } else if (!PQsendQuery(obj->intern->conn, query_str)) {
2392 throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
2393 } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
2394 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
2395 } else {
2396 obj->intern->poller = PQconsumeInput;
2397 php_pq_callback_dtor(&obj->intern->onevent);
2398 if (resolver.fci.size > 0) {
2399 obj->intern->onevent = resolver;
2400 php_pq_callback_addref(&obj->intern->onevent);
2401 }
2402 php_pqconn_notify_listeners(obj TSRMLS_CC);
2403 }
2404 }
2405 }
2406
2407 static int apply_to_oid(void *p, void *arg TSRMLS_DC)
2408 {
2409 Oid **types = arg;
2410 zval **ztype = p;
2411
2412 if (Z_TYPE_PP(ztype) != IS_LONG) {
2413 convert_to_long_ex(ztype);
2414 }
2415
2416 **types = Z_LVAL_PP(ztype);
2417 ++*types;
2418
2419 if (*ztype != *(zval **)p) {
2420 zval_ptr_dtor(ztype);
2421 }
2422 return ZEND_HASH_APPLY_KEEP;
2423 }
2424
2425 static int apply_to_param(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
2426 {
2427 char ***params;
2428 HashTable *zdtor;
2429 zval **zparam = p;
2430
2431 params = (char ***) va_arg(argv, char ***);
2432 zdtor = (HashTable *) va_arg(argv, HashTable *);
2433
2434 if (Z_TYPE_PP(zparam) == IS_NULL) {
2435 **params = NULL;
2436 ++*params;
2437 } else {
2438 if (Z_TYPE_PP(zparam) != IS_STRING) {
2439 convert_to_string_ex(zparam);
2440 }
2441
2442 **params = Z_STRVAL_PP(zparam);
2443 ++*params;
2444
2445 if (*zparam != *(zval **)p) {
2446 zend_hash_next_index_insert(zdtor, zparam, sizeof(zval *), NULL);
2447 }
2448 }
2449 return ZEND_HASH_APPLY_KEEP;
2450 }
2451
2452 static int php_pq_types_to_array(HashTable *ht, Oid **types TSRMLS_DC)
2453 {
2454 int count = zend_hash_num_elements(ht);
2455
2456 *types = NULL;
2457
2458 if (count) {
2459 Oid *tmp;
2460
2461 /* +1 for when less types than params are specified */
2462 *types = tmp = ecalloc(count + 1, sizeof(**types));
2463 zend_hash_apply_with_argument(ht, apply_to_oid, &tmp TSRMLS_CC);
2464 }
2465
2466 return count;
2467 }
2468
2469 static int php_pq_params_to_array(HashTable *ht, char ***params, HashTable *zdtor TSRMLS_DC)
2470 {
2471 int count = zend_hash_num_elements(ht);
2472
2473 *params = NULL;
2474
2475 if (count) {
2476 char **tmp;
2477
2478 *params = tmp = ecalloc(count, sizeof(char *));
2479 zend_hash_apply_with_arguments(ht TSRMLS_CC, apply_to_param, 2, &tmp, zdtor);
2480 }
2481
2482 return count;
2483 }
2484 /*
2485 static Oid *php_pq_ntypes_to_array(zend_bool fill, int argc, ...)
2486 {
2487 int i;
2488 Oid *oids = ecalloc(argc + 1, sizeof(*oids));
2489 va_list argv;
2490
2491 va_start(argv, argc);
2492 for (i = 0; i < argc; ++i) {
2493 if (!fill || !i) {
2494 oids[i] = va_arg(argv, Oid);
2495 } else {
2496 oids[i] = oids[0];
2497 }
2498 }
2499 va_end(argv);
2500
2501 return oids;
2502 }
2503 */
2504 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params, 0, 0, 2)
2505 ZEND_ARG_INFO(0, query)
2506 ZEND_ARG_ARRAY_INFO(0, params, 0)
2507 ZEND_ARG_ARRAY_INFO(0, types, 1)
2508 ZEND_END_ARG_INFO();
2509 static PHP_METHOD(pqconn, execParams) {
2510 zend_error_handling zeh;
2511 char *query_str;
2512 int query_len;
2513 zval *zparams;
2514 zval *ztypes = NULL;
2515 STATUS rv;
2516
2517 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2518 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!", &query_str, &query_len, &zparams, &ztypes);
2519 zend_restore_error_handling(&zeh TSRMLS_CC);
2520
2521 if (SUCCESS == rv) {
2522 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2523
2524 if (!obj->intern) {
2525 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2526 } else {
2527 PGresult *res;
2528 int count;
2529 Oid *types = NULL;
2530 char **params = NULL;
2531 HashTable zdtor;
2532
2533 ZEND_INIT_SYMTABLE(&zdtor);
2534 count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
2535
2536 if (ztypes) {
2537 php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC);
2538 }
2539
2540 res = PQexecParams(obj->intern->conn, query_str, count, types, (const char *const*) params, NULL, NULL, 0);
2541
2542 zend_hash_destroy(&zdtor);
2543 if (types) {
2544 efree(types);
2545 }
2546 if (params) {
2547 efree(params);
2548 }
2549
2550 if (!res) {
2551 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
2552 } else {
2553 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
2554 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
2555 } else {
2556 PHP_PQclear(res);
2557 }
2558
2559 php_pqconn_notify_listeners(obj TSRMLS_CC);
2560 }
2561 }
2562 }
2563 }
2564
2565 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params_async, 0, 0, 2)
2566 ZEND_ARG_INFO(0, query)
2567 ZEND_ARG_ARRAY_INFO(0, params, 0)
2568 ZEND_ARG_ARRAY_INFO(0, types, 1)
2569 ZEND_ARG_INFO(0, callable)
2570 ZEND_END_ARG_INFO();
2571 static PHP_METHOD(pqconn, execParamsAsync) {
2572 zend_error_handling zeh;
2573 php_pq_callback_t resolver = {{0}};
2574 char *query_str;
2575 int query_len;
2576 zval *zparams;
2577 zval *ztypes = NULL;
2578 STATUS rv;
2579
2580 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2581 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!f", &query_str, &query_len, &zparams, &ztypes, &resolver.fci, &resolver.fcc);
2582 zend_restore_error_handling(&zeh TSRMLS_CC);
2583
2584 if (SUCCESS == rv) {
2585 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2586
2587 if (!obj->intern) {
2588 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2589 } else {
2590 int count;
2591 Oid *types = NULL;
2592 char **params = NULL;
2593 HashTable zdtor;
2594
2595 ZEND_INIT_SYMTABLE(&zdtor);
2596 count = php_pq_params_to_array(Z_ARRVAL_P(zparams), &params, &zdtor TSRMLS_CC);
2597
2598 if (ztypes) {
2599 php_pq_types_to_array(Z_ARRVAL_P(ztypes), &types TSRMLS_CC);
2600 }
2601
2602 if (!PQsendQueryParams(obj->intern->conn, query_str, count, types, (const char *const*) params, NULL, NULL, 0)) {
2603 throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
2604 } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
2605 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
2606 } else {
2607 obj->intern->poller = PQconsumeInput;
2608 php_pq_callback_dtor(&obj->intern->onevent);
2609 if (resolver.fci.size > 0) {
2610 obj->intern->onevent = resolver;
2611 php_pq_callback_addref(&obj->intern->onevent);
2612 }
2613 php_pqconn_notify_listeners(obj TSRMLS_CC);
2614 }
2615
2616 zend_hash_destroy(&zdtor);
2617 if (types) {
2618 efree(types);
2619 }
2620 if (params) {
2621 efree(params);
2622 }
2623 }
2624 }
2625 zend_restore_error_handling(&zeh TSRMLS_CC);
2626 }
2627
2628 static STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, HashTable *typest TSRMLS_DC)
2629 {
2630 Oid *types = NULL;
2631 int count = 0;
2632 PGresult *res;
2633 STATUS rv;
2634
2635 if (!obj) {
2636 obj = zend_object_store_get_object(object TSRMLS_CC);
2637 }
2638
2639 if (typest) {
2640 count = zend_hash_num_elements(typest);
2641 php_pq_types_to_array(typest, &types TSRMLS_CC);
2642 }
2643
2644 res = PQprepare(obj->intern->conn, name, query, count, types);
2645
2646 if (types) {
2647 efree(types);
2648 }
2649
2650 if (!res) {
2651 rv = FAILURE;
2652 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn));
2653 } else {
2654 rv = php_pqres_success(res TSRMLS_CC);
2655 PHP_PQclear(res);
2656 php_pqconn_notify_listeners(obj TSRMLS_CC);
2657 }
2658
2659 return rv;
2660 }
2661
2662 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2)
2663 ZEND_ARG_INFO(0, type)
2664 ZEND_ARG_INFO(0, query)
2665 ZEND_ARG_ARRAY_INFO(0, types, 1)
2666 ZEND_END_ARG_INFO();
2667 static PHP_METHOD(pqconn, prepare) {
2668 zend_error_handling zeh;
2669 zval *ztypes = NULL;
2670 char *name_str, *query_str;
2671 int name_len, *query_len;
2672 STATUS rv;
2673
2674 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2675 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes);
2676 zend_restore_error_handling(&zeh TSRMLS_CC);
2677
2678 if (SUCCESS == rv) {
2679 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2680
2681 if (!obj->intern) {
2682 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2683 } else if (SUCCESS == php_pqconn_prepare(getThis(), obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
2684 php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
2685
2686 php_pq_object_addref(obj TSRMLS_CC);
2687 stm->conn = obj;
2688 stm->name = estrdup(name_str);
2689 ZEND_INIT_SYMTABLE(&stm->bound);
2690
2691 return_value->type = IS_OBJECT;
2692 return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
2693 }
2694 }
2695 }
2696
2697 static STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, HashTable *typest TSRMLS_DC)
2698 {
2699 STATUS rv;
2700 int count;
2701 Oid *types = NULL;
2702
2703 if (!obj) {
2704 obj = zend_object_store_get_object(object TSRMLS_CC);
2705 }
2706
2707 if (typest) {
2708 count = php_pq_types_to_array(typest, &types TSRMLS_CC);
2709 }
2710
2711 if (!PQsendPrepare(obj->intern->conn, name, query, count, types)) {
2712 rv = FAILURE;
2713 throw_exce(EX_IO TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn));
2714 } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
2715 rv = FAILURE;
2716 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
2717 } else {
2718 rv = SUCCESS;
2719 obj->intern->poller = PQconsumeInput;
2720 php_pqconn_notify_listeners(obj TSRMLS_CC);
2721 }
2722
2723 if (types) {
2724 efree(types);
2725 }
2726
2727 return rv;
2728 }
2729
2730 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2)
2731 ZEND_ARG_INFO(0, type)
2732 ZEND_ARG_INFO(0, query)
2733 ZEND_ARG_ARRAY_INFO(0, types, 1)
2734 ZEND_END_ARG_INFO();
2735 static PHP_METHOD(pqconn, prepareAsync) {
2736 zend_error_handling zeh;
2737 zval *ztypes = NULL;
2738 char *name_str, *query_str;
2739 int name_len, *query_len;
2740 STATUS rv;
2741
2742 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2743 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes);
2744 zend_restore_error_handling(&zeh TSRMLS_CC);
2745
2746 if (SUCCESS == rv) {
2747 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2748
2749 if (!obj->intern) {
2750 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2751 } else if (SUCCESS == php_pqconn_prepare_async(getThis(), obj, name_str, query_str, ztypes ? Z_ARRVAL_P(ztypes) : NULL TSRMLS_CC)) {
2752 php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
2753
2754 php_pq_object_addref(obj TSRMLS_CC);
2755 stm->conn = obj;
2756 stm->name = estrdup(name_str);
2757 ZEND_INIT_SYMTABLE(&stm->bound);
2758
2759 return_value->type = IS_OBJECT;
2760 return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
2761 }
2762 }
2763 }
2764
2765 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote, 0, 0, 1)
2766 ZEND_ARG_INFO(0, string)
2767 ZEND_END_ARG_INFO();
2768 static PHP_METHOD(pqconn, quote) {
2769 char *str;
2770 int len;
2771
2772 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
2773 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2774
2775 if (!obj->intern) {
2776 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2777 } else {
2778 char *quoted = PQescapeLiteral(obj->intern->conn, str, len);
2779
2780 if (!quoted) {
2781 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote string (%s)", PHP_PQerrorMessage(obj->intern->conn));
2782 RETVAL_FALSE;
2783 } else {
2784 RETVAL_STRING(quoted, 1);
2785 PQfreemem(quoted);
2786 }
2787 }
2788 }
2789 }
2790
2791 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote_name, 0, 0, 1)
2792 ZEND_ARG_INFO(0, type)
2793 ZEND_END_ARG_INFO();
2794 static PHP_METHOD(pqconn, quoteName) {
2795 char *str;
2796 int len;
2797
2798 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
2799 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2800
2801 if (!obj->intern) {
2802 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2803 } else {
2804 char *quoted = PQescapeIdentifier(obj->intern->conn, str, len);
2805
2806 if (!quoted) {
2807 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote name (%s)", PHP_PQerrorMessage(obj->intern->conn));
2808 RETVAL_FALSE;
2809 } else {
2810 RETVAL_STRING(quoted, 1);
2811 PQfreemem(quoted);
2812 }
2813 }
2814 }
2815 }
2816
2817 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_escape_bytea, 0, 0, 1)
2818 ZEND_ARG_INFO(0, bytea)
2819 ZEND_END_ARG_INFO();
2820 static PHP_METHOD(pqconn, escapeBytea) {
2821 char *str;
2822 int len;
2823
2824 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
2825 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2826
2827 if (!obj->intern) {
2828 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2829 } else {
2830 size_t escaped_len;
2831 char *escaped_str = (char *) PQescapeByteaConn(obj->intern->conn, (unsigned char *) str, len, &escaped_len);
2832
2833 if (!escaped_str) {
2834 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to escape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn));
2835 RETVAL_FALSE;
2836 } else {
2837 RETVAL_STRINGL(escaped_str, escaped_len - 1, 1);
2838 PQfreemem(escaped_str);
2839 }
2840 }
2841 }
2842 }
2843
2844 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unescape_bytea, 0, 0, 1)
2845 ZEND_ARG_INFO(0, bytea)
2846 ZEND_END_ARG_INFO();
2847 static PHP_METHOD(pqconn, unescapeBytea) {
2848 char *str;
2849 int len;
2850
2851 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
2852 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2853
2854 if (!obj->intern) {
2855 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2856 } else {
2857 size_t unescaped_len;
2858 char *unescaped_str = (char *) PQunescapeBytea((unsigned char *)str, &unescaped_len);
2859
2860 if (!unescaped_str) {
2861 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to unescape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn));
2862 RETVAL_FALSE;
2863 } else {
2864 RETVAL_STRINGL(unescaped_str, unescaped_len, 1);
2865 PQfreemem(unescaped_str);
2866 }
2867 }
2868 }
2869 }
2870
2871 static const char *isolation_level(long *isolation) {
2872 switch (*isolation) {
2873 case PHP_PQTXN_SERIALIZABLE:
2874 return "SERIALIZABLE";
2875 case PHP_PQTXN_REPEATABLE_READ:
2876 return "REPEATABLE READ";
2877 default:
2878 *isolation = PHP_PQTXN_READ_COMMITTED;
2879 /* no break */
2880 case PHP_PQTXN_READ_COMMITTED:
2881 return "READ COMMITTED";
2882 }
2883 }
2884
2885 static STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
2886 {
2887 STATUS rv = FAILURE;
2888
2889 if (!conn_obj) {
2890 conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
2891 }
2892
2893 if (!conn_obj->intern) {
2894 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2895 } else {
2896 PGresult *res;
2897 smart_str cmd = {0};
2898 const char *il = isolation_level(&isolation);
2899
2900 smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL ");
2901 smart_str_appends(&cmd, il);
2902 smart_str_appends(&cmd, ", READ ");
2903 smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE");
2904 smart_str_appends(&cmd, ",");
2905 smart_str_appends(&cmd, deferrable ? "" : " NOT");
2906 smart_str_appends(&cmd, " DEFERRABLE");
2907 smart_str_0(&cmd);
2908
2909 res = PQexec(conn_obj->intern->conn, cmd.c);
2910
2911 if (!res) {
2912 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn));
2913 } else {
2914 rv = php_pqres_success(res TSRMLS_CC);
2915 PHP_PQclear(res);
2916 php_pqconn_notify_listeners(conn_obj TSRMLS_CC);
2917 }
2918
2919 smart_str_free(&cmd);
2920 }
2921
2922 return rv;
2923 }
2924
2925 static STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
2926 {
2927 STATUS rv = FAILURE;
2928
2929 if (!conn_obj) {
2930 conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
2931 }
2932
2933 if (!conn_obj->intern) {
2934 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
2935 } else {
2936 smart_str cmd = {0};
2937 const char *il = isolation_level(&isolation);
2938
2939 smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL ");
2940 smart_str_appends(&cmd, il);
2941 smart_str_appends(&cmd, ", READ ");
2942 smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE");
2943 smart_str_appends(&cmd, ",");
2944 smart_str_appends(&cmd, deferrable ? "" : "NOT ");
2945 smart_str_appends(&cmd, " DEFERRABLE");
2946 smart_str_0(&cmd);
2947
2948 if (!PQsendQuery(conn_obj->intern->conn, cmd.c)) {
2949 throw_exce(EX_IO TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn));
2950 } else {
2951 rv = SUCCESS;
2952 conn_obj->intern->poller = PQconsumeInput;
2953 php_pqconn_notify_listeners(conn_obj TSRMLS_CC);
2954 }
2955
2956 smart_str_free(&cmd);
2957 }
2958
2959 return rv;
2960 }
2961
2962 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction, 0, 0, 0)
2963 ZEND_ARG_INFO(0, isolation)
2964 ZEND_ARG_INFO(0, readonly)
2965 ZEND_ARG_INFO(0, deferrable)
2966 ZEND_END_ARG_INFO();
2967 static PHP_METHOD(pqconn, startTransaction) {
2968 zend_error_handling zeh;
2969 long isolation = PHP_PQTXN_READ_COMMITTED;
2970 zend_bool readonly = 0, deferrable = 0;
2971 STATUS rv;
2972
2973 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
2974 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable);
2975 zend_restore_error_handling(&zeh TSRMLS_CC);
2976
2977 if (SUCCESS == rv) {
2978 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
2979
2980 rv = php_pqconn_start_transaction(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC);
2981
2982 if (SUCCESS == rv) {
2983 php_pqtxn_t *txn = ecalloc(1, sizeof(*txn));
2984
2985 php_pq_object_addref(obj TSRMLS_CC);
2986 txn->conn = obj;
2987 txn->open = 1;
2988 txn->isolation = isolation;
2989 txn->readonly = readonly;
2990 txn->deferrable = deferrable;
2991
2992 return_value->type = IS_OBJECT;
2993 return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC);
2994 }
2995 }
2996 }
2997
2998 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction_async, 0, 0, 0)
2999 ZEND_ARG_INFO(0, isolation)
3000 ZEND_ARG_INFO(0, readonly)
3001 ZEND_ARG_INFO(0, deferrable)
3002 ZEND_END_ARG_INFO();
3003 static PHP_METHOD(pqconn, startTransactionAsync) {
3004 zend_error_handling zeh;
3005 long isolation = PHP_PQTXN_READ_COMMITTED;
3006 zend_bool readonly = 0, deferrable = 0;
3007 STATUS rv;
3008
3009 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
3010 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable);
3011 zend_restore_error_handling(&zeh TSRMLS_CC);
3012 if (SUCCESS == rv) {
3013 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
3014
3015 rv = php_pqconn_start_transaction_async(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC);
3016
3017 if (SUCCESS == rv) {
3018 php_pqtxn_t *txn = ecalloc(1, sizeof(*txn));
3019
3020 php_pq_object_addref(obj TSRMLS_CC);
3021 txn->conn = obj;
3022 txn->isolation = isolation;
3023 txn->readonly = readonly;
3024 txn->deferrable = deferrable;
3025
3026 return_value->type = IS_OBJECT;
3027 return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC);
3028 }
3029 }
3030 }
3031
3032 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_trace, 0, 0, 0)
3033 ZEND_ARG_INFO(0, stdio_stream)
3034 ZEND_END_ARG_INFO();
3035 static PHP_METHOD(pqconn, trace) {
3036 zval *zstream = NULL;
3037
3038 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream)) {
3039 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
3040
3041 if (!obj->intern) {
3042 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
3043 } else {
3044 if (!zstream) {
3045 PQuntrace(obj->intern->conn);
3046 RETVAL_TRUE;
3047 } else {
3048 FILE *fp;
3049 php_stream *stream = NULL;
3050
3051 php_stream_from_zval(stream, &zstream);
3052
3053 if (SUCCESS != php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) {
3054 RETVAL_FALSE;
3055 } else {
3056 stream->flags |= PHP_STREAM_FLAG_NO_CLOSE;
3057 PQtrace(obj->intern->conn, fp);
3058 RETVAL_TRUE;
3059 }
3060 }
3061 }
3062 }
3063 }
3064
3065 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_on, 0, 0, 2)
3066 ZEND_ARG_INFO(0, type)
3067 ZEND_ARG_INFO(0, callable)
3068 ZEND_END_ARG_INFO();
3069 static PHP_METHOD(pqconn, on) {
3070 zend_error_handling zeh;
3071 char *type_str;
3072 int type_len;
3073 php_pq_callback_t cb;
3074 STATUS rv;
3075
3076 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
3077 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &type_str, &type_len, &cb.fci, &cb.fcc);
3078 zend_restore_error_handling(&zeh TSRMLS_CC);
3079
3080 if (SUCCESS == rv) {
3081 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
3082
3083 if (!obj->intern) {
3084 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
3085 } else {
3086 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
3087
3088 RETVAL_LONG(php_pqconn_add_eventhandler(obj, type_str, type_len, &cb TSRMLS_CC));
3089 }
3090 }
3091 }
3092
3093 static zend_function_entry php_pqconn_methods[] = {
3094 PHP_ME(pqconn, __construct, ai_pqconn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
3095 PHP_ME(pqconn, reset, ai_pqconn_reset, ZEND_ACC_PUBLIC)
3096 PHP_ME(pqconn, resetAsync, ai_pqconn_reset_async, ZEND_ACC_PUBLIC)
3097 PHP_ME(pqconn, poll, ai_pqconn_poll, ZEND_ACC_PUBLIC)
3098 PHP_ME(pqconn, exec, ai_pqconn_exec, ZEND_ACC_PUBLIC)
3099 PHP_ME(pqconn, execAsync, ai_pqconn_exec_async, ZEND_ACC_PUBLIC)
3100 PHP_ME(pqconn, execParams, ai_pqconn_exec_params, ZEND_ACC_PUBLIC)
3101 PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC)
3102 PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC)
3103 PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC)
3104 PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC)
3105 PHP_ME(pqconn, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC)
3106 PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC)
3107 PHP_ME(pqconn, notifyAsync, ai_pqconn_notify_async, ZEND_ACC_PUBLIC)
3108 PHP_ME(pqconn, getResult, ai_pqconn_get_result, ZEND_ACC_PUBLIC)
3109 PHP_ME(pqconn, quote, ai_pqconn_quote, ZEND_ACC_PUBLIC)
3110 PHP_ME(pqconn, quoteName, ai_pqconn_quote_name, ZEND_ACC_PUBLIC)
3111 PHP_ME(pqconn, escapeBytea, ai_pqconn_escape_bytea, ZEND_ACC_PUBLIC)
3112 PHP_ME(pqconn, unescapeBytea, ai_pqconn_unescape_bytea, ZEND_ACC_PUBLIC)
3113 PHP_ME(pqconn, startTransaction, ai_pqconn_start_transaction, ZEND_ACC_PUBLIC)
3114 PHP_ME(pqconn, startTransactionAsync, ai_pqconn_start_transaction_async, ZEND_ACC_PUBLIC)
3115 PHP_ME(pqconn, trace, ai_pqconn_trace, ZEND_ACC_PUBLIC)
3116 PHP_ME(pqconn, on, ai_pqconn_on, ZEND_ACC_PUBLIC)
3117 {0}
3118 };
3119
3120 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_construct, 0, 0, 1)
3121 ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
3122 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
3123 ZEND_END_ARG_INFO();
3124 static PHP_METHOD(pqtypes, __construct) {
3125 zend_error_handling zeh;
3126 zval *zconn, *znsp = NULL;
3127 STATUS rv;
3128
3129 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
3130 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|a!", &zconn, php_pqconn_class_entry, &znsp);
3131 zend_restore_error_handling(&zeh TSRMLS_CC);
3132
3133 if (SUCCESS == rv) {
3134 php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
3135
3136 if (!conn_obj->intern) {
3137 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
3138 } else {
3139 php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
3140 zval *retval = NULL;
3141
3142 obj->intern = ecalloc(1, sizeof(*obj->intern));
3143 obj->intern->conn = conn_obj;
3144 php_pq_object_addref(conn_obj TSRMLS_CC);
3145 zend_hash_init(&obj->intern->types, 300, NULL, ZVAL_PTR_DTOR, 0);
3146
3147 if (znsp) {
3148 zend_call_method_with_1_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval, znsp);
3149 } else {
3150 zend_call_method_with_0_params(&getThis(), Z_OBJCE_P(getThis()), NULL, "refresh", &retval);
3151 }
3152
3153 if (retval) {
3154 zval_ptr_dtor(&retval);
3155 }
3156 }
3157 }
3158 }
3159
3160 #define PHP_PQ_TYPES_QUERY \
3161 "select t.oid, t.* " \
3162 "from pg_type t join pg_namespace n on t.typnamespace=n.oid " \
3163 "where typisdefined " \
3164 "and typrelid=0"
3165 #define PHP_PQ_OID_TEXT 25
3166
3167 ZEND_BEGIN_ARG_INFO_EX(ai_pqtypes_refresh, 0, 0, 0)
3168 ZEND_ARG_ARRAY_INFO(0, namespaces, 1)
3169 ZEND_END_ARG_INFO();
3170 static PHP_METHOD(pqtypes, refresh) {
3171 HashTable *nsp = NULL;
3172 zend_error_handling zeh;
3173 STATUS rv;
3174
3175 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
3176 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|H/!", &nsp);
3177 zend_restore_error_handling(&zeh TSRMLS_CC);
3178
3179 if (SUCCESS == rv) {
3180 php_pqtypes_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
3181
3182 if (!obj->intern) {
3183 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Types not initialized");
3184 } else {
3185 PGresult *res;
3186
3187 if (!nsp || !zend_hash_num_elements(nsp)) {
3188 res = PQexec(obj->intern->conn->intern->conn, PHP_PQ_TYPES_QUERY " and nspname in ('public', 'pg_catalog')");
3189 } else {
3190 int i, count;
3191 Oid *oids;
3192 char **params = NULL;
3193 HashTable zdtor;
3194 smart_str str = {0};
3195
3196 smart_str_appends(&str, PHP_PQ_TYPES_QUERY " and nspname in(");
3197 zend_hash_init(&zdtor, 0, NULL, ZVAL_PTR_DTOR, 0);
3198 count = php_pq_params_to_array(nsp, &params, &zdtor TSRMLS_CC);
3199 oids = ecalloc(count + 1, sizeof(*oids));
3200 for (i = 0; i < count; ++i) {
3201 oids[i] = PHP_PQ_OID_TEXT;
3202 if (i) {
3203 smart_str_appendc(&str, ',');
3204 }
3205 smart_str_appendc(&str, '$');
3206 smart_str_append_unsigned(&str, i+1);
3207 }
3208 smart_str_appendc(&str, ')');
3209 smart_str_0(&str);
3210
3211 res = PQexecParams(obj->intern->conn->intern->conn, str.c, count, oids, (const char *const*) params, NULL, NULL, 0);
3212
3213 smart_str_free(&str);
3214 efree(oids);
3215 efree(params);
3216 zend_hash_destroy(&zdtor);
3217 }
3218
3219 if (!res) {
3220 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to fetch types (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
3221 } else {
3222 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
3223 int r, rows;
3224
3225 for (r = 0, rows = PQntuples(res); r < rows; ++r) {
3226 zval *row = php_pqres_row_to_zval(res, r, PHP_PQRES_FETCH_OBJECT, NULL TSRMLS_CC);
3227 long oid = atol(PQgetvalue(res, r, 0 ));
3228 char *name = PQgetvalue(res, r, 1);
3229
3230 Z_ADDREF_P(row);
3231
3232 zend_hash_index_update(&obj->intern->types, oid, (void *) &row, sizeof(zval *), NULL);
3233 zend_hash_add(&obj->intern->types, name, strlen(name) + 1, (void *) &row, sizeof(zval *), NULL);
3234 }
3235 }
3236
3237 PHP_PQclear(res);
3238 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
3239 }
3240 }
3241 }
3242 }
3243
3244 static zend_function_entry php_pqtypes_methods[] = {
3245 PHP_ME(pqtypes, __construct, ai_pqtypes_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
3246 PHP_ME(pqtypes, refresh, ai_pqtypes_refresh, ZEND_ACC_PUBLIC)
3247 {0}
3248 };
3249
3250 static STATUS php_pqres_iteration(zval *this_ptr, php_pqres_object_t *obj, php_pqres_fetch_t fetch_type, zval ***row TSRMLS_DC)
3251 {
3252 STATUS rv;
3253 php_pqres_fetch_t orig_fetch;
3254
3255 if (!obj) {
3256 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
3257 }
3258
3259 if (!obj->intern->iter) {
3260 obj->intern->iter = (php_pqres_iterator_t *) php_pqres_iterator_init(Z_OBJCE_P(getThis()), getThis(), 0 TSRMLS_CC);