Cover V05, I09
Article
Listing 1
Table 1

sep96.tar


Listing 1

/*
* oci.xs -- Perl/Oracle XSUBs
*
* by Danny Lawrence

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

static double
constant(name, arg)
char *name;=7F
int arg;
{
errno = ENOENT;
return 0;
}


#define ORA_EOF     1403 /* csrarc return value */
#define ORA_EOL     1007 /* variable not in select list */
#define FT_SELECT   4    /* value in ft field if stmt is select */
#define EXT_ASCIIZ  5    /* external null-terminated data type */
#define BIND_NULL   (-1) /* indicator val for null fetches */

#ifndef min
#define min(a,b)    ((a)<(b)?(a):(b))
#endif

#ifdef ORACLE7
#include 
#else

typedef signed short sb2;
typedef unsigned short ub2;
typedef unsigned int ub4;
typedef unsigned char ub1;
typedef signed int sword;
typedef char text;

/* V6 cursor
* Adapted from ORACLE_HOME/c/demo/sample.c and Pro*C User's Guide Version
1.1
*/
struct cda_def {
sb2 v2_rc;               /* return code */
ub2 ft;                  /* function type */
ub4 rpc;                 /* rows processed count */
ub2 peo;                 /* parse error offset */
ub1 fc;                  /* function code */
ub1 rcs1;                /* filler */
ub2 rc;                  /* reserved, private */
ub1 wrn;                 /* warning flags */
ub1 rcs2;                /* error flags */
sword rcs3;              /* cursor number */
struct {                 /* rowid structure */
struct {
ub4 rcs4;      /* rba of first block of table */
ub2 rcs5;      /* partition id of table */
ub1 rcs6;      /* table id of table */
} rd;
ub4 rcs7;           /* rba of datablock */
ub2 rcs8;           /* sequence number of row in bock */
} rid;
sword ose;               /* os dependent error code */
ub1 chk;                 /* check byte */
ub1 rcs9[26];            /* private, reserved fill */
};

/* prototypes for V6 functions */

int olon(struct cda_def *lda, text *uid, sword uidlen, text *psw, sword
pswl,
sword audit);

int oopen(struct cda_def *cursor, struct cda_def *lda, text *dbn,
sword dbnlen, sword areasize, text *uid, sword uidlen);

int osql3(struct cda_def *cursor, text *sqlstatement, sword sqllen);

int odsc(struct cda_def *cursor, sword position, sb2 *dbsize,
sb2 *fsize, sb2 *rcode, sb2 *dbtype, text *cbuf, sb2 *cbufl,
sb2 *dsize);

int odefin(struct cda_def *cursor, sword pos, ub1 *buffer, sword bufl,
sword ftype, sword scale, sb2 *indp, text *fmt, sword fmtl,
sword fmtt, ub2 *retl, ub2 *rcode);

int obndrv(struct cda_def *cursor, text *sqlvar, sword sqlvl, ub1 *progvar,
sword progvl, sword ftype, sword scale, sb2 *indp, text *fmt,
sword fmtl, sword fmtt);

int oexec(struct cda_def *cursor);

int ofetch(struct cda_def *cursor);

int ocan(struct cda_def *cursor);

int ocom(struct cda_def *lda);

int orol(struct cda_def *lda);

int oermsg(ub2 rcode, text *msgbuf);

int oclose(struct cda_def *cursor);

int ologof(struct cda_def *lda);

#endif /* ORACLE7 */


/* linked list node for select lists and bind lists */
typedef struct node {
struct node *next;       /* next node in list */
text *buf;               /* oracle field i/o buf */
sb2 bufsize;             /* allocated size of buf */
sb2 ind;                 /* null indicator */
ub2 retl;                /* ofetch actual length @@@*/
ub2 rcode;               /* ofetch return code */
text *var;               /* name of substitution var */
SV *ref;                 /* perl reference var */
} NODE;

typedef struct {
struct cda_def cur;
NODE *sfirst;            /* first NODE in select list */
NODE *slast;             /* last NODE in select list */
NODE *bfirst;            /* first NODE in bind list */
NODE *blast;             /* last NODE in bind list */
} CURSOR;


static struct cda_def lda;    /* oracle logon data area */
#ifdef ORACLE7
static ub1 hda[256];
#endif


static CURSOR *new_cursor()
{
CURSOR *c;

New(0, c, 1, CURSOR);
c->sfirst = c->slast = NULL;
c->bfirst = c->blast = NULL;
return c;
}


