/*
	Copyright (c) 1994-2006 Tim Bunce
	Copyright (c) 2006-2008 John Scoles (The Pythian Group), Canada

	See the COPYRIGHT section in the Oracle.pm file for terms.

*/

/* ====== define data types ====== */

typedef struct taf_callback_st taf_callback_t;

struct taf_callback_st {
	SV   *function;     /*User supplied TAF function*/
	SV   *dbh_ref;
};


typedef struct box_st box_t;
typedef struct imp_fbh_st imp_fbh_t;

/* this structure is used to communicate parameters of login */
typedef struct dblogin_info_st dblogin_info_t;
struct dblogin_info_st {
    SV * dbh;
    imp_dbh_t * imp_dbh;
    SV * pool_class; /* this and next SV * is "mortal" */
    SV * pool_tag;
    char *dbname;
    char *uid;
    char *pwd;
    char * cset;
    char * ncset;
    ub4 mode;
    ub4 session_mode;
#ifdef ORA_OCI_112
	ub4			pool_min;
	ub4			pool_max;
	ub4			pool_incr;
	ub4			pool_rlb;
    ub4 driver_name_len;
    char * driver_name;
#endif
};


/* Define implementation specific driver handle data structure */
struct imp_drh_st {
	dbih_drc_t com;		/* MUST be first element in structure	*/
	SV *ora_long;
	SV *ora_trunc;
	SV *ora_cache;
	SV *ora_cache_o;		/* for ora_open() cache override */
};


/* Define implementation specific database handle data structure */
struct imp_dbh_st {
	dbih_dbc_t com;		/* MUST be first element in structure	*/

	void *(*get_oci_handle) _((imp_dbh_t *imp_dbh, int handle_type, int flags));
    box_t       *lock;  /* this contains pointer to slot that holds lock */
	OCIEnv 		*envhp;		/* session environment handler, this is mostly */
							/* a copy of imp_drh->envhp, see also connect  */
							/* attr ora_envhp                              */
	OCIError 	*errhp;
	OCIServer 	*srvhp;
	OCISvcCtx 	*svchp;
	OCISession	*seshp;
    SV          *taf_function; /*User supplied TAF function */
    taf_callback_t taf_ctx;
	int RowCacheSize; /* both of these are defined by DBI spec*/
	int RowsInCache;	/* this vaue is RO and cannot be set*/
	int ph_type;		/* default oratype for placeholders */
	int parse_error_offset;	/* position in statement of last error */
	int max_nested_cursors;	 /* limit on cached nested cursors per stmt */
	int array_chunk_size;  /* the max size for an array bind */
    ub4 server_version; /* version of Oracle server */
#ifdef ORA_OCI_112
    SV * session_tag;
#endif
    ub2 cset;
    ub2 ncset;
	ub1 ph_csform;		/* default charset for placeholders */
#if defined(USE_ITHREADS) && defined(PERL_MAGIC_shared_scalar)
    ub1 is_shared;
#endif
};

#define DBH_DUP_OFF sizeof(dbih_dbc_t)
#define DBH_DUP_LEN (sizeof(struct imp_dbh_st) - sizeof(dbih_dbc_t))



typedef struct lob_refetch_st lob_refetch_t; /* Define sth implementor data structure */


/* Define implementation specific statement data structure */
struct imp_sth_st {

	dbih_stc_t com;		/* MUST be first element in structure	*/

