/* $Id: DBIXS.h,v 11.7 2002/06/05 22:39:41 timbo Exp $
 *
 * Copyright (c) 1994-2002  Tim Bunce  Ireland
 *
 * See COPYRIGHT section in DBI.pm for usage and distribution rights.
 */

/* DBI Interface Definitions for DBD Modules */

#ifndef DBIXS_VERSION				/* prevent multiple inclusion */

#ifndef DBIS
#define DBIS	dbis	/* default name for dbistate_t variable	*/
#endif

/* first pull in the standard Perl header files for extensions	*/
#define PERL_POLLUTE
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#ifdef debug		/* causes problems with DBIS->debug	*/
#undef debug
#endif

/* Perl backwards compatibility definitions */
#include "dbipport.h"

/* DBI SQL_* type definitions */
#include "dbi_sql.h"


/* The DBIXS_VERSION value will be incremented whenever new code is
 * added to the interface (this file) or significant changes are made.
 * It's primary goal is to allow newer drivers to compile against an
 * older installed DBI. This is mainly an issue whilst the API grows
 * and learns from the needs of various drivers.  See also the
 * DBISTATE_VERSION macro below. You can think of DBIXS_VERSION as
 * being a compile time check and DBISTATE_VERSION as a runtime check.
 */
#define DBIXS_VERSION 93

#ifdef NEED_DBIXS_VERSION
#if NEED_DBIXS_VERSION > DBIXS_VERSION
error You_need_to_upgrade_your_DBI_module_before_building_this_driver
#endif
#else
#define NEED_DBIXS_VERSION DBIXS_VERSION
#endif


#if defined(USE_THREADS) && !defined(DBI_NO_THREADS)
#define DBI_USE_THREADS
#define DBI_LOCK	MUTEX_LOCK(DBIS->mutex)
#define DBI_UNLOCK	MUTEX_UNLOCK(DBIS->mutex)
#define dbi_mutex perl_mutex
#define dbi_cond  perl_cond
#else
#define DBI_LOCK
#define DBI_UNLOCK
#define dbi_mutex void
#define dbi_cond  void
#endif


/* forward struct declarations						*/

typedef struct dbistate_st dbistate_t;
/* implementor needs to define actual struct { dbih_??c_t com; ... }*/
typedef struct imp_drh_st imp_drh_t;	/* driver			*/
typedef struct imp_dbh_st imp_dbh_t;	/* database			*/
typedef struct imp_sth_st imp_sth_t;	/* statement			*/
typedef struct imp_fdh_st imp_fdh_t;	/* field descriptor		*/
typedef struct imp_xxh_st imp_xxh_t;	/* any (defined below)		*/



/* --- DBI Handle Common Data Structure (all handles have one) ---	*/

/* Handle types. Code currently assumes child = parent + 1.		*/
#define DBIt_DR		1
#define DBIt_DB		2
#define DBIt_ST		3
#define DBIt_FD		4

/* component structures */

typedef struct dbih_com_std_st {
    U32  flags;
    int  call_depth;	/* used by DBI to track nested calls (int)	*/
    U16  type;		/* DBIt_DR, DBIt_DB, DBIt_ST			*/
    HV   *my_h;		/* copy of outer handle HV (not refcounted)	*/
    SV   *parent_h;	/* parent inner handle (ref to hv) (r.c.inc)	*/
    imp_xxh_t *parent_com;	/* parent com struct shortcut		*/
    dbi_cond  *thr_cond;/* condition for thread access (see dispatch)	*/

    HV   *imp_stash;	/* who is the implementor for this handle	*/
    SV   *imp_data;	/* optional implementors data (for perl imp's)	*/

    I32  kids;		/* count of db's for dr's, st's for db's etc	*/
    I32  active_kids;	/* kids which are currently DBIc_ACTIVE		*/
    U32  thr_user;	/* thread id currently using the handle		*/
    dbistate_t *dbistate;
} dbih_com_std_t;

