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