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