typedef struct dbih_com_attr_st {
    /* These are copies of the Hash values (ref.cnt.inc'd)	*/
    /* Many of the hash values are themselves references	*/
    SV *Debug;
    SV *State;		/* Standard SQLSTATE, 5 char string	*/
    SV *Err;		/* Native engine error code		*/
    SV *Errstr;		/* Native engine error message		*/
    SV *spare;
    U32  LongReadLen;	/* auto read length for long/blob types	*/
    SV *FetchHashKeyName;	/* for fetchrow_hashref		*/
    /* (NEW FIELDS?... DON'T FORGET TO UPDATE dbih_clearcom()!)	*/
} dbih_com_attr_t;


struct dbih_com_st {	/* complete core structure (typedef'd above)	*/
    dbih_com_std_t	std;
    dbih_com_attr_t	attr;
};

/* This 'implementors' type the DBI defines by default as a way to	*/
/* refer to the imp_??h data of a handle without considering its type.	*/
struct imp_xxh_st { struct dbih_com_st com; };

/* Define handle-type specific structures for implementors to include	*/
/* at the start of their private structures.				*/

typedef struct {		/* -- DRIVER --				*/
    dbih_com_std_t	std;
    dbih_com_attr_t	attr;
    HV          *cached_kids;	/* $drh->connect_cached(...)		*/
} dbih_drc_t;

typedef struct {		/* -- DATABASE --			*/
    dbih_com_std_t	std;	/* \__ standard structure		*/
    dbih_com_attr_t	attr;	/* /   plus... (nothing else right now)	*/
    HV          *cached_kids;	/* $dbh->prepare_cached(...)		*/
} dbih_dbc_t;

typedef struct {		/* -- STATEMENT --			*/
    dbih_com_std_t	std;	/* \__ standard structure		*/
    dbih_com_attr_t	attr;	/* /   plus ...				*/

    int 	num_params;	/* number of placeholders		*/
    int 	num_fields;	/* NUM_OF_FIELDS, must be set		*/
    AV  	*fields_svav;	/* special row buffer (inc bind_cols)	*/
    IV		row_count;	/* incremented by get_fbav()		*/

    AV		*fields_fdav;	/* not used yet, may change */

    I32  spare1;
    void *spare2;
} dbih_stc_t;


/* XXX THIS STRUCTURE SHOULD NOT BE USED */
typedef struct {		/* -- FIELD DESCRIPTOR --		*/
    dbih_com_std_t	std;	/* standard structure (not fully setup)	*/

    /* core attributes (from DescribeCol in ODBC)		*/
    char *col_name;		/* see dbih_make_fdsv		*/
    I16   col_name_len;
    I16   col_sql_type;
    I16   col_precision;
    I16   col_scale;
    I16   col_nullable;

    /* additional attributes (from ColAttributes in ODBC)	*/
    I32   col_length;
    I32   col_disp_size;

    I32  spare1;
    void *spare2;
} dbih_fdc_t;


#define _imp2com(p,f)      	((p)->com.f)

#define DBIc_FLAGS(imp)		_imp2com(imp, std.flags)
#define DBIc_TYPE(imp)		_imp2com(imp, std.type)
#define DBIc_CALL_DEPTH(imp)	_imp2com(imp, std.call_depth)
#define DBIc_MY_H(imp)  	_imp2com(imp, std.my_h)
#define DBIc_PARENT_H(imp)  	_imp2com(imp, std.parent_h)
#define DBIc_PARENT_COM(imp)  	_imp2com(imp, std.parent_com)
#define DBIc_THR_COND(imp)  	_imp2com(imp, std.thr_cond)
#define DBIc_THR_USER(imp)  	_imp2com(imp, std.thr_user)
#define DBIc_THR_USER_NONE  	(0xFFFF)
#define DBIc_IMP_STASH(imp)  	_imp2com(imp, std.imp_stash)
#define DBIc_IMP_DATA(imp)  	_imp2com(imp, std.imp_data)
#define DBIc_DBISTATE(imp)  	_imp2com(imp, std.dbistate)
#define DBIc_LOGPIO(imp)  	DBIc_DBISTATE(imp)->logfp
#define DBIc_KIDS(imp)  	_imp2com(imp, std.kids)
#define DBIc_ACTIVE_KIDS(imp)  	_imp2com(imp, std.active_kids)
#define DBIc_LAST_METHOD(imp)  	_imp2com(imp, std.last_method)