	void *(*get_oci_handle) _((imp_sth_t *imp_sth, int handle_type, int flags));
	OCIEnv			*envhp;	/* copy of dbh pointer	*/
	OCIError		*errhp;	/* copy of dbh pointer	*/
	OCIServer		*srvhp;	/* copy of dbh pointer	*/
	OCISvcCtx		*svchp;	/* copy of dbh pointer	*/
	OCIStmt			*stmhp;	/* oci statement  handle */
	OCIDescribe 	*dschp; /* oci describe handle */
	int				is_child;  /* if this is child from a ref cursor or SP*/
	ub2				stmt_type;	/* OCIAttrGet OCI_ATTR_STMT_TYPE	*/
	U16				auto_lob;	/* use auto lobs*/
	int				pers_lob;	/*use dblink for lobs only for 10g Release 2. or later*/
	int				clbk_lob;	/*use dblink for lobs only for 10g Release 2. or later*/
	int				piece_lob;	/*use piece fetch for lobs*/
	ub4				piece_size;	/*used in callback to set the size of the piece to get*/
	int				has_lobs;	/*Statement has bound LOBS */
    int				ret_lobs;	/*Statement returns LOBS */
 	lob_refetch_t	*lob_refetch;
	int				nested_cursor;	/* cursors fetched from SELECTs */
	AV				*bind_tuples;	/* Bind tuples in array execute, or NULL */
	int				rowwise;		/* If true, bind_tuples is list of */
									/* tuples, otherwise list of columns. */
	/* Input Details	*/
	char			*statement;		/* sql (see sth_scan)		*/
	HV				*all_params_hv;	/* all params, keyed by name	*/
	AV				*out_params_av;	/* quick access to inout params	*/
	int				ora_pad_empty;	/* convert ""->" " when binding	*/

	/* Select Column Output Details	*/
	int				done_desc;		/* have we described this sth yet ?	*/
	imp_fbh_t		*fbh;			/* array of imp_fbh_t structs	*/
	char			*fbh_cbuf;		/* memory for all field names	   */
	int				t_dbsize;	 	/* raw data width of a row		*/
	UV				long_readlen; 	/* local copy to handle oraperl	*/
	HV				*fbh_tdo_hv;	/* hash of row #(0 based) and tdo object name from ora_oci_type_names hash */
	 /* Select Row Cache Details */
	sb4				cache_rows;
	int				in_cache;
	int				next_entry;
	int				eod_errno;
	int				est_width;	/* est'd avg row width on-the-wire	*/
	/* (In/)Out Parameter Details */
	int 			has_inout_params;
	/* execute mode*/
	/* will be using this alot later me thinks  */
	ub4				exe_mode;
	/* fetch scrolling values */
	int 			fetch_orient;
	int				fetch_offset;
	int				fetch_position;
	int 			prefetch_memory;	/* OCI_PREFETCH_MEMORY*/
	int				prefetch_rows;		/* OCI_PREFETCH_ROWS */
	/* array fetch: state variables */
	int				row_cache_off;
	int 			rs_fetch_count;		/*fetch count*/
	int				rs_array_size;		/*array size local value for RowCacheSize as I do not want to change RowCacheSize */
	int				rs_array_num_rows;	/* num rows in last fetch */
	int				rs_array_idx;		/* index of current row */
	sword			rs_array_status;	/* status of last fetch */
	int 			RowCacheSize; 		/* both of these are defined by DBI spec*/
	int 			RowsInCache;		/* this vaue is RO and cannot be set*/

};
#define IMP_STH_EXECUTING	0x0001


typedef struct fb_ary_st fb_ary_t;	/* field buffer array	*/
struct fb_ary_st { 	/* field buffer array EXPERIMENTAL	*/
	ub4				bufl;		/* length of data buffer		*/
	ub4				cb_bufl;	/* length of piece of data fetched in callback.*/
	ub4				piece_count;/*# of pieces retrieved*/
	sb2				*aindp;	/* null/trunc indicator variable	*/
	ub1				*abuf;		/* data buffer (points to sv data)	*/
	ub1				*cb_abuf;	/*yet another buffer for picewise callbacks this means I only need to allocate memory once a prepare rather than at each fetch*/
	ub2				*arlen;	/* length of returned data		*/
	ub2				*arcode;	/* field level error status		*/
};


typedef struct fbh_obj_st fbh_obj_t; /*Ebbedded Object Descriptor */

