~percona-dev/percona-server/percona-server-5.5-xtrabackup

« back to all changes in this revision

Viewing changes to HandlerSocket-Plugin-for-MySQL/perl-Net-HandlerSocket/HandlerSocket.xs

  • Committer: Ignacio Nin
  • Date: 2011-03-13 17:18:23 UTC
  • mfrom: (33.3.17 release-5.5.8-20)
  • Revision ID: ignacio.nin@percona.com-20110313171823-m06xs104nekulywb
Merge changes from release-5.5.8-20 to 5.5.9

Merge changes from the release branch of 5.5.8 to 5.5.9. These include
the HandlerSocket and UDF directories and the building scripts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
// vim:ai:sw=2:ts=8
 
3
 
 
4
/*
 
5
 * Copyright (C) 2010 DeNA Co.,Ltd.. All rights reserved.
 
6
 * See COPYRIGHT.txt for details.
 
7
 */
 
8
 
 
9
#include "EXTERN.h"
 
10
#include "perl.h"
 
11
#include "XSUB.h"
 
12
 
 
13
#include "ppport.h"
 
14
 
 
15
#include "hstcpcli.hpp"
 
16
 
 
17
#define DBG(x)
 
18
 
 
19
static SV *
 
20
arr_get_entry(AV *av, I32 avmax, I32 idx)
 
21
{
 
22
  if (idx > avmax) {
 
23
    DBG(fprintf(stderr, "arr_get_entry1 %d %d\n", avmax, idx));
 
24
    return 0;
 
25
  }
 
26
  SV **const ev = av_fetch(av, idx, 0);
 
27
  if (ev == 0) {
 
28
    DBG(fprintf(stderr, "arr_get_entry2 %d %d\n", avmax, idx));
 
29
    return 0;
 
30
  }
 
31
  return *ev;
 
32
}
 
33
 
 
34
static int
 
35
arr_get_intval(AV *av, I32 avmax, I32 idx, int default_val = 0)
 
36
{
 
37
  SV *const e = arr_get_entry(av, avmax, idx);
 
38
  if (e == 0) {
 
39
    return default_val;
 
40
  }
 
41
  return SvIV(e);
 
42
}
 
43
 
 
44
static const char *
 
45
sv_get_strval(SV *sv)
 
46
{
 
47
  if (sv == 0 || !SvPOK(sv)) {
 
48
    DBG(fprintf(stderr, "sv_get_strval\n"));
 
49
    return 0;
 
50
  }
 
51
  return SvPV_nolen(sv);
 
52
}
 
53
 
 
54
static const char *
 
55
arr_get_strval(AV *av, I32 avmax, I32 idx)
 
56
{
 
57
  SV *const e = arr_get_entry(av, avmax, idx);
 
58
  return sv_get_strval(e);
 
59
}
 
60
 
 
61
static AV *
 
62
sv_get_arrval(SV *sv)
 
63
{
 
64
  if (sv == 0 || !SvROK(sv)) {
 
65
    DBG(fprintf(stderr, "sv_get_arrval1\n"));
 
66
    return 0;
 
67
  }
 
68
  SV *const svtarget = SvRV(sv);
 
69
  if (svtarget == 0 || SvTYPE(svtarget) != SVt_PVAV) {
 
70
    DBG(fprintf(stderr, "sv_get_arrval2\n"));
 
71
    return 0;
 
72
  }
 
73
  return (AV *)svtarget;
 
74
}
 
75
 
 
76
static AV *
 
77
arr_get_arrval(AV *av, I32 avmax, I32 idx)
 
78
{
 
79
  SV *const e = arr_get_entry(av, avmax, idx);
 
80
  if (e == 0) {
 
81
    DBG(fprintf(stderr, "arr_get_arrval1\n"));
 
82
    return 0;
 
83
  }
 
84
  return sv_get_arrval(e);
 
85
}
 
86
 
 
87
static void
 
88
hv_to_strmap(HV *hv, std::map<std::string, std::string>& m_r)
 
