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