#define DBIc_DEBUG(imp)		(_imp2com(imp, attr.Debug))
#define DBIc_DEBUGIV(imp)	SvIV(DBIc_DEBUG(imp))
#define DBIc_STATE(imp)		SvRV(_imp2com(imp, attr.State))
#define DBIc_ERR(imp)		SvRV(_imp2com(imp, attr.Err))
#define DBIc_ERRSTR(imp)	SvRV(_imp2com(imp, attr.Errstr))
#define DBIc_LongReadLen(imp)  	_imp2com(imp, attr.LongReadLen)
#define DBIc_LongReadLen_init	80	/* may change */
#define DBIc_FetchHashKeyName(imp) (_imp2com(imp, attr.FetchHashKeyName))

/* handle sub-type specific fields						*/
/*	dbh	*/
#define DBIc_CACHED_KIDS(imp)  	_imp2com(imp, cached_kids)
/*	sth	*/
#define DBIc_NUM_FIELDS(imp)  	_imp2com(imp, num_fields)
#define DBIc_NUM_PARAMS(imp)  	_imp2com(imp, num_params)
#define DBIc_NUM_PARAMS_AT_EXECUTE	-9 /* see Driver.xst */
#define DBIc_ROW_COUNT(imp)  	_imp2com(imp, row_count)
#define DBIc_FIELDS_AV(imp)  	_imp2com(imp, fields_svav)
#define DBIc_FDESC_AV(imp)  	_imp2com(imp, fields_fdav)
#define DBIc_FDESC(imp, i)  	((imp_fdh_t*)(void*)SvPVX(AvARRAY(DBIc_FDESC_AV(imp))[i]))

#define DBIcf_COMSET	  0x000001	/* needs to be clear'd before free'd	*/
#define DBIcf_IMPSET	  0x000002	/* has implementor data to be clear'd	*/
#define DBIcf_ACTIVE	  0x000004	/* needs finish/disconnect before clear	*/
#define DBIcf_IADESTROY	  0x000008	/* do DBIc_ACTIVE_off before DESTROY	*/
#define DBIcf_WARN  	  0x000010	/* warn about poor practice etc  	*/
#define DBIcf_COMPAT  	  0x000020	/* compat/emulation mode (eg oraperl)	*/

#define DBIcf_ChopBlanks  0x000040	/* rtrim spaces from fetch char columns	*/
#define DBIcf_RaiseError  0x000080	/* throw exception (croak) on error	*/
#define DBIcf_PrintError  0x000100	/* warn() on error			*/
#define DBIcf_AutoCommit  0x000200	/* dbh only. used by drivers		*/
#define DBIcf_LongTruncOk 0x000400	/* truncation to LongReadLen is okay	*/
#define DBIcf_MultiThread 0x000800	/* allow multiple threads to enter	*/
#define DBIcf_Taint       0x001000	/* taint fetched data			*/
#define DBIcf_ShowErrorStatement  0x002000	/* include Statement in error	*/
#define DBIcf_BegunWork   0x004000	/* between begin_work & commit/rollback */
#define DBIcf_HandleError 0x008000	/* has coderef in HandleError attribute	*/
#define DBIcf_Profile     0x010000	/* profile activity on this handle	*/

#define DBIcf_INHERITMASK		/* what NOT to pass on to children */	\
  (U32)( DBIcf_COMSET | DBIcf_IMPSET | DBIcf_ACTIVE | DBIcf_IADESTROY		\
  /* These are for dbh only:	*/						\
  | DBIcf_AutoCommit | DBIcf_BegunWork	)

/* general purpose bit setting and testing macros			*/
#define DBIbf_is( bitset,flag)		((bitset) &   (flag))
#define DBIbf_has(bitset,flag)		DBIbf_is(bitset, flag) /* alias for _is */
#define DBIbf_on( bitset,flag)		((bitset) |=  (flag))
#define DBIbf_off(bitset,flag)		((bitset) &= ~(flag))
#define DBIbf_set(bitset,flag,on)	((on) ? DBIbf_on(bitset, flag) : DBIbf_off(bitset,flag))