89
{
 
90
  if (hv == 0) {
 
91
    return;
 
92
  }
 
93
  hv_iterinit(hv);
 
94
  HE *hent = 0;
 
95
  while ((hent = hv_iternext(hv)) != 0) {
 
96
    I32 klen = 0;
 
97
    char *const k = hv_iterkey(hent, &klen);
 
98
    DBG(fprintf(stderr, "k=%s\n", k));
 
99
    const std::string key(k, klen);
 
100
    SV *const vsv = hv_iterval(hv, hent);
 
101
    STRLEN vlen = 0;
 
102
    char *const v = SvPV(vsv, vlen);
 
103
    DBG(fprintf(stderr, "v=%s\n", v));
 
104
    const std::string val(v, vlen);
 
105
    m_r[key] = val;
 
106
  }
 
107
}
 
108
 
 
109
static void
 
110
strrefarr_push_back(std::vector<dena::string_ref>& a_r, SV *sv)
 
111
{
 
112
  if (sv == 0 || SvTYPE(sv) == SVt_NULL) {
 
113
    DBG(fprintf(stderr, "strrefarr_push_back: null\n"));
 
114
    return a_r.push_back(dena::string_ref());
 
115
  }
 
116
  STRLEN vlen = 0;
 
117
  char *const v = SvPV(sv, vlen);
 
118
  DBG(fprintf(stderr, "strrefarr_push_back: %s\n", v));
 
119
  a_r.push_back(dena::string_ref(v, vlen));
 
120
}
 
121
 
 
122
static void
 
123
av_to_strrefarr(AV *av, std::vector<dena::string_ref>& a_r)
 
124
{
 
125
  if (av == 0) {
 
126
    return;
 
127
  }
 
128
  const I32 len = av_len(av) + 1;
 
129
  for (I32 i = 0; i < len; ++i) {
 
130
    SV **const ev = av_fetch(av, i, 0);
 
131
    strrefarr_push_back(a_r, ev ? *ev : 0);
 
132
  }
 
133
}
 
134
 
 
135
static dena::string_ref
 
136
sv_get_string_ref(SV *sv)
 
137
{
 
138
  if (sv == 0) {
 
139
    return dena::string_ref();
 
140
  }
 
141
  STRLEN vlen = 0;
 
142
  char *const v = SvPV(sv, vlen);
 
143
  return dena::string_ref(v, vlen);
 
144
}
 
145
 
 
146
static IV
 
147
sv_get_iv(SV *sv)
 
148
{
 
149
  if (sv == 0 || !SvIOK(sv)) {
 
150
    return 0;
 
151
  }
 
152
  return SvIV(sv);
 
153
}
 
154
 
 
155
static void
 
156
av_to_filters(AV *av, std::vector<dena::hstcpcli_filter>& f_r)
 
157
{
 
158
  DBG(fprintf(stderr, "av_to_filters: %p\n", av));
 
159
  if (av == 0) {
 
160
    return;
 
161
  }
 
162
  const I32 len = av_len(av) + 1;
 
163
  DBG(fprintf(stderr, "av_to_filters: len=%d\n", (int)len));
 
164
  for (I32 i = 0; i < len; ++i) {
 
165
    AV *const earr = arr_get_arrval(av, len, i);
 
166
    if (earr == 0) {
 
167
      continue;
 
168
    }
 
169
    const I32 earrlen = av_len(earr) + 1;
 
170
    dena::hstcpcli_filter fe;
 
171
    fe.filter_type = sv_get_string_ref(arr_get_entry(earr, earrlen, 0));
 
172
    fe.op = sv_get_string_ref(arr_get_entry(earr, earrlen, 1));
 
173
    fe.ff_offset = sv_get_iv(arr_get_entry(earr, earrlen, 2));
 
174
    fe.val = sv_get_string_ref(arr_get_entry(earr, earrlen, 3));
 
175
    f_r.push_back(fe);
 
176
    DBG(fprintf(stderr, "av_to_filters: %s %s %d %s\n",
 
177
      fe.filter_action.begin(), fe.filter_op.begin(), (int)fe.ff_offset,
 
178
      fe.value.begin()));
 
179
  }
 
180
}
 
181
 
 
182
static void
 
183
set_process_verbose_level(const std::map<std::string, std::string>& m)
 
