The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#ifndef __DBE_DEF_H__
#define __DBE_DEF_H__ 1

#include "dbe.h"
#include "dbe_config.h"
#include "dbe_build.h"

#if defined _WIN32
#define INLINE __inline
#define EXTERN extern
#elif defined __GNUC__
#define INLINE inline
#define EXTERN extern inline
#else
#define INLINE
#define EXTERN
#endif

#if (PERL_VERSION <= 8)
#define SvREFCNT_inc_NN		SvREFCNT_inc
#endif

#ifdef DBE_DEBUG
EXTERN int my_debug( const char *fmt, ... );
#define _debug my_debug
#endif

#if DBE_DEBUG > 1

#undef malloc
#undef calloc
#undef realloc
#undef free
#undef memcpy
#undef memset

#undef Newx
#undef Newxz
#undef Safefree
#undef Renew

extern HV				*hv_dbg_mem;
extern perl_mutex		dbg_mem_lock;

void debug_init();
void debug_free();

#define Newx(v,n,t) { \
	char __v[41], __msg[128]; \
	MUTEX_LOCK( &dbg_mem_lock ); \
	(v) = ((t*) safemalloc( (size_t) (n) * sizeof(t) )); \
	sprintf( __v, "0x%lx", (size_t) (v) ); \
	sprintf( __msg, "0x%lx malloc(%lu * %lu) called at %s:%d", \
		(size_t) (v), (size_t) (n), sizeof(t), __FILE__, __LINE__ ); \
	_debug( "%s\n", __msg ); \
	(void) hv_store( hv_dbg_mem, \
		__v, (I32) strlen( __v ), newSVpvn( __msg, strlen( __msg ) ), 0 ); \
	MUTEX_UNLOCK( &dbg_mem_lock ); \
}

#define Newxz(v,n,t) { \
	char __v[41], __msg[128]; \
	MUTEX_LOCK( &dbg_mem_lock ); \
	(v) = ((t*) safecalloc( (size_t) (n), sizeof(t) )); \
	sprintf( __v, "0x%lx", (size_t) (v) ); \
	sprintf( __msg, "0x%lx calloc(%lu * %lu) called at %s:%d", \
		(size_t) (v), (size_t) (n), sizeof(t), __FILE__, __LINE__ ); \
	_debug( "%s\n", __msg ); \
	(void) hv_store( hv_dbg_mem, \
		__v, (I32) strlen( __v ), newSVpvn( __msg, strlen( __msg ) ), 0 ); \
	MUTEX_UNLOCK( &dbg_mem_lock ); \
}

#define Safefree(x) { \
	char __v[41]; \
	MUTEX_LOCK( &dbg_mem_lock ); \
	if( (x) != NULL ) { \
		sprintf( __v, "0x%lx", (size_t) (x) ); \
		_debug( "0x%lx free() called at %s:%d\n", \
			(size_t) (x), __FILE__, __LINE__ ); \
		(void) hv_delete( hv_dbg_mem, __v, (I32) strlen( __v ), G_DISCARD ); \
		safefree( (x) ); (x) = NULL; \
	} \
	MUTEX_UNLOCK( &dbg_mem_lock ); \
}

#define Renew(v,n,t) { \
	register void *__p = (v); \
	char __v[41], __msg[128]; \
	MUTEX_LOCK( &dbg_mem_lock ); \
	sprintf( __v, "0x%lx", (size_t) (v) ); \
	(void) hv_delete( hv_dbg_mem, __v, (I32) strlen( __v ), G_DISCARD ); \
	(v) = ((t*) saferealloc( __p, (size_t) (n) * sizeof(t) )); \
	sprintf( __v, "0x%lx", (size_t) (v) ); \
	sprintf( __msg, "0x%lx realloc(0x%lx, %lu * %lu) called at %s:%d", \
		(size_t) (v), (size_t) __p, (size_t) (n), sizeof(t), \
		__FILE__, __LINE__ ); \
	_debug( "%s\n", __msg ); \
	(void) hv_store( hv_dbg_mem, \
		__v, (I32) strlen( __v ), newSVpvn( __msg, strlen( __msg ) ), 0 ); \
	MUTEX_UNLOCK( &dbg_mem_lock ); \
}