/* as above, but specifically for DBIc_FLAGS imp flags (except ACTIVE)	*/
#define DBIc_is(imp, flag)	DBIbf_is( DBIc_FLAGS(imp), flag)
#define DBIc_has(imp,flag)	DBIc_is(imp, flag) /* alias for DBIc_is */
#define DBIc_on(imp, flag)	DBIbf_on( DBIc_FLAGS(imp), flag)
#define DBIc_off(imp,flag)	DBIbf_off(DBIc_FLAGS(imp), flag)
#define DBIc_set(imp,flag,on)	DBIbf_set(DBIc_FLAGS(imp), flag, on)

#define DBIc_COMSET(imp)	DBIc_is(imp, DBIcf_COMSET)
#define DBIc_COMSET_on(imp)	DBIc_on(imp, DBIcf_COMSET)
#define DBIc_COMSET_off(imp)	DBIc_off(imp,DBIcf_COMSET)

#define DBIc_IMPSET(imp)	DBIc_is(imp, DBIcf_IMPSET)
#define DBIc_IMPSET_on(imp)	DBIc_on(imp, DBIcf_IMPSET)
#define DBIc_IMPSET_off(imp)	DBIc_off(imp,DBIcf_IMPSET)

#define DBIc_ACTIVE(imp)	(DBIc_FLAGS(imp) &   DBIcf_ACTIVE)
#define DBIc_ACTIVE_on(imp)	/* adjust parent's active kid count */	\
    do {								\
	imp_xxh_t *ph_com = DBIc_PARENT_COM(imp);			\
	if (!DBIc_ACTIVE(imp) && ph_com && !dirty			\
		&& ++DBIc_ACTIVE_KIDS(ph_com) > DBIc_KIDS(ph_com))	\
	    croak("panic: DBI active kids (%d) > kids (%d)",		\
		DBIc_ACTIVE_KIDS(ph_com), DBIc_KIDS(ph_com));		\
	DBIc_FLAGS(imp) |=  DBIcf_ACTIVE;				\
    } while(0)
#define DBIc_ACTIVE_off(imp)	/* adjust parent's active kid count */	\
    do {								\
	imp_xxh_t *ph_com = DBIc_PARENT_COM(imp);			\
	if (DBIc_ACTIVE(imp) && ph_com && !dirty			\
		&& (--DBIc_ACTIVE_KIDS(ph_com) > DBIc_KIDS(ph_com)	\
		   || DBIc_ACTIVE_KIDS(ph_com) < 0) )			\
	    croak("panic: DBI active kids (%d) < 0 or > kids (%d)",	\
		DBIc_ACTIVE_KIDS(ph_com), DBIc_KIDS(ph_com));		\
	DBIc_FLAGS(imp) &= ~DBIcf_ACTIVE;				\
    } while(0)

#define DBIc_IADESTROY(imp)	(DBIc_FLAGS(imp) &   DBIcf_IADESTROY)
#define DBIc_IADESTROY_on(imp)	(DBIc_FLAGS(imp) |=  DBIcf_IADESTROY)
#define DBIc_IADESTROY_off(imp)	(DBIc_FLAGS(imp) &= ~DBIcf_IADESTROY)

#define DBIc_WARN(imp)   	(DBIc_FLAGS(imp) &   DBIcf_WARN)
#define DBIc_WARN_on(imp)	(DBIc_FLAGS(imp) |=  DBIcf_WARN)
#define DBIc_WARN_off(imp)	(DBIc_FLAGS(imp) &= ~DBIcf_WARN)

#define DBIc_COMPAT(imp)   	(DBIc_FLAGS(imp) &   DBIcf_COMPAT)
#define DBIc_COMPAT_on(imp)	(DBIc_FLAGS(imp) |=  DBIcf_COMPAT)
#define DBIc_COMPAT_off(imp)	(DBIc_FLAGS(imp) &= ~DBIcf_COMPAT)


#ifdef IN_DBI_XS		/* get Handle Common Data Structure	*/
#define DBIh_COM(h)         	(dbih_getcom(h))
#else
#define DBIh_COM(h)         	(DBIS->getcom(h))
#define neatsvpv(sv,len)       	(DBIS->neat_svpv(sv,len))
#endif