struct fbh_obj_st {  /* embedded object or table will work recursively*/
	text			*type_name;			/*object's name (TDO)*/
	ub4				type_namel;			/*length of the name*/
	OCIParam		*parmdp;			/*Describe attributes of the object OCI_DTYPE_PARAM*/
	OCIParam		*parmap;			/*Describe attributes of the object OCI_ATTR_COLLECTION_ELEMENT OCI_ATTR_PARAM*/
 	OCIType	 		*tdo;				/*object's TDO handle */
	OCITypeCode 	typecode;			/*object's OCI_ATTR_TYPECODE */
	OCITypeCode 	col_typecode;		/*if collection this is its OCI_ATTR_COLLECTION_TYPECODE */
	OCITypeCode 	element_typecode;	/*if collection this is its element's OCI_ATTR_TYPECODE*/
	OCIRef			*obj_ref;			/*if an embedded object this is ref handle to its TDO*/
	OCIInd			*obj_ind;			/*Null indicator for object */
	OCIComplexObject *obj_value;		/*the actual value from the DB*/
	OCIType			*obj_type;		 	/*if an embeded object this is the  OCIType returned by a OCIObjectPin*/
	ub1				is_final_type;		/*object's OCI_ATTR_IS_FINAL_TYPE*/
	fbh_obj_t		*fields;			/*one object for each field/property*/
	ub2				field_count;		/*The number of fields Not really needed but nice to have*/
	fbh_obj_t		*next_subtype;		/*There is stored information about subtypes for inherited objects*/
	AV				*value;				/*The value to send back to Perl This way there are no memory leaks*/
	SV				*full_type_name;	/*Perl value of full type name = schema_name "." type_name*/

};

struct imp_fbh_st { 	/* field buffer EXPERIMENTAL */
	imp_sth_t *imp_sth;	/* 'parent' statement	*/
	int field_num;	/* 0..n-1		*/

	/* Oracle's description of the field	*/
	OCIParam	*parmdp;
	OCIDefine	*defnp;
	void 		*desc_h;	/* descriptor if needed (LOBs, cursors etc)	*/
	ub4			desc_t;	/* OCI type of descriptor		*/
	ub4 		define_mode; /*the normal case for a define*/
	int			(*fetch_func) _((SV *sth, imp_fbh_t *fbh, SV *dest_sv));
	void 		(*fetch_cleanup) _((SV *sth, imp_fbh_t *fbh));
	ub2			dbtype;	/* actual type of field (see ftype)	*/
	ub2			dbsize;
	ub2			prec;		/* XXX docs say ub1 but ub2 is needed	*/
	sb1			scale;
	ub1			nullok;
	char 		*name;
	SV			*name_sv;	/* only set for OCI8			*/
	/* OCI docs say OCI_ATTR_CHAR_USED is ub4, they're wrong	*/
	ub1			len_char_used;	/* OCI_ATTR_CHAR_USED			*/
	ub2			len_char_size;	/* OCI_ATTR_CHAR_SIZE			*/
	ub2			csid;		/* OCI_ATTR_CHARSET_ID			*/
	ub1			csform;		/* OCI_ATTR_CHARSET_FORM		*/
	ub4			disize;		/* max display/buffer size		*/
	ub4			piece_size; /*used in callback to set the size of the piece to get*/
	char		*bless;		/* for Oracle::OCI style handle data	*/
	void		*special;	/* hook for special purposes (LOBs etc)	*/
	int			pers_lob;   /*for persistant lobs 10g Release 2. or later*/
	int			clbk_lob;   /*for persistant lobs 10g Release 2. or later*/
	int			piece_lob;  /*use piecewise fetch for lobs*/

	/* Our storage space for the field data as it's fetched	*/