#endif /* DBE_DEBUG > 1 */

#ifdef _WIN32
#undef vsnprintf
#define vsnprintf _vsnprintf
#endif

#define DBE_TYPE_NAME(t) ( \
	((t) == DBE_TYPE_VARCHAR)			? "varchar" : \
	((t) == DBE_TYPE_INTEGER)			? "integer" : \
	((t) == DBE_TYPE_DOUBLE)			? "double" : \
	((t) == DBE_TYPE_BINARY)			? "binary" : \
	((t) == DBE_TYPE_BIGINT)			? "bigint" : \
										"unknown" )

typedef struct st_dbe_vrt_row		dbe_vrt_row_t;
typedef struct st_dbe_vrt_column	dbe_vrt_column_t;
typedef struct st_dbe_vrt_res		dbe_vrt_res_t;
typedef struct st_dbe_lob_stream	dbe_lob_stream_t;
typedef struct st_dbe_param			dbe_param_t;
typedef struct st_dbe_stmt			dbe_stmt_t;
typedef struct st_dbe_global		dbe_global_t;

struct st_dbe_drv {
	struct st_dbe_drv				*prev;
	struct st_dbe_drv				*next;
	char							*name;
	drv_def_t						*def;
	char							*class_con;
	char							*class_res;
	char							*class_stmt;
};

struct st_dbe_vrt_row {
	char							**data;
	size_t							*lengths;
};

struct st_dbe_vrt_column {
	enum dbe_type					type;
	char							*name;
	size_t							name_length;
	char							*name_conv;
	size_t							name_conv_length;
};

struct st_dbe_vrt_res {
	dbe_vrt_column_t				*columns;
	u_long							column_count;
	u_long							column_pos;
	dbe_vrt_row_t					*rows;
	u_long							row_pos;
	u_long							row_count;
};

#define RES_TYPE_DRV				1
#define RES_TYPE_VRT				2

#define RES_FREE_NAMES				1

struct st_dbe_res {
	char							type;
	struct st_dbe_res				*prev;
	struct st_dbe_res				*next;
	u_long							id;
	u_long							num_fields;
	SV								**sv_buffer;
	u_long							bind_count;
	SV								**sv_bind;
	dbe_str_t						*names;
	dbe_str_t						*names_conv;
	u_long							flags;								
	struct st_dbe_con				*con;
	union {
		drv_res_t					*drv_data;
		dbe_vrt_res_t				*vrt_res;
	};
	int								refcnt;
};

struct st_dbe_lob_stream {
	dbe_lob_stream_t				*prev;
	dbe_lob_stream_t				*next;
	u_long							id;
	void							*context;
	int								offset;
	int								size;
	int								refcnt;
	char							closed;
};

struct st_dbe_param {
	SV								*sv;
	char							type;
	char							*name;
};

struct st_dbe_stmt {
	dbe_stmt_t						*prev;
	dbe_stmt_t						*next;
	dbe_con_t						*con;
	u_long							id;
	drv_stmt_t						*drv_data;
	int								refcnt;
	dbe_param_t						*params;
	char							**param_names;
	int								num_params;
};

#define CON_ONERRORDIE				1
#define CON_ONERRORWARN				2
#define CON_NAME_LC					4
#define CON_NAME_UC					8

