1 | #include <sys/errno.h> |
1 | #include <errno.h> |
2 | #include <unistd.h> |
2 | #include <unistd.h> |
3 | #include <fcntl.h> |
3 | #include <fcntl.h> |
|
|
4 | |
|
|
5 | /* mariadb/mysql uses all these reserved macro names, and probably more :( */ |
|
|
6 | #undef read |
|
|
7 | #undef write |
|
|
8 | #undef close |
4 | |
9 | |
5 | #include <mysql.h> |
10 | #include <mysql.h> |
6 | |
11 | |
7 | #include "EXTERN.h" |
12 | #include "EXTERN.h" |
8 | #include "perl.h" |
13 | #include "perl.h" |
9 | #include "XSUB.h" |
14 | #include "XSUB.h" |
10 | |
15 | |
|
|
16 | #if HAVE_EV |
|
|
17 | # include "EVAPI.h" |
|
|
18 | # include "CoroAPI.h" |
|
|
19 | #endif |
|
|
20 | |
|
|
21 | #define IN_DESTRUCT PL_dirty |
|
|
22 | |
11 | typedef U16 uint16; |
23 | typedef U16 uint16; |
12 | |
24 | |
13 | /* cached function gv's */ |
25 | /* cached function gv's */ |
14 | static CV *readable, *writable; |
26 | static CV *readable, *writable; |
|
|
27 | static int use_ev; |
15 | |
28 | |
|
|
29 | #if MARIADB_VERSION_ID >= 100300 |
|
|
30 | |
|
|
31 | typedef unsigned char uchar; /* bug? */ |
|
|
32 | #include <ma_pvio.h> |
|
|
33 | |
|
|
34 | #define PVIO 1 |
|
|
35 | #define VIOPTR MARIADB_PVIO * |
|
|
36 | #define VIOM(vio) (vio)->methods |
|
|
37 | #define vioblocking blocking |
|
|
38 | #define vioclose close |
|
|
39 | #define VIODATA(vio) (vio)->data |
|
|
40 | /* ma_pvio_get_socket would be it, but it's only declared, not defined */ |
|
|
41 | #define VIOSD(vio) mysql_get_socket ((vio)->mysql) |
|
|
42 | #define VIO_READ_BUFFER_SIZE PVIO_READ_AHEAD_CACHE_SIZE |
|
|
43 | #define my_to_vio(sock) (sock)->net.pvio |
|
|
44 | |
|
|
45 | #define OURDATAPTR ((ourdata *)vio->methods) |
|
|
46 | |
|
|
47 | typedef uchar *xgptr; |
|
|
48 | typedef const uchar *cxgptr; |
|
|
49 | typedef size_t xsize_t; |
|
|
50 | typedef ssize_t xssize_t; |
|
|
51 | typedef my_bool xmy_bool; |
|
|
52 | |
|
|
53 | #else |
|
|
54 | |
16 | #include "violite.h" |
55 | #include "violite.h" |
17 | |
56 | |
18 | #define DESC_OFFSET 22 |
57 | #define PVIO 0 |
|
|
58 | #define VIOPTR Vio * |
|
|
59 | #define VIOM(vio) vio |
|
|
60 | #define VIODATA(vio) (vio)->desc |
|
|
61 | #define VIOSD(vio) (vio)->sd |
|
|
62 | #define my_to_vio(sock) (sock)->net.vio |
|
|
63 | |
|
|
64 | typedef int xmy_bool; |
|
|
65 | |
|
|
66 | #endif |
19 | |
67 | |
20 | #define CoMy_MAGIC 0x436f4d79 |
68 | #define CoMy_MAGIC 0x436f4d79 |
21 | |
69 | |
22 | typedef struct { |
70 | typedef struct { |
|
|
71 | #if PVIO |
|
|
72 | /* must be first member */ |
|
|
73 | struct st_ma_pvio_methods methods; |
|
|
74 | #else |
|
|
75 | #if DESC_IS_PTR |
|
|
76 | char desc[30]; |
|
|
77 | const char *old_desc; |
|
|
78 | #endif |
|
|
79 | #endif |
23 | int magic; |
80 | int magic; |
24 | SV *corosocket; |
81 | SV *corohandle_sv, *corohandle; |
25 | int bufofs, bufcnt; |
82 | int bufofs, bufcnt; |
|
|
83 | #if HAVE_EV |
|
|
84 | ev_io rw, ww; |
|
|
85 | #endif |
26 | char buf[VIO_READ_BUFFER_SIZE]; |
86 | char buf[VIO_READ_BUFFER_SIZE]; |
|
|
87 | #if PVIO |
|
|
88 | struct st_ma_pvio_methods *oldmethods; |
|
|
89 | #else |
|
|
90 | xssize_t (*old_read)(VIOPTR, uchar *, size_t); |
|
|
91 | xssize_t (*old_write)(VIOPTR, const uchar *, size_t); |
|
|
92 | xmy_bool (*old_close)(VIOPTR); |
|
|
93 | #endif |
27 | } ourdata; |
94 | } ourdata; |
28 | |
95 | |
|
|
96 | #ifndef OURDATAPTR |
|
|
97 | #if DESC_IS_PTR |
|
|
98 | # define OURDATAPTR (*(ourdata **)&((vio)->desc)) |
|
|
99 | #else |
|
|
100 | # define DESC_OFFSET 22 |
29 | #define OURDATAPTR (*((ourdata **)((vio)->desc + DESC_OFFSET))) |
101 | # define OURDATAPTR (*((ourdata **)((vio)->desc + DESC_OFFSET))) |
|
|
102 | #endif |
|
|
103 | #endif |
30 | |
104 | |
31 | static int |
105 | static xssize_t |
32 | our_read (Vio *vio, gptr p, int len) |
106 | our_read (VIOPTR vio, xgptr p, xsize_t len) |
33 | { |
107 | { |
34 | ourdata *our = OURDATAPTR; |
108 | ourdata *our = OURDATAPTR; |
35 | |
109 | |
36 | if (!our->bufcnt) |
110 | if (!our->bufcnt) |
37 | { |
111 | { |
38 | int rd; |
112 | int rd; |
39 | my_bool dummy; |
113 | my_bool dummy; |
40 | |
114 | |
41 | vio->vioblocking (vio, 0, &dummy); |
115 | VIOM (vio)->vioblocking (vio, 0, &dummy); |
42 | |
116 | |
43 | for (;;) |
117 | for (;;) |
44 | { |
118 | { |
45 | rd = recv (vio->sd, our->buf, sizeof (our->buf), 0); |
119 | rd = recv (VIOSD (vio), our->buf, sizeof (our->buf), 0); |
46 | |
120 | |
47 | if (rd >= 0 || errno != EAGAIN) |
121 | if (rd >= 0 || errno != EAGAIN) |
48 | break; |
122 | break; |
49 | |
123 | |
|
|
124 | #if HAVE_EV |
|
|
125 | if (use_ev) |
50 | { |
126 | { |
|
|
127 | our->rw.data = (void *)sv_2mortal (SvREFCNT_inc (CORO_CURRENT)); |
|
|
128 | ev_io_start (EV_DEFAULT_UC, &(our->rw)); |
|
|
129 | CORO_SCHEDULE; |
|
|
130 | ev_io_stop (EV_DEFAULT_UC, &(our->rw)); /* avoids races */ |
|
|
131 | } |
|
|
132 | else |
|
|
133 | #endif |
|
|
134 | { |
51 | dSP; |
135 | dSP; |
52 | PUSHMARK (SP); |
136 | PUSHMARK (SP); |
53 | XPUSHs (our->corosocket); |
137 | XPUSHs (our->corohandle); |
54 | PUTBACK; |
138 | PUTBACK; |
55 | call_sv ((SV *)readable, G_VOID | G_DISCARD); |
139 | call_sv ((SV *)readable, G_VOID | G_DISCARD); |
56 | } |
140 | } |
57 | } |
141 | } |
58 | |
142 | |
59 | if (rd <= 0) |
143 | if (rd <= 0) |
60 | return rd; |
144 | return rd; |
61 | |
145 | |
… | |
… | |
71 | our->bufcnt -= len; |
155 | our->bufcnt -= len; |
72 | |
156 | |
73 | return len; |
157 | return len; |
74 | } |
158 | } |
75 | |
159 | |
76 | static int |
160 | static xssize_t |
77 | our_write (Vio *vio, const gptr p, int len) |
161 | our_write (VIOPTR vio, cxgptr p, xsize_t len) |
78 | { |
162 | { |
79 | char *ptr = (char *)p; |
163 | char *ptr = (char *)p; |
80 | my_bool dummy; |
164 | my_bool dummy; |
81 | |
165 | |
82 | vio->vioblocking (vio, 0, &dummy); |
166 | VIOM (vio)->vioblocking (vio, 0, &dummy); |
83 | |
167 | |
84 | while (len > 0) |
168 | while (len > 0) |
85 | { |
169 | { |
86 | int wr = send (vio->sd, ptr, len, 0); |
170 | int wr = send (VIOSD (vio), ptr, len, 0); |
87 | |
171 | |
88 | if (wr > 0) |
172 | if (wr > 0) |
89 | { |
173 | { |
90 | ptr += wr; |
174 | ptr += wr; |
91 | len -= wr; |
175 | len -= wr; |
92 | } |
176 | } |
93 | else if (errno == EAGAIN) |
177 | else if (errno == EAGAIN) |
94 | { |
178 | { |
|
|
179 | ourdata *our = OURDATAPTR; |
|
|
180 | |
|
|
181 | #if HAVE_EV |
|
|
182 | if (use_ev) |
|
|
183 | { |
|
|
184 | our->ww.data = (void *)sv_2mortal (SvREFCNT_inc (CORO_CURRENT)); |
|
|
185 | ev_io_start (EV_DEFAULT_UC, &(our->ww)); |
|
|
186 | CORO_SCHEDULE; |
|
|
187 | ev_io_stop (EV_DEFAULT_UC, &(our->ww)); /* avoids races */ |
|
|
188 | } |
|
|
189 | else |
|
|
190 | #endif |
|
|
191 | { |
95 | dSP; |
192 | dSP; |
96 | PUSHMARK (SP); |
193 | PUSHMARK (SP); |
97 | XPUSHs (OURDATAPTR->corosocket); |
194 | XPUSHs (our->corohandle); |
98 | PUTBACK; |
195 | PUTBACK; |
99 | call_sv ((SV *)writable, G_VOID | G_DISCARD); |
196 | call_sv ((SV *)writable, G_VOID | G_DISCARD); |
|
|
197 | } |
100 | } |
198 | } |
101 | else if (ptr == (char *)p) |
199 | else if (ptr == (char *)p) |
102 | return -1; |
200 | return -1; |
103 | else |
201 | else |
104 | break; |
202 | break; |
105 | } |
203 | } |
106 | |
204 | |
107 | return ptr - (char *)p; |
205 | return ptr - (char *)p; |
108 | } |
206 | } |
109 | |
207 | |
|
|
208 | static xmy_bool |
|
|
209 | our_close (VIOPTR vio) |
|
|
210 | { |
|
|
211 | ourdata *our = OURDATAPTR; |
|
|
212 | |
|
|
213 | if (VIOM (vio)->read != our_read) |
|
|
214 | croak ("vio.read has unexpected content during unpatch - wtf?"); |
|
|
215 | |
|
|
216 | if (VIOM (vio)->write != our_write) |
|
|
217 | croak ("vio.write has unexpected content during unpatch - wtf?"); |
|
|
218 | |
|
|
219 | if (VIOM (vio)->vioclose != our_close) |
|
|
220 | croak ("vio.vioclose has unexpected content during unpatch - wtf?"); |
|
|
221 | |
|
|
222 | #if HAVE_EV |
|
|
223 | if (use_ev) |
|
|
224 | { |
|
|
225 | ev_io_stop (EV_DEFAULT_UC, &(our->rw)); |
|
|
226 | ev_io_stop (EV_DEFAULT_UC, &(our->ww)); |
|
|
227 | } |
|
|
228 | #endif |
|
|
229 | |
|
|
230 | SvREFCNT_dec (our->corohandle); |
|
|
231 | SvREFCNT_dec (our->corohandle_sv); |
|
|
232 | |
|
|
233 | #if DESC_IS_PTR |
|
|
234 | vio->desc = our->old_desc; |
|
|
235 | #endif |
|
|
236 | |
|
|
237 | #if PVIO |
|
|
238 | vio->methods = our->oldmethods; |
|
|
239 | #else |
|
|
240 | VIOM (vio)->vioclose = our->old_close; |
|
|
241 | VIOM (vio)->write = our->old_write; |
|
|
242 | VIOM (vio)->read = our->old_read; |
|
|
243 | #endif |
|
|
244 | |
|
|
245 | Safefree (our); |
|
|
246 | |
|
|
247 | VIOM (vio)->vioclose (vio); |
|
|
248 | } |
|
|
249 | |
|
|
250 | #if HAVE_EV |
|
|
251 | static void |
|
|
252 | iocb (EV_P_ ev_io *w, int revents) |
|
|
253 | { |
|
|
254 | ev_io_stop (EV_A, w); |
|
|
255 | CORO_READY ((SV *)w->data); |
|
|
256 | } |
|
|
257 | #endif |
|
|
258 | |
110 | MODULE = Coro::Mysql PACKAGE = Coro::Mysql |
259 | MODULE = Coro::Mysql PACKAGE = Coro::Mysql |
111 | |
260 | |
112 | BOOT: |
261 | BOOT: |
113 | { |
262 | { |
114 | readable = get_cv ("Coro::Mysql::readable", 0); |
263 | readable = get_cv ("Coro::Mysql::readable", 0); |
… | |
… | |
116 | } |
265 | } |
117 | |
266 | |
118 | PROTOTYPES: ENABLE |
267 | PROTOTYPES: ENABLE |
119 | |
268 | |
120 | void |
269 | void |
121 | _patch (IV sock, int fd, SV *corosocket) |
270 | _use_ev () |
|
|
271 | PPCODE: |
|
|
272 | { |
|
|
273 | static int onceonly; |
|
|
274 | |
|
|
275 | if (!onceonly) |
|
|
276 | { |
|
|
277 | onceonly = 1; |
|
|
278 | #if HAVE_EV |
|
|
279 | I_EV_API ("Coro::Mysql"); |
|
|
280 | I_CORO_API ("Coro::Mysql"); |
|
|
281 | use_ev = 1; |
|
|
282 | #endif |
|
|
283 | } |
|
|
284 | |
|
|
285 | XPUSHs (use_ev ? &PL_sv_yes : &PL_sv_no); |
|
|
286 | } |
|
|
287 | |
|
|
288 | void |
|
|
289 | _patch (IV sock, int fd, unsigned long client_version, SV *corohandle_sv, SV *corohandle) |
122 | CODE: |
290 | CODE: |
123 | { |
291 | { |
124 | MYSQL *my = (MYSQL *)sock; |
292 | MYSQL *my = (MYSQL *)sock; |
125 | Vio *vio = my->net.vio; |
293 | VIOPTR vio = my_to_vio (my); |
126 | ourdata *our; |
294 | ourdata *our; |
|
|
295 | |
|
|
296 | /* matching versions are required but not sufficient */ |
|
|
297 | if (client_version != mysql_get_client_version ()) |
|
|
298 | croak ("DBD::mysql linked against different libmysqlclient library than Coro::Mysql (%lu vs. %lu).", |
|
|
299 | client_version, mysql_get_client_version ()); |
127 | |
300 | |
128 | if (fd != my->net.fd) |
301 | if (fd != my->net.fd) |
129 | croak ("DBD::mysql fd and libmysql disagree - library mismatch, unsupported transport or API changes?"); |
302 | croak ("DBD::mysql fd and libmysql disagree - library mismatch, unsupported transport or API changes?"); |
130 | |
303 | |
131 | if (fd != vio->sd) |
304 | if (fd != VIOSD (vio)) |
132 | croak ("DBD::mysql fd and vio-sd disagree - library mismatch, unsupported transport or API changes?"); |
305 | croak ("DBD::mysql fd and vio-sd disagree - library mismatch, unsupported transport or API changes?"); |
|
|
306 | #if MYSQL_VERSION_ID < 100010 && !defined(MARIADB_BASE_VERSION) |
|
|
307 | if (VIOM (vio)->vioclose != vio_close) |
|
|
308 | croak ("vio.vioclose has unexpected content - library mismatch, unsupported transport or API changes?"); |
133 | |
309 | |
134 | if (vio->write != vio_write) |
310 | if (VIOM (vio)->write != vio_write) |
135 | croak ("vio.write has unexpected content - library mismatch, unsupported transport or API changes?"); |
311 | croak ("vio.write has unexpected content - library mismatch, unsupported transport or API changes?"); |
136 | |
312 | |
137 | if (vio->read != vio_read && vio->read != vio_read_buff) |
313 | if (VIOM (vio)->read != vio_read |
|
|
314 | && VIOM (vio)->read != vio_read_buff) |
138 | croak ("vio.read has unexpected content - library mismatch, unsupported transport or API changes?"); |
315 | croak ("vio.read has unexpected content - library mismatch, unsupported transport or API changes?"); |
|
|
316 | #endif |
|
|
317 | #if PVIO |
|
|
318 | if (vio->type != PVIO_TYPE_UNIXSOCKET && vio->type != PVIO_TYPE_SOCKET) |
|
|
319 | croak ("connection type mismatch: Coro::Mysql only supports 'unixsocket' and 'socket' types at this time"); |
|
|
320 | #endif |
139 | |
321 | |
140 | Newz (0, our, 1, ourdata); |
322 | Newz (0, our, 1, ourdata); |
141 | our->magic = CoMy_MAGIC; |
323 | our->magic = CoMy_MAGIC; |
|
|
324 | our->corohandle_sv = newSVsv (corohandle_sv); |
142 | our->corosocket = newSVsv (corosocket); |
325 | our->corohandle = newSVsv (corohandle); |
143 | |
326 | #if HAVE_EV |
|
|
327 | if (use_ev) |
|
|
328 | { |
|
|
329 | ev_io_init (&(our->rw), iocb, VIOSD (vio), EV_READ); |
|
|
330 | ev_io_init (&(our->ww), iocb, VIOSD (vio), EV_WRITE); |
|
|
331 | } |
|
|
332 | #endif |
|
|
333 | #if PVIO |
|
|
334 | /* with pvio, we replace methods by our own struct, |
|
|
335 | * both becauase the original might be read-only, |
|
|
336 | * and because we have no private data member, so the |
|
|
337 | * methods pointer includes our data as well |
|
|
338 | */ |
|
|
339 | our->methods = *vio->methods; |
|
|
340 | our->oldmethods = vio->methods; |
|
|
341 | vio->methods = &our->methods; |
|
|
342 | #else |
|
|
343 | OURDATAPTR = our; |
|
|
344 | #if DESC_IS_PTR |
|
|
345 | our->old_desc = vio->desc; |
|
|
346 | strncpy (our->desc, vio->desc, sizeof (our->desc)); |
|
|
347 | our->desc [sizeof (our->desc) - 1] = 0; |
|
|
348 | #else |
144 | vio->desc [DESC_OFFSET - 1] = 0; |
349 | vio->desc [DESC_OFFSET - 1] = 0; |
145 | OURDATAPTR = our; |
350 | #endif |
|
|
351 | our->old_close = VIOM (vio)->vioclose; |
|
|
352 | our->old_write = VIOM (vio)->write; |
|
|
353 | our->old_read = VIOM (vio)->read; |
|
|
354 | #endif |
146 | |
355 | |
|
|
356 | /* with pvio, this patches our own struct */ |
|
|
357 | VIOM (vio)->vioclose = our_close; |
147 | vio->write = our_write; |
358 | VIOM (vio)->write = our_write; |
148 | vio->read = our_read; |
359 | VIOM (vio)->read = our_read; |
149 | } |
360 | } |
150 | |
361 | |
151 | void |
362 | int |
152 | _unpatch (IV sock) |
363 | _is_patched (IV sock) |
153 | CODE: |
364 | CODE: |
154 | { |
365 | { |
155 | MYSQL *my = (MYSQL *)sock; |
366 | MYSQL *my = (MYSQL *)sock; |
156 | Vio *vio = my->net.vio; |
367 | VIOPTR vio = my_to_vio (my); |
157 | my_bool dummy; |
368 | RETVAL = VIOM (vio)->write == our_write; |
158 | |
|
|
159 | if (vio->read != our_read) |
|
|
160 | croak ("vio.read has unexpected content during unpatch - wtf?"); |
|
|
161 | |
|
|
162 | SvREFCNT_dec (OURDATAPTR->corosocket); |
|
|
163 | |
|
|
164 | Safefree (OURDATAPTR); |
|
|
165 | |
|
|
166 | vio->read = vio_read; |
|
|
167 | vio->write = vio_write; |
|
|
168 | } |
369 | } |
|
|
370 | OUTPUT: RETVAL |
169 | |
371 | |
|
|
372 | int |
|
|
373 | have_ev () |
|
|
374 | CODE: |
|
|
375 | RETVAL = 0; |
|
|
376 | #if HAVE_EV |
|
|
377 | RETVAL = 1; |
|
|
378 | #endif |
|
|
379 | OUTPUT: RETVAL |
170 | |
380 | |
171 | |
|
|