fix possible invalid free and a leak
[m6w6/ext-pq] / src / php_pqconn.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: pq |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <php.h>
18
19 #define SMART_STR_PREALLOC 256
20 #include <ext/standard/php_smart_str.h>
21
22 #include <libpq-events.h>
23 #include <fnmatch.h>
24
25 #include "php_pq.h"
26 #include "php_pq_misc.h"
27 #include "php_pq_object.h"
28 #include "php_pqexc.h"
29 #include "php_pqconn.h"
30 #include "php_pqconn_event.h"
31 #include "php_pqres.h"
32 #include "php_pqstm.h"
33 #include "php_pqtxn.h"
34 #include "php_pqcur.h"
35
36 zend_class_entry *php_pqconn_class_entry;
37 static zend_object_handlers php_pqconn_object_handlers;
38 static HashTable php_pqconn_object_prophandlers;
39
40 /*
41 static void php_pqconn_del_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, ulong id TSRMLS_DC)
42 {
43 zval **evhs;
44
45 if (SUCCESS == zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) {
46 zend_hash_index_del(Z_ARRVAL_PP(evhs), id);
47 }
48 }
49 */
50
51 static ulong php_pqconn_add_eventhandler(php_pqconn_object_t *obj, const char *type_str, size_t type_len, php_pq_callback_t *cb TSRMLS_DC)
52 {
53 ulong h;
54 HashTable *evhs;
55
56 if (SUCCESS != zend_hash_find(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evhs)) {
57 HashTable evh;
58
59 zend_hash_init(&evh, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0);
60 zend_hash_add(&obj->intern->eventhandlers, type_str, type_len + 1, (void *) &evh, sizeof(evh), (void *) &evhs);
61 }
62
63 php_pq_callback_addref(cb);
64 h = zend_hash_next_free_element(evhs);
65 zend_hash_index_update(evhs, h, (void *) cb, sizeof(*cb), NULL);
66
67 return h;
68 }
69
70 static void php_pqconn_object_free(void *o TSRMLS_DC)
71 {
72 php_pqconn_object_t *obj = o;
73 #if DBG_GC
74 fprintf(stderr, "FREE conn(#%d) %p\n", obj->zv.handle, obj);
75 #endif
76 if (obj->intern) {
77 php_pq_callback_dtor(&obj->intern->onevent);
78 php_resource_factory_handle_dtor(&obj->intern->factory, obj->intern->conn TSRMLS_CC);
79 php_resource_factory_dtor(&obj->intern->factory);
80 zend_hash_destroy(&obj->intern->listeners);
81 zend_hash_destroy(&obj->intern->converters);
82 zend_hash_destroy(&obj->intern->eventhandlers);
83 efree(obj->intern);
84 obj->intern = NULL;
85 }
86 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
87 efree(obj);
88 }
89
90
91 zend_object_value php_pqconn_create_object_ex(zend_class_entry *ce, php_pqconn_t *intern, php_pqconn_object_t **ptr TSRMLS_DC)
92 {
93 php_pqconn_object_t *o;
94
95 o = ecalloc(1, sizeof(*o));
96 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
97 object_properties_init((zend_object *) o, ce);
98 o->prophandler = &php_pqconn_object_prophandlers;
99
100 if (ptr) {
101 *ptr = o;
102 }
103
104 if (intern) {
105 o->intern = intern;
106 }
107
108 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqconn_object_free, NULL TSRMLS_CC);
109 o->zv.handlers = &php_pqconn_object_handlers;
110
111 return o->zv;
112 }
113
114 static zend_object_value php_pqconn_create_object(zend_class_entry *class_type TSRMLS_DC)
115 {
116 return php_pqconn_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
117 }
118
119 static void php_pqconn_object_read_status(zval *object, void *o, zval *return_value TSRMLS_DC)
120 {
121 php_pqconn_object_t *obj = o;
122
123 RETVAL_LONG(PQstatus(obj->intern->conn));
124 }
125
126 static void php_pqconn_object_read_transaction_status(zval *object, void *o, zval *return_value TSRMLS_DC)
127 {
128 php_pqconn_object_t *obj = o;
129
130 RETVAL_LONG(PQtransactionStatus(obj->intern->conn));
131 }
132
133 static void php_pqconn_object_read_error_message(zval *object, void *o, zval *return_value TSRMLS_DC)
134 {
135 php_pqconn_object_t *obj = o;
136 char *error = PHP_PQerrorMessage(obj->intern->conn);
137
138 if (error) {
139 RETVAL_STRING(error, 1);
140 } else {
141 RETVAL_NULL();
142 }
143 }
144
145 static int apply_notify_listener(void *p, void *arg TSRMLS_DC)
146 {
147 php_pq_callback_t *listener = p;
148 PGnotify *nfy = arg;
149 zval *zpid, *zchannel, *zmessage;
150
151 MAKE_STD_ZVAL(zpid);
152 ZVAL_LONG(zpid, nfy->be_pid);
153 MAKE_STD_ZVAL(zchannel);
154 ZVAL_STRING(zchannel, nfy->relname, 1);
155 MAKE_STD_ZVAL(zmessage);
156 ZVAL_STRING(zmessage, nfy->extra, 1);
157
158 zend_fcall_info_argn(&listener->fci TSRMLS_CC, 3, &zchannel, &zmessage, &zpid);
159 zend_fcall_info_call(&listener->fci, &listener->fcc, NULL, NULL TSRMLS_CC);
160
161 zval_ptr_dtor(&zchannel);
162 zval_ptr_dtor(&zmessage);
163 zval_ptr_dtor(&zpid);
164
165 return ZEND_HASH_APPLY_KEEP;
166 }
167
168 static int apply_notify_listeners(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
169 {
170 HashTable *listeners = p;
171 PGnotify *nfy = va_arg(argv, PGnotify *);
172
173 if (0 == fnmatch(key->arKey, nfy->relname, 0)) {
174 zend_hash_apply_with_argument(listeners, apply_notify_listener, nfy TSRMLS_CC);
175 }
176
177 return ZEND_HASH_APPLY_KEEP;
178 }
179
180 void php_pqconn_notify_listeners(php_pqconn_object_t *obj TSRMLS_DC)
181 {
182 PGnotify *nfy;
183
184 while ((nfy = PQnotifies(obj->intern->conn))) {
185 zend_hash_apply_with_arguments(&obj->intern->listeners TSRMLS_CC, apply_notify_listeners, 1, nfy);
186 PQfreemem(nfy);
187 }
188 }
189
190 static void php_pqconn_object_read_busy(zval *object, void *o, zval *return_value TSRMLS_DC)
191 {
192 php_pqconn_object_t *obj = o;
193
194 RETVAL_BOOL(PQisBusy(obj->intern->conn));
195 }
196
197 static void php_pqconn_object_read_encoding(zval *object, void *o, zval *return_value TSRMLS_DC)
198 {
199 php_pqconn_object_t *obj = o;
200
201 RETVAL_STRING(pg_encoding_to_char(PQclientEncoding(obj->intern->conn)), 1);
202 }
203
204 static void php_pqconn_object_write_encoding(zval *object, void *o, zval *value TSRMLS_DC)
205 {
206 php_pqconn_object_t *obj = o;
207 zval *zenc = value;
208
209 if (Z_TYPE_P(value) != IS_STRING) {
210 if (Z_REFCOUNT_P(value) > 1) {
211 zval *tmp;
212 MAKE_STD_ZVAL(tmp);
213 ZVAL_ZVAL(tmp, zenc, 1, 0);
214 convert_to_string(tmp);
215 zenc = tmp;
216 } else {
217 convert_to_string_ex(&zenc);
218 }
219 }
220
221 if (0 > PQsetClientEncoding(obj->intern->conn, Z_STRVAL_P(zenc))) {
222 zend_error(E_NOTICE, "Unrecognized encoding '%s'", Z_STRVAL_P(zenc));
223 }
224
225 if (zenc != value) {
226 zval_ptr_dtor(&zenc);
227 }
228 }
229
230 static void php_pqconn_object_read_unbuffered(zval *object, void *o, zval *return_value TSRMLS_DC)
231 {
232 php_pqconn_object_t *obj = o;
233
234 RETVAL_BOOL(obj->intern->unbuffered);
235 }
236
237 static void php_pqconn_object_write_unbuffered(zval *object, void *o, zval *value TSRMLS_DC)
238 {
239 php_pqconn_object_t *obj = o;
240
241 obj->intern->unbuffered = z_is_true(value);
242 }
243
244 static void php_pqconn_object_read_db(zval *object, void *o, zval *return_value TSRMLS_DC)
245 {
246 php_pqconn_object_t *obj = o;
247 char *db = PQdb(obj->intern->conn);
248
249 if (db) {
250 RETVAL_STRING(db, 1);
251 } else {
252 RETVAL_EMPTY_STRING();
253 }
254 }
255
256 static void php_pqconn_object_read_user(zval *object, void *o, zval *return_value TSRMLS_DC)
257 {
258 php_pqconn_object_t *obj = o;
259 char *user = PQuser(obj->intern->conn);
260
261 if (user) {
262 RETVAL_STRING(user, 1);
263 } else {
264 RETVAL_EMPTY_STRING();
265 }
266 }
267
268 static void php_pqconn_object_read_pass(zval *object, void *o, zval *return_value TSRMLS_DC)
269 {
270 php_pqconn_object_t *obj = o;
271 char *pass = PQpass(obj->intern->conn);
272
273 if (pass) {
274 RETVAL_STRING(pass, 1);
275 } else {
276 RETVAL_EMPTY_STRING();
277 }
278 }
279
280 static void php_pqconn_object_read_host(zval *object, void *o, zval *return_value TSRMLS_DC)
281 {
282 php_pqconn_object_t *obj = o;
283 char *host = PQhost(obj->intern->conn);
284
285 if (host) {
286 RETVAL_STRING(host, 1);
287 } else {
288 RETVAL_EMPTY_STRING();
289 }
290 }
291
292 static void php_pqconn_object_read_port(zval *object, void *o, zval *return_value TSRMLS_DC)
293 {
294 php_pqconn_object_t *obj = o;
295 char *port = PQport(obj->intern->conn);
296
297 if (port) {
298 RETVAL_STRING(port, 1);
299 } else {
300 RETVAL_EMPTY_STRING();
301 }
302 }
303
304 static void php_pqconn_object_read_options(zval *object, void *o, zval *return_value TSRMLS_DC)
305 {
306 php_pqconn_object_t *obj = o;
307 char *options = PQoptions(obj->intern->conn);
308
309 if (options) {
310 RETVAL_STRING(options, 1);
311 } else {
312 RETVAL_EMPTY_STRING();
313 }
314 }
315
316 static int apply_read_event_handler_ex(void *p, void *arg TSRMLS_DC)
317 {
318 HashTable *rv = arg;
319 zval *zcb = php_pq_callback_to_zval(p);
320
321 zend_hash_next_index_insert(rv, &zcb, sizeof(zval *), NULL);
322
323 return ZEND_HASH_APPLY_KEEP;
324 }
325
326 static int apply_read_event_handlers(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
327 {
328 HashTable *evhs = p, *rv = va_arg(argv, HashTable *);
329 zval *entry, **entry_ptr;
330
331 MAKE_STD_ZVAL(entry);
332 array_init_size(entry, zend_hash_num_elements(evhs));
333
334 if (key->nKeyLength) {
335 zend_hash_add(rv, key->arKey, key->nKeyLength, &entry, sizeof(zval *), (void *) &entry_ptr);
336 } else {
337 zend_hash_index_update(rv, key->h, &entry, sizeof(zval *), (void *) &entry_ptr);
338 }
339
340 zend_hash_apply_with_argument(evhs, apply_read_event_handler_ex, Z_ARRVAL_PP(entry_ptr) TSRMLS_CC);
341
342 return ZEND_HASH_APPLY_KEEP;
343 }
344 static void php_pqconn_object_read_event_handlers(zval *object, void *o, zval *return_value TSRMLS_DC)
345 {
346 php_pqconn_object_t *obj = o;
347
348 array_init(return_value);
349 zend_hash_apply_with_arguments(&obj->intern->eventhandlers TSRMLS_CC, apply_read_event_handlers, 1, Z_ARRVAL_P(return_value) TSRMLS_CC);
350 }
351
352 static STATUS php_pqconn_update_socket(zval *this_ptr, php_pqconn_object_t *obj TSRMLS_DC)
353 {
354 zval *zsocket, zmember;
355 php_stream *stream;
356 STATUS retval;
357 int socket;
358
359 if (!obj) {
360 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
361 }
362
363 INIT_PZVAL(&zmember);
364 ZVAL_STRINGL(&zmember, "socket", sizeof("socket")-1, 0);
365 MAKE_STD_ZVAL(zsocket);
366
367 if ((CONNECTION_BAD != PQstatus(obj->intern->conn))
368 && (-1 < (socket = PQsocket(obj->intern->conn)))
369 && (stream = php_stream_fopen_from_fd(socket, "r+b", NULL))) {
370 stream->flags |= PHP_STREAM_FLAG_NO_CLOSE;
371 php_stream_to_zval(stream, zsocket);
372 retval = SUCCESS;
373 } else {
374 ZVAL_NULL(zsocket);
375 retval = FAILURE;
376 }
377 zend_get_std_object_handlers()->write_property(getThis(), &zmember, zsocket, NULL TSRMLS_CC);
378 zval_ptr_dtor(&zsocket);
379
380 return retval;
381 }
382
383 static void *php_pqconn_resource_factory_ctor(void *data, void *init_arg TSRMLS_DC)
384 {
385 php_pqconn_resource_factory_data_t *o = init_arg;
386 PGconn *conn = NULL;;
387
388 if (o->flags & PHP_PQCONN_ASYNC) {
389 conn = PQconnectStart(o->dsn);
390 } else {
391 conn = PQconnectdb(o->dsn);
392 }
393
394 if (conn) {
395 PQregisterEventProc(conn, php_pqconn_event, "ext-pq", NULL);
396 }
397
398 return conn;
399 }
400
401 static void php_pqconn_resource_factory_dtor(void *opaque, void *handle TSRMLS_DC)
402 {
403 php_pqconn_event_data_t *evdata = PQinstanceData(handle, php_pqconn_event);
404
405 /* we don't care for anything, except free'ing evdata */
406 if (evdata) {
407 PQsetInstanceData(handle, php_pqconn_event, NULL);
408 memset(evdata, 0, sizeof(*evdata));
409 efree(evdata);
410 }
411
412 PQfinish(handle);
413 }
414
415 static php_resource_factory_ops_t php_pqconn_resource_factory_ops = {
416 php_pqconn_resource_factory_ctor,
417 NULL,
418 php_pqconn_resource_factory_dtor
419 };
420
421 php_resource_factory_ops_t *php_pqconn_get_resource_factory_ops(void)
422 {
423 return &php_pqconn_resource_factory_ops;
424 }
425
426 static void php_pqconn_wakeup(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC)
427 {
428 // FIXME: ping server
429 }
430
431 static inline PGresult *unlisten(PGconn *conn, const char *channel_str, size_t channel_len TSRMLS_DC)
432 {
433 char *quoted_channel = PQescapeIdentifier(conn, channel_str, channel_len);
434 PGresult *res = NULL;
435
436 if (quoted_channel) {
437 smart_str cmd = {0};
438
439 smart_str_appends(&cmd, "UNLISTEN ");
440 smart_str_appends(&cmd, quoted_channel);
441 smart_str_0(&cmd);
442
443 res = PQexec(conn, cmd.c);
444
445 smart_str_free(&cmd);
446 PQfreemem(quoted_channel);
447 }
448
449 return res;
450 }
451
452 static int apply_unlisten(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
453 {
454 php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *);
455 PGresult *res = unlisten(obj->intern->conn, key->arKey, key->nKeyLength - 1);
456
457 if (res) {
458 PHP_PQclear(res);
459 }
460
461 return ZEND_HASH_APPLY_REMOVE;
462 }
463
464 static void php_pqconn_retire(php_persistent_handle_factory_t *f, void **handle TSRMLS_DC)
465 {
466 php_pqconn_event_data_t *evdata = PQinstanceData(*handle, php_pqconn_event);
467 PGcancel *cancel;
468 PGresult *res;
469
470 /* go away */
471 PQsetInstanceData(*handle, php_pqconn_event, NULL);
472
473 /* ignore notices */
474 PQsetNoticeReceiver(*handle, php_pqconn_notice_ignore, NULL);
475
476 /* cancel async queries */
477 if (PQisBusy(*handle) && (cancel = PQgetCancel(*handle))) {
478 char err[256] = {0};
479
480 PQcancel(cancel, err, sizeof(err));
481 PQfreeCancel(cancel);
482 }
483 /* clean up async results */
484 while ((res = PQgetResult(*handle))) {
485 PHP_PQclear(res);
486 }
487
488 /* clean up transaction & session */
489 switch (PQtransactionStatus(*handle)) {
490 case PQTRANS_IDLE:
491 res = PQexec(*handle, "RESET ALL");
492 break;
493 default:
494 res = PQexec(*handle, "ROLLBACK; RESET ALL");
495 break;
496 }
497
498 if (res) {
499 PHP_PQclear(res);
500 }
501
502 if (evdata) {
503 /* clean up notify listeners */
504 zend_hash_apply_with_arguments(&evdata->obj->intern->listeners TSRMLS_CC, apply_unlisten, 1, evdata->obj);
505
506 /* release instance data */
507 //memset(evdata, 0, sizeof(*evdata));
508 efree(evdata);
509 }
510 }
511
512 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_construct, 0, 0, 1)
513 ZEND_ARG_INFO(0, dsn)
514 ZEND_ARG_INFO(0, async)
515 ZEND_END_ARG_INFO();
516 static PHP_METHOD(pqconn, __construct) {
517 zend_error_handling zeh;
518 char *dsn_str = "";
519 int dsn_len = 0;
520 long flags = 0;
521 STATUS rv;
522
523 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
524 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sl", &dsn_str, &dsn_len, &flags);
525 zend_restore_error_handling(&zeh TSRMLS_CC);
526
527 if (SUCCESS == rv) {
528 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
529
530 if (obj->intern) {
531 throw_exce(EX_BAD_METHODCALL TSRMLS_CC, "pq\\Connection already initialized");
532 } else {
533 php_pqconn_event_data_t *evdata = php_pqconn_event_data_init(obj TSRMLS_CC);
534 php_pqconn_resource_factory_data_t rfdata = {dsn_str, flags};
535
536 obj->intern = ecalloc(1, sizeof(*obj->intern));
537
538 zend_hash_init(&obj->intern->listeners, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
539 zend_hash_init(&obj->intern->converters, 0, NULL, ZVAL_PTR_DTOR, 0);
540 zend_hash_init(&obj->intern->eventhandlers, 0, NULL, (dtor_func_t) zend_hash_destroy, 0);
541
542 if (flags & PHP_PQCONN_PERSISTENT) {
543 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);
544 php_resource_factory_init(&obj->intern->factory, php_persistent_handle_get_resource_factory_ops(), phf, (void (*)(void*)) php_persistent_handle_abandon);
545 } else {
546 php_resource_factory_init(&obj->intern->factory, &php_pqconn_resource_factory_ops, NULL, NULL);
547 }
548
549 if (flags & PHP_PQCONN_ASYNC) {
550 obj->intern->poller = (int (*)(PGconn*)) PQconnectPoll;
551 }
552
553 obj->intern->conn = php_resource_factory_handle_ctor(&obj->intern->factory, &rfdata TSRMLS_CC);
554
555 PQsetInstanceData(obj->intern->conn, php_pqconn_event, evdata);
556 PQsetNoticeReceiver(obj->intern->conn, php_pqconn_notice_recv, evdata);
557
558 if (SUCCESS != php_pqconn_update_socket(getThis(), obj TSRMLS_CC)) {
559 throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection failed (%s)", PHP_PQerrorMessage(obj->intern->conn));
560 }
561 }
562 }
563 }
564
565 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset, 0, 0, 0)
566 ZEND_END_ARG_INFO();
567 static PHP_METHOD(pqconn, reset) {
568 zend_error_handling zeh;
569 STATUS rv;
570
571 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
572 rv = zend_parse_parameters_none();
573 zend_restore_error_handling(&zeh TSRMLS_CC);
574
575 if (SUCCESS == rv) {
576 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
577
578 if (!obj->intern) {
579 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
580 } else {
581 PQreset(obj->intern->conn);
582
583 if (CONNECTION_OK != PQstatus(obj->intern->conn)) {
584 throw_exce(EX_CONNECTION_FAILED TSRMLS_CC, "Connection reset failed: (%s)", PHP_PQerrorMessage(obj->intern->conn));
585 }
586
587 php_pqconn_notify_listeners(obj TSRMLS_CC);
588 }
589 }
590 }
591
592 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_reset_async, 0, 0, 0)
593 ZEND_END_ARG_INFO();
594 static PHP_METHOD(pqconn, resetAsync) {
595 zend_error_handling zeh;
596 STATUS rv;
597
598 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
599 rv = zend_parse_parameters_none();
600 zend_restore_error_handling(&zeh TSRMLS_CC);
601
602 if (SUCCESS == rv) {
603 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
604
605 if (!obj->intern) {
606 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
607 } else {
608 if (!PQresetStart(obj->intern->conn)) {
609 throw_exce(EX_IO TSRMLS_CC, "Failed to start connection reset (%s)", PHP_PQerrorMessage(obj->intern->conn));
610 } else {
611 obj->intern->poller = (int (*)(PGconn*)) PQresetPoll;
612 }
613
614 php_pqconn_notify_listeners(obj TSRMLS_CC);
615 }
616 }
617 }
618
619 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unlisten, 0, 0, 1)
620 ZEND_ARG_INFO(0, channel)
621 ZEND_END_ARG_INFO();
622 static PHP_METHOD(pqconn, unlisten)
623 {
624 zend_error_handling zeh;
625 char *channel_str;
626 int channel_len;
627 STATUS rv;
628
629 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
630 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len);
631 zend_restore_error_handling(&zeh TSRMLS_CC);
632
633 if (SUCCESS == rv) {
634 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
635
636 if (!obj->intern) {
637 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
638 } else if (SUCCESS == zend_hash_del(&obj->intern->listeners, channel_str, channel_len + 1)) {
639 PGresult *res = unlisten(obj->intern->conn, channel_str, channel_len);
640
641 if (res) {
642 php_pqres_success(res TSRMLS_CC);
643 PHP_PQclear(res);
644 }
645 }
646 }
647 }
648
649 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unlisten_async, 0, 0, 1)
650 ZEND_ARG_INFO(0, channel)
651 ZEND_END_ARG_INFO();
652 static PHP_METHOD(pqconn, unlistenAsync) {
653 zend_error_handling zeh;
654 char *channel_str;
655 int channel_len;
656 STATUS rv;
657
658 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
659 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &channel_str, &channel_len);
660 zend_restore_error_handling(&zeh TSRMLS_CC);
661
662 if (SUCCESS == rv) {
663 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
664
665 if (!obj->intern) {
666 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
667 } else {
668 char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len);
669
670 if (!quoted_channel) {
671 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn));
672 } else {
673 smart_str cmd = {0};
674
675 smart_str_appends(&cmd, "UNLISTEN ");
676 smart_str_appends(&cmd, quoted_channel);
677 smart_str_0(&cmd);
678
679 if (!PQsendQuery(obj->intern->conn, cmd.c)) {
680 throw_exce(EX_IO TSRMLS_CC, "Failed to uninstall listener (%s)", PHP_PQerrorMessage(obj->intern->conn));
681 } else {
682 obj->intern->poller = PQconsumeInput;
683 zend_hash_del(&obj->intern->listeners, channel_str, channel_len + 1);
684 }
685
686 smart_str_free(&cmd);
687 PQfreemem(quoted_channel);
688 php_pqconn_notify_listeners(obj TSRMLS_CC);
689 }
690 }
691 }
692 }
693
694 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)
695 {
696 HashTable ht, *existing_listeners;
697
698 php_pq_callback_addref(listener);
699
700 if (SUCCESS == zend_hash_find(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &existing_listeners)) {
701 zend_hash_next_index_insert(existing_listeners, (void *) listener, sizeof(*listener), NULL);
702 } else {
703 zend_hash_init(&ht, 1, NULL, (dtor_func_t) php_pq_callback_dtor, 0);
704 zend_hash_next_index_insert(&ht, (void *) listener, sizeof(*listener), NULL);
705 zend_hash_add(&obj->intern->listeners, channel_str, channel_len + 1, (void *) &ht, sizeof(HashTable), NULL);
706 }
707 }
708
709 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen, 0, 0, 2)
710 ZEND_ARG_INFO(0, channel)
711 ZEND_ARG_INFO(0, callable)
712 ZEND_END_ARG_INFO();
713 static PHP_METHOD(pqconn, listen) {
714 zend_error_handling zeh;
715 char *channel_str = NULL;
716 int channel_len = 0;
717 php_pq_callback_t listener = {{0}};
718 STATUS rv;
719
720 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
721 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc);
722 zend_restore_error_handling(&zeh TSRMLS_CC);
723
724 if (SUCCESS == rv) {
725 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
726
727 if (!obj->intern) {
728 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
729 } else {
730 char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len);
731
732 if (!quoted_channel) {
733 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn));
734 } else {
735 PGresult *res;
736 smart_str cmd = {0};
737
738 smart_str_appends(&cmd, "LISTEN ");
739 smart_str_appends(&cmd, quoted_channel);
740 smart_str_0(&cmd);
741
742 res = PQexec(obj->intern->conn, cmd.c);
743
744 smart_str_free(&cmd);
745 PQfreemem(quoted_channel);
746
747 if (!res) {
748 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to install listener (%s)", PHP_PQerrorMessage(obj->intern->conn));
749 } else {
750 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
751 obj->intern->poller = PQconsumeInput;
752 php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC);
753 }
754 PHP_PQclear(res);
755 }
756
757 php_pqconn_notify_listeners(obj TSRMLS_CC);
758 }
759 }
760 }
761 }
762
763 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_listen_async, 0, 0, 0)
764 ZEND_ARG_INFO(0, channel)
765 ZEND_ARG_INFO(0, callable)
766 ZEND_END_ARG_INFO();
767 static PHP_METHOD(pqconn, listenAsync) {
768 zend_error_handling zeh;
769 char *channel_str = NULL;
770 int channel_len = 0;
771 php_pq_callback_t listener = {{0}};
772 STATUS rv;
773
774 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
775 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &channel_str, &channel_len, &listener.fci, &listener.fcc);
776 zend_restore_error_handling(&zeh TSRMLS_CC);
777
778 if (SUCCESS == rv) {
779 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
780
781 if (!obj->intern) {
782 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
783 } else {
784 char *quoted_channel = PQescapeIdentifier(obj->intern->conn, channel_str, channel_len);
785
786 if (!quoted_channel) {
787 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to escape channel identifier (%s)", PHP_PQerrorMessage(obj->intern->conn));
788 } else {
789 smart_str cmd = {0};
790
791 smart_str_appends(&cmd, "LISTEN ");
792 smart_str_appends(&cmd, quoted_channel);
793 smart_str_0(&cmd);
794
795 if (!PQsendQuery(obj->intern->conn, cmd.c)) {
796 throw_exce(EX_IO TSRMLS_CC, "Failed to install listener (%s)", PHP_PQerrorMessage(obj->intern->conn));
797 } else {
798 obj->intern->poller = PQconsumeInput;
799 php_pqconn_add_listener(obj, channel_str, channel_len, &listener TSRMLS_CC);
800 }
801
802 smart_str_free(&cmd);
803 PQfreemem(quoted_channel);
804 php_pqconn_notify_listeners(obj TSRMLS_CC);
805 }
806 }
807 }
808 }
809
810 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify, 0, 0, 2)
811 ZEND_ARG_INFO(0, channel)
812 ZEND_ARG_INFO(0, message)
813 ZEND_END_ARG_INFO();
814 static PHP_METHOD(pqconn, notify) {
815 zend_error_handling zeh;
816 char *channel_str, *message_str;
817 int channel_len, message_len;
818 STATUS rv;
819
820 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
821 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len);
822 zend_restore_error_handling(&zeh TSRMLS_CC);
823
824 if (SUCCESS == rv) {
825 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
826
827 if (!obj->intern) {
828 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
829 } else {
830 PGresult *res;
831 char *params[2] = {channel_str, message_str};
832
833 res = PQexecParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0);
834
835 if (!res) {
836 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn));
837 } else {
838 php_pqres_success(res TSRMLS_CC);
839 PHP_PQclear(res);
840 }
841
842 php_pqconn_notify_listeners(obj TSRMLS_CC);
843 }
844 }
845 }
846
847 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_notify_async, 0, 0, 2)
848 ZEND_ARG_INFO(0, channel)
849 ZEND_ARG_INFO(0, message)
850 ZEND_END_ARG_INFO();
851 static PHP_METHOD(pqconn, notifyAsync) {
852 zend_error_handling zeh;
853 char *channel_str, *message_str;
854 int channel_len, message_len;
855 STATUS rv;
856
857 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
858 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &channel_str, &channel_len, &message_str, &message_len);
859 zend_restore_error_handling(&zeh TSRMLS_CC);
860
861 if (SUCCESS == rv) {
862 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
863
864 if (!obj->intern) {
865 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
866 } else {
867 char *params[2] = {channel_str, message_str};
868
869 if (!PQsendQueryParams(obj->intern->conn, "select pg_notify($1, $2)", 2, NULL, (const char *const*) params, NULL, NULL, 0)) {
870 throw_exce(EX_IO TSRMLS_CC, "Failed to notify listeners (%s)", PHP_PQerrorMessage(obj->intern->conn));
871 } else {
872 obj->intern->poller = PQconsumeInput;
873 }
874
875 php_pqconn_notify_listeners(obj TSRMLS_CC);
876 }
877 }
878 }
879
880 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_poll, 0, 0, 0)
881 ZEND_END_ARG_INFO();
882 static PHP_METHOD(pqconn, poll) {
883 zend_error_handling zeh;
884 STATUS rv;
885
886 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
887 rv = zend_parse_parameters_none();
888 zend_restore_error_handling(&zeh TSRMLS_CC);
889
890 if (SUCCESS == rv) {
891 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
892
893 if (!obj->intern) {
894 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
895 } else if (!obj->intern->poller) {
896 throw_exce(EX_RUNTIME TSRMLS_CC, "No asynchronous operation active");
897 } else {
898 if (obj->intern->poller == PQconsumeInput) {
899 RETVAL_LONG(obj->intern->poller(obj->intern->conn) * PGRES_POLLING_OK);
900 } else {
901 RETVAL_LONG(obj->intern->poller(obj->intern->conn));
902 }
903 php_pqconn_notify_listeners(obj TSRMLS_CC);
904 }
905 }
906 }
907
908 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec, 0, 0, 1)
909 ZEND_ARG_INFO(0, query)
910 ZEND_END_ARG_INFO();
911 static PHP_METHOD(pqconn, exec) {
912 zend_error_handling zeh;
913 char *query_str;
914 int query_len;
915 STATUS rv;
916
917 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
918 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query_str, &query_len);
919 zend_restore_error_handling(&zeh TSRMLS_CC);
920
921 if (SUCCESS == rv) {
922 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
923
924 if (!obj->intern) {
925 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
926 } else {
927 PGresult *res = PQexec(obj->intern->conn, query_str);
928
929 if (!res) {
930 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
931 } else if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
932 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
933 } else {
934 PHP_PQclear(res);
935 }
936
937 php_pqconn_notify_listeners(obj TSRMLS_CC);
938 }
939 }
940 }
941
942 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_get_result, 0, 0, 0)
943 ZEND_END_ARG_INFO();
944 static PHP_METHOD(pqconn, getResult) {
945 zend_error_handling zeh;
946 STATUS rv;
947
948 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
949 rv = zend_parse_parameters_none();
950 zend_restore_error_handling(&zeh TSRMLS_CC);
951
952 if (SUCCESS == rv) {
953 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
954
955 if (!obj->intern) {
956 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
957 } else {
958 PGresult *res = PQgetResult(obj->intern->conn);
959
960 if (!res) {
961 RETVAL_NULL();
962 } else {
963 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
964 }
965
966 php_pqconn_notify_listeners(obj TSRMLS_CC);
967 }
968 }
969 }
970
971 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_async, 0, 0, 1)
972 ZEND_ARG_INFO(0, query)
973 ZEND_ARG_INFO(0, callable)
974 ZEND_END_ARG_INFO();
975 static PHP_METHOD(pqconn, execAsync) {
976 zend_error_handling zeh;
977 php_pq_callback_t resolver = {{0}};
978 char *query_str;
979 int query_len;
980 STATUS rv;
981
982 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
983 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|f", &query_str, &query_len, &resolver.fci, &resolver.fcc);
984 zend_restore_error_handling(&zeh TSRMLS_CC);
985
986 if (SUCCESS == rv) {
987 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
988
989 if (!obj->intern) {
990 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
991 } else if (!PQsendQuery(obj->intern->conn, query_str)) {
992 throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
993 } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
994 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
995 } else {
996 php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC);
997 obj->intern->poller = PQconsumeInput;
998 php_pqconn_notify_listeners(obj TSRMLS_CC);
999 }
1000 }
1001 }
1002
1003 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params, 0, 0, 2)
1004 ZEND_ARG_INFO(0, query)
1005 ZEND_ARG_ARRAY_INFO(0, params, 0)
1006 ZEND_ARG_ARRAY_INFO(0, types, 1)
1007 ZEND_END_ARG_INFO();
1008 static PHP_METHOD(pqconn, execParams) {
1009 zend_error_handling zeh;
1010 char *query_str;
1011 int query_len;
1012 zval *zparams;
1013 zval *ztypes = NULL;
1014 STATUS rv;
1015
1016 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1017 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!", &query_str, &query_len, &zparams, &ztypes);
1018 zend_restore_error_handling(&zeh TSRMLS_CC);
1019
1020 if (SUCCESS == rv) {
1021 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1022
1023 if (!obj->intern) {
1024 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1025 } else {
1026 PGresult *res;
1027 php_pq_params_t *params;
1028
1029 params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, Z_ARRVAL_P(zparams) TSRMLS_CC);
1030 res = PQexecParams(obj->intern->conn, query_str, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0);
1031 php_pq_params_free(&params);
1032
1033 if (!res) {
1034 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
1035 } else {
1036 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
1037 php_pq_object_to_zval_no_addref(PQresultInstanceData(res, php_pqconn_event), &return_value TSRMLS_CC);
1038 } else {
1039 PHP_PQclear(res);
1040 }
1041
1042 php_pqconn_notify_listeners(obj TSRMLS_CC);
1043 }
1044 }
1045 }
1046 }
1047
1048 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_exec_params_async, 0, 0, 2)
1049 ZEND_ARG_INFO(0, query)
1050 ZEND_ARG_ARRAY_INFO(0, params, 0)
1051 ZEND_ARG_ARRAY_INFO(0, types, 1)
1052 ZEND_ARG_INFO(0, callable)
1053 ZEND_END_ARG_INFO();
1054 static PHP_METHOD(pqconn, execParamsAsync) {
1055 zend_error_handling zeh;
1056 php_pq_callback_t resolver = {{0}};
1057 char *query_str;
1058 int query_len;
1059 zval *zparams;
1060 zval *ztypes = NULL;
1061 STATUS rv;
1062
1063 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1064 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa/|a/!f", &query_str, &query_len, &zparams, &ztypes, &resolver.fci, &resolver.fcc);
1065 zend_restore_error_handling(&zeh TSRMLS_CC);
1066
1067 if (SUCCESS == rv) {
1068 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1069
1070 if (!obj->intern) {
1071 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1072 } else {
1073 int rc;
1074 php_pq_params_t *params;
1075
1076 params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, Z_ARRVAL_P(zparams) TSRMLS_CC);
1077 rc = PQsendQueryParams(obj->intern->conn, query_str, params->param.count, params->type.oids, (const char *const*) params->param.strings, NULL, NULL, 0);
1078 php_pq_params_free(&params);
1079
1080 if (!rc) {
1081 throw_exce(EX_IO TSRMLS_CC, "Failed to execute query (%s)", PHP_PQerrorMessage(obj->intern->conn));
1082 } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
1083 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
1084 } else {
1085 php_pq_callback_recurse(&obj->intern->onevent, &resolver TSRMLS_CC);
1086 obj->intern->poller = PQconsumeInput;
1087 php_pqconn_notify_listeners(obj TSRMLS_CC);
1088 }
1089 }
1090 }
1091 zend_restore_error_handling(&zeh TSRMLS_CC);
1092 }
1093
1094 STATUS php_pqconn_prepare(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
1095 {
1096 PGresult *res;
1097 STATUS rv;
1098
1099 if (!obj) {
1100 obj = zend_object_store_get_object(object TSRMLS_CC);
1101 }
1102
1103 res = PQprepare(obj->intern->conn, name, query, params->type.count, params->type.oids);
1104
1105 if (!res) {
1106 rv = FAILURE;
1107 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn));
1108 } else {
1109 rv = php_pqres_success(res TSRMLS_CC);
1110 PHP_PQclear(res);
1111 php_pqconn_notify_listeners(obj TSRMLS_CC);
1112 }
1113
1114 return rv;
1115 }
1116
1117 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare, 0, 0, 2)
1118 ZEND_ARG_INFO(0, name)
1119 ZEND_ARG_INFO(0, query)
1120 ZEND_ARG_ARRAY_INFO(0, types, 1)
1121 ZEND_END_ARG_INFO();
1122 static PHP_METHOD(pqconn, prepare) {
1123 zend_error_handling zeh;
1124 zval *ztypes = NULL;
1125 char *name_str, *query_str;
1126 int name_len, *query_len;
1127 STATUS rv;
1128
1129 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1130 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes);
1131 zend_restore_error_handling(&zeh TSRMLS_CC);
1132
1133 if (SUCCESS == rv) {
1134 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1135
1136 if (!obj->intern) {
1137 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1138 } else {
1139 php_pq_params_t *params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC);
1140
1141 if (SUCCESS != php_pqconn_prepare(getThis(), obj, name_str, query_str, params TSRMLS_CC)) {
1142 php_pq_params_free(&params);
1143 } else {
1144 php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
1145
1146 php_pq_object_addref(obj TSRMLS_CC);
1147 stm->conn = obj;
1148 stm->name = estrdup(name_str);
1149 stm->params = params;
1150 ZEND_INIT_SYMTABLE(&stm->bound);
1151
1152 return_value->type = IS_OBJECT;
1153 return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
1154 }
1155 }
1156 }
1157 }
1158
1159 STATUS php_pqconn_prepare_async(zval *object, php_pqconn_object_t *obj, const char *name, const char *query, php_pq_params_t *params TSRMLS_DC)
1160 {
1161 STATUS rv;
1162
1163 if (!obj) {
1164 obj = zend_object_store_get_object(object TSRMLS_CC);
1165 }
1166
1167 if (!PQsendPrepare(obj->intern->conn, name, query, params->type.count, params->type.oids)) {
1168 rv = FAILURE;
1169 throw_exce(EX_IO TSRMLS_CC, "Failed to prepare statement (%s)", PHP_PQerrorMessage(obj->intern->conn));
1170 } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
1171 rv = FAILURE;
1172 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
1173 } else {
1174 rv = SUCCESS;
1175 obj->intern->poller = PQconsumeInput;
1176 php_pqconn_notify_listeners(obj TSRMLS_CC);
1177 }
1178
1179 return rv;
1180 }
1181
1182 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_prepare_async, 0, 0, 2)
1183 ZEND_ARG_INFO(0, name)
1184 ZEND_ARG_INFO(0, query)
1185 ZEND_ARG_ARRAY_INFO(0, types, 1)
1186 ZEND_END_ARG_INFO();
1187 static PHP_METHOD(pqconn, prepareAsync) {
1188 zend_error_handling zeh;
1189 zval *ztypes = NULL;
1190 char *name_str, *query_str;
1191 int name_len, *query_len;
1192 STATUS rv;
1193
1194 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1195 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a/!", &name_str, &name_len, &query_str, &query_len, &ztypes);
1196 zend_restore_error_handling(&zeh TSRMLS_CC);
1197
1198 if (SUCCESS == rv) {
1199 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1200
1201 if (!obj->intern) {
1202 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1203 } else {
1204 php_pq_params_t *params = php_pq_params_init(&obj->intern->converters, ztypes ? Z_ARRVAL_P(ztypes) : NULL, NULL TSRMLS_CC);
1205
1206 if (SUCCESS != php_pqconn_prepare_async(getThis(), obj, name_str, query_str, params TSRMLS_CC)) {
1207 php_pq_params_free(&params);
1208 } else {
1209 php_pqstm_t *stm = ecalloc(1, sizeof(*stm));
1210
1211 php_pq_object_addref(obj TSRMLS_CC);
1212 stm->conn = obj;
1213 stm->name = estrdup(name_str);
1214 stm->params = params;
1215 ZEND_INIT_SYMTABLE(&stm->bound);
1216
1217 return_value->type = IS_OBJECT;
1218 return_value->value.obj = php_pqstm_create_object_ex(php_pqstm_class_entry, stm, NULL TSRMLS_CC);
1219 }
1220 }
1221 }
1222 }
1223
1224 static inline char *declare_str(const char *name_str, size_t name_len, unsigned flags, const char *query_str, size_t query_len)
1225 {
1226 size_t decl_len = name_len + query_len + sizeof("DECLARE BINARY INSENSITIVE NO SCROLL CURSOR WITHOUT HOLD FOR ");
1227 char *decl_str;
1228
1229 decl_str = emalloc(decl_len);
1230 decl_len = slprintf(decl_str, decl_len, "DECLARE %s %s %s %s CURSOR %s FOR %s",
1231 name_str,
1232 (flags & PHP_PQ_DECLARE_BINARY) ? "BINARY" : "",
1233 (flags & PHP_PQ_DECLARE_INSENSITIVE) ? "INSENSITIVE" : "",
1234 (flags & PHP_PQ_DECLARE_NO_SCROLL) ? "NO SCROLL" :
1235 (flags & PHP_PQ_DECLARE_SCROLL) ? "SCROLL" : "",
1236 (flags & PHP_PQ_DECLARE_WITH_HOLD) ? "WITH HOLD" : "",
1237 query_str
1238 );
1239 return decl_str;
1240 }
1241
1242 STATUS php_pqconn_declare(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
1243 {
1244 PGresult *res;
1245 STATUS rv;
1246
1247 if (!obj) {
1248 obj = zend_object_store_get_object(object TSRMLS_CC);
1249 }
1250
1251 res = PQexec(obj->intern->conn, decl);
1252
1253 if (!res) {
1254 rv = FAILURE;
1255 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn));
1256 } else {
1257 rv = php_pqres_success(res TSRMLS_CC);
1258 PHP_PQclear(res);
1259 php_pqconn_notify_listeners(obj TSRMLS_CC);
1260 }
1261
1262 return rv;
1263 }
1264
1265 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare, 0, 0, 3)
1266 ZEND_ARG_INFO(0, name)
1267 ZEND_ARG_INFO(0, flags)
1268 ZEND_ARG_INFO(0, query)
1269 ZEND_END_ARG_INFO();
1270 static PHP_METHOD(pqconn, declare) {
1271 zend_error_handling zeh;
1272 char *name_str, *query_str;
1273 int name_len, query_len;
1274 long flags;
1275 STATUS rv;
1276
1277 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1278 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
1279 zend_restore_error_handling(&zeh TSRMLS_CC);
1280
1281 if (SUCCESS == rv) {
1282 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1283
1284 if (!obj->intern) {
1285 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1286 } else {
1287 char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
1288
1289 if (SUCCESS != php_pqconn_declare(getThis(), obj, decl TSRMLS_CC)) {
1290 efree(decl);
1291 } else {
1292 php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
1293
1294 php_pq_object_addref(obj TSRMLS_CC);
1295 cur->conn = obj;
1296 cur->open = 1;
1297 cur->name = estrdup(name_str);
1298 cur->decl = decl;
1299
1300 return_value->type = IS_OBJECT;
1301 return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
1302 }
1303 }
1304 }
1305 }
1306
1307 STATUS php_pqconn_declare_async(zval *object, php_pqconn_object_t *obj, const char *decl TSRMLS_DC)
1308 {
1309 STATUS rv;
1310
1311 if (!obj) {
1312 obj = zend_object_store_get_object(object TSRMLS_CC);
1313 }
1314
1315 if (!PQsendQuery(obj->intern->conn, decl)) {
1316 rv = FAILURE;
1317 throw_exce(EX_IO TSRMLS_CC, "Failed to declare cursor (%s)", PHP_PQerrorMessage(obj->intern->conn));
1318 } else if (obj->intern->unbuffered && !PQsetSingleRowMode(obj->intern->conn)) {
1319 rv = FAILURE;
1320 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to enable unbuffered mode (%s)", PHP_PQerrorMessage(obj->intern->conn));
1321 } else {
1322 rv = SUCCESS;
1323 obj->intern->poller = PQconsumeInput;
1324 php_pqconn_notify_listeners(obj TSRMLS_CC);
1325 }
1326
1327 return rv;
1328 }
1329
1330 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_declare_async, 0, 0, 2)
1331 ZEND_ARG_INFO(0, name)
1332 ZEND_ARG_INFO(0, flags)
1333 ZEND_ARG_INFO(0, query)
1334 ZEND_END_ARG_INFO();
1335 static PHP_METHOD(pqconn, declareAsync) {
1336 zend_error_handling zeh;
1337 char *name_str, *query_str;
1338 int name_len, query_len;
1339 long flags;
1340 STATUS rv;
1341
1342 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1343 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sls", &name_str, &name_len, &flags, &query_str, &query_len);
1344 zend_restore_error_handling(&zeh TSRMLS_CC);
1345
1346 if (SUCCESS == rv) {
1347 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1348
1349 if (!obj->intern) {
1350 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1351 } else {
1352 char *decl = declare_str(name_str, name_len, flags, query_str, query_len);
1353
1354 if (SUCCESS != php_pqconn_declare_async(getThis(), obj, decl TSRMLS_CC)) {
1355 efree(decl);
1356 } else {
1357 php_pqcur_t *cur = ecalloc(1, sizeof(*cur));
1358
1359 php_pq_object_addref(obj TSRMLS_CC);
1360 cur->conn = obj;
1361 cur->open = 1;
1362 cur->name = estrdup(name_str);
1363 cur->decl = decl;
1364
1365 return_value->type = IS_OBJECT;
1366 return_value->value.obj = php_pqcur_create_object_ex(php_pqcur_class_entry, cur, NULL TSRMLS_CC);
1367 }
1368 }
1369 }
1370 }
1371
1372 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote, 0, 0, 1)
1373 ZEND_ARG_INFO(0, string)
1374 ZEND_END_ARG_INFO();
1375 static PHP_METHOD(pqconn, quote) {
1376 char *str;
1377 int len;
1378
1379 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
1380 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1381
1382 if (!obj->intern) {
1383 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1384 } else {
1385 char *quoted = PQescapeLiteral(obj->intern->conn, str, len);
1386
1387 if (!quoted) {
1388 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote string (%s)", PHP_PQerrorMessage(obj->intern->conn));
1389 RETVAL_FALSE;
1390 } else {
1391 RETVAL_STRING(quoted, 1);
1392 PQfreemem(quoted);
1393 }
1394 }
1395 }
1396 }
1397
1398 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_quote_name, 0, 0, 1)
1399 ZEND_ARG_INFO(0, type)
1400 ZEND_END_ARG_INFO();
1401 static PHP_METHOD(pqconn, quoteName) {
1402 char *str;
1403 int len;
1404
1405 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
1406 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1407
1408 if (!obj->intern) {
1409 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1410 } else {
1411 char *quoted = PQescapeIdentifier(obj->intern->conn, str, len);
1412
1413 if (!quoted) {
1414 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to quote name (%s)", PHP_PQerrorMessage(obj->intern->conn));
1415 RETVAL_FALSE;
1416 } else {
1417 RETVAL_STRING(quoted, 1);
1418 PQfreemem(quoted);
1419 }
1420 }
1421 }
1422 }
1423
1424 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_escape_bytea, 0, 0, 1)
1425 ZEND_ARG_INFO(0, bytea)
1426 ZEND_END_ARG_INFO();
1427 static PHP_METHOD(pqconn, escapeBytea) {
1428 char *str;
1429 int len;
1430
1431 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
1432 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1433
1434 if (!obj->intern) {
1435 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1436 } else {
1437 size_t escaped_len;
1438 char *escaped_str = (char *) PQescapeByteaConn(obj->intern->conn, (unsigned char *) str, len, &escaped_len);
1439
1440 if (!escaped_str) {
1441 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to escape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn));
1442 RETVAL_FALSE;
1443 } else {
1444 RETVAL_STRINGL(escaped_str, escaped_len - 1, 1);
1445 PQfreemem(escaped_str);
1446 }
1447 }
1448 }
1449 }
1450
1451 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_unescape_bytea, 0, 0, 1)
1452 ZEND_ARG_INFO(0, bytea)
1453 ZEND_END_ARG_INFO();
1454 static PHP_METHOD(pqconn, unescapeBytea) {
1455 char *str;
1456 int len;
1457
1458 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len)) {
1459 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1460
1461 if (!obj->intern) {
1462 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1463 } else {
1464 size_t unescaped_len;
1465 char *unescaped_str = (char *) PQunescapeBytea((unsigned char *)str, &unescaped_len);
1466
1467 if (!unescaped_str) {
1468 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to unescape bytea (%s)", PHP_PQerrorMessage(obj->intern->conn));
1469 RETVAL_FALSE;
1470 } else {
1471 RETVAL_STRINGL(unescaped_str, unescaped_len, 1);
1472 PQfreemem(unescaped_str);
1473 }
1474 }
1475 }
1476 }
1477
1478 STATUS php_pqconn_start_transaction(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
1479 {
1480 STATUS rv = FAILURE;
1481
1482 if (!conn_obj) {
1483 conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
1484 }
1485
1486 if (!conn_obj->intern) {
1487 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1488 } else {
1489 PGresult *res;
1490 smart_str cmd = {0};
1491 const char *il = isolation_level(&isolation);
1492
1493 smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL ");
1494 smart_str_appends(&cmd, il);
1495 smart_str_appends(&cmd, ", READ ");
1496 smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE");
1497 smart_str_appends(&cmd, ",");
1498 smart_str_appends(&cmd, deferrable ? "" : " NOT");
1499 smart_str_appends(&cmd, " DEFERRABLE");
1500 smart_str_0(&cmd);
1501
1502 res = PQexec(conn_obj->intern->conn, cmd.c);
1503
1504 if (!res) {
1505 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn));
1506 } else {
1507 rv = php_pqres_success(res TSRMLS_CC);
1508 PHP_PQclear(res);
1509 php_pqconn_notify_listeners(conn_obj TSRMLS_CC);
1510 }
1511
1512 smart_str_free(&cmd);
1513 }
1514
1515 return rv;
1516 }
1517
1518 STATUS php_pqconn_start_transaction_async(zval *zconn, php_pqconn_object_t *conn_obj, long isolation, zend_bool readonly, zend_bool deferrable TSRMLS_DC)
1519 {
1520 STATUS rv = FAILURE;
1521
1522 if (!conn_obj) {
1523 conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
1524 }
1525
1526 if (!conn_obj->intern) {
1527 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1528 } else {
1529 smart_str cmd = {0};
1530 const char *il = isolation_level(&isolation);
1531
1532 smart_str_appends(&cmd, "START TRANSACTION ISOLATION LEVEL ");
1533 smart_str_appends(&cmd, il);
1534 smart_str_appends(&cmd, ", READ ");
1535 smart_str_appends(&cmd, readonly ? "ONLY" : "WRITE");
1536 smart_str_appends(&cmd, ",");
1537 smart_str_appends(&cmd, deferrable ? "" : "NOT ");
1538 smart_str_appends(&cmd, " DEFERRABLE");
1539 smart_str_0(&cmd);
1540
1541 if (!PQsendQuery(conn_obj->intern->conn, cmd.c)) {
1542 throw_exce(EX_IO TSRMLS_CC, "Failed to start transaction (%s)", PHP_PQerrorMessage(conn_obj->intern->conn));
1543 } else {
1544 rv = SUCCESS;
1545 conn_obj->intern->poller = PQconsumeInput;
1546 php_pqconn_notify_listeners(conn_obj TSRMLS_CC);
1547 }
1548
1549 smart_str_free(&cmd);
1550 }
1551
1552 return rv;
1553 }
1554
1555 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction, 0, 0, 0)
1556 ZEND_ARG_INFO(0, isolation)
1557 ZEND_ARG_INFO(0, readonly)
1558 ZEND_ARG_INFO(0, deferrable)
1559 ZEND_END_ARG_INFO();
1560 static PHP_METHOD(pqconn, startTransaction) {
1561 zend_error_handling zeh;
1562 long isolation = PHP_PQTXN_READ_COMMITTED;
1563 zend_bool readonly = 0, deferrable = 0;
1564 STATUS rv;
1565
1566 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1567 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable);
1568 zend_restore_error_handling(&zeh TSRMLS_CC);
1569
1570 if (SUCCESS == rv) {
1571 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1572
1573 rv = php_pqconn_start_transaction(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC);
1574
1575 if (SUCCESS == rv) {
1576 php_pqtxn_t *txn = ecalloc(1, sizeof(*txn));
1577
1578 php_pq_object_addref(obj TSRMLS_CC);
1579 txn->conn = obj;
1580 txn->open = 1;
1581 txn->isolation = isolation;
1582 txn->readonly = readonly;
1583 txn->deferrable = deferrable;
1584
1585 return_value->type = IS_OBJECT;
1586 return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC);
1587 }
1588 }
1589 }
1590
1591 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_start_transaction_async, 0, 0, 0)
1592 ZEND_ARG_INFO(0, isolation)
1593 ZEND_ARG_INFO(0, readonly)
1594 ZEND_ARG_INFO(0, deferrable)
1595 ZEND_END_ARG_INFO();
1596 static PHP_METHOD(pqconn, startTransactionAsync) {
1597 zend_error_handling zeh;
1598 long isolation = PHP_PQTXN_READ_COMMITTED;
1599 zend_bool readonly = 0, deferrable = 0;
1600 STATUS rv;
1601
1602 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1603 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lbb", &isolation, &readonly, &deferrable);
1604 zend_restore_error_handling(&zeh TSRMLS_CC);
1605 if (SUCCESS == rv) {
1606 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1607
1608 rv = php_pqconn_start_transaction_async(getThis(), obj, isolation, readonly, deferrable TSRMLS_CC);
1609
1610 if (SUCCESS == rv) {
1611 php_pqtxn_t *txn = ecalloc(1, sizeof(*txn));
1612
1613 php_pq_object_addref(obj TSRMLS_CC);
1614 txn->conn = obj;
1615 txn->isolation = isolation;
1616 txn->readonly = readonly;
1617 txn->deferrable = deferrable;
1618
1619 return_value->type = IS_OBJECT;
1620 return_value->value.obj = php_pqtxn_create_object_ex(php_pqtxn_class_entry, txn, NULL TSRMLS_CC);
1621 }
1622 }
1623 }
1624
1625 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_trace, 0, 0, 0)
1626 ZEND_ARG_INFO(0, stdio_stream)
1627 ZEND_END_ARG_INFO();
1628 static PHP_METHOD(pqconn, trace) {
1629 zval *zstream = NULL;
1630
1631 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r!", &zstream)) {
1632 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1633
1634 if (!obj->intern) {
1635 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1636 } else {
1637 if (!zstream) {
1638 PQuntrace(obj->intern->conn);
1639 RETVAL_TRUE;
1640 } else {
1641 FILE *fp;
1642 php_stream *stream = NULL;
1643
1644 php_stream_from_zval(stream, &zstream);
1645
1646 if (SUCCESS != php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void *) &fp, REPORT_ERRORS)) {
1647 RETVAL_FALSE;
1648 } else {
1649 stream->flags |= PHP_STREAM_FLAG_NO_CLOSE;
1650 PQtrace(obj->intern->conn, fp);
1651 RETVAL_TRUE;
1652 }
1653 }
1654 }
1655 }
1656 }
1657
1658 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_off, 0, 0, 1)
1659 ZEND_ARG_INFO(0, type)
1660 ZEND_END_ARG_INFO();
1661 static PHP_METHOD(pqconn, off) {
1662 zend_error_handling zeh;
1663 char *type_str;
1664 int type_len;
1665 STATUS rv;
1666
1667 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1668 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &type_str, &type_len);
1669 zend_restore_error_handling(&zeh TSRMLS_CC);
1670
1671 if (SUCCESS == rv) {
1672 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1673
1674 if (!obj->intern) {
1675 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1676 } else {
1677 RETURN_BOOL(SUCCESS == zend_hash_del(&obj->intern->eventhandlers, type_str, type_len + 1));
1678 }
1679 }
1680 }
1681
1682 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_on, 0, 0, 2)
1683 ZEND_ARG_INFO(0, type)
1684 ZEND_ARG_INFO(0, callable)
1685 ZEND_END_ARG_INFO();
1686 static PHP_METHOD(pqconn, on) {
1687 zend_error_handling zeh;
1688 char *type_str;
1689 int type_len;
1690 php_pq_callback_t cb = {{0}};
1691 STATUS rv;
1692
1693 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1694 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf", &type_str, &type_len, &cb.fci, &cb.fcc);
1695 zend_restore_error_handling(&zeh TSRMLS_CC);
1696
1697 if (SUCCESS == rv) {
1698 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1699
1700 if (!obj->intern) {
1701 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1702 } else {
1703 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1704
1705 RETVAL_LONG(php_pqconn_add_eventhandler(obj, type_str, type_len, &cb TSRMLS_CC));
1706 }
1707 }
1708 }
1709
1710 static int apply_set_converter(void *p TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
1711 {
1712 zval *tmp, **zoid = p, **zcnv = va_arg(argv, zval **);
1713 HashTable *converters = va_arg(argv, HashTable *);
1714
1715 tmp = *zoid;
1716 Z_ADDREF_P(tmp);
1717 convert_to_long_ex(&tmp);
1718 Z_ADDREF_PP(zcnv);
1719 zend_hash_index_update(converters, Z_LVAL_P(tmp), zcnv, sizeof(zval *), NULL);
1720 zval_ptr_dtor(&tmp);
1721
1722 return ZEND_HASH_APPLY_KEEP;
1723 }
1724
1725 ZEND_BEGIN_ARG_INFO_EX(ai_pqconn_set_converter, 0, 0, 1)
1726 ZEND_ARG_OBJ_INFO(0, converter, pq\\ConverterInterface, 0)
1727 ZEND_END_ARG_INFO();
1728 static PHP_METHOD(pqconn, setConverter) {
1729 STATUS rv;
1730 zend_error_handling zeh;
1731 zval *zcnv;
1732
1733 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
1734 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zcnv, php_pqconv_class_entry);
1735 zend_restore_error_handling(&zeh TSRMLS_CC);
1736
1737 if (SUCCESS == rv) {
1738 php_pqconn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1739
1740 if (!obj->intern) {
1741 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
1742 } else {
1743 zval *tmp, *zoids = NULL;
1744
1745 zend_call_method_with_0_params(&zcnv, NULL, NULL, "converttypes", &zoids);
1746 tmp = zoids;
1747 Z_ADDREF_P(tmp);
1748 convert_to_array_ex(&tmp);
1749 zend_hash_apply_with_arguments(Z_ARRVAL_P(tmp) TSRMLS_CC, apply_set_converter, 2, &zcnv, &obj->intern->converters);
1750 zval_ptr_dtor(&tmp);
1751 zval_ptr_dtor(&zoids);
1752 }
1753 }
1754 }
1755
1756 static zend_function_entry php_pqconn_methods[] = {
1757 PHP_ME(pqconn, __construct, ai_pqconn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
1758 PHP_ME(pqconn, reset, ai_pqconn_reset, ZEND_ACC_PUBLIC)
1759 PHP_ME(pqconn, resetAsync, ai_pqconn_reset_async, ZEND_ACC_PUBLIC)
1760 PHP_ME(pqconn, poll, ai_pqconn_poll, ZEND_ACC_PUBLIC)
1761 PHP_ME(pqconn, exec, ai_pqconn_exec, ZEND_ACC_PUBLIC)
1762 PHP_ME(pqconn, execAsync, ai_pqconn_exec_async, ZEND_ACC_PUBLIC)
1763 PHP_ME(pqconn, execParams, ai_pqconn_exec_params, ZEND_ACC_PUBLIC)
1764 PHP_ME(pqconn, execParamsAsync, ai_pqconn_exec_params_async, ZEND_ACC_PUBLIC)
1765 PHP_ME(pqconn, prepare, ai_pqconn_prepare, ZEND_ACC_PUBLIC)
1766 PHP_ME(pqconn, prepareAsync, ai_pqconn_prepare_async, ZEND_ACC_PUBLIC)
1767 PHP_ME(pqconn, declare, ai_pqconn_declare, ZEND_ACC_PUBLIC)
1768 PHP_ME(pqconn, declareAsync, ai_pqconn_declare_async, ZEND_ACC_PUBLIC)
1769 PHP_ME(pqconn, unlisten, ai_pqconn_unlisten, ZEND_ACC_PUBLIC)
1770 PHP_ME(pqconn, unlistenAsync, ai_pqconn_unlisten_async, ZEND_ACC_PUBLIC)
1771 PHP_ME(pqconn, listen, ai_pqconn_listen, ZEND_ACC_PUBLIC)
1772 PHP_ME(pqconn, listenAsync, ai_pqconn_listen_async, ZEND_ACC_PUBLIC)
1773 PHP_ME(pqconn, notify, ai_pqconn_notify, ZEND_ACC_PUBLIC)
1774 PHP_ME(pqconn, notifyAsync, ai_pqconn_notify_async, ZEND_ACC_PUBLIC)
1775 PHP_ME(pqconn, getResult, ai_pqconn_get_result, ZEND_ACC_PUBLIC)
1776 PHP_ME(pqconn, quote, ai_pqconn_quote, ZEND_ACC_PUBLIC)
1777 PHP_ME(pqconn, quoteName, ai_pqconn_quote_name, ZEND_ACC_PUBLIC)
1778 PHP_ME(pqconn, escapeBytea, ai_pqconn_escape_bytea, ZEND_ACC_PUBLIC)
1779 PHP_ME(pqconn, unescapeBytea, ai_pqconn_unescape_bytea, ZEND_ACC_PUBLIC)
1780 PHP_ME(pqconn, startTransaction, ai_pqconn_start_transaction, ZEND_ACC_PUBLIC)
1781 PHP_ME(pqconn, startTransactionAsync, ai_pqconn_start_transaction_async, ZEND_ACC_PUBLIC)
1782 PHP_ME(pqconn, trace, ai_pqconn_trace, ZEND_ACC_PUBLIC)
1783 PHP_ME(pqconn, off, ai_pqconn_off, ZEND_ACC_PUBLIC)
1784 PHP_ME(pqconn, on, ai_pqconn_on, ZEND_ACC_PUBLIC)
1785 PHP_ME(pqconn, setConverter, ai_pqconn_set_converter, ZEND_ACC_PUBLIC)
1786 {0}
1787 };
1788
1789 PHP_MSHUTDOWN_FUNCTION(pqconn)
1790 {
1791 zend_hash_destroy(&php_pqconn_object_prophandlers);
1792 return SUCCESS;
1793 }
1794
1795 PHP_MINIT_FUNCTION(pqconn)
1796 {
1797 zend_class_entry ce = {0};
1798 php_pq_object_prophandler_t ph = {0};
1799
1800 INIT_NS_CLASS_ENTRY(ce, "pq", "Connection", php_pqconn_methods);
1801 php_pqconn_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
1802 php_pqconn_class_entry->create_object = php_pqconn_create_object;
1803
1804 memcpy(&php_pqconn_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1805 php_pqconn_object_handlers.read_property = php_pq_object_read_prop;
1806 php_pqconn_object_handlers.write_property = php_pq_object_write_prop;
1807 php_pqconn_object_handlers.clone_obj = NULL;
1808 php_pqconn_object_handlers.get_property_ptr_ptr = NULL;
1809 php_pqconn_object_handlers.get_gc = NULL;
1810 php_pqconn_object_handlers.get_properties = php_pq_object_properties;
1811 php_pqconn_object_handlers.get_debug_info = php_pq_object_debug_info;
1812
1813 zend_hash_init(&php_pqconn_object_prophandlers, 14, NULL, NULL, 1);
1814
1815 zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("status"), CONNECTION_BAD, ZEND_ACC_PUBLIC TSRMLS_CC);
1816 ph.read = php_pqconn_object_read_status;
1817 zend_hash_add(&php_pqconn_object_prophandlers, "status", sizeof("status"), (void *) &ph, sizeof(ph), NULL);
1818
1819 zend_declare_property_long(php_pqconn_class_entry, ZEND_STRL("transactionStatus"), PQTRANS_UNKNOWN, ZEND_ACC_PUBLIC TSRMLS_CC);
1820 ph.read = php_pqconn_object_read_transaction_status;
1821 zend_hash_add(&php_pqconn_object_prophandlers, "transactionStatus", sizeof("transactionStatus"), (void *) &ph, sizeof(ph), NULL);
1822
1823 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("socket"), ZEND_ACC_PUBLIC TSRMLS_CC);
1824 ph.read = NULL; /* forward to std prophandler */
1825 zend_hash_add(&php_pqconn_object_prophandlers, "socket", sizeof("socket"), (void *) &ph, sizeof(ph), NULL);
1826
1827 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("errorMessage"), ZEND_ACC_PUBLIC TSRMLS_CC);
1828 ph.read = php_pqconn_object_read_error_message;
1829 zend_hash_add(&php_pqconn_object_prophandlers, "errorMessage", sizeof("errorMessage"), (void *) &ph, sizeof(ph), NULL);
1830
1831 zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("busy"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1832 ph.read = php_pqconn_object_read_busy;
1833 zend_hash_add(&php_pqconn_object_prophandlers, "busy", sizeof("busy"), (void *) &ph, sizeof(ph), NULL);
1834
1835 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("encoding"), ZEND_ACC_PUBLIC TSRMLS_CC);
1836 ph.read = php_pqconn_object_read_encoding;
1837 ph.write = php_pqconn_object_write_encoding;
1838 zend_hash_add(&php_pqconn_object_prophandlers, "encoding", sizeof("encoding"), (void *) &ph, sizeof(ph), NULL);
1839 ph.write = NULL;
1840
1841 zend_declare_property_bool(php_pqconn_class_entry, ZEND_STRL("unbuffered"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
1842 ph.read = php_pqconn_object_read_unbuffered;
1843 ph.write = php_pqconn_object_write_unbuffered;
1844 zend_hash_add(&php_pqconn_object_prophandlers, "unbuffered", sizeof("unbuffered"), (void *) &ph, sizeof(ph), NULL);
1845 ph.write = NULL;
1846
1847 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("db"), ZEND_ACC_PUBLIC TSRMLS_CC);
1848 ph.read = php_pqconn_object_read_db;
1849 zend_hash_add(&php_pqconn_object_prophandlers, "db", sizeof("db"), (void *) &ph, sizeof(ph), NULL);
1850
1851 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("user"), ZEND_ACC_PUBLIC TSRMLS_CC);
1852 ph.read = php_pqconn_object_read_user;
1853 zend_hash_add(&php_pqconn_object_prophandlers, "user", sizeof("user"), (void *) &ph, sizeof(ph), NULL);
1854
1855 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("pass"), ZEND_ACC_PUBLIC TSRMLS_CC);
1856 ph.read = php_pqconn_object_read_pass;
1857 zend_hash_add(&php_pqconn_object_prophandlers, "pass", sizeof("pass"), (void *) &ph, sizeof(ph), NULL);
1858
1859 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("host"), ZEND_ACC_PUBLIC TSRMLS_CC);
1860 ph.read = php_pqconn_object_read_host;
1861 zend_hash_add(&php_pqconn_object_prophandlers, "host", sizeof("host"), (void *) &ph, sizeof(ph), NULL);
1862
1863 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("port"), ZEND_ACC_PUBLIC TSRMLS_CC);
1864 ph.read = php_pqconn_object_read_port;
1865 zend_hash_add(&php_pqconn_object_prophandlers, "port", sizeof("port"), (void *) &ph, sizeof(ph), NULL);
1866
1867 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("options"), ZEND_ACC_PUBLIC TSRMLS_CC);
1868 ph.read = php_pqconn_object_read_options;
1869 zend_hash_add(&php_pqconn_object_prophandlers, "options", sizeof("options"), (void *) &ph, sizeof(ph), NULL);
1870
1871 zend_declare_property_null(php_pqconn_class_entry, ZEND_STRL("eventHandlers"), ZEND_ACC_PUBLIC TSRMLS_CC);
1872 ph.read = php_pqconn_object_read_event_handlers;
1873 zend_hash_add(&php_pqconn_object_prophandlers, "eventHandlers", sizeof("eventHandlers"), (void *) &ph, sizeof(ph), NULL);
1874
1875 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("OK"), CONNECTION_OK TSRMLS_CC);
1876 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("BAD"), CONNECTION_BAD TSRMLS_CC);
1877 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("STARTED"), CONNECTION_STARTED TSRMLS_CC);
1878 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("MADE"), CONNECTION_MADE TSRMLS_CC);
1879 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("AWAITING_RESPONSE"), CONNECTION_AWAITING_RESPONSE TSRMLS_CC);
1880 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("AUTH_OK"), CONNECTION_AUTH_OK TSRMLS_CC);
1881 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("SSL_STARTUP"), CONNECTION_SSL_STARTUP TSRMLS_CC);
1882 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("SETENV"), CONNECTION_SETENV TSRMLS_CC);
1883
1884 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_IDLE"), PQTRANS_IDLE TSRMLS_CC);
1885 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_ACTIVE"), PQTRANS_ACTIVE TSRMLS_CC);
1886 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_INTRANS"), PQTRANS_INTRANS TSRMLS_CC);
1887 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_INERROR"), PQTRANS_INERROR TSRMLS_CC);
1888 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("TRANS_UNKNOWN"), PQTRANS_UNKNOWN TSRMLS_CC);
1889
1890 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_FAILED"), PGRES_POLLING_FAILED TSRMLS_CC);
1891 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_READING"), PGRES_POLLING_READING TSRMLS_CC);
1892 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_WRITING"), PGRES_POLLING_WRITING TSRMLS_CC);
1893 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("POLLING_OK"), PGRES_POLLING_OK TSRMLS_CC);
1894
1895 zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_NOTICE"), ZEND_STRL("notice") TSRMLS_CC);
1896 zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_RESULT"), ZEND_STRL("result") TSRMLS_CC);
1897 zend_declare_class_constant_stringl(php_pqconn_class_entry, ZEND_STRL("EVENT_RESET"), ZEND_STRL("reset") TSRMLS_CC);
1898
1899 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("ASYNC"), 0x1 TSRMLS_CC);
1900 zend_declare_class_constant_long(php_pqconn_class_entry, ZEND_STRL("PERSISTENT"), 0x2 TSRMLS_CC);
1901
1902 return SUCCESS;
1903 }
1904
1905 /*
1906 * Local variables:
1907 * tab-width: 4
1908 * c-basic-offset: 4
1909 * End:
1910 * vim600: noet sw=4 ts=4 fdm=marker
1911 * vim<600: noet sw=4 ts=4
1912 */