	sword		ftype;		/* external datatype we wish to get	*/
	IV			req_type;	/* type passed to bind_col */
	UV			bind_flags;	/* flags passed to bind_col */
	fb_ary_t	*fb_ary ;	/* field buffer array			*/
	/* if this is an embedded object we use this */
	fbh_obj_t	*obj;


 };

 /* Placeholder structure */
 /* Note: phs_t is serialized into scalar value, and de-serialized then. */
 /* Be carefull! */

typedef struct phs_st phs_t;	/* scalar placeholder   */

struct phs_st {	/* scalar placeholder EXPERIMENTAL	*/
	imp_sth_t		*imp_sth; /* 'parent' statement			*/
	sword 			ftype;	/* external OCI field type		*/

	SV				*sv;		/* the scalar holding the value		*/
	U32 			sv_type;	/* original sv type at time of bind	*/
	ub2 			csid_orig;	/* original oracle default csid 	*/
	ub2 			csid;		/* 0 for automatic			*/
	ub1 			csform;		/* 0 for automatic			*/
	ub4 			maxdata_size;	/* set OCI_ATTR_MAXDATA_SIZE if >0	*/
	bool			is_inout;

	IV				maxlen;		/* max possible len (=allocated buffer)	*/
					/* Note: for array bind = buffer for each entry */
	OCIBind			*bndhp;
	void			*desc_h;	/* descriptor if needed (LOBs etc)	*/
	ub4				desc_t;	/* OCI type of desc_h			*/
	ub4				alen;
	ub2				arcode;
	int				idx;	  /* 0-based index for ?/:1 style, or -1  */

	sb2				indp;		/* null indicator			*/
	char			*progv;

	int(*out_prepost_exec)_((SV *, imp_sth_t *, phs_t *, int pre_exec));
	SV				*ora_field;		/* from attribute (for LOB binds)	*/
	ub4				alen_incnull;	/* 0 or 1 if alen should include null	*/
	/* Array bind support */
	char			*array_buf;			/* Temporary buffer = malloc(array_buflen) */
	int				array_buflen;		 /* Allocated length of array_buf */
	int				array_numstruct;	  /* Number of bound structures in buffer */
	OCIInd			*array_indicators;	 /* Indicator array	   = malloc( array_numallocated * sizeof(OCIInd) ) */
	unsigned short	*array_lengths; /* Array entries lengths = malloc( array_numallocated * sizeof(unsigned short) ) */
	int				array_numallocated;   /* Allocated number of indicators/lengths */
	int				ora_maxarray_numentries; /* Number of entries to send allocated to Oracle. (may be less, than total allocated) */

	/* Support for different internal C-types, representing Oracle data */
	int				ora_internal_type; /* Which C-type would be bound instead of SQLT_CHR. */

	char			name[1];	/* struct is malloc'd bigger as needed	*/
};


/* ------ define functions and external variables ------ */

extern int ora_fetchtest;
extern int dbd_verbose;
extern int oci_warn;
extern int ora_objects;
extern int ora_ncs_buff_mtpl;
extern ub2 us7ascii_csid;
extern ub2 utf8_csid;
extern ub2 al32utf8_csid;
extern ub2 al16utf16_csid;

#define CS_IS_UTF8( cs ) \
	(  ( cs == utf8_csid ) || ( cs == al32utf8_csid ) )

#define CS_IS_NOT_UTF8_COMPATIBLE( cs ) \
  ( cs == us7ascii_csid  )

 #define CS_IS_UTF16( cs ) ( cs == al16utf16_csid )


#define CSFORM_IMPLIED_CSID(imp_xxh, csform) \
	((csform==SQLCS_NCHAR) ? (imp_xxh)->ncset : (imp_xxh)->cset)

#define CSFORM_IMPLIES_UTF8(imp_xxh, csform) \
	CS_IS_UTF8( CSFORM_IMPLIED_CSID(imp_xxh, csform ) )