/* --- Implementors Private Data Support --- */

#define D_impdata(name,type,h)	type *name = (type*)(DBIh_COM(h))
#define D_imp_drh(h) D_impdata(imp_drh, imp_drh_t, h)
#define D_imp_dbh(h) D_impdata(imp_dbh, imp_dbh_t, h)
#define D_imp_sth(h) D_impdata(imp_sth, imp_sth_t, h)
#define D_imp_xxh(h) D_impdata(imp_xxh, imp_xxh_t, h)

#define D_imp_from_child(name,type,child)	\
				type *name = (type*)(DBIc_PARENT_COM(child))
#define D_imp_drh_from_dbh D_imp_from_child(imp_drh, imp_drh_t, imp_dbh)
#define D_imp_dbh_from_sth D_imp_from_child(imp_dbh, imp_dbh_t, imp_sth)

#define DBI_IMP_SIZE(n,s) sv_setiv(perl_get_sv((n), GV_ADDMULTI), (s)) /* XXX */


/* --- Implementors Field Descriptor Support --- */


/* --- Event Support (VERY LIABLE TO CHANGE) --- */

#define DBIh_EVENTx(h,t,a1,a2)	(DBIS->event((h), (t), (a1), (a2)))
#define DBIh_EVENT0(h,t)	DBIh_EVENTx((h), (t), &sv_undef, &sv_undef)
#define DBIh_EVENT1(h,t, a1)	DBIh_EVENTx((h), (t), (a1),      &sv_undef)
#define DBIh_EVENT2(h,t, a1,a2)	DBIh_EVENTx((h), (t), (a1),      (a2))

#define ERROR_event	"ERROR"
#define WARN_event	"WARN"
#define MSG_event	"MESSAGE"
#define DBEVENT_event	"DBEVENT"
#define UNKNOWN_event	"UNKNOWN"


/* --- Handy Macros --- */

#define DBIh_CLEAR_ERROR(imp_xxh) (void)( \
	(void)SvOK_off(DBIc_ERR(imp_xxh)),    	\
	(void)SvOK_off(DBIc_ERRSTR(imp_xxh)),	\
	(SvPOK(DBIc_STATE(imp_xxh)) ? SvCUR(DBIc_STATE(imp_xxh))=0 : 0)	\
    )


/* --- DBI State Structure --- */

struct dbistate_st {

#define DBISTATE_VERSION  94	/* Must change whenever dbistate_t does	*/

    /* this must be the first member in structure			*/
    void (*check_version) _((char *name,
		int dbis_cv, int dbis_cs, int need_dbixs_cv,
		int drc_s, int dbc_s, int stc_s, int fdc_s));

    /* version and size are used to check for DBI/DBD version mis-match	*/
    U16 version;	/* version of this structure			*/
    U16 size;
    U16 xs_version;	/* version of the overall DBIXS / DBD interface	*/
    U16 spare_pad;

    I32 debug;
    PerlIO *logfp;

    /* pointers to DBI functions which the DBD's will want to use	*/
    char      * (*neat_svpv)	_((SV *sv, STRLEN maxlen));
    imp_xxh_t * (*getcom)	_((SV *h));	/* see DBIh_COM macro	*/
    void        (*clearcom)	_((imp_xxh_t *imp_xxh));
    SV        * (*event)	_((SV *h, char *name, SV*, SV*));
    int         (*set_attr_k)	_((SV *h, SV *keysv, int dbikey, SV *valuesv));
    SV        * (*get_attr_k)	_((SV *h, SV *keysv, int dbikey));
    AV        * (*get_fbav)	_((imp_sth_t *imp_sth));
    SV        * (*make_fdsv)	_((SV *sth, char *imp_class, STRLEN imp_size, char *col_name));
    int         (*bind_as_num)	_((int sql_type, int p, int s, int *t, void *v));
    int         (*hash)		_((char *string, long i));
    SV        * (*preparse)	_((SV *sth, char *statement, IV ps_return, IV ps_accept, void *foo));

    SV *neatsvpvlen;	/* only show dbgpvlen chars when debugging pv's	*/