struct st_dbe_con {
	struct st_dbe_con				*prev;
	struct st_dbe_con				*next;
	u_long							id;
	dbe_res_t						*first_res;
	dbe_res_t						*last_res;
	dbe_stmt_t						*first_stmt;
	dbe_stmt_t						*last_stmt;
	drv_con_t						*drv_data;
	dbe_drv_t						*drv;
	char							last_error[1024];
	int								last_errno;
	unsigned int					flags;
	unsigned int					reconnect_count;
	char							catalog_separator;
	char							quote_id_prefix;
	char							quote_id_suffix;
	char							escape_pattern;
	char							catalog_location;
	SV								*error_handler;
	SV								*error_handler_arg;
	int								refcnt;
	SV								*sv_trace_out;
	int								trace_level;
	int								trace_local;
	SV								*this;
#ifdef USE_ITHREADS
	perl_mutex						thread_lock;
#endif
	dbe_lob_stream_t				*first_lob;
	dbe_lob_stream_t				*last_lob;
};

struct st_dbe_global {
	dbe_drv_t						*first_drv;
	dbe_drv_t						*last_drv;
	dbe_con_t						*first_con;
	dbe_con_t						*last_con;
	char							last_error[1024];
	int								last_errno;
	int								cleanup;
	int								destroyed;
	u_long							counter;
	SV								*sv_trace_out;
	int								trace_level;
#ifdef USE_ITHREADS
	perl_mutex						thread_lock;
#if DBE_DEBUG > 1
	int								lock_counter;
#endif
#endif
};

#ifdef USE_ITHREADS

#if DBE_DEBUG > 1

#define GLOBAL_LOCK() { \
	_debug( "enter global lock (%d) at %s:%d\n", \
		global.lock_counter + 1, __FILE__, __LINE__ ); \
	MUTEX_LOCK( &global.thread_lock ); \
	global.lock_counter ++; \
}

#define GLOBAL_UNLOCK() { \
	_debug( "leave global lock (%d) at %s:%d\n", \
		global.lock_counter, __FILE__, __LINE__ ); \
	MUTEX_UNLOCK( &global.thread_lock ); \
	global.lock_counter --; \
}

#define CON_LOCK(con) { \
	_debug( "enter con (%u) lock at %s:%d\n", con->id, __FILE__, __LINE__ ); \
	MUTEX_LOCK( &con->thread_lock ); \
}

#define CON_UNLOCK(con) { \
	_debug( "leave con (%u) lock at %s:%d\n", con->id, __FILE__, __LINE__ ); \
	MUTEX_UNLOCK( &con->thread_lock ); \
}

#else /*DBE_DEBUG > 1*/

#define GLOBAL_LOCK()			MUTEX_LOCK( &global.thread_lock )
#define GLOBAL_UNLOCK()			MUTEX_UNLOCK( &global.thread_lock )

#define CON_LOCK(con)			MUTEX_LOCK( &con->thread_lock )
#define CON_UNLOCK(con)			MUTEX_UNLOCK( &con->thread_lock )

#endif /*DBE_DEBUG > 1*/

#else /*USE_ITHREADS*/

#define GLOBAL_LOCK()			{}
#define GLOBAL_UNLOCK()			{}
#define CON_LOCK(con)			{}
#define CON_UNLOCK(con)			{}

#endif /*USE_ITHREADS*/

#define NOFIELDS_ERROR		"Driver did not return a field count for the" \
							" result set"

#define NOFIELDS_XS(this, obj_id, con, action) { \
	dbe_drv_set_error( con, -1, NOFIELDS_ERROR ); \
	HANDLE_ERROR( this, obj_id, con, action ); \
	XSRETURN_EMPTY; \
}

#define NOFUNCTION_ERROR	"Driver does not implement \"%s()\""
#define NOFUNCTION_ACTION	"Undefined function"

#define NOFUNCTION(this, obj_id, con, fnc) { \
	SV *__this = this; \
	dbe_drv_set_error( con, -1, NOFUNCTION_ERROR, fnc ); \
	HANDLE_ERROR( __this, obj_id, con, NOFUNCTION_ACTION ); \
}

#define NOFUNCTION_XS(this, obj_id, con, fnc) { \
	NOFUNCTION( this, obj_id, con, fnc ); \
	XSRETURN_EMPTY; \
}

