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