    dbi_mutex	*mutex;

    int         (*logmsg)	_((imp_xxh_t *imp_xxh, char *fmt, ...));
    int         (*set_err)	_((imp_xxh_t *imp_xxh, char *fmt, ...));
    void *pad[7];
};

/* macros for backwards compatibility */
#define set_attr(h, k, v)	set_attr_k(h, k, 0, v)
#define get_attr(h, k)		get_attr_k(h, k, 0)

#define DBISTATE_PERLNAME "DBI::_dbistate"
#define DBISTATE_ADDRSV   (perl_get_sv(DBISTATE_PERLNAME, 0x05))
#define DBILOGFP	(DBIS->logfp)
#ifdef IN_DBI_XS
#define DBILOGMSG	(dbih_logmsg)
#else
#define DBILOGMSG	(DBIS->logmsg)
#endif


/* --- perl object (ActiveState) / multiplicity hooks and hoops --- */
/* note that USE_ITHREADS implies MULTIPLICITY                      */
#if defined(MULTIPLICITY) || defined(PERL_OBJECT) || defined(PERL_CAPI)

# define DBISTATE_DECLARE typedef int dummy_dbistate /* keep semicolon from feeling lonely */
# define DBISTATE_ASSIGN(st)
# define DBISTATE_INIT
static dbistate_t **get_dbistate() {
    return ((dbistate_t**)&SvIVX(DBISTATE_ADDRSV));
}
# undef DBIS
# define DBIS (*get_dbistate())
# define dbis (*get_dbistate()) /* temp for bad drivers using 'dbis' instead of 'DBIS' */

#else	/* plain and simple non perl object / multiplicity case */

# define DBISTATE_DECLARE	static dbistate_t *DBIS
# define DBISTATE_ASSIGN(st)	(DBIS = (st))
# define DBISTATE_INIT_DBIS	DBISTATE_ASSIGN((dbistate_t*)SvIV(DBISTATE_ADDRSV))
# define DBISTATE_INIT {	/* typically use in BOOT: of XS file	*/    \
    DBISTATE_INIT_DBIS;	\
    if (DBIS == NULL)	\
	croak("Unable to get DBI state. DBI not loaded.");	\
    DBIS->check_version(__FILE__, DBISTATE_VERSION, sizeof(*DBIS), NEED_DBIXS_VERSION, \
		sizeof(dbih_drc_t), sizeof(dbih_dbc_t), sizeof(dbih_stc_t), sizeof(dbih_fdc_t) \
    ); \
}
#endif


/* --- Assorted Utility Macros	--- */

#define DBD_ATTRIB_OK(attribs)	/* is this a usable attrib value */	\
	(attribs && SvROK(attribs) && SvTYPE(SvRV(attribs))==SVt_PVHV)

/* If attribs value supplied then croak if it's not a hash ref.		*/
/* Also map undef to Null. Should always be called to pre-process the	*/
/* attribs value. One day we may add some extra magic in here.		*/
#define DBD_ATTRIBS_CHECK(func, h, attribs)	\
    if ((attribs) && SvOK(attribs)) {		\
	STRLEN lna=0;				\
	if (!SvROK(attribs) || SvTYPE(SvRV(attribs))!=SVt_PVHV)		\
	    croak("%s->%s(...): attribute parameter '%s' is not a hash ref",	\
		    SvPV(h,lna), func, SvPV(attribs,lna));		\
    } else (attribs) = Nullsv

#define DBD_ATTRIB_GET_SVP(attribs, key,klen)			\
	(DBD_ATTRIB_OK(attribs)					\
	    ? hv_fetch((HV*)SvRV(attribs), key,klen, 0)		\
	    : (SV **)Nullsv)
	
#define DBD_ATTRIB_GET_BOOL(attribs, key,klen, svp, var)		\
	if ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)	\
	    var = SvTRUE(*svp)

#define DBD_ATTRIB_GET_IV(attribs, key,klen, svp, var)			\
	if ((svp=DBD_ATTRIB_GET_SVP(attribs, key,klen)) != NULL)	\
	    var = SvIV(*svp)

#endif /* DBIXS_VERSION */
/* end of DBIXS.h */