void dbd_init_oci _((dbistate_t *dbistate));
void dbd_preparse _((imp_sth_t *imp_sth, char *statement));
void dbd_fbh_dump(imp_sth_t *imp_sth, imp_fbh_t *fbh, int i, int aidx);
void ora_free_fbh_contents _((SV *sth, imp_fbh_t *fbh));
void ora_free_templob _((SV *sth, imp_sth_t *imp_sth, OCILobLocator *lobloc));
int ora_dbtype_is_long _((int dbtype));
fb_ary_t *fb_ary_alloc _((ub4 bufl, int size));
fb_ary_t *fb_ary_cb_alloc _((ub4 piece_size,ub4 max_len, int size));

int ora_db_reauthenticate _((SV *dbh, imp_dbh_t *imp_dbh, char *uid, char *pwd));

void dbd_phs_sv_complete _((imp_sth_t *imp_sth, phs_t *phs, SV *sv, I32 debug));
void dbd_phs_avsv_complete _((imp_sth_t *imp_sth, phs_t *phs, I32 index, I32 debug));

int pp_exec_rset _((SV *sth, imp_sth_t *imp_sth, phs_t *phs, int pre_exec));
int pp_rebind_ph_rset_in _((SV *sth, imp_sth_t *imp_sth, phs_t *phs));

#define OTYPE_IS_LONG(t)  ((t)==8 || (t)==24 || (t)==94 || (t)==95)

int oci_error_err _((SV *h, OCIError *errhp, sword status, char *what, sb4 force_err));
#define oci_error(h, errhp, status, what) oci_error_err(h, errhp, status, what, 0)
char *oci_stmt_type_name _((int stmt_type));
char *oci_typecode_name _((int typecode));
char *sql_typecode_name _((int dbtype));
char *oci_status_name _((sword status));
char *oci_mode _((ub4  mode));
char *oci_bind_options _((ub4 options));
char *oci_define_options _((ub4 options));
char *oci_hdtype_name _((ub4 hdtype));
char *oci_attr_name _((ub4 attr));
char *oci_exe_mode _((ub4 mode));
char *dbd_yes_no _((int yes_no));
char *oci_col_return_codes _((int rc));
char *oci_csform_name _((ub4 attr));
/*char *oci_sql_function_code_name _((int sqlfncode));
  char *oci_ptype_name _((int ptype));*/

int dbd_rebind_ph_lob _((SV *sth, imp_sth_t *imp_sth, phs_t *phs));

int dbd_rebind_ph_nty _((SV *sth, imp_sth_t *imp_sth, phs_t *phs));

int ora_st_execute_array _((SV *sth, imp_sth_t *imp_sth, SV *tuples,
							SV *tuples_status, SV *columns, ub4 exe_count, SV *err_count));


SV * ora_create_xml _((SV *dbh, char *source));

void ora_free_lob_refetch _((SV *sth, imp_sth_t *imp_sth));
void dbd_phs_avsv_complete _((imp_sth_t *imp_sth, phs_t *phs, I32 index, I32 debug));
void dbd_phs_sv_complete _((imp_sth_t *imp_sth, phs_t *phs, SV *sv, I32 debug));
int post_execute_lobs _((SV *sth, imp_sth_t *imp_sth, ub4 row_count));
char *ora_sql_error _((imp_sth_t *imp_sth, char *msg));
char *ora_env_var(char *name, char *buf, unsigned long size);

#if defined(__CYGWIN__) || defined(__CYGWIN32__)
void ora_cygwin_set_env(char *name, char *value);

#endif /* __CYGWIN__ */

sb4 dbd_phs_in _((dvoid *octxp, OCIBind *bindp, ub4 iter, ub4 index,
			  dvoid **bufpp, ub4 *alenp, ub1 *piecep, dvoid **indpp));
sb4 dbd_phs_out _((dvoid *octxp, OCIBind *bindp, ub4 iter, ub4 index,
			 dvoid **bufpp, ub4 **alenpp, ub1 *piecep,
			 dvoid **indpp, ub2 **rcodepp));
