home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume25
/
sybperl
/
part01
/
sybperl.c
< prev
Wrap
C/C++ Source or Header
|
1991-11-11
|
16KB
|
662 lines
static char SccsId[] = "@(#)sybperl.c 1.5 9/9/91";
/************************************************************************/
/* Copyright 1991 by Michael Peppler and ITF Management SA */
/* */
/* Full ownership of this software, and all rights pertaining to */
/* the for-profit distribution of this software, are retained by */
/* Michael Peppler and ITF Management SA. You are permitted to */
/* use this software without fee. This software is provided "as */
/* is" without express or implied warranty. You may redistribute */
/* this software, provided that this copyright notice is retained, */
/* and that the software is not distributed for profit. If you */
/* wish to use this software in a profit-making venture, you must */
/* first license this code and its underlying technology from */
/* ITF Management SA. */
/* */
/* Bottom line: you can have this software, you can use it, you */
/* can give it away. You just can't sell any or all parts of it */
/* without prior permission from Harris Corporation. */
/************************************************************************/
/* sybase.c
*
* Call Sybase DB-Library functions from Perl.
* Written by Michael Peppler (mpeppler@itf.ch)
* ITF Management SA, 13 rue de la Fontaine
* CH-1204 Geneva, Switzerland
* Tel: (+4122) 312 1311 Fax: (+4122) 312 1322
*/
/*
* The Perl/Sybase savestr() conflict.
* Both Perl and Sybase DB-Library have a function called savestr().
* This creates a problem when calling dbcmd() and dbuse(). There are
* several ways to work around this, one of which is to #define
* BROKEN_DBCMD, which enables some code that I've written to simulate
* dbcmd() locally. See Makefile and BUGS for details.
*/
#include "EXTERN.h"
#include "perl.h"
#undef MAX
#undef MIN
#if !defined(VERSION3)
#define str_2static(s) str_2mortal(s)
#endif
#include <sybfront.h>
#include <sybdb.h>
#include <syberror.h>
#include "patchlevel.h"
extern int wantarray;
char *savestr();
/*
* The variables that the Sybase routines set, and that you may want
* to test in your Perl script. These variables are READ-ONLY.
*/
static enum uservars
{
UV_SUCCEED, /* Returns SUCCEED */
UV_FAIL, /* Returns FAIL */
UV_NO_MORE_ROWS, /* Returns NO_MORE_ROWS */
UV_NO_MORE_RESULTS, /* Returns NO_MORE_RESULTS */
UV_ComputeId, /* Returns the compute id of the row (in dbnextrow()) */
UV_SybperlVer, /* Returns Sybperl Version/Patchlevel */
};
/*
* User subroutines that we have implemented. I've found that I can do
* all the stuff I want to with this subset of DB-Library. Let me know
* if you implement further routines.
* The names are self-explanatory.
*/
static enum usersubs
{
US_dblogin, /* This also performs a dbopen() */
US_dbopen,
US_dbclose,
US_dbcmd,
US_dbsqlexec,
US_dbresults,
US_dbnextrow,
US_dbcancel,
US_dbcanquery,
US_dbexit,
US_dbuse,
};
#define MAX_DBPROCS 25 /* Change this if you really want your perl script to talk to */
/* more than 25 dataserver connections at a time ...*/
static LOGINREC *login;
static DBPROCESS *dbproc[MAX_DBPROCS];
static int exitCalled = 0; /* Set to 1 if dbexit() has been called. */
static int ComputeId;
static int usersub();
static int userset();
static int userval();
static int err_handler(), msg_handler();
int userinit()
{
init_sybase();
}
int
init_sybase()
{
struct ufuncs uf;
char *filename = "sybase.c";
if (dbinit() == FAIL) /* initialize dblibrary */
exit(ERREXIT);
/*
* Install the user-supplied error-handling and message-handling routines.
* They are defined at the bottom of this source file.
*/
dberrhandle(err_handler);
dbmsghandle(msg_handler);
uf.uf_set = userset;
uf.uf_val = userval;
#define MAGICVAR(name, ix) uf.uf_index = ix, magicname(name, &uf, sizeof uf)
MAGICVAR("SUCCEED", UV_SUCCEED);
MAGICVAR("FAIL",UV_FAIL);
MAGICVAR("NO_MORE_ROWS", UV_NO_MORE_ROWS);
MAGICVAR("NO_MORE_RESULTS", UV_NO_MORE_RESULTS);
MAGICVAR("ComputeId", UV_ComputeId);
MAGICVAR("SybperlVer", UV_SybperlVer);
make_usub("dblogin", US_dblogin, usersub, filename);
make_usub("dbopen", US_dbopen, usersub, filename);
make_usub("dbclose", US_dbclose, usersub, filename);
make_usub("dbcmd", US_dbcmd, usersub, filename);
make_usub("dbsqlexec", US_dbsqlexec, usersub, filename);
make_usub("dbresults", US_dbresults, usersub, filename);
make_usub("dbnextrow", US_dbnextrow, usersub, filename);
make_usub("dbcancel", US_dbcancel, usersub, filename);
make_usub("dbcanquery", US_dbcanquery, usersub, filename);
make_usub("dbexit", US_dbexit, usersub, filename);
make_usub("dbuse", US_dbuse, usersub, filename);
}
static int
usersub(ix, sp, items)
int ix;
register int sp;
register int items;
{
STR **st = stack->ary_array + sp;
ARRAY *ary = stack;
register int i;
register STR *Str; /* used in str_get and str_gnum macros */
int inx = -1; /* Index into dbproc[] array. Passed as first parameter to nearly all &dbxxx() calls */
if(exitCalled)
fatal("&dbexit() has been called. Access to Sybase impossible.");
switch (ix)
{
case US_dblogin:
if (items > 2)
fatal("Usage: &dblogin([user[,pwd]])");
if (login)
fatal("&dblogin() called twice.");
else
{
int retval;
login = dblogin();
if(items)
{
DBSETLUSER(login, (char *)str_get(st[1]));
if(items > 1)
DBSETLPWD(login, (char *)str_get(st[2]));
}
dbproc[0] = dbopen(login, NULL);
str_numset(st[0], (double) 0);
}
break;
case US_dbopen:
if (items != 0)
fatal("Usage: $dbproc = &dbopen;");
else
{
int j;
for(j = 0; j < MAX_DBPROCS; ++j)
if(dbproc[j] == NULL)
break;
if(j == MAX_DBPROCS)
fatal("&dbopen: No more dbprocs available.");
dbproc[j] = dbopen(login, NULL);
str_numset(st[0], (double) j);
}
break;
case US_dbclose:
if (items != 1)
fatal("Usage: $ret = &dbclose($dbproc);");
else
{
inx = getDbProc(st[1]);
dbclose(dbproc[inx]);
dbproc[inx] = (DBPROCESS *)NULL;
}
break;
case US_dbcancel:
if (items != 1)
fatal("Usage: &dbcancel($dbproc)");
else
{
int retval;
#if defined(BROKEN_DBCMD)
DBSTRING *ptr;
DBSTRING *old;
#endif
inx = getDbProc(st[1]);
retval = dbcancel(dbproc[inx]);
str_numset(st[0], (double) retval);
#if defined(BROKEN_DBCMD)
ptr = dbproc[inx]->dbcmdbuf;
while(ptr)
{
old = ptr;
ptr = ptr->strnext;
free(old->strtext);
free(old);
}
dbproc[inx]->dbcmdbuf = NULL;
#endif
}
break;
case US_dbcanquery:
if (items != 1)
fatal("Usage: &dbcanquery($dbproc)");
else
{
int retval;
inx = getDbProc(st[1]);
retval = dbcanquery(dbproc[inx]);
str_numset(st[0], (double) retval);
}
break;
case US_dbexit:
if (items != 0)
fatal("Usage: &dbexit()");
else
{
dbexit(dbproc[0]);
exitCalled++;
str_numset(st[0], (double) 1);
}
break;
case US_dbuse:
if (items != 2)
fatal("Usage: &dbuse($dbproc, $database)");
else
{
#if defined(BROKEN_DBCMD)
/*
* Why doesn't this $@#! dbuse() call not work from within
* Perl????? (So we emulate it here, but I sure can't
* guarantee anything about portability to future versions
* of DB-Library!
*/
DBSTRING *new;
DBSTRING *sav;
char *strdup();
char buff[256];
int ret;
inx = getDbProc(st[1]);
strcpy(buff, "use ");
strcat(buff, (char *)str_get(st[2]));
sav = dbproc[inx]->dbcmdbuf;
new = (DBSTRING *)calloc(1,sizeof(DBSTRING));
new->strtext = (BYTE *)strdup((char *)buff);
new->strtotlen = strlen(new->strtext)+1;
dbproc[inx]->dbcmdbuf = new;
ret = dbsqlexec(dbproc[inx]);
ret = dbresults(dbproc[inx]);
while((ret = dbnextrow(dbproc[inx])) != NO_MORE_ROWS)
;
free(new->strtext);
free(new);
dbproc[inx]->dbcmdbuf = sav;
str_numset(st[0], (double) SUCCEED);
#else
int retval;
char str[255];
strcpy(str, (char *)str_get(st[2]));
inx = getDbProc(st[1]);
retval = dbuse(dbproc[inx], str);
str_numset(st[0], (double) retval);
#endif
}
break;
case US_dbsqlexec:
if (items != 1)
fatal("Usage: &dbsqlexec($dbproc)");
else
{
int retval;
inx = getDbProc(st[1]);
retval = dbsqlexec(dbproc[inx]);
str_numset(st[0], (double) retval);
}
break;
case US_dbresults:
if (items != 1)
fatal("Usage: &dbresults($dbproc)");
else
{
int retval;
inx = getDbProc(st[1]);
retval = dbresults(dbproc[inx]);
str_numset(st[0], (double) retval);
#if defined(BROKEN_DBCMD)
if(retval==NO_MORE_RESULTS)
{
DBSTRING *ptr = dbproc[inx]->dbcmdbuf;
DBSTRING *old;
while(ptr)
{
old = ptr;
ptr = ptr->strnext;
free(old->strtext);
free(old);
}
dbproc[inx]->dbcmdbuf = NULL;
}
#endif
}
break;
case US_dbcmd:
if (items != 2)
fatal("Usage: &dbcmd($dbproc, $str)");
else
{
int retval;
#if defined(BROKEN_DBCMD)
DBSTRING *ptr;
DBSTRING *new, *old;
char *strdup();
#endif
inx = getDbProc(st[1]);
#if defined(BROKEN_DBCMD)
ptr = dbproc[inx]->dbcmdbuf;
new = (DBSTRING *)calloc(1,sizeof(DBSTRING));
new->strtext = (BYTE *)strdup((char *)str_get(st[2]));
new->strtotlen = strlen(new->strtext)+1;
if(!ptr)
dbproc[inx]->dbcmdbuf = new;
else
{
while(ptr->strnext)
ptr = ptr->strnext;
ptr->strnext = new;
}
#else
retval = dbcmd(dbproc[inx], (char *)str_get(st[2]));
#endif
str_numset(st[0], (double) retval);
}
break;
case US_dbnextrow:
if (items != 1)
fatal("Usage: @arr = &dbnextrow($dbproc)");
else
{
int retval;
inx = getDbProc(st[1]);
--sp; /* otherwise you get an empty element at the beginning of the results array! */
retval = dbnextrow(dbproc[inx]);
if(retval == REG_ROW)
{
char buff[1024], *p;
BYTE *data;
int col, type, numcols = dbnumcols(dbproc[inx]);
int len;
DBFLT8 tmp;
ComputeId = 0;
for(col = 1, buff[0] = 0; col <= numcols; ++col)
{
type = dbcoltype(dbproc[inx], col);
len = dbdatlen(dbproc[inx],col);
data = (BYTE *)dbdata(dbproc[inx],col);
if(!data && !len)
{
strcpy(buff,"NULL");
}
else
{
switch(type)
{
case SYBCHAR:
strncpy(buff,data,len);
buff[len] = 0;
break;
case SYBINT1:
case SYBBIT: /* a bit is at least a byte long... */
sprintf(buff,"%u",*(unsigned char *)data);
break;
case SYBINT2:
sprintf(buff,"%d",*(short *)data);
break;
case SYBINT4:
sprintf(buff,"%d",*(long *)data);
break;
case SYBFLT8:
sprintf(buff,"%.6f",*(double *)data);
break;
case SYBMONEY:
dbconvert(dbproc[inx], SYBMONEY, data,-1,SYBFLT8,&tmp,-1);
sprintf(buff,"%.6f",tmp);
break;
case SYBDATETIME:
dbconvert(dbproc[inx], SYBDATETIME, data,-1,SYBCHAR,buff,-1);
break;
default:
/* ignored at the moment... */
break;
}
}
(void)astore(ary,++sp,str_2static(str_make(buff,0)));
}
}
if (retval > 0)
{
char buff[1024], *p;
BYTE *data;
int col, type, numcols;
int len;
DBFLT8 tmp;
ComputeId = retval;
numcols = dbnumalts(dbproc[inx], ComputeId);
for(col = 1, buff[0] = 0; col <= numcols; ++col)
{
type = dbalttype(dbproc[inx], ComputeId, col);
len = dbadlen(dbproc[inx], ComputeId, col);
data = (BYTE *)dbadata(dbproc[inx], ComputeId, col);
if(!data && !len)
{
strcpy(buff,"NULL");
}
else
{
switch(type)
{
case SYBCHAR:
strncpy(buff,data,len);
buff[len] = 0;
break;
case SYBINT1:
case SYBBIT: /* a bit is at least a byte long... */
sprintf(buff,"%d",*(char *)data);
break;
case SYBINT2:
sprintf(buff,"%d",*(short *)data);
break;
case SYBINT4:
sprintf(buff,"%d",*(long *)data);
break;
case SYBFLT8:
sprintf(buff,"%.6f",*(double *)data);
break;
case SYBMONEY:
dbconvert(dbproc[inx], SYBMONEY, data,-1,SYBFLT8,&tmp,-1);
sprintf(buff,"%.6f",tmp);
break;
case SYBDATETIME:
dbconvert(dbproc[inx], SYBDATETIME, data,-1,SYBCHAR,buff,-1);
break;
default:
/* ignored at the moment... */
break;
}
}
(void)astore(ary,++sp,str_2static(str_make(buff,0)));
}
}
#if defined(BROKEN_DBCMD)
/*
* We can't rely on dbcmd(),dbresults() etc. to clean up
* the dbcmdbuf linked list, so we have to it ourselves...
*/
if(retval == NO_MORE_ROWS && !DBMORECMDS(dbproc[inx]))
{
DBSTRING *ptr = dbproc[inx]->dbcmdbuf;
DBSTRING *new, *old;
while(ptr)
{
old = ptr;
ptr = ptr->strnext;
free(old->strtext);
free(old);
}
dbproc[inx]->dbcmdbuf = NULL;
}
#endif
}
break;
default:
fatal("Unimplemented user-defined subroutine");
}
return sp;
}
/*
* Return the value of a userdefined variable. These variables are all
* READ-ONLY in Perl.
*/
static int
userval(ix, str)
int ix;
STR *str;
{
char buff[24];
switch (ix)
{
case UV_SUCCEED:
str_numset(str, (double)SUCCEED);
break;
case UV_FAIL:
str_numset(str, (double)FAIL);
break;
case UV_NO_MORE_ROWS:
str_numset(str, (double)NO_MORE_ROWS);
break;
case UV_NO_MORE_RESULTS:
str_numset(str, (double)NO_MORE_RESULTS);
break;
case UV_ComputeId:
str_numset(str, (double)ComputeId);
break;
case UV_SybperlVer:
sprintf(buff, "%d.%3.3d", VERSION, PATCHLEVEL);
str_set(str, buff);
break;
}
return 0;
}
static int
userset(ix, str) /* Not used. None of these variables are user-settable */
int ix;
STR *str;
{
return 0;
}
/*ARGSUSED*/
static int err_handler(dbprocl, severity, dberr, oserr, dberrstring, oserrstr)
DBPROCESS *dbprocl;
int severity;
int dberr;
int oserr;
char *dberrstring;
char *oserrstr;
{
if ((dbprocl == NULL) || (DBDEAD(dbprocl)))
return(INT_EXIT);
else
{
fprintf(stderr,"DB-Library error:\n\t%s\n", dberrstring);
if (oserr != DBNOERR)
fprintf(stderr,"Operating-system error:\n\t%s\n", oserrstr);
return(INT_CANCEL);
}
}
/*ARGSUSED*/
static int msg_handler(dbprocl, msgno, msgstate, severity, msgtext, srvname, procname, Line)
DBPROCESS *dbprocl;
DBINT msgno;
int msgstate;
int severity;
char *msgtext;
char *srvname;
char *procname;
DBUSMALLINT Line;
{
if(msgno != 5701) /* Ignore 'Changed database context' messages */
{
fprintf (stderr,"Msg %ld, Level %d, State %d\n",
msgno, severity, msgstate);
if (strlen(srvname) > 0)
fprintf (stderr,"Server '%s', ", srvname);
if (strlen(procname) > 0)
fprintf (stderr,"Procedure '%s', ", procname);
if (Line > 0)
fprintf (stderr,"Line %d", Line);
fprintf(stderr,"\n\t%s\n", msgtext);
}
if(severity)
exit(-1);
return(0);
}
/*
* Get the index into the dbproc[] array from a Perl STR datatype.
* Check that the index is reasonably valid...
*/
int getDbProc(Str)
STR *Str;
{
int ix = (int)str_gnum(Str);
if(ix < 0 || ix >= MAX_DBPROCS)
fatal("$dbproc parameter is out of range.");
return ix;
}