static NODE *new_node(sb2 size)
{
NODE *p;

New(0, p, 1, NODE);
New(0, p->buf, size, text);
p->bufsize = size;
p->ind = 0;
p->retl = 0;
p->rcode = 0;
p->var = NULL;
p->ref = NULL;
p->next = NULL;
return p;
}


static void free_node(NODE *p)
{
Safefree(p->buf);
if (p->var)
Safefree(p->var);
if (p->ref)
SvREFCNT_dec(p->ref);
Safefree(p);
}


static void free_list(NODE *p)
{
NODE *prev = NULL;

for (; p; p = p->next) {
if (prev)
free_node(prev);
prev = p;
}
if (prev)
free_node(prev);
}


static void free_cursor(CURSOR *c)
{
free_list(c->sfirst);
free_list(c->bfirst);
Safefree(c);
}


static void set_oerr(int n)
{
char buf[133];
SV *p;

p = perl_get_sv("Oci::oerr", TRUE);
if (p)
sv_setnv(p, (double) n);
p = perl_get_sv("Oci::oermsg", TRUE);
if (p) {
oermsg(n, buf);
sv_setpv(p, buf);
}
}


static int build_slist(CURSOR *c, char *stmt)
{
sb2 displaysize;
sword pos;
NODE *p;

for (pos = 1; ;pos++) {
odsc(&c->cur, pos, (sb2 *)0, (sb2 *)0, (sb2 *)0, (sb2 *)0,
(text *)0, (sb2 *)0, &displaysize);
if (c->cur.rc == ORA_EOL)
break;
if (c->cur.rc != 0) {
set_oerr(c->cur.rc);
return c->cur.rc;
}

p = new_node(displaysize+1);  /* +1 for \0 */

if (c->sfirst == NULL)
c->sfirst = p;
else
c->slast->next = p;
c->slast = p;

odefin(&c->cur, pos, p->buf, displaysize+1,
(sword)EXT_ASCIIZ, (sword)-1, &p->ind, (text *)0,
(sword)-1, (sword)-1, &p->retl, &p->rcode);
if (c->cur.rc != 0) {
set_oerr(c->cur.rc);
return c->cur.rc;
}
}
return 0;
}


static int bind_vars(CURSOR *c)    /* bind oracle vars to node buffers */
{
NODE *p;

for (p = c->bfirst; p; p = p->next) {
/* if node is a reference, copy current value of
* referenced var to buf
*/
if (p->ref != NULL) {
strncpy(p->buf, SvPV(SvRV(p->ref), na), p->bufsize);
p->buf[p->bufsize-1] = '\0';
}
obndrv(&c->cur, p->var, (sword)-1, p->buf, (sword) p->bufsize,
(sword)EXT_ASCIIZ, (sword)-1, (sb2 *)0, (text *)0, (sword)-1,
(sword)-1);
if (c->cur.rc != 0)
return c->cur.rc;
}
return 0;
}


static void copy_back(CURSOR *c)    /* copy node buffers to referenced vars
*/
{
NODE *p;

/* for every node that has a referenced var, set var to buf */
for (p = c->bfirst; p; p = p->next)
if (p->ref)
sv_setpv(SvRV(p->ref), p->buf);
}


MODULE = Oci  PACKAGE = Oci


double
constant(name,arg)
char *  name
int  arg


#-----------------------------------------------------------
# connect
#

int
oraconnect(connect_string, pwd="")
char * connect_string
char * pwd
CODE:
{
#ifdef ORACLE7
olog(&lda, hda,
(text *)connect_string, (sword)-1,
(text *)pwd, (sword)-1,
(text *)0, (sword)-1,
(ub4) OCI_LM_DEF);
#else
olon(&lda, (text *)connect_string, (sword)-1, (text *)pwd, (sword)-1,
(sword)-1);
#endif
RETVAL = (lda.rc == 0);
set_oerr(lda.rc);
}
OUTPUT:
RETVAL

#-----------------------------------------------------------
# disconnect
#

int
oradisconnect()
CODE:
{
ologof(&lda);
RETVAL = (lda.rc == 0);
set_oerr(lda.rc);=7F
}
OUTPUT:
RETVAL

#-----------------------------------------------------------
# cursor -- create cursor for statement
#