sb4 presist_lob_fetch_cbk _((dvoid *octxp, OCIDefine *dfnhp, ub4 iter, dvoid **bufpp,
					  ub4 **alenpp, ub1 *piecep, dvoid **indpp, ub2 **rcpp));
int dbd_rebind_ph_rset _((SV *sth, imp_sth_t *imp_sth, phs_t *phs));

void * oci_db_handle(imp_dbh_t *imp_dbh, int handle_type, int flags);
void * oci_st_handle(imp_sth_t *imp_sth, int handle_type, int flags);
void fb_ary_free(fb_ary_t *fb_ary);
void rs_array_init(imp_sth_t *imp_sth);

ub4 ora_db_version _((SV *dbh, imp_dbh_t *imp_dbh));
sb4 reg_taf_callback _((SV *dbh, imp_dbh_t *imp_dbh));

int cnx_establish (pTHX_ dblogin_info_t * );
void cnx_drop_dr(pTHX_ imp_drh_t *);
void cnx_clean(pTHX_ imp_dbh_t * );
void cnx_detach(pTHX_ imp_dbh_t * );
void ora_shared_release(pTHX_ SV * );

#ifdef ORA_OCI_112
int cnx_get_pool_mode(pTHX_ SV *, imp_dbh_t * );
void cnx_pool_mode(pTHX_ SV * , imp_dbh_t * , ub4);

#if OCI_MAJOR_VERSION > 18
int cnx_get_pool_wait(pTHX_ SV *, imp_dbh_t * );
void cnx_pool_wait(pTHX_ SV * , imp_dbh_t * , ub4);
#endif

int cnx_get_pool_used(pTHX_ SV *, imp_dbh_t * );

int cnx_get_pool_rlb(pTHX_ SV *, imp_dbh_t * );

int cnx_get_pool_incr(pTHX_ SV *, imp_dbh_t * );
void cnx_pool_incr(pTHX_ SV * , imp_dbh_t * , ub4);

int cnx_get_pool_min(pTHX_ SV *, imp_dbh_t * );
void cnx_pool_min(pTHX_ SV * , imp_dbh_t * , ub4);

int cnx_get_pool_max(pTHX_ SV *, imp_dbh_t * );
void cnx_pool_max(pTHX_ SV * , imp_dbh_t * , ub4);

int cnx_is_pooled_session(pTHX_ SV *, imp_dbh_t *);
#endif

/* These defines avoid name clashes for multiple statically linked DBD's	*/

#define dbd_init			ora_init
#define dbd_dr_destroy		ora_dr_destroy
#define dbd_db_login		ora_db_login
#define dbd_db_login6		ora_db_login6
#define dbd_db_do			ora_db_do
#define dbd_db_commit		ora_db_commit
#define dbd_db_rollback		ora_db_rollback
#define dbd_db_cancel		ora_db_cancel
#define dbd_db_disconnect	ora_db_disconnect
#define dbd_db_destroy		ora_db_destroy
#define dbd_take_imp_data	ora_take_imp_data
#define dbd_db_STORE_attrib	ora_db_STORE_attrib
#define dbd_db_FETCH_attrib	ora_db_FETCH_attrib
#define dbd_st_prepare		ora_st_prepare
#define dbd_st_rows			ora_st_rows
#define dbd_st_cancel		ora_st_cancel
#define dbd_st_execute		ora_st_execute
#define dbd_st_fetch		ora_st_fetch
#define dbd_st_finish		ora_st_finish
#define dbd_st_destroy		ora_st_destroy
#define dbd_st_blob_read	ora_st_blob_read
#define dbd_st_STORE_attrib	ora_st_STORE_attrib
#define dbd_st_FETCH_attrib	ora_st_FETCH_attrib
#define dbd_describe		ora_describe
#define dbd_bind_ph			ora_bind_ph
#define dbd_st_bind_col		ora_st_bind_col
#include "ocitrace.h"

/* end */