184
{
 
185
  std::map<std::string, std::string>::const_iterator iter = m.find("verbose");
 
186
  if (iter != m.end()) {
 
187
    dena::verbose_level = atoi(iter->second.c_str());
 
188
  }
 
189
}
 
190
 
 
191
static AV *
 
192
execute_internal(SV *obj, int id, const char *op, AV *keys, int limit,
 
193
  int skip, const char *modop, AV *modvals, AV *filters)
 
194
{
 
195
  AV *retval = (AV *)&PL_sv_undef;
 
196
  dena::hstcpcli_i *const ptr =
 
197
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
198
  do {
 
199
    std::vector<dena::string_ref> keyarr, mvarr;
 
200
    std::vector<dena::hstcpcli_filter> farr;
 
201
    av_to_strrefarr(keys, keyarr);
 
202
    dena::string_ref modop_ref;
 
203
    if (modop != 0) {
 
204
      modop_ref = dena::string_ref(modop, strlen(modop));
 
205
      av_to_strrefarr(modvals, mvarr);
 
206
    }
 
207
    if (filters != 0) {
 
208
      av_to_filters(filters, farr);
 
209
    }
 
210
    ptr->request_buf_exec_generic(id, dena::string_ref(op, strlen(op)),
 
211
      &keyarr[0], keyarr.size(), limit, skip, modop_ref, &mvarr[0],
 
212
      mvarr.size(), &farr[0], farr.size());
 
213
    AV *const av = newAV();
 
214
    retval = av;
 
215
    if (ptr->request_send() != 0) {
 
216
      break;
 
217
    }
 
218
    size_t nflds = 0;
 
219
    ptr->response_recv(nflds);
 
220
    const int e = ptr->get_error_code();
 
221
    DBG(fprintf(stderr, "e=%d nflds=%zu\n", e, nflds));
 
222
    av_push(av, newSViv(e));
 
223
    if (e != 0) {
 
224
      const std::string s = ptr->get_error();
 
225
      av_push(av, newSVpvn(s.data(), s.size()));
 
226
    } else {
 
227
      const dena::string_ref *row = 0;
 
228
      while ((row = ptr->get_next_row()) != 0) {
 
229
        DBG(fprintf(stderr, "row=%p\n", row));
 
230
        for (size_t i = 0; i < nflds; ++i) {
 
231
          const dena::string_ref& v = row[i];
 
232
          DBG(fprintf(stderr, "FLD %zu v=%s vbegin=%p\n", i,
 
233
            std::string(v.begin(), v.size())
 
234
              .c_str(), v.begin()));
 
235
          if (v.begin() != 0) {
 
236
            SV *const e = newSVpvn(
 
237
              v.begin(), v.size());
 
238
            av_push(av, e);
 
239
          } else {
 
240
            av_push(av, &PL_sv_undef);
 
241
          }
 
242
        }
 
243
      }
 
244
    }
 
245
    if (e >= 0) {
 
246
      ptr->response_buf_remove();
 
247
    }
 
248
  } while (0);
 
249
  return retval;
 
250
}
 
251
 
 
252
struct execute_arg {
 
253
  int id;
 
254
  const char *op;
 
255
  AV *keys;
 
256
  int limit;
 
257
  int skip;
 
258
  const char *modop;
 
259
  AV *modvals;
 
260
  AV *filters;
 
261
  execute_arg() : id(0), op(0), keys(0), limit(0), skip(0), modop(0),
 
262
    modvals(0), filters(0) { }
 
263
};
 
264
 
 
265
static AV *
 
266
execute_multi_internal(SV *obj, const execute_arg *args, size_t num_args)
 