#define NOFUNCTION_C(this, obj_id, con, fnc) { \
	NOFUNCTION( this, obj_id, con, fnc ); \
	return DBE_ERROR; \
}

#define HANDLE_ERROR(this, obj_id, con, action) \
	dbe_handle_error( this, obj_id, con, action )

#define HANDLE_ERROR_XS(this, obj_id, con, action) { \
	HANDLE_ERROR( this, obj_id, con, action ); \
	XSRETURN_EMPTY; \
}

#define STR_TRUE(str) \
	( (str) != NULL && (str)[0] != '\0' && (str)[0] != '0' \
		&& (str)[0] != 'f' && (str)[0] != 'F' \
		&& (str)[0] != 'n' && (str)[0] != 'N' )

#define BOOL_STR(x)		(x) ? "True" : "False"
#define ONOFF_STR(x)	(x) ? "On" : "Off"

extern dbe_global_t global;
extern dbe_t g_dbe;

/* global */
EXTERN dbe_drv_t *dbe_init_driver( const char *name, size_t name_len );
EXTERN void dbe_free();
EXTERN void dbe_cleanup();

/* error */
EXTERN void dbe_drv_set_error(
	dbe_con_t *dbe_con, int code, const char *msg, ... );
void dbe_handle_error(
	SV *obj, const char *obj_id, dbe_con_t *con, const char *action
);

/* trace */
EXTERN void dbe_drv_trace(
	dbe_con_t *dbe_con, int level, int show_caller, const char *msg, ...
);
const char *dbe_trace_getinfo_name( int code );
EXTERN int dbe_trace_level_by_name( const char *name );
EXTERN const char *dbe_trace_name_by_level( int level );
#define TRACE dbe_drv_trace
#define IF_TRACE_CON(con,level) \
	if( (con->trace_local && con->trace_level >= level) \
		|| (! con->trace_local && global.trace_level >= level) )
#define IF_TRACE_GLOBAL(level) \
	if( global.trace_level >= level )

/* misc */
EXTERN int dbe_dsn_parse( char *dsn, char ***pargs );
EXTERN SV *dbe_quote( const char *str, size_t str_len );
EXTERN SV *dbe_quote_id(
	const char **args, int argc,
	const char quote_id_prefix, const char quote_id_suffix,
	const char catalog_sep, const short catalog_location
);
EXTERN SV *dbe_escape_pattern(
	const char *str, size_t str_len, const char esc );
EXTERN char *dbe_convert_query(
	dbe_con_t *con, const char *query, size_t query_len, size_t *res_len,
	char ***p_params, int *p_param_count
);
EXTERN enum sql_type dbe_sql_type_by_name( const char *name );
EXTERN void dbe_perl_print( SV *sv, const char *msg, size_t msg_len );
EXTERN void dbe_perl_printf( SV *sv, const char *msg, ... );
char *my_dump_var( SV *sv, const char *name, size_t *rlen, int print );

/* lob stream */
EXTERN void *dbe_lob_stream_find( SV *sv, dbe_con_t **pcon );
EXTERN void dbe_lob_stream_rem( dbe_con_t *con, dbe_lob_stream_t *ls );
EXTERN void dbe_lob_stream_free( dbe_con_t *con, dbe_lob_stream_t *ls );

/* connection */
EXTERN void dbe_con_add( dbe_con_t *con );
EXTERN void dbe_con_rem( dbe_con_t *con );
EXTERN void dbe_con_free( dbe_con_t *con );
EXTERN void dbe_con_cleanup( dbe_con_t *con );
EXTERN dbe_con_t *dbe_con_find( SV *sv );
EXTERN dbe_con_t *dbe_con_find_by_id( SV *sv, u_long id );
EXTERN int dbe_con_reconnect( SV *obj, const char *obj_id, dbe_con_t *con );

