From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "tdsql.h"
/* Global variables shared by subroutines */
int g_msglevel;
double g_activcount;
int g_errorcode;
char g_errormsg[260];
/* Data descriptors for Results */
struct datadescr ddesc[3];
/* Common variables within this file only */
static SV * c_msgl_sv;
static SV * c_actv_sv;
static SV * c_errc_sv;
static SV * c_emsg_sv;
/* Many applications will use only one request at a time,
so we pre-allocate this one to avoid having to allocate
and free all the time. */
Request request0;
int r0_in_use = 0; /* Is it in use? */
static int
not_here(char *s)
{
croak("%s not implemented on this architecture", s);
return -1;
}
MODULE = Teradata::SQL PACKAGE = Teradata::SQL
# CONNECT to Teradata
SV *
Xconnect(log, ccs, tmode, logmech)
PROTOTYPE:$$$$
INPUT:
char * log
char * ccs
char * tmode
char * logmech
PREINIT:
pSession sess_ptr;
int ok;
CODE:
c_msgl_sv = get_sv("Teradata::SQL::msglevel", FALSE);
c_actv_sv = get_sv("Teradata::SQL::activcount", FALSE);
c_errc_sv = get_sv("Teradata::SQL::errorcode", FALSE);
c_emsg_sv = get_sv("Teradata::SQL::errormsg", FALSE);
g_msglevel = SvIV(c_msgl_sv);
New(0, sess_ptr, 1, Session);
ok = Zconnect(sess_ptr, log, ccs, tmode, logmech);
if (ok) {
RETVAL = newSVpvn((char *) sess_ptr, sizeof(Session));
} else {
RETVAL = newSVpvn("NO SESS", 7);
}
Safefree(sess_ptr);
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# DISCONNECT
int
Xdisconnect(sess)
PROTOTYPE:$
INPUT:
SV * sess
CODE:
pSession sess_ptr;
g_msglevel = SvIV(c_msgl_sv);
sess_ptr = (pSession) SvPV_nolen(sess);
RETVAL = Zdisconnect(sess_ptr);
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# EXECUTE without arguments
int
Xexecute(sess, sql)
PROTOTYPE:$$
INPUT:
SV * sess
char * sql
CODE:
pSession sess_ptr;
g_msglevel = SvIV(c_msgl_sv);
sess_ptr = (pSession) SvPV_nolen(sess);
RETVAL = Zexecute(sess_ptr, sql);
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# OPEN without arguments
SV *
Xopen(sess, sql)
PROTOTYPE:$$
INPUT:
SV * sess
char * sql
PREINIT:
pSession sess_ptr;
Request req;
int ok;
CODE:
g_msglevel = SvIV(c_msgl_sv);
sess_ptr = (pSession) SvPV_nolen(sess);
req.dbcp = &(sess_ptr->dbc);
ok = Zopen(&req, sql);
if (ok) {
RETVAL = newSVpvn((char *) &req, sizeof(Request));
} else {
RETVAL = newSV(0); /* undef */
}
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# EXECUTE a prepared request with optional arguments
int
Xexecutep(sess, sql, ...)
PROTOTYPE:$$
INPUT:
SV * sess
char * sql
PREINIT:
pSession sess_ptr;
int i, nindic, idlen, nargs;
Int32 wint;
char * sptr;
STRLEN slen;
double wdouble;
Byte hv_data[MAX_RDA_LEN];
Byte * hvdata_ptr;
Byte * hvindic_ptr;
Byte indic_mask;
struct ModCliDataInfoType hv_datainfo;
struct ModCliDInfoType * hv_datainfo_ptr;
CODE:
g_msglevel = SvIV(c_msgl_sv);
sess_ptr = (pSession) SvPV_nolen(sess);
if (items == 2) {
RETVAL = Zexecutep(sess_ptr, sql);
} else {
/* Store the Perl variables in an IndicData array. */
/* First, reserve the indicator bytes. */
nargs = items - 2;
nindic = (nargs + 7) / 8;
idlen = nindic; /* IndicData length in bytes */
hvindic_ptr = hv_data;
*hvindic_ptr = 0x00;
indic_mask = 0x80;
hvdata_ptr = hv_data + nindic;
/* DataInfo */
hv_datainfo.FieldCount = (PclWord) nargs;
hv_datainfo_ptr = &(hv_datainfo.InfoVar[0]);
for (i = 2; i < items; i++) {
if ( SvIOK(ST(i)) ) {
hv_datainfo_ptr->SQLType = INTEGER_N;
hv_datainfo_ptr->SQLLen = 4;
wint = SvIV(ST(i));
memcpy(hvdata_ptr, &wint, 4);
hvdata_ptr += 4;
idlen += 4;
} else if ( SvNOK(ST(i)) ) {
hv_datainfo_ptr->SQLType = FLOAT_N;
hv_datainfo_ptr->SQLLen = 8;
wdouble = SvNV(ST(i));
memcpy(hvdata_ptr, &wdouble, 8);
hvdata_ptr += 8;
idlen += 8;
} else if ( SvPOK(ST(i)) ) {
sptr = SvPV(ST(i), slen);
hv_datainfo_ptr->SQLType = CHAR_N;
hv_datainfo_ptr->SQLLen = slen;
memcpy(hvdata_ptr, sptr, slen);
hvdata_ptr += slen;
idlen += slen;
} else { /* Null */
hv_datainfo_ptr->SQLType = INTEGER_N;
hv_datainfo_ptr->SQLLen = 4;
wint = 0;
memcpy(hvdata_ptr, &wint, 4);
*hvindic_ptr |= indic_mask;
hvdata_ptr += 4;
idlen += 4;
}
/* Point to the next DataInfo field. */
hv_datainfo_ptr++;
/* Point to the next indicator bit. */
if (indic_mask != 0x01) {
indic_mask >>= 1;
} else {
indic_mask = 0x80;
hvindic_ptr++;
}
}
RETVAL = Zexecutep_args(sess_ptr, sql, &hv_datainfo,
hv_data, idlen);
}
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# OPEN without arguments, segmented
SV *
Xopenseg(sess, sql, save_spl)
PROTOTYPE:$$
INPUT:
SV * sess
char * sql
char * save_spl
PREINIT:
pSession sess_ptr;
Request req;
int ok;
CODE:
g_msglevel = SvIV(c_msgl_sv);
sess_ptr = (pSession) SvPV_nolen(sess);
req.dbcp = &(sess_ptr->dbc);
ok = Zopenseg(&req, sql, save_spl);
if (ok) {
RETVAL = newSVpvn((char *) &req, sizeof(Request));
} else {
RETVAL = newSV(0); /* undef */
}
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# OPEN a prepared request with optional arguments
SV *
Xopenp(sess, sql, ...)
PROTOTYPE:$$
INPUT:
SV * sess
char * sql
PREINIT:
int i, wint, nindic, idlen, nargs;
int ok;
pSession sess_ptr;
Request req;
char * sptr;
STRLEN slen;
double wdouble;
Byte hv_data[MAX_RDA_LEN]; /* "host variables" */
Byte * hvdata_ptr;
Byte * hvindic_ptr;
Byte indic_mask;
struct ModCliDataInfoType hv_datainfo;
struct ModCliDInfoType * hv_datainfo_ptr;
CODE:
g_msglevel = SvIV(c_msgl_sv);
sess_ptr = (pSession) SvPV_nolen(sess);
req.dbcp = &(sess_ptr->dbc);
if (items == 2) {
ok = Zopenp(&req, sql);
} else {
/* Store the Perl variables in an IndicData array. */
/* First, reserve the indicator bytes. */
nargs = items - 2;
nindic = (nargs + 7) / 8;
idlen = nindic; /* IndicData length in bytes */
hvindic_ptr = hv_data;
*hvindic_ptr = 0x00;
indic_mask = 0x80;
hvdata_ptr = hv_data + nindic;
/* DataInfo */
hv_datainfo.FieldCount = (PclWord) nargs;
hv_datainfo_ptr = &(hv_datainfo.InfoVar[0]);
for (i = 2; i < items; i++) {
if ( SvIOK(ST(i)) ) {
hv_datainfo_ptr->SQLType = INTEGER_N;
hv_datainfo_ptr->SQLLen = 4;
wint = SvIV(ST(i));
memcpy(hvdata_ptr, &wint, 4);
hvdata_ptr += 4;
idlen += 4;
} else if ( SvNOK(ST(i)) ) {
hv_datainfo_ptr->SQLType = FLOAT_N;
hv_datainfo_ptr->SQLLen = 8;
wdouble = SvNV(ST(i));
memcpy(hvdata_ptr, &wdouble, 8);
hvdata_ptr += 8;
idlen += 8;
} else if ( SvPOK(ST(i)) ) {
sptr = SvPV(ST(i), slen);
hv_datainfo_ptr->SQLType = CHAR_N;
hv_datainfo_ptr->SQLLen = slen;
memcpy(hvdata_ptr, sptr, slen);
hvdata_ptr += slen;
idlen += slen;
} else { /* Null */
hv_datainfo_ptr->SQLType = INTEGER_N;
hv_datainfo_ptr->SQLLen = 4;
wint = 0;
memcpy(hvdata_ptr, &wint, 4);
*hvindic_ptr |= indic_mask;
hvdata_ptr += 4;
idlen += 4;
}
/* Point to the next DataInfo field. */
hv_datainfo_ptr++;
/* Point to the next indicator bit. */
if (indic_mask != 0x01) {
indic_mask >>= 1;
} else {
indic_mask = 0x80;
hvindic_ptr++;
}
}
ok = Zopenp_args(&req, sql,
&hv_datainfo, hv_data, idlen);
}
if (ok) {
RETVAL = newSVpvn((char *) &req, sizeof(Request));
} else {
RETVAL = newSV(0); /* undef */
}
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# FETCH. Last argument says whether this is fetching into a
# hash or not.
void
Xfetch(req, hash)
PROTOTYPE:$$
INPUT:
SV * req
int hash
PREINIT:
int i, decp, decs;
pRequest req_ptr;
STRLEN slen;
Int16 dlen; /* data length */
char * sptr;
char * ret_data;
Byte * indic_ptr;
Byte * data_ptr;
struct datadescr * ddesc_ptr;
Int32 wint;
double wdouble;
char wstring[24];
Byte indic_mask;
PPCODE:
g_msglevel = SvIV(c_msgl_sv);
req_ptr = (pRequest) SvPV_nolen(req);
ret_data = Zfetch(req_ptr);
if (ret_data) {
ddesc_ptr = &(req_ptr->ddesc);
/* Point to the indicators and the data. */
indic_ptr = (Byte *) ret_data;
indic_mask = 0x80;
data_ptr = ((Byte *) ret_data) + ( (ddesc_ptr->nfields + 7) / 8);
for (i = 0; i < ddesc_ptr->nfields; i++) {
/* If this is a hash request, push the name first. */
if (hash) {
slen = strlen(ddesc_ptr->sqlvar[i].colident);
sptr = (char *)ddesc_ptr->sqlvar[i].colident;
XPUSHs(sv_2mortal(newSVpvn(sptr, slen)));
}
/* Now push the value, testing for null first. */
switch (ddesc_ptr->sqlvar[i].sqltype) {
case INTEGER_N:
if ( (*indic_ptr & indic_mask) > 0) { /* Null */
XPUSHs(&PL_sv_undef);
} else {
wint = *((Int32 *)data_ptr) + 0;
XPUSHs(sv_2mortal(newSViv(wint)));
}
data_ptr += 4;
break;
case SMALLINT_N:
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
} else {
wint = *((Int16 *)data_ptr) + 0;
XPUSHs(sv_2mortal(newSViv(wint)));
}
data_ptr += 2;
break;
case BYTEINT_N:
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
} else {
wint = *((ByteInt *)data_ptr) + 0;
XPUSHs(sv_2mortal(newSViv(wint)));
}
data_ptr++;
break;
case CHAR_N:
slen = ddesc_ptr->sqlvar[i].datalen;
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
} else {
sptr = (char *) data_ptr;
XPUSHs(sv_2mortal(newSVpvn(sptr, slen)));
}
data_ptr += slen;
break;
case VARCHAR_N:
slen = *((UInt16 *) data_ptr);
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
} else {
sptr = (char *) (data_ptr + 2);
XPUSHs(sv_2mortal(newSVpvn(sptr, slen)));
}
data_ptr += slen + 2;
break;
case DECIMAL_N:
/* Decimal precision and scale */
decp = ddesc_ptr->sqlvar[i].datalen;
decs = ddesc_ptr->sqlvar[i].decscale;
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
data_ptr += ddesc_ptr->sqlvar[i].dlb;
} else if (decp <= 9) {
wdouble = _dec_to_double(data_ptr, decp, decs);
XPUSHs(sv_2mortal(newSVnv(wdouble)));
data_ptr += ddesc_ptr->sqlvar[i].dlb;
} else if (decp <= 18) {
_dec_to_string(wstring, data_ptr, decs);
slen = strlen(wstring);
XPUSHs(sv_2mortal(newSVpvn(wstring, slen)));
data_ptr += 8;
} else {
if (g_msglevel > 0)
warn("Decimal field too large");
XPUSHs(sv_2mortal(newSVnv(0.0)));
data_ptr += 16;
}
break;
case BIGINT_N:
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
} else {
_dec_to_string(wstring, data_ptr, 0);
slen = strlen(wstring);
XPUSHs(sv_2mortal(newSVpvn(wstring, slen)));
}
data_ptr += 8;
break;
case NUMBER_N:
/* Precision and scale */
dlen = 0 + *((char *) data_ptr);
decp = ddesc_ptr->sqlvar[i].datalen;
decs = ddesc_ptr->sqlvar[i].decscale;
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
data_ptr += dlen + 1;
} else if (dlen <= 9) {
wdouble = _num_to_double(data_ptr);
XPUSHs(sv_2mortal(newSVnv(wdouble)));
data_ptr += dlen + 1;
} else if (dlen == 10) {
_num_to_string(wstring, data_ptr);
slen = strlen(wstring);
XPUSHs(sv_2mortal(newSVpvn(wstring, slen)));
data_ptr += dlen + 1;
} else {
if (g_msglevel > 0)
warn("Number field too large");
XPUSHs(sv_2mortal(newSVnv(0.0)));
data_ptr += dlen + 1;
}
break;
case FLOAT_N:
if ( (*indic_ptr & indic_mask) > 0) {
XPUSHs(&PL_sv_undef);
} else {
wdouble = *((double *) data_ptr);
XPUSHs(sv_2mortal(newSVnv(wdouble)));
}
data_ptr += 8;
break;
default:
croak("Data type %d not supported\n",
ddesc_ptr->sqlvar[i].sqltype);
}
/* Point to the next indicator bit. */
if (indic_mask != 0x01) {
indic_mask >>= 1;
} else {
indic_mask = 0x80;
indic_ptr++;
}
}
}
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
# CLOSE
int
Xclose(req)
PROTOTYPE:$
INPUT:
SV * req
CODE:
pRequest req_ptr;
g_msglevel = SvIV(c_msgl_sv);
req_ptr = (pRequest) SvPV_nolen(req);
RETVAL = Zclose(req_ptr);
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# ABORT (DBFABT)
int
Xabort(sess)
PROTOTYPE:$
INPUT:
SV * sess
CODE:
pSession sess_ptr;
g_msglevel = SvIV(c_msgl_sv);
sess_ptr = (pSession) SvPV_nolen(sess);
RETVAL = Zabort(sess_ptr);
sv_setnv(c_actv_sv, g_activcount);
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);
OUTPUT:
RETVAL
# SERVER_INFO (DBCHQE)
void
Xserver_info(server, qepitem)
PROTOTYPE:$$
INPUT:
char * server
int qepitem
PREINIT:
int wint, string_len;
int ok;
/* DBCHQE request area */
DBCHQEP our_qep;
/* Data returned from DBCHQE */
Byte hqe_ret_data[200];
char * RArea_char_ptr;
char * ret_ptr;
/* Error message from DBCHQE */
char hqe_message[258];
struct QEPDBLIMIT_ our_dblimit;
PPCODE:
c_msgl_sv = get_sv("Teradata::SQL::msglevel", FALSE);
c_errc_sv = get_sv("Teradata::SQL::errorcode", FALSE);
c_emsg_sv = get_sv("Teradata::SQL::errormsg", FALSE);
g_msglevel = SvIV(c_msgl_sv);
/* Store the fields needed for the request. */
our_qep.qepLevel = QEPL10NLVL1;
our_qep.qepItem = qepitem;
our_qep.qepTLen = strlen(server);
#ifdef CLI_64BIT
our_qep.qepTIdP = server;
#else
/* 32-bit CLI insanely defines this as an Int32!! */
our_qep.qepTDP = (Int32) server;
#endif
our_qep.qepRALen = 200;
our_qep.qepRArea = hqe_ret_data;
memcpy(our_qep.qepMLid, "EN", 2);
our_qep.qepMsgP = hqe_message;
our_qep.qepMsgM = 256;
ok = Zserver_info(&our_qep);
if (ok) {
RArea_char_ptr = (char *) our_qep.qepRArea;
switch (our_qep.qepItem) {
case QEPIDBR: /* DBS release info */
/* Returned to perl as a two-element list */
ret_ptr = RArea_char_ptr;
for (string_len=0; string_len < 30; string_len++) {
if (*ret_ptr == ' ') break;
else ret_ptr++;
}
XPUSHs(sv_2mortal(newSVpvn(RArea_char_ptr, string_len)));
RArea_char_ptr += 30;
ret_ptr = RArea_char_ptr;
for (string_len=0; string_len < 32; string_len++) {
if (*ret_ptr == ' ') break;
else ret_ptr++;
}
XPUSHs(sv_2mortal(newSVpvn(RArea_char_ptr, string_len)));
break;
case QEPISC: /* char set */
case QEPICL2R: /* CLIv2 Release Info */
case QEPIASL: /* server default char set */
case QEPIDCS: /* server default char set */
XPUSHs(sv_2mortal(newSVpvn(RArea_char_ptr,
our_qep.qepRDLen)));
break;
case QEPIFTSM: /* transaction semantics */
case QEPIFLCS: /* lang-conformance support */
case QEPIFUCR: /* updatable cursor support */
case QEPIFRFI: /* referential integ. support */
case QEPIDTSM: /* tx_semantics default */
case QEP64K: /* 64KB parcel support */
case QEPIFSSO: /* SSO support */
case QEPIFUPS: /* Atomic UPSERT support */
case QEPIAOP: /* array-ops support */
case QEPIFMRG: /* Merge-Into support */
case QEPIFLOB: /* LOB support */
case QEPIXRS: /* extended response support */
case QEPIUD: /* Identity Column Support */
case QEPIRPO: /* cursor positioning sup. */
case QEPIESS: /* Enhanced Statement Status */
case QEPIUDT: /* User-defined types */
case QEPIRCA: /* relaxed call arguments */
XPUSHs(sv_2mortal(newSVpvn(our_qep.qepRArea, 1)));
break;
case QEPIDPF: /* internal (V)AMP count */
case QEPIDMSS: /* MaxSegmentSize */
wint = *((int *)our_qep.qepRArea);
XPUSHs(sv_2mortal(newSViv(wint)));
break;
case QEPIEPU: /* enlarged parcel (APH) support */
case QEPIAPH: /* APH responses */
wint = *((Int16 *)our_qep.qepRArea);
XPUSHs(sv_2mortal(newSViv(wint)));
break;
case QEPITPR: /* precision on timestamp */
wint = *((Int16 *)RArea_char_ptr);
XPUSHs(sv_2mortal(newSViv(wint)));
wint = *((Int16 *)(RArea_char_ptr + 2));
XPUSHs(sv_2mortal(newSViv(wint)));
break;
case QEPISQL: /* SQL limits */
memcpy(&our_dblimit, our_qep.qepRArea,
sizeof(struct QEPDBLIMIT_));
wint = (int) our_dblimit.MaxRowBytes;
XPUSHs(sv_2mortal(newSViv(wint)));
wint = (int) our_dblimit.MaxLobBytes;
XPUSHs(sv_2mortal(newSViv(wint)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxObjectNameChars)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxColinTbl)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxTblinSel)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxColinSel)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxColGrpBy)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxColOrdrBy)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxCharLiteralChars)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxBinLiteralChars)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxColBytes)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxCharChars)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxVarcharChars)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxGraphicChars)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxVargraphicChars)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxByteBytes)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxVarbyteBytes)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxDecimal)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxTimeScale)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxTimeStampScale)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxIntervalToSecondScale)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxFldsUsingRow)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxParamsInRequest)));
XPUSHs(sv_2mortal(newSViv(our_dblimit.MaxSPParams)));
break;
/*case QEPIACS: not implemented */
/* remainder are undocumented or not implemented here */
default:
XPUSHs(&PL_sv_undef);
}
} else {
XPUSHs(&PL_sv_undef);
}
sv_setiv(c_errc_sv, g_errorcode);
sv_setpv(c_emsg_sv, g_errormsg);