Merge pull request #33 from m6w6/fix-19
[m6w6/ext-pq] / src / php_pqconn_event.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 #include <Zend/zend_smart_str.h>
20
21 #include <libpq-events.h>
22
23 #include "php_pq.h"
24 #include "php_pq_misc.h"
25 #include "php_pq_object.h"
26 #include "php_pqconn_event.h"
27 #include "php_pqstm.h"
28 #include "php_pqres.h"
29
30 static int apply_event(zval *p, void *a)
31 {
32 php_pq_callback_t *cb = Z_PTR_P(p);
33 zval *args = a;
34 zval rv;
35
36 ZVAL_NULL(&rv);
37 zend_fcall_info_args(&cb->fci, args);
38 zend_fcall_info_call(&cb->fci, &cb->fcc, &rv, NULL);
39 zend_fcall_info_args_clear(&cb->fci, 0);
40 zval_ptr_dtor(&rv);
41
42 return ZEND_HASH_APPLY_KEEP;
43 }
44
45
46 static inline PGresult *relisten(PGconn *conn, const char *channel_str, size_t channel_len)
47 {
48 char *quoted_channel = PQescapeIdentifier(conn, channel_str, channel_len);
49 PGresult *res = NULL;
50
51 if (quoted_channel) {
52 smart_str cmd = {0};
53
54 smart_str_appends(&cmd, "LISTEN ");
55 smart_str_appends(&cmd, quoted_channel);
56 smart_str_0(&cmd);
57
58 res = PQexec(conn, smart_str_v(&cmd));
59
60 smart_str_free(&cmd);
61 PQfreemem(quoted_channel);
62 }
63
64 return res;
65 }
66
67 static int apply_relisten(zval *p, int argc, va_list argv, zend_hash_key *key)
68 {
69 php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *);
70 PGresult *res = relisten(obj->intern->conn, key->key->val, key->key->len);
71
72 if (res) {
73 php_pqres_clear(res);
74 }
75
76 return ZEND_HASH_APPLY_KEEP;
77 }
78
79 static int apply_reprepare(zval *p, int argc, va_list argv, zend_hash_key *key)
80 {
81 php_pqconn_object_t *obj = va_arg(argv, php_pqconn_object_t *);
82 php_pqstm_t *stm = Z_PTR_P(p);
83
84 php_pqconn_prepare(NULL, obj, stm->name, stm->query, stm->params);
85
86 return ZEND_HASH_APPLY_KEEP;
87 }
88
89 static void php_pqconn_event_connreset(PGEventConnReset *event)
90 {
91 php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event);
92
93 if (data) {
94 zval *zevhs;
95
96 /* restore listeners */
97 zend_hash_apply_with_arguments(&data->obj->intern->listeners, apply_relisten, 1, data->obj);
98
99 /* restore statements */
100 zend_hash_apply_with_arguments(&data->obj->intern->statements, apply_reprepare, 1, data->obj);
101
102 /* eventhandler */
103 if ((zevhs = zend_hash_str_find(&data->obj->intern->eventhandlers, ZEND_STRL("reset")))) {
104 zval args, connection;
105
106 array_init(&args);
107 php_pq_object_to_zval(data->obj, &connection);
108 add_next_index_zval(&args, &connection);
109 zend_hash_apply_with_argument(Z_ARRVAL_P(zevhs), apply_event, &args);
110 zval_ptr_dtor(&args);
111 }
112 }
113 }
114
115 static void php_pqconn_event_resultcreate(PGEventResultCreate *event)
116 {
117 php_pqconn_event_data_t *data = PQinstanceData(event->conn, php_pqconn_event);
118
119 if (data) {
120 php_pqres_object_t *obj = php_pqres_init_instance_data(event->result, data->obj);
121 zval *zevhs;
122
123 /* event listener */
124 if ((zevhs = zend_hash_str_find(&data->obj->intern->eventhandlers, ZEND_STRL("result")))) {
125 zval args, connection, res;
126
127 array_init(&args);
128 php_pq_object_to_zval(data->obj, &connection);
129 add_next_index_zval(&args, &connection);
130 php_pq_object_to_zval(obj, &res);
131 add_next_index_zval(&args, &res);
132 zend_hash_apply_with_argument(Z_ARRVAL_P(zevhs), apply_event, &args);
133 zval_ptr_dtor(&args);
134 }
135
136 /* async callback */
137 if (php_pq_callback_is_enabled(&data->obj->intern->onevent)) {
138 zval res;
139
140 php_pq_object_to_zval(obj, &res);
141 zend_fcall_info_argn(&data->obj->intern->onevent.fci, 1, &res);
142 zend_fcall_info_call(&data->obj->intern->onevent.fci, &data->obj->intern->onevent.fcc, NULL, NULL);
143 zval_ptr_dtor(&res);
144 }
145
146 }
147 }
148
149 static void php_pqconn_event_resultdestroy(PGEventResultDestroy *event)
150 {
151 php_pqres_object_t *obj = PQresultInstanceData(event->result, php_pqconn_event);
152
153 if (obj) {
154 obj->intern->res = NULL;
155 assert(GC_REFCOUNT(&obj->zo));
156 php_pq_object_delref(obj);
157 }
158 }
159
160 int php_pqconn_event(PGEventId id, void *e, void *data)
161 {
162 switch (id) {
163 case PGEVT_CONNRESET:
164 php_pqconn_event_connreset(e);
165 break;
166 case PGEVT_RESULTCREATE:
167 php_pqconn_event_resultcreate(e);
168 break;
169 case PGEVT_RESULTDESTROY:
170 php_pqconn_event_resultdestroy(e);
171 break;
172 default:
173 break;
174 }
175
176 return 1;
177 }
178
179 php_pqconn_event_data_t *php_pqconn_event_data_init(php_pqconn_object_t *obj)
180 {
181 php_pqconn_event_data_t *data = emalloc(sizeof(*data));
182
183 data->obj = obj;
184 data->res = NULL;
185
186 return data;
187 }
188
189 void php_pqconn_notice_recv(void *p, const PGresult *res)
190 {
191 php_pqconn_event_data_t *data = p;
192
193 if (data) {
194 zval *zevhs;
195
196 if ((zevhs = zend_hash_str_find(&data->obj->intern->eventhandlers, ZEND_STRL("notice")))) {
197 zval args, connection;
198
199 array_init(&args);
200 php_pq_object_to_zval(data->obj, &connection);
201 add_next_index_zval(&args, &connection);
202 add_next_index_string(&args, PHP_PQresultErrorMessage(res));
203 zend_hash_apply_with_argument(Z_ARRVAL_P(zevhs), apply_event, &args);
204 zval_ptr_dtor(&args);
205 }
206 }
207 }
208
209 void php_pqconn_notice_ignore(void *p, const PGresult *res)
210 {
211 }
212
213 /*
214 * Local variables:
215 * tab-width: 4
216 * c-basic-offset: 4
217 * End:
218 * vim600: noet sw=4 ts=4 fdm=marker
219 * vim<600: noet sw=4 ts=4
220 */