/* result set */
EXTERN void dbe_res_add( dbe_con_t *con, dbe_res_t *res );
EXTERN void dbe_res_rem( dbe_res_t *res );
EXTERN void dbe_res_free_vrt( dbe_res_t *res );
EXTERN void dbe_res_free_drv( dbe_res_t *res );
EXTERN void dbe_res_free( dbe_res_t *res );
EXTERN dbe_res_t *dbe_res_find( SV *sv );
EXTERN int dbe_res_fetch_names( SV *this, dbe_res_t *res );
EXTERN int dbe_res_fetch_row( dbe_res_t *res, int names );
EXTERN int dbe_res_fetch_hash( SV *this, dbe_res_t *res, HV **phv, int nc );
EXTERN int dbe_res_fetchall_arrayref(
	SV *this, dbe_res_t *res, AV **pav, int fn );
EXTERN int dbe_res_fetchall_hashref(
	SV *this, dbe_res_t *res, dbe_str_t *fields, u_long ks, HV **phv );

/* statement */
EXTERN void dbe_stmt_add( dbe_con_t *con, dbe_stmt_t *stmt );
EXTERN void dbe_stmt_rem( dbe_stmt_t *stmt );
EXTERN void dbe_stmt_free( dbe_stmt_t *stmt );
EXTERN dbe_stmt_t *dbe_stmt_find( SV *sv );
EXTERN int dbe_bind_param_num(
	SV *obj, const char *obj_id, dbe_con_t *con, drv_stmt_t *stmt,
	drv_def_t *dd, int p_num, SV *p_val, char p_type, const char *p_name
);
EXTERN int dbe_bind_param_inout(
	SV *obj, const char *obj_id, dbe_con_t *con, drv_stmt_t *stmt,
	drv_def_t *dd, int p_num, SV *p_var, char p_type, const char *p_name
);
EXTERN int dbe_bind_param_str(
	SV *obj, const char *obj_id, dbe_con_t *con, drv_stmt_t *stmt,
	drv_def_t *dd, char **params, int param_count,
	char *p_num, SV *p_val, char p_type
);

/* virtual result set */
EXTERN SV *dbe_vrt_res_fetch( int type, const char *data, size_t data_len );
EXTERN int dbe_vrt_res_fetch_hash( dbe_res_t *res, HV **phv, int nc );
EXTERN int dbe_vrt_res_fetchall_arrayref( dbe_res_t *res, AV **pav, int fn );
EXTERN int dbe_vrt_res_fetchall_hashref(
	dbe_res_t *res, dbe_str_t *fields, u_long ks, HV **phv );

/* common */
EXTERN char *my_strncpy( char *dst, const char *src, size_t len );
EXTERN char *my_strcpy( char *dst, const char *src );
EXTERN char *my_strcpyl( char *dst, const char *src );
EXTERN char *my_strcpyu( char *dst, const char *src );
EXTERN char *my_itoa( char *str, long value, int radix );
EXTERN char *my_ltoa( char *str, xlong value, int radix );
EXTERN char *my_ultoa( char *str, u_xlong value, int radix );
EXTERN char *my_ftoa( register char *buf, double f );
EXTERN int my_stricmp( const char *cs, const char *ct );
EXTERN int my_strnicmp( const char *cs, const char *ct, size_t len );
EXTERN char *my_stristr( const char *str1, const char *str2 );

/* unicode */
EXTERN int my_utf8_to_unicode(
	const char *src, size_t src_len, char *dst, size_t dst_max );
EXTERN int my_unicode_to_utf8(
	const char *src, size_t src_len, char *dst, size_t dst_max );
EXTERN int my_utf8_toupper(
	const char *src, size_t src_len, char *dst, size_t dst_len );
EXTERN int my_utf8_tolower(
	const char *src, size_t src_len, char *dst, size_t dst_len );
EXTERN unsigned int my_towupper( unsigned int c );
EXTERN unsigned int my_towlower( unsigned int c );

#endif /* __DBE_DEF_H__ */