267
{
 
268
  dena::hstcpcli_i *const ptr =
 
269
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
270
  /* appends multiple requests to the send buffer */
 
271
  for (size_t i = 0; i < num_args; ++i) {
 
272
    std::vector<dena::string_ref> keyarr, mvarr;
 
273
    std::vector<dena::hstcpcli_filter> farr;
 
274
    const execute_arg& arg = args[i];
 
275
    av_to_strrefarr(arg.keys, keyarr);
 
276
    dena::string_ref modop_ref;
 
277
    if (arg.modop != 0) {
 
278
      modop_ref = dena::string_ref(arg.modop, strlen(arg.modop));
 
279
      av_to_strrefarr(arg.modvals, mvarr);
 
280
    }
 
281
    if (arg.filters != 0) {
 
282
      av_to_filters(arg.filters, farr);
 
283
    }
 
284
    ptr->request_buf_exec_generic(arg.id,
 
285
      dena::string_ref(arg.op, strlen(arg.op)), &keyarr[0], keyarr.size(),
 
286
      arg.limit, arg.skip, modop_ref, &mvarr[0], mvarr.size(), &farr[0],
 
287
      farr.size());
 
288
  }
 
289
  AV *const retval = newAV();
 
290
  /* sends the requests */
 
291
  if (ptr->request_send() < 0) {
 
292
    /* IO error */
 
293
    AV *const av_respent = newAV();
 
294
    av_push(retval, newRV_noinc((SV *)av_respent));
 
295
    av_push(av_respent, newSViv(ptr->get_error_code()));
 
296
    const std::string& s = ptr->get_error();
 
297
    av_push(av_respent, newSVpvn(s.data(), s.size()));
 
298
    return retval; /* retval : [ [ err_code, err_message ] ] */
 
299
  }
 
300
  /* receives responses */
 
301
  for (size_t i = 0; i < num_args; ++i) {
 
302
    AV *const av_respent = newAV();
 
303
    av_push(retval, newRV_noinc((SV *)av_respent));
 
304
    size_t nflds = 0;
 
305
    const int e = ptr->response_recv(nflds);
 
306
    av_push(av_respent, newSViv(e));
 
307
    if (e != 0) {
 
308
      const std::string& s = ptr->get_error();
 
309
      av_push(av_respent, newSVpvn(s.data(), s.size()));
 
310
    } else {
 
311
      const dena::string_ref *row = 0;
 
312
      while ((row = ptr->get_next_row()) != 0) {
 
313
        for (size_t i = 0; i < nflds; ++i) {
 
314
          const dena::string_ref& v = row[i];
 
315
          DBG(fprintf(stderr, "%zu %s\n", i,
 
316
            std::string(v.begin(), v.size()).c_str()));
 
317
          if (v.begin() != 0) {
 
318
            av_push(av_respent, newSVpvn(v.begin(), v.size()));
 
319
          } else {
 
320
            /* null */
 
321
            av_push(av_respent, &PL_sv_undef);
 
322
          }
 
323
        }
 
324
      }
 
325
    }
 
326
    if (e >= 0) {
 
327
      ptr->response_buf_remove();
 
328
    }
 
329
    if (e < 0) {
 
330
      return retval;
 
331
    }
 
332
  }
 
333
  return retval;
 
334
}
 
335
 
 
336
MODULE = Net::HandlerSocket    PACKAGE = Net::HandlerSocket
 
337
 
 
338
SV *
 
339
new(klass, args)
 
340
  char *klass
 
341
  HV *args
 
342
CODE:
 
343
  RETVAL = &PL_sv_undef;
 
344
  dena::config conf;
 
345
  hv_to_strmap(args, conf);
 
346
  set_process_verbose_level(conf);
 
347
  dena::socket_args sargs;
 
348
  sargs.set(conf);
 
349
  dena::hstcpcli_ptr p = dena::hstcpcli_i::create(sargs);
 
350
  SV *const objref = newSViv(0);
 
351
  SV *const obj = newSVrv(objref, klass);
 
352
  dena::hstcpcli_i *const ptr = p.get();
 
353
  sv_setiv(obj, reinterpret_cast<IV>(ptr));
 
354
  p.release();
 
355
  SvREADONLY_on(obj);
 
356
  RETVAL = objref;
 
357
OUTPUT:
 
358
  RETVAL
 
359
 
 
360
void
 
361
DESTROY(obj)
 
362
  SV *obj
 
363
CODE:
 
364
  dena::hstcpcli_i *const ptr =
 
365
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
366
  delete ptr;
 
367
 
 
368
void
 
369
close(obj)
 
370
  SV *obj
 
371
CODE:
 
372
  dena::hstcpcli_i *const ptr =
 
373
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
374
  ptr->close();
 
375
 
 
376
int
 
377
reconnect(obj)
 