void
oracursor(stmt,areasize=-1)
char * stmt
int areasize
PPCODE:
{
CURSOR *c;

set_oerr(0);

c = new_cursor();

oopen(&c->cur, &lda, (text *)0, (sword)-1, (sword)areasize, (text *)0,
(sword)-1);
if (c->cur.rc != 0) {
XPUSHs(sv_2mortal(newSVnv(0)));
set_oerr(c->cur.rc);
free_cursor(c);
return;
}

osql3(&c->cur, (text *)stmt, (sword)-1);
if (c->cur.rc != 0) {
XPUSHs(sv_2mortal(newSVnv(0)));
set_oerr(c->cur.rc);
oclose(&c->cur);
free_cursor(c);
return;
}

if (c->cur.ft == FT_SELECT)
if (build_slist(c, stmt)) {
XPUSHs(sv_2mortal(newSVnv(0)));
oclose(&c->cur);
free_cursor(c);
return;
}

EXTEND(sp, 2);
PUSHs(sv_2mortal(newSVnv(1)));
PUSHs(sv_newmortal());
sv_setref_pv(ST(1), "CURSORPtr", (void *) c);
}


#-----------------------------------------------------------
# oraexec -- execute PL/SQL block
#

int
oraexec(c)
CURSOR * c
CODE:
{=7F
if (bind_vars(c) == 0)
if (oexec(&c->cur) == 0) {
copy_back(c);
ocan(&c->cur);=7F
}

RETVAL = (c->cur.rc == 0);
set_oerr(c->cur.rc);
}
OUTPUT:
RETVAL


#-----------------------------------------------------------
# bind
#

int
orabind(c, var, val, len=0)
CURSOR * c
char * var
char * val
int len
CODE:
{
NODE *p;
SV *sv;

RETVAL = 1;
set_oerr(0);

/* determine len if not specified */
if (len == 0) {
if (SvROK(ST(2))) {
sv = SvRV(ST(2));
if (SvPOK(sv))
len = SvLEN(sv);
else {
set_oerr(20000);
RETVAL = 0;
goto skip_it;
}
} else
len = strlen(val);
}

/* look for existing entry with same var name */
for (p = c->bfirst; p; p = p->next)
if (strEQ(p->var, var))
break;

/* if no matching entry exists, add new node to list */
if (p == NULL) {
p = new_node(len+1);     /* +1 for \0 */
New(0, p->var, strlen(var)+1, text);
strcpy(p->var, var);

if (c->bfirst == NULL)
c->bfirst = p;
else
c->blast->next = p;
c->blast = p;
}

/* make sure buffer is big enough */
if (len+1 > p->bufsize) {
Renew(p->buf, len+1, text);
p->bufsize = len + 1;
}

/* set entry to val or reference */
if (p->ref)
SvREFCNT_dec(p->ref);
if (SvROK(ST(2))) {
p->ref = ST(2);
SvREFCNT_inc(ST(2));
SvGROW(SvRV(ST(2)), len);
} else {
strncpy(p->buf, val, min(len, strlen(val)));
p->buf[len] = '\0';
p->ref = NULL;
}
skip_it:
}
OUTPUT:
RETVAL


#-----------------------------------------------------------
# open
#

int
oraopen(c)
CURSOR * c
CODE:
{
if (bind_vars(c) == 0)
oexec(&c->cur);

RETVAL = (c->cur.rc == 0);
set_oerr(c->cur.rc);
}
OUTPUT:
RETVAL


#-----------------------------------------------------------
# fetch
#

void
orafetch(c)
CURSOR * c
PPCODE:
{
NODE *p;

ofetch(&c->cur);
if (c->cur.rc != 0) {
set_oerr(c->cur.rc);
XPUSHs(sv_2mortal(newSVnv(0)));
} else {
XPUSHs(sv_2mortal(newSVnv(1)));
for (p = c->sfirst; p; p = p->next) {
if (p->ind == BIND_NULL)
p->buf[0] = '\0';
XPUSHs(sv_2mortal(newSVpv(p->buf, strlen(p->buf))));
}
}
}


#-----------------------------------------------------------
# close
#

int
oraclose(c)
CURSOR * c
CODE:
{
ocan(&c->cur);
RETVAL = (c->cur.rc == 0);
set_oerr(c->cur.rc);
}
OUTPUT:
RETVAL

#-----------------------------------------------------------
# commit
#

int
oracommit()
CODE:
{
ocom(&lda);
RETVAL = (lda.rc == 0);
set_oerr(lda.rc);
}
OUTPUT:
RETVAL


#-----------------------------------------------------------
# rollback
#

int
orarollback()
CODE:
{
orol(&lda);
RETVAL = (lda.rc == 0);
set_oerr(lda.rc);
}
OUTPUT:
RETVAL

#-----------------------------------------------------------
MODULE = Oci PACKAGE = CURSORPtr

void
DESTROY(c)
CURSOR * c
CODE:
{
if (c != (CURSOR *) &sv_undef) {
oclose(&c->cur);
free_cursor(c);
}
}