/*
Project : DBD::Fulcrum
Module/Library:
Author : $Author: shari $
Revision : $Revision: 2.12 $
Check-in date : $Date: 1998/12/04 09:57:18 $
Locked by : $Locker: $
$Log: dbdimp.c,v $
Revision 2.12 1998/12/04 09:57:18 shari
0.19_03: now honors $sth->{CursorName}. NUM_OF_PARAMS unhandled, DBI takes care of it.
Revision 2.11 1998/11/24 16:14:54 shari
Now honors AutoCommit and table_info (for DBI::Shell)
Revision 2.10 1998/11/23 14:00:21 shari
Renamed Num_of_params to NUM_OF_PARAMS.
Revision 2.9 1998/11/11 16:48:36 shari
Multiple connects; release 0.19
Revision 2.8 1998/11/11 11:50:38 shari
Release 0.18.
*/
static char rcsid[]="$Id: dbdimp.c,v 2.12 1998/12/04 09:57:18 shari Exp $ (c) 1996-98, Davide Migliavacca & Inferentia (Milano) IT";
#include <stdio.h>
#include "Fulcrum.h"
#define EOI(x) if (x < 0 || x == 100) return (0)
#define NHENV SQL_NULL_HENV
#define NHDBC SQL_NULL_HDBC
#define NHSTMT SQL_NULL_HDBC
#define ERRTYPE(x) ((x < 0) && (x == SQL_ERROR))
DBISTATE_DECLARE;
static U32 options_show_matches; /* must know this when allocating buffers... */
static SQLCHAR ful_SQLSTATE[6]; /* SQLSTATE */
static SQLINTEGER ful_SQLCODE; /* SQLCODE */
static SQLCHAR ful_data_truncated[6] = "01004"; /* data truncation is controlled via LongTruncOk */
static SV *ful_mhic; /* DBD::Fulcrum::ful_maxhitsinternalcolumns */
void
dbd_init(dbistate_t *dbistate)
{
DBIS = dbistate;
ful_mhic = perl_get_sv("DBD::Fulcrum::ful_maxhitsinternalcolumns", GV_ADDMULTI);
}
int
check_error(SV *h, IV rc, char *what)
{
D_imp_xxh(h);
imp_dbh_t *imp_dbh = NULL;
imp_sth_t *imp_sth = NULL;
SQLHENV h_env = SQL_NULL_HENV;
SQLHDBC h_conn = SQL_NULL_HDBC;
SQLHSTMT h_stmt = SQL_NULL_HSTMT;
if (rc == SQL_SUCCESS && dbis->debug < 3)
return rc;
switch(DBIc_TYPE(imp_xxh)) {
case DBIt_ST:
imp_sth = (struct imp_sth_st *)(imp_xxh);
imp_dbh = (struct imp_dbh_st *)(DBIc_PARENT_COM(imp_sth));
h_stmt = imp_sth->phstmt;
break;
case DBIt_DB:
imp_dbh = (struct imp_dbh_st *)(imp_xxh);
break;
default:
croak("panic dbd_error on bad handle type");
}
h_conn = imp_dbh->hdbc;
h_env = imp_dbh->henv;
if (rc != SQL_SUCCESS && rc != SQL_NO_DATA_FOUND) {
if (h_env == NHENV) {
do_error(h,rc,NHENV,NHDBC,NHSTMT,what);
}
else {
do_error(h,rc,(SQLHENV)h_env,(SQLHDBC)h_conn,(SQLHSTMT)h_stmt,what);
}
}
/* fprintf(DBILOGFP, "<><><> CHECK_ERROR WILL RETURN %d\n", (rc == SQL_SUCCESS_WITH_INFO ? SQL_SUCCESS : rc)); */
return((rc == SQL_SUCCESS_WITH_INFO ? SQL_SUCCESS : rc));
}
void
do_error(SV *h, IV rc, SQLHENV h_env, SQLHDBC h_conn, SQLHSTMT h_stmt,
SQLCHAR *what)
{
D_imp_xxh(h);
SV *errstr = DBIc_ERRSTR(imp_xxh);
SV *state = DBIc_STATE(imp_xxh);
SQLSMALLINT length;
SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH+1];
int msgsize = SQL_MAX_MESSAGE_LENGTH+1;
msg[0]='\0';
if (h_env != NHENV) {
SQLError(h_env,h_conn,h_stmt, ful_SQLSTATE, &ful_SQLCODE, msg,
msgsize,&length);
if (dbis->debug >= 3)
fprintf(DBILOGFP,"SQLSTATE = %s\n",ful_SQLSTATE);
}
else {
strcpy((char *)msg, (char *)what);
}
sv_setiv(DBIc_ERR(imp_xxh), (IV)rc);
sv_setpv(state, (char *)ful_SQLSTATE);
sv_setpv(errstr, (char*)msg);
if (what && (h_env == NHENV)) {
sv_catpv(errstr, " (DBD: ");
sv_catpv(errstr, (char *)what);
sv_catpv(errstr, ")");
}
DBIh_EVENT2(h, ERROR_event, DBIc_ERR(imp_xxh), errstr);
if (dbis->debug >= 2)
fprintf(DBILOGFP, "%s error %d recorded: %s\n",
what, rc, SvPV(errstr,na));
}
void
fbh_dump(imp_fbh_t *fbh, int i)
{
FILE *fp = DBILOGFP;
fprintf(fp, "fbh %d: '%s' %s, ",
i, fbh->cbuf, (fbh->nullok) ? "NULLable" : "");
fprintf(fp, "type %d, dbsize %ld, dsize %ld, p%d s%d\n",
fbh->dbtype, (long)fbh->dbsize, (long)fbh->dsize,
fbh->prec, fbh->scale);
fprintf(fp, " out: ftype %d, indp %d, bufl %d, rlen %d, rcode %d\n",
fbh->ftype, fbh->indp, fbh->bufl, fbh->rlen, fbh->rcode);
}
static int
dbtype_is_long(int dbtype)
{
/* Is it a LONG, LONG RAW, LONG VARCHAR or LONG VARRAW type? */
/* Return preferred type code to use if it's a long, else 0. */
#if 0
/* not used by Fulcrum */
if (dbtype == SQL_CLOB || dbtype == SQL_BLOB) /* LONG or LONG RAW */
return dbtype; /* --> same */
#endif
if (dbtype == SQL_LONGVARCHAR) /* LONG VARCHAR */
return dbtype; /* --> LONG */
if (dbtype == SQL_LONGVARBINARY) /* LONG VARRAW */
return dbtype; /* --> LONG RAW */
return 0;
}
static int
dbtype_is_string(int dbtype) /* 'can we use SvPV to pass buffer?' */
{
switch(dbtype) {
case SQL_VARCHAR: /* VARCHAR2 */
case SQL_INTEGER: /* LONG */
case SQL_CHAR: /* RAW */
case SQL_LONGVARBINARY: /* LONG RAW */
case SQL_LONGVARCHAR: /* LONG VARCHAR*/
#if 0 /* not used by Fulcrum */
case SQL_CLOB: /* Char blob */
#endif
return 1;
}
return 0;
}
/* ================================================================== */
/* ================================================================== */
int
dbd_db_connect(SV *dbh,imp_dbh_t *imp_dbh,SQLCHAR *dbname,SQLCHAR *uid,SQLCHAR *pwd)
{
D_imp_drh_from_dbh;
char *msg;
int ret;
ret = SQLAllocConnect(imp_drh->henv,&imp_dbh->hdbc);
msg = (ERRTYPE(ret) ? "Connect allocation failed" :
"Invalid Handle");
ret = check_error(dbh,ret,msg);
if (ret != SQL_SUCCESS) {
if (imp_drh->connects == 0) {
SQLFreeEnv(imp_drh->henv);
imp_drh->henv = NHENV;
}
}
EOI(ret);
if (dbis->debug >= 2)
fprintf(DBILOGFP, "connect '%s', '%s', '%s'", dbname, uid, pwd);
ret = SQLConnect(imp_dbh->hdbc,dbname,SQL_NTS,uid,SQL_NTS,pwd,SQL_NTS);
msg = ( ERRTYPE(ret) ? "Connect failed" : "Invalid handle");
ret = check_error(dbh,ret,msg);
if (ret != SQL_SUCCESS) {
SQLFreeConnect (imp_dbh->hdbc);
if (imp_drh->connects == 0) {
SQLFreeEnv(imp_drh->henv);
imp_drh->henv = NHENV;
}
}
EOI(ret);
return 1;
}
int
dbd_db_login(SV *dbh, imp_dbh_t *imp_dbh, char *dbname, char *user, char *pwd)
{
D_imp_drh_from_dbh;
int ret;
char *msg;
if (!imp_drh->connects) {
ret = SQLAllocEnv(&imp_drh->henv);
msg = (imp_drh->henv == NHENV ?
"Total Environment allocation failure!" :
"Environment allocation failed");
ret = check_error(dbh,ret,msg);
EOI(ret);
}
imp_dbh->henv = imp_drh->henv;
ret = dbd_db_connect(dbh,imp_dbh,dbname, user, pwd);
EOI(ret);
imp_drh->connects++;
DBIc_IMPSET_on(imp_dbh); /* imp_dbh set up now */
DBIc_ACTIVE_on(imp_dbh); /* call disconnect before freeing */
return 1;
}
int
dbd_db_do(SV *dbh, char *statement) /* return 1 else 0 on failure */
{
D_imp_dbh(dbh);
int ret;
char *msg;
SQLHSTMT stmt;
ret = SQLAllocStmt(imp_dbh->hdbc,&stmt);
msg = "Statement allocation error";
ret = check_error(NULL,ret,msg);
EOI(ret);
ret = SQLExecDirect(stmt,statement,SQL_NTS);
ret = check_error(NULL,ret,"Execute immediate failed");
(void)check_error(NULL, SQLFreeStmt(stmt, SQL_DROP),
"Statement destruction error");
EOI(ret);
return 1;
}
int
dbd_db_commit(SV *dbh, imp_dbh_t *imp_dbh)
{
int ret;
char *msg;
ret = SQLTransact(imp_dbh->henv,imp_dbh->hdbc,SQL_COMMIT);
msg = (ERRTYPE(ret) ? "Commit failed" : "Invalid handle");
ret = check_error(dbh,ret,msg);
EOI(ret);
return 1;
}
int
dbd_db_rollback(SV *dbh, imp_dbh_t *imp_dbh)
{
return 1; /* Fulcrum does not support SQL_ROLLBACK */
/* int ret;
char *msg;
ret = SQLTransact(henv,imp_dbh->hdbc,SQL_ROLLBACK);
msg = (ERRTYPE(ret) ? "Rollback failed" : "Invalid handle");
ret = check_error(dbh,ret,msg);
EOI(ret);
return 1;
*/
}
int
dbd_db_disconnect(SV *dbh, imp_dbh_t *imp_dbh)
{
D_imp_drh_from_dbh;
int ret;
char *msg;
/* We assume that disconnect will always work */
/* since most errors imply already disconnected. */
DBIc_ACTIVE_off(imp_dbh);
ret = SQLDisconnect(imp_dbh->hdbc);
msg = (ERRTYPE(ret) ? "Disconnect failed" : "Invalid handle");
ret = check_error(dbh,ret,msg);
EOI(ret);
SQLFreeConnect (imp_dbh->hdbc);
msg = (ERRTYPE(ret) ? "FreeConnect failed" : "Invalid handle");
ret = check_error(dbh,ret,msg);
EOI(ret);
imp_dbh->hdbc = SQL_NULL_HDBC;
imp_drh->connects--;
if (imp_drh->connects == 0) {
ret = SQLFreeEnv(imp_drh->henv);
msg = (ERRTYPE(ret) ? "FreeEnv failed" : "Invalid handle");
ret = check_error(dbh,ret,msg);
EOI(ret);
}
/* We don't free imp_dbh since a reference still exists */
/* The DESTROY method is the only one to 'free' memory. */
/* Note that statement objects may still exists for this dbh! */
return 1;
}
int
dbd_discon_all(SV *drh, imp_drh_t *imp_drh)
{
dTHR;
/* The disconnect_all concept is flawed and needs more work */
if (!dirty && !SvTRUE(perl_get_sv("DBI::PERL_ENDING",0))) {
sv_setiv(DBIc_ERR(imp_drh), (IV)1);
sv_setpv(DBIc_ERRSTR(imp_drh),
(char*)"disconnect_all not implemented");
DBIh_EVENT2(drh, ERROR_event,
DBIc_ERR(imp_drh), DBIc_ERRSTR(imp_drh));
return FALSE;
}
if (perl_destruct_level)
perl_destruct_level = 0;
return FALSE;
}
void
dbd_db_destroy(SV *dbh, imp_dbh_t *imp_dbh)
{
if (DBIc_ACTIVE(imp_dbh))
dbd_db_disconnect(dbh, imp_dbh);
/* Nothing in imp_dbh to be freed */
DBIc_IMPSET_off(imp_dbh);
}
int
dbd_db_STORE_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv, SV *valuesv)
{
STRLEN kl;
char *key = SvPV(keysv,kl);
SV *cachesv = NULL;
int on = SvTRUE(valuesv);
int ret;
char *msg;
if (kl==10 && strEQ(key, "AutoCommit")) {
/* do nothing, not supported by SearchServer,
BUT honor correct behaviour */
if (on) {
DBIc_set(imp_dbh,DBIcf_AutoCommit, on);
}
cachesv = &sv_yes; /* cache new state */
} else {
return FALSE;
}
if (cachesv) /* cache value for later DBI 'quick' fetch? */
hv_store((HV*)SvRV(dbh), key, kl, cachesv, 0);
return TRUE;
}
SV *
dbd_db_FETCH_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv)
{
STRLEN kl;
char *key = SvPV(keysv,kl);
SV *retsv = NULL;
/* Default to caching results for DBI dispatch quick_FETCH */
int cacheit = TRUE;
if (kl==10 && strEQ(key, "AutoCommit")) {
retsv = boolSV(DBIc_has(imp_dbh,DBIcf_AutoCommit));
}
if (!retsv)
return Nullsv;
if (cacheit) { /* cache for next time (via DBI quick_FETCH) */
SV **svp = hv_fetch((HV*)SvRV(dbh), key, kl, 1);
sv_free(*svp);
*svp = retsv;
(void)SvREFCNT_inc(retsv); /* so sv_2mortal won't free it */
}
if (retsv == &sv_yes || retsv == &sv_no)
return retsv; /* no need to mortalize yes or no */
return sv_2mortal(retsv);
}
/* ================================================================== */
int
dbd_st_prepare(SV *sth, imp_sth_t *imp_sth, char *statement, SV *attribs)
{
D_imp_dbh_from_sth;
int ret =0;
short params =0;
char *msg;
imp_sth->done_desc = 0;
ret = SQLAllocStmt(imp_dbh->hdbc,&imp_sth->phstmt);
msg = "SQLAllocStmt error";
ret = check_error(sth,ret,msg);
EOI(ret);
ret = SQLPrepare(imp_sth->phstmt,statement,SQL_NTS);
msg = "SQLPrepare error";
ret = check_error(sth,ret,msg);
EOI(ret);
/* ret = SQLSetStmtOption(imp_sth->phstmt, SQL_SS_CAPABLE, SQL_SS_NOTIFYDOCSTAT);
msg = "SQLSetStmtOption error";
ret = check_error(sth,ret,msg);
EOI(ret);
*/
if (dbis->debug >= 5) {
stmt_dump (imp_sth->phstmt);
}
#if 0
ret = SQLNumParams(imp_sth->phstmt,¶ms);
#else
ret = 0;
params = 0;
#endif
msg = "Unable to determine number of parameters";
ret = check_error(sth,ret,msg);
EOI(ret);
DBIc_NUM_PARAMS(imp_sth) = params;
if (params > 0 ){
/* scan statement for '?', ':1' and/or ':foo' style placeholders*/
dbd_preparse(imp_sth, statement);
} else { /* assuming a parameterless select */
dbd_describe(sth,imp_sth );
}
DBIc_IMPSET_on(imp_sth);
return 1;
}
void
dbd_preparse(imp_sth_t *imp_sth, SQLCHAR *statement)
{
bool in_literal = FALSE;
SQLCHAR *src;
SQLCHAR *start;
SQLCHAR *dest;
phs_t phs_tpl;
SV *phs_sv;
int idx=0, style=0, laststyle=0;
/* allocate room for copy of statement with spare capacity */
/* for editing ':1' into ':p1' so we can use obndrv. */
imp_sth->statement = (char*)safemalloc(strlen(statement) +
(DBIc_NUM_PARAMS(imp_sth)*4));
/* initialise phs ready to be cloned per placeholder */
memset(&phs_tpl, '\0',sizeof(phs_tpl));
phs_tpl.ftype = 1; /* VARCHAR2 */
src = statement;
dest = imp_sth->statement;
while(*src) {
if (*src == '\'')
in_literal = ~in_literal;
if ((*src != ':' && *src != '?') || in_literal) {
*dest++ = *src++;
continue;
}
start = dest; /* save name inc colon */
*dest++ = *src++;
if (*start == '?') { /* X/Open standard */
sprintf(start,":p%d", ++idx); /* '?' -> ':1' (etc) */
dest = start+strlen(start);
style = 3;
} else if (isDIGIT(*src)) { /* ':1' */
idx = atoi(src);
*dest++ = 'p'; /* ':1'->':p1' */
if (idx > MAX_BIND_VARS || idx <= 0)
croak("Placeholder :%d index out of range", idx);
while(isDIGIT(*src))
*dest++ = *src++;
style = 1;
} else if (isALNUM(*src)) { /* ':foo' */
while(isALNUM(*src)) /* includes '_' */
*dest++ = *src++;
style = 2;
} else { /* perhaps ':=' PL/SQL construct */
continue;
}
*dest = '\0'; /* handy for debugging */
if (laststyle && style != laststyle)
croak("Can't mix placeholder styles (%d/%d)",style,laststyle);
laststyle = style;
if (imp_sth->bind_names == NULL)
imp_sth->bind_names = newHV();
phs_tpl.sv = &sv_undef;
phs_sv = newSVpv((char*)&phs_tpl, sizeof(phs_tpl));
hv_store(imp_sth->bind_names, start, (STRLEN)(dest-start), phs_sv, 0);
/* warn("bind_names: '%s'\n", start); */
}
*dest = '\0';
if (imp_sth->bind_names) {
DBIc_NUM_PARAMS(imp_sth) = (int)HvKEYS(imp_sth->bind_names);
if (dbis->debug >= 2)
fprintf(DBILOGFP, "scanned %d distinct placeholders\n",
(int)DBIc_NUM_PARAMS(imp_sth));
}
}
int
dbd_bind_ph(SV *sth,
imp_sth_t *imp_sth,
SV *ph_namesv,
SV *newvalue,
IV sql_type,
SV *attribs,
int is_inout,
IV maxlen)
{
D_imp_dbh_from_sth;
SV **svp;
STRLEN name_len;
char *name;
phs_t *phs;
STRLEN value_len;
void *value_ptr;
int ret;
char *msg;
short param = SQL_PARAM_INPUT,
ctype = SQL_C_DEFAULT, stype = SQL_CHAR, scale = 0;
unsigned prec=0;
int nullok = SQL_NTS;
if (SvNIOK(ph_namesv) ) { /* passed as a number */
char buf[90];
name = buf;
sprintf(name, ":p%d", (int)SvIV(ph_namesv));
name_len = strlen(name);
} else {
name = SvPV(ph_namesv, name_len);
}
if (dbis->debug >= 2)
fprintf(DBILOGFP, "bind %s <== '%s' (attribs: %s)\n",
name, SvPV(newvalue,na), SvPV(attribs,na) );
svp = hv_fetch(imp_sth->bind_names, name, name_len, 0);
if (svp == NULL)
croak("dbd_bind_ph placeholder '%s' unknown", name);
phs = (phs_t*)((void*)SvPVX(*svp)); /* placeholder struct */
if (phs->sv == &sv_undef) { /* first bind for this placeholder */
phs->sv = newSV(0);
phs->ftype = 1;
}
if (attribs) {
/* Setup / Clear attributes as defined by attribs. */
/* If attribs is EMPTY then reset attribs to default. */
/* XXX */
if ( (svp =hv_fetch((HV*)SvRV(attribs), "Stype",5, 0)) != NULL)
stype = phs->ftype = SvIV(*svp);
if ( (svp=hv_fetch((HV*)SvRV(attribs), "Ctype",5, 0)) != NULL)
ctype = SvIV(*svp);
if ( (svp=hv_fetch((HV*)SvRV(attribs), "Prec",4, 0)) != NULL)
prec = SvIV(*svp);
if ( (svp=hv_fetch((HV*)SvRV(attribs), "Scale",5, 0)) != NULL)
scale = SvIV(*svp);
if ( (svp=hv_fetch((HV*)SvRV(attribs), "Nullok",6, 0)) != NULL)
nullok = SvIV(*svp);
} /* else if NULL / UNDEF then don't alter attributes. */
/* This approach allows maximum performance when */
/* rebinding parameters often (for multiple executes). */
/* At the moment we always do sv_setsv() and rebind. */
/* Later we may optimise this so that more often we can */
/* just copy the value & length over and not rebind. */
if (SvOK(newvalue)) {
/* XXX need to consider oraperl null vs space issues? */
/* XXX need to consider taking reference to source var */
sv_setsv(phs->sv, newvalue);
value_ptr = SvPV(phs->sv, value_len);
phs->indp = 0;
} else {
sv_setsv(phs->sv,0);
value_ptr = "";
value_len = 0;
phs->indp = SQL_NULL_DATA;
}
if (!nullok && !SvOK(phs->sv)) {
fprintf(DBILOGFP,"phs->sv is not OK\n");
}
#if 0
ret = SQLBindParameter(imp_sth->phstmt,(int)SvIV(ph_namesv),
param,ctype,stype,prec,scale,SvPVX(phs->sv),0,(phs->indp != 0 && nullok)?&phs->indp:NULL);
#else
ret = 0;
#endif
msg = ( ERRTYPE(ret) ? "Bind failed" : "Invalid Handle");
ret = check_error(sth,ret,msg);
EOI(ret);
return 1;
}
int
dbd_describe(SV *h, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
SQLCHAR *cbuf_ptr;
int t_cbufl=0;
short f_cbufl[MAX_COLS];
short num_fields;
int i, ret;
char *msg;
SV *svp;
if (imp_sth->done_desc)
return 1; /* success, already done it */
imp_sth->done_desc = 1;
ret = SQLNumResultCols(imp_sth->phstmt,&num_fields);
msg = ( ERRTYPE(ret) ? "SQLNumResultCols failed" : "Invalid Handle");
ret = check_error(h,ret,msg);
EOI(ret);
DBIc_NUM_FIELDS(imp_sth) = num_fields;
/* allocate field buffers */
Newz(42, imp_sth->fbh, num_fields, imp_fbh_t);
/* allocate a buffer to hold all the column names */
Newz(42, imp_sth->fbh_cbuf,(num_fields * (MAX_COL_NAME_LEN+1)), char);
cbuf_ptr = (char *)imp_sth->fbh_cbuf;
/* Get number of fields and space needed for field names */
for(i=0; i < num_fields; ++i ) {
char cbuf[MAX_COL_NAME_LEN];
char dbtype;
imp_fbh_t *fbh = &imp_sth->fbh[i];
f_cbufl[i] = sizeof(cbuf);
ret = SQLDescribeCol(imp_sth->phstmt,i+1,cbuf_ptr,MAX_COL_NAME_LEN,
&f_cbufl[i],&fbh->dbtype,&fbh->prec,&fbh->scale,&fbh->nullok);
msg = (ERRTYPE(ret) ? "DescribeCol failed" : "Invalid Handle");
ret = check_error(h,ret,msg);
EOI(ret);
ret = SQLColAttributes(imp_sth->phstmt,
i+1,
SQL_COLUMN_LENGTH,
NULL,
0,
NULL ,
(SDWORD FAR *)&fbh->dbsize);
msg = (ERRTYPE(ret) ? "ColAttributes failed" : "Invalid Handle");
ret = check_error(h,ret,msg);
EOI(ret);
ret = SQLColAttributes(imp_sth->phstmt,i+1,SQL_COLUMN_DISPLAY_SIZE,
NULL, 0, NULL ,(SDWORD FAR *)&fbh->dsize);
msg = (ERRTYPE(ret) ? "ColAttributes failed" : "Invalid Handle");
ret = check_error(h,ret,msg);
EOI(ret);
/* SHARI
fprintf(DBILOGFP, "Describe: \"%s\" of size %i\n", cbuf_ptr, fbh->dbsize);
SHARI */
fbh->imp_sth = imp_sth;
fbh->cbuf = cbuf_ptr;
fbh->cbufl = f_cbufl[i];
fbh->cbuf[fbh->cbufl] = '\0'; /* ensure null terminated */
cbuf_ptr += fbh->cbufl + 1; /* increment name pointer */
/* Now define the storage for this field data. */
fbh->ftype = SQL_C_CHAR ;
fbh->rlen = fbh->bufl = fbh->dsize+1;/* +1: STRING null terminator */
/* If DBD::Fulcrum::ful_maxhitsinternalcolumns is set, add space for 7 * 2 * its value bytes
to account for match start/end codes */
if (SvIV(ful_mhic) > 0) {
if (dbis->debug > 7)
fprintf(DBILOGFP, "[ful_mhic expanding buffer from %d to %d]",
fbh->bufl,
fbh->bufl + (SvIV(ful_mhic) * 14)
);
fbh->rlen = (fbh->bufl += (SvIV(ful_mhic) * 14));
}
/* currently we use an sv, later we'll use an array */
fbh->sv = newSV((STRLEN)fbh->bufl);
(void)SvUPGRADE(fbh->sv, SVt_PV);
SvREADONLY_on(fbh->sv);
(void)SvPOK_only(fbh->sv);
fbh->buf = (char *)SvPVX(fbh->sv);
/* BIND */
ret = SQLBindCol(imp_sth->phstmt,
i+1,
SQL_C_CHAR,
fbh->buf,
fbh->bufl,
(SDWORD FAR *)&fbh->rlen);
if (ret == SQL_SUCCESS_WITH_INFO ) {
warn("BindCol error on %s: %d", fbh->cbuf);
} else {
msg = (ERRTYPE(ret) ? "BindCol failed" : "Invalid Handle");
ret = check_error(h,ret,msg);
EOI(ret);
}
if (dbis->debug >= 2)
fbh_dump(fbh, i);
}
return 1;
}
int
dbd_st_execute(SV *sth, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
char *msg;
int ret ;
/* describe and allocate storage for results */
if (!imp_sth->done_desc && !dbd_describe(sth, imp_sth)) {
/* dbd_describe has already called check_error() */
return 0;
}
ret = SQLExecute(imp_sth->phstmt);
msg = "SQLExecute failed";
ret = check_error(sth,ret,msg);
EOI(ret);
if (DBIc_NUM_FIELDS(imp_sth) > 0) { /* is a SELECT */
DBIc_ACTIVE_on(imp_sth);
}
else {
/* assume CRUD, get last row's FT_CID */
SQLGetStmtOption (imp_sth->phstmt, SQL_SS_ROW_ID, (PTR *)&imp_sth->ful_last_row_id);
}
return 1;
}
AV *
dbd_st_fetch(SV *sth, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
int debug = dbis->debug;
int num_fields;
int i,ret;
AV *av;
char *msg;
int data_truncated_detected = 0;
int data_truncated = 0;
/* Check that execute() was executed sucessfuly. This also implies */
/* that dbd_describe() executed sucessfuly so the memory buffers */
/* are allocated and bound. */
if ( !DBIc_ACTIVE(imp_sth) ) {
check_error(sth, -3 , "no statement executing (perhaps you need to call execute first)");
return Nullav;
}
if ((ret = SQLFetch(imp_sth->phstmt)) != SQL_SUCCESS ) {
if (ret != SQL_NO_DATA_FOUND) { /* was not just end-of-fetch */
msg = (ERRTYPE(ret) ? "Fetch failed" : "Invalid Handle");
ret = check_error(sth,ret,msg);
if (ret == SQL_SUCCESS && strEQ(SvPV(DBIc_STATE(imp_sth),na), ful_data_truncated)) {
/* check_error transforms SQL_SUCCESS_WITH_INFO in SQL_SUCCESS */
data_truncated_detected = 1;
}
else {
return Nullav;
}
if (debug >= 3)
fprintf(DBILOGFP, " dbd_st_fetch failed, rc=%d,",ret);
}
else
return Nullav;
}
av = DBIS->get_fbav(imp_sth);
num_fields = AvFILL(av)+1;
if (debug >= 3)
fprintf(DBILOGFP, " dbd_st_fetch %d fields\n", num_fields);
for(i=0; i < num_fields; ++i) {
imp_fbh_t *fbh = &imp_sth->fbh[i];
SV *sv = AvARRAY(av)[i]; /* Note: we reuse the supplied SV */
data_truncated = 0;
if (fbh->rlen > -1) { /* normal case - column is not null */
if (fbh->rlen > fbh->bufl) {
data_truncated = 1; /* should the previous check miss... pg 2-9 C Dev Ref */
sv_setpvn(sv, fbh->buf, fbh->bufl); /* not null-terminated when trunc! */
}
else {
/*SvCUR_set(fbh->sv, fbh->rlen);*/
sv_setpvn(sv,fbh->buf, fbh->rlen);
}
}
else { /* column contains a null value */
/* fbh->indp = fbh->rlen;
fbh->rlen = 0; */
(void)SvOK_off(sv);
}
if (debug >= 2)
fprintf(DBILOGFP, "\t%d: rc=%d '%s' (bufl: %d rlen: %d trunc: %d)\n", i, ret, SvPV(sv,na), fbh->bufl, fbh->rlen, data_truncated);
}
if (debug >= 2 && (data_truncated ^ data_truncated_detected))
fprintf(DBILOGFP, "\tWarning: no truncated field detected but 01004\n");
return av;
}
int
dbd_st_blob_read(SV *sth,
imp_sth_t *imp_sth,
int field,
long offset,
long len,
SV *destrv,
long destoffset)
{
D_imp_dbh_from_sth;
int retl=0;
SV *bufsv;
int rtval=0;
char *msg;
bufsv = SvRV(destrv);
sv_setpvn(bufsv,"",0); /* ensure it's writable string */
SvGROW(bufsv, len+destoffset+1); /* SvGROW doesn't do +1 */
rtval = SQLGetData(imp_sth->phstmt,
field,
SQL_C_DEFAULT,
SvPVX(bufsv),
len+destoffset,
(SDWORD FAR *)&retl);
msg = (ERRTYPE(rtval) ? "GetData failed to read blob":"Invalid Handle");
rtval = check_error(sth,rtval,msg);
EOI(rtval);
SvCUR_set(bufsv, len );
*SvEND(bufsv) = '\0'; /* consistent with perl sv_setpvn etc */
return 1;
}
int
dbd_st_rows(SV *sth,imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
int rows, ret;
char *msg;
ret = SQLRowCount(imp_sth->phstmt,(SDWORD FAR *)&rows);
msg = (ERRTYPE(ret) ? "SQLRowCount failed" : "Invalid Handle");
ret = check_error(sth,ret,msg);
EOI(ret);
return(rows);
}
int
dbd_st_finish(SV *sth, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
int ret;
char *msg;
if (DBIc_ACTIVE(imp_sth)) {
/*ret = SQLCancel(imp_sth->phstmt);*/
ret = SQLFreeStmt(imp_sth->phstmt, SQL_CLOSE);
msg = (ERRTYPE(ret) ? "SQLCancel failed" : "Invalid Handle");
}
DBIc_ACTIVE_off(imp_sth);
return 1;
}
void
dbd_st_destroy(SV *sth, imp_sth_t *imp_sth)
{
D_imp_dbh_from_sth;
int i;
/* Check if an explicit disconnect() or global destruction has */
/* disconnected us from the database before attempting to close. */
if (DBIc_ACTIVE(imp_dbh)) {
}
/* Free off contents of imp_sth */
for(i=0; i < DBIc_NUM_FIELDS(imp_sth); ++i) {
imp_fbh_t *fbh = &imp_sth->fbh[i];
sv_free(fbh->sv);
}
Safefree(imp_sth->fbh);
Safefree(imp_sth->fbh_cbuf);
Safefree(imp_sth->statement);
if (imp_sth->bind_names) {
HV *hv = imp_sth->bind_names;
SV *sv;
char *key;
I32 retlen;
hv_iterinit(hv);
while( (sv = hv_iternextsv(hv, &key, &retlen)) != NULL ) {
phs_t *phs_tpl;
if (sv != &sv_undef) {
phs_tpl = (phs_t*)SvPVX(sv);
sv_free(phs_tpl->sv);
}
}
sv_free((SV*)imp_sth->bind_names);
}
i = SQLFreeStmt (imp_sth->phstmt, SQL_DROP);
if (i != SQL_SUCCESS && i != SQL_INVALID_HANDLE) {
check_error(NULL,i, "Statement destruction error");
}
/* End Chet */
DBIc_IMPSET_off(imp_sth); /* let DBI know we've done it */
}
int
dbd_st_STORE_attrib (SV *sth, imp_sth_t *imp_sth, SV *keysv, SV *valuesv)
{
STRLEN kl;
char *key = SvPV(keysv,kl);
SV *cachesv = NULL;
int on = SvTRUE(valuesv);
if (kl==4 && strEQ(key, "long")) {
imp_sth->long_buflen = SvIV(valuesv);
} else if (kl==5 && strEQ(key, "trunc")) {
imp_sth->long_trunc_ok = on;
} else {
return FALSE;
}
if (cachesv) /* cache value for later DBI 'quick' fetch? */
hv_store((HV*)SvRV(sth), key, kl, cachesv, 0);
return TRUE;
}
SV *
dbd_st_FETCH_attrib (SV *sth, imp_sth_t *imp_sth, SV *keysv)
{
STRLEN kl;
char *key = SvPV(keysv,kl);
int i;
SV *retsv = NULL;
/* Default to caching results for DBI dispatch quick_FETCH */
int cacheit = TRUE;
if (kl==13 && strEQ(key, "NUM_OF_PARAMS")) {
return Nullsv; /* handled by DBI */
}
if (!imp_sth->done_desc && !dbd_describe(sth, imp_sth)) {
/* dbd_describe has already called ora_error() */
return Nullsv; /* XXX not quite the right thing to do? */
}
i = DBIc_NUM_FIELDS(imp_sth);
if (kl == 7 && strEQ(key, "lengths")) {
AV *av = newAV();
retsv = newRV((SV*)av);
while(--i >= 0)
av_store(av, i, newSViv((IV)imp_sth->fbh[i].dsize));
} else if (kl == 5 && strEQ(key, "types")) {
AV *av = newAV();
retsv = newRV((SV*)av);
while(--i >= 0)
av_store(av, i, newSViv(imp_sth->fbh[i].dbtype));
} else if (kl == 15 && strEQ(key, "ful_last_row_id")) {
/* thanks to Loic Dachary for this... */
HV *bn = imp_sth->bind_names;
retsv = newSViv( imp_sth->ful_last_row_id );
} else if (kl == 4 && strEQ(key, "NAME")) {
AV *av = newAV();
retsv = newRV((SV*)av);
while(--i >= 0)
av_store(av, i, newSVpv((char*)imp_sth->fbh[i].cbuf,0));
} else if (kl == 10 && strEQ(key, "CursorName")) {
/* Thanks to Peter Wyngaard for this ... */
char cursor_name[SQL_MAX_CURSOR_NAME_LEN + 1];
if (SQLGetCursorName (imp_sth->phstmt,
cursor_name,
SQL_MAX_CURSOR_NAME_LEN,
NULL)
== SQL_SUCCESS)
retsv = newSVpv (cursor_name, 0);
else
retsv = Nullsv;
}
else {
return Nullsv;
}
if (cacheit) { /* cache for next time (via DBI quick_FETCH) */
SV **svp = hv_fetch((HV*)SvRV(sth), key, kl, 1);
sv_free(*svp);
*svp = retsv;
(void)SvREFCNT_inc(retsv); /* so sv_2mortal won't free it */
}
return sv_2mortal(retsv);
}
void
stmt_dump(SQLHSTMT hstmt)
{
FILE *fp = DBILOGFP;
I32 outopt;
fprintf(fp, "SearchServer-specific Options for stmt hndl: '%x'\n\t", hstmt);
SQLGetStmtOption(hstmt, SQL_CONCURRENCY, &outopt);
fprintf(fp, "SQL_CONCURRENCY: %x, ", outopt);
SQLGetStmtOption(hstmt, SQL_CURSOR_TYPE, &outopt);
fprintf(fp, "SQL_CURSOR_TYPE: %x, ", outopt);
SQLGetStmtOption(hstmt, SQL_MAX_ROWS, &outopt);
fprintf(fp, "SQL_MAX_ROWS: %x, ", outopt);
SQLGetStmtOption(hstmt, SQL_QUERY_TIMEOUT, &outopt);
fprintf(fp, "SQL_QUERY_TIMEOUT: %x, ", outopt);
SQLGetStmtOption(hstmt, SQL_ROWSET_SIZE, &outopt);
fprintf(fp, "SQL_ROWSET_SIZE: %x, ", outopt);
SQLGetStmtOption(hstmt, SQL_SS_CAPABLE, &outopt);
fprintf(fp, "\n\tSQL_SS_CAPABLE: %x ", outopt);
if (outopt & SQL_SS_KEEPRESULT) fprintf(fp, "keepresult, ");
if (outopt & SQL_SS_NOTIFYMAXROWS) fprintf(fp, "notifymaxrows, ");
if (outopt & SQL_SS_NOTIFYTIMEOUT) fprintf(fp, "notifytimeout, ");
if (outopt & SQL_SS_NOTIFYNOROWS) fprintf(fp, "notifynorows, ");
if (outopt & SQL_SS_NOTIFYDOCSTAT) fprintf(fp, "notifydocstat, ");
SQLGetStmtOption(hstmt, SQL_SS_SHOW_MATCHES, &outopt);
fprintf(fp,"\n\tSQL_SS_SHOW_MATCHES: %x, ", outopt);
SQLGetStmtOption(hstmt, SQL_SS_SHOW_SGR, &outopt);
fprintf(fp,"SQL_SS_SHOW_SGR: %x", outopt);
SQLGetStmtOption(hstmt, SQL_SS_ROW_ID, &outopt);
fprintf(fp, "\n\tSQL_SS_ROW_ID: %x ", outopt);
fprintf(fp,"\n");
}
/* --------------------------------------- */