378
  SV *obj
 
379
CODE:
 
380
  RETVAL = 0;
 
381
  dena::hstcpcli_i *const ptr =
 
382
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
383
  RETVAL = ptr->reconnect();
 
384
OUTPUT:
 
385
  RETVAL
 
386
 
 
387
int
 
388
stable_point(obj)
 
389
  SV *obj
 
390
CODE:
 
391
  RETVAL = 0;
 
392
  dena::hstcpcli_i *const ptr =
 
393
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
394
  const bool rv = ptr->stable_point();
 
395
  RETVAL = static_cast<int>(rv);
 
396
OUTPUT:
 
397
  RETVAL
 
398
 
 
399
int
 
400
get_error_code(obj)
 
401
  SV *obj
 
402
CODE:
 
403
  RETVAL = 0;
 
404
  dena::hstcpcli_i *const ptr =
 
405
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
406
  RETVAL = ptr->get_error_code();
 
407
OUTPUT:
 
408
  RETVAL
 
409
 
 
410
SV *
 
411
get_error(obj)
 
412
  SV *obj
 
413
CODE:
 
414
  RETVAL = &PL_sv_undef;
 
415
  dena::hstcpcli_i *const ptr =
 
416
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
417
  const std::string s = ptr->get_error();
 
418
  RETVAL = newSVpvn(s.data(), s.size());
 
419
OUTPUT:
 
420
  RETVAL
 
421
 
 
422
int
 
423
open_index(obj, id, db, table, index, fields, ffields = 0)
 
424
  SV *obj
 
425
  int id
 
426
  const char *db
 
427
  const char *table
 
428
  const char *index
 
429
  const char *fields
 
430
  SV *ffields
 
431
CODE:
 
432
  const char *const ffields_str = sv_get_strval(ffields);
 
433
  RETVAL = 0;
 
434
  dena::hstcpcli_i *const ptr =
 
435
    reinterpret_cast<dena::hstcpcli_i *>(SvIV(SvRV(obj)));
 
436
  do {
 
437
    ptr->request_buf_open_index(id, db, table, index, fields, ffields_str);
 
438
    if (ptr->request_send() != 0) {
 
439
      break;
 
440
    }
 
441
    size_t nflds = 0;
 
442
    ptr->response_recv(nflds);
 
443
    const int e = ptr->get_error_code();
 
444
    DBG(fprintf(stderr, "errcode=%d\n", ptr->get_error_code()));
 
445
    if (e >= 0) {
 
446
      ptr->response_buf_remove();
 
447
    }
 
448
    DBG(fprintf(stderr, "errcode=%d\n", ptr->get_error_code()));
 
449
  } while (0);
 
450
  RETVAL = ptr->get_error_code();
 
451
OUTPUT:
 
452
  RETVAL
 
453
 
 
454
AV *
 
455
execute_single(obj, id, op, keys, limit, skip, mop = 0, mvs = 0, fils = 0)
 
456
  SV *obj
 
457
  int id
 
458
  const char *op
 
459
  AV *keys
 
460
  int limit
 
461
  int skip
 
462
  SV *mop
 
463
  SV *mvs
 
464
  SV *fils
 
465
CODE:
 
466
  const char *const mop_str = sv_get_strval(mop);
 
467
  AV *const mvs_av = sv_get_arrval(mvs);
 
468
  AV *const fils_av = sv_get_arrval(fils);
 
469
  RETVAL = execute_internal(obj, id, op, keys, limit, skip, mop_str, mvs_av,
 
470
    fils_av);
 
471
  sv_2mortal((SV *)RETVAL);
 
472
OUTPUT:
 
473
  RETVAL
 
474
 
 
475
AV *
 
476
execute_multi(obj, cmds)
 
477
  SV *obj
 
478
  AV *cmds
 
479
CODE:
 
480
  DBG(fprintf(stderr, "execute_multi0\n"));
 
481
  const I32 cmdsmax = av_len(cmds);
 
482
  execute_arg args[cmdsmax + 1]; /* GNU */
 
483
  for (I32 i = 0; i <= cmdsmax; ++i) {
 
484
    AV *const avtarget = arr_get_arrval(cmds, cmdsmax, i);
 
485
    if (avtarget == 0) {
 
486
      DBG(fprintf(stderr, "execute_multi1 %d\n", i));
 
487
      continue;
 
488
    }
 
489
    const I32 argmax = av_len(avtarget);
 
490
    if (argmax < 2) {
 
491
      DBG(fprintf(stderr, "execute_multi2 %d\n", i));
 
492
      continue;
 
493
    }
 
494
    execute_arg& ag = args[i];
 
495
    ag.id = arr_get_intval(avtarget, argmax, 0);
 
496
    ag.op = arr_get_strval(avtarget, argmax, 1);
 
497
    ag.keys = arr_get_arrval(avtarget, argmax, 2);
 
498
    ag.limit = arr_get_intval(avtarget, argmax, 3);
 
499
    ag.skip = arr_get_intval(avtarget, argmax, 4);
 
500
    ag.modop = arr_get_strval(avtarget, argmax, 5);
 
501
    ag.modvals = arr_get_arrval(avtarget, argmax, 6);
 
502
    ag.filters = arr_get_arrval(avtarget, argmax, 7);
 
503
    DBG(fprintf(stderr, "execute_multi3 %d: %d %s %p %d %d %s %p %p\n",
 
504
      i, ag.id, ag.op, ag.keys, ag.limit, ag.skip, ag.modop, ag.modvals,
 
505
      ag.filters));
 
506
  }
 
507
  RETVAL = execute_multi_internal(obj, args, cmdsmax + 1);
 
508
  sv_2mortal((SV *)RETVAL);
 
509
OUTPUT:
 
510
  RETVAL
 
511
 
 
512
AV *
 
513
execute_find(obj, id, op, keys, limit, skip, mop = 0, mvs = 0, fils = 0)
 
514
  SV *obj
 
515
  int id
 
516
  const char *op
 
517
  AV *keys
 
518
  int limit
 
519
  int skip
 
520
  SV *mop
 
521
  SV *mvs
 
522
  SV *fils
 
523
CODE:
 
524
  const char *const mop_str = sv_get_strval(mop);
 
525
  AV *const mvs_av = sv_get_arrval(mvs);
 
526
  AV *const fils_av = sv_get_arrval(fils);
 
527
  RETVAL = execute_internal(obj, id, op, keys, limit, skip, mop_str, mvs_av,
 
528
    fils_av);
 
529
  sv_2mortal((SV *)RETVAL);
 
530
OUTPUT:
 
531
  RETVAL
 
532
 
 
533
AV *
 
534
execute_update(obj, id, op, keys, limit, skip, modvals, fils = 0)
 
535
  SV *obj
 
536
  int id
 
537
  const char *op
 
538
  AV *keys
 
539
  int limit
 
540
  int skip
 
541
  AV *modvals
 
542
  SV *fils
 
543
CODE:
 
544
  AV *const fils_av = sv_get_arrval(fils);
 
545
  RETVAL = execute_internal(obj, id, op, keys, limit, skip, "U",
 
546
    modvals, fils_av);
 
547
  sv_2mortal((SV *)RETVAL);
 
548
OUTPUT:
 
549
  RETVAL
 
550
 
 
551
AV *
 
552
execute_delete(obj, id, op, keys, limit, skip, fils = 0)
 
553
  SV *obj
 
554
  int id
 
555
  const char *op
 
556
  AV *keys
 
557
  int limit
 
558
  int skip
 
559
  SV *fils
 
560
CODE:
 
561
  AV *const fils_av = sv_get_arrval(fils);
 
562
  RETVAL = execute_internal(obj, id, op, keys, limit, skip, "D", 0, fils_av);
 
563
  sv_2mortal((SV *)RETVAL);
 
564
OUTPUT:
 
565
  RETVAL
 
566
 
 
567
AV *
 
568
execute_insert(obj, id, fvals)
 
569
  SV *obj
 
570
  int id
 
571
  AV *fvals
 
572
CODE:
 
573
  RETVAL = execute_internal(obj, id, "+", fvals, 0, 0, 0, 0, 0);
 
574
  sv_2mortal((SV *)RETVAL);
 
575
OUTPUT:
 
576
  RETVAL
 
577