/* File:			connection.h
 *
 * Description:		See "connection.c"
 *
 * Comments:		See "notice.txt" for copyright and license information.
 *
 */

#ifndef __CONNECTION_H__
#define __CONNECTION_H__

#include "psqlodbc.h"

#include <stdlib.h>
#include <string.h>
#include "descriptor.h"


typedef enum
{
	CONN_NOT_CONNECTED,			/* Connection has not been established */
	CONN_CONNECTED,				/* Connection is up and has been
								 * established */
	CONN_DOWN,					/* Connection is broken */
	CONN_EXECUTING				/* the connection is currently executing a
								 * statement */
} CONN_Status;

/*	These errors have general sql error state */
#define CONNECTION_SERVER_NOT_REACHED				101
#define CONNECTION_MSG_TOO_LONG						103
#define CONNECTION_COULD_NOT_SEND					104
#define CONNECTION_NO_SUCH_DATABASE					105
#define CONNECTION_BACKEND_CRAZY					106
#define CONNECTION_NO_RESPONSE						107
#define CONNECTION_SERVER_REPORTED_ERROR			108
#define CONNECTION_COULD_NOT_RECEIVE				109
#define CONNECTION_SERVER_REPORTED_WARNING			110
#define CONNECTION_NEED_PASSWORD					112

/*	These errors correspond to specific SQL states */
#define CONN_INIREAD_ERROR							201
#define CONN_OPENDB_ERROR							202
#define CONN_STMT_ALLOC_ERROR						203
#define CONN_IN_USE									204
#define CONN_UNSUPPORTED_OPTION						205
/* Used by SetConnectoption to indicate unsupported options */
#define CONN_INVALID_ARGUMENT_NO					206
/* SetConnectOption: corresponds to ODBC--"S1009" */
#define CONN_TRANSACT_IN_PROGRES					207
#define CONN_NO_MEMORY_ERROR						208
#define CONN_NOT_IMPLEMENTED_ERROR					209
#define CONN_INVALID_AUTHENTICATION					210
#define CONN_AUTH_TYPE_UNSUPPORTED					211
#define CONN_UNABLE_TO_LOAD_DLL						212

#define CONN_OPTION_VALUE_CHANGED					213
#define CONN_VALUE_OUT_OF_RANGE						214

#define CONN_TRUNCATED								215

/* Conn_status defines */
#define CONN_IN_AUTOCOMMIT							0x01
#define CONN_IN_TRANSACTION							0x02

/* AutoCommit functions */
#define CC_set_autocommit_off(x)	(x->transact_status &= ~CONN_IN_AUTOCOMMIT)
#define CC_set_autocommit_on(x)		(x->transact_status |= CONN_IN_AUTOCOMMIT)
#define CC_is_in_autocommit(x)		(x->transact_status & CONN_IN_AUTOCOMMIT)

/* Transaction in/not functions */
#define CC_set_in_trans(x)	(x->transact_status |= CONN_IN_TRANSACTION)
#define CC_set_no_trans(x)	(x->transact_status &= ~CONN_IN_TRANSACTION)
#define CC_is_in_trans(x)	(x->transact_status & CONN_IN_TRANSACTION)


/* Authentication types */
#define AUTH_REQ_OK									0
#define AUTH_REQ_KRB4								1
#define AUTH_REQ_KRB5								2
#define AUTH_REQ_PASSWORD							3
#define AUTH_REQ_CRYPT								4
#define AUTH_REQ_MD5								5
#define AUTH_REQ_SCM_CREDS							6

/*	Startup Packet sizes */
#define SM_DATABASE									64
#define SM_USER										32
#define SM_OPTIONS									64
#define SM_UNUSED									64
#define SM_TTY										64

/*	Old 6.2 protocol defines */
#define NO_AUTHENTICATION							7
#define PATH_SIZE									64
#define ARGV_SIZE									64
#define USRNAMEDATALEN		16

typedef unsigned int ProtocolVersion;

#define PG_PROTOCOL(major, minor)	(((major) << 16) | (minor))
#define PG_PROTOCOL_LATEST							PG_PROTOCOL(2, 0)
#define PG_PROTOCOL_63								PG_PROTOCOL(1, 0)
#define PG_PROTOCOL_62								PG_PROTOCOL(0, 0)

/*	This startup packet is to support latest Postgres protocol (6.4, 6.3) */
typedef struct _StartupPacket
{
	ProtocolVersion protoVersion;
	char		database[SM_DATABASE];
	char		user[SM_USER];
	char		options[SM_OPTIONS];
	char		unused[SM_UNUSED];
	char		tty[SM_TTY];
} StartupPacket;


/*	This startup packet is to support pre-Postgres 6.3 protocol */
typedef struct _StartupPacket6_2
{
	unsigned int authtype;
	char		database[PATH_SIZE];
	char		user[USRNAMEDATALEN];
	char		options[ARGV_SIZE];
	char		execfile[ARGV_SIZE];
	char		tty[PATH_SIZE];
} StartupPacket6_2;

/* Transferred from pqcomm.h:  */


typedef ProtocolVersion MsgType;

#define PG_PROTOCOL(m,n)   (((m) << 16) | (n))
#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678)

typedef struct CancelRequestPacket
{
	/* Note that each field is stored in network byte order! */
	MsgType		cancelRequestCode;	/* code to identify a cancel request */
	unsigned int	backendPID;	/* PID of client's backend */
	unsigned int	cancelAuthCode; /* secret key to authorize cancel */
} CancelRequestPacket;

/*	Structure to hold all the connection attributes for a specific
	connection (used for both registry and file, DSN and DRIVER)
*/
typedef struct
{
	char		dsn[MEDIUM_REGISTRY_LEN];
	char		desc[MEDIUM_REGISTRY_LEN];
	char		driver[MEDIUM_REGISTRY_LEN];
	char		server[MEDIUM_REGISTRY_LEN];
	char		database[MEDIUM_REGISTRY_LEN];
	char		username[MEDIUM_REGISTRY_LEN];
	char		password[MEDIUM_REGISTRY_LEN];
	char		conn_settings[LARGE_REGISTRY_LEN];
	char		protocol[SMALL_REGISTRY_LEN];
	char		port[SMALL_REGISTRY_LEN];
	char		onlyread[SMALL_REGISTRY_LEN];
	char		fake_oid_index[SMALL_REGISTRY_LEN];
	char		show_oid_column[SMALL_REGISTRY_LEN];
	char		row_versioning[SMALL_REGISTRY_LEN];
	char		show_system_tables[SMALL_REGISTRY_LEN];
	char		translation_dll[MEDIUM_REGISTRY_LEN];
	char		translation_option[SMALL_REGISTRY_LEN];
	char		focus_password;
	char		disallow_premature;
	char		allow_keyset;
	char		updatable_cursors;
	char		lf_conversion;
	char		true_is_minus1;
	char		int8_as;
	GLOBAL_VALUES drivers;		/* moved from driver's option */
} ConnInfo;

/*	Macro to determine is the connection using 6.2 protocol? */
#define PROTOCOL_62(conninfo_)		(strncmp((conninfo_)->protocol, PG62, strlen(PG62)) == 0)

/*	Macro to determine is the connection using 6.3 protocol? */
#define PROTOCOL_63(conninfo_)		(strncmp((conninfo_)->protocol, PG63, strlen(PG63)) == 0)

/*
 *	Macros to compare the server's version with a specified version
 *		1st parameter: pointer to a ConnectionClass object
 *		2nd parameter: major version number
 *		3rd parameter: minor version number
 */
#define SERVER_VERSION_GT(conn, major, minor) \
	((conn)->pg_version_major > major || \
	((conn)->pg_version_major == major && (conn)->pg_version_minor > minor))
#define SERVER_VERSION_GE(conn, major, minor) \
	((conn)->pg_version_major > major || \
	((conn)->pg_version_major == major && (conn)->pg_version_minor >= minor))
#define SERVER_VERSION_EQ(conn, major, minor) \
	((conn)->pg_version_major == major && (conn)->pg_version_minor == minor)
#define SERVER_VERSION_LE(conn, major, minor) (! SERVER_VERSION_GT(conn, major, minor))
#define SERVER_VERSION_LT(conn, major, minor) (! SERVER_VERSION_GE(conn, major, minor))
/*#if ! defined(HAVE_CONFIG_H) || defined(HAVE_STRINGIZE)*/
#define STRING_AFTER_DOT(string)	(strchr(#string, '.') + 1)
/*#else
#define STRING_AFTER_DOT(str)	(strchr("str", '.') + 1)
#endif*/
/*
 *	Simplified macros to compare the server's version with a
 *		specified version
 *	Note: Never pass a variable as the second parameter.
 *		  It must be a decimal constant of the form %d.%d .
 */
#define PG_VERSION_GT(conn, ver) \
 (SERVER_VERSION_GT(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
#define PG_VERSION_GE(conn, ver) \
 (SERVER_VERSION_GE(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
#define PG_VERSION_EQ(conn, ver) \
 (SERVER_VERSION_EQ(conn, (int) ver, atoi(STRING_AFTER_DOT(ver))))
#define PG_VERSION_LE(conn, ver) (! PG_VERSION_GT(conn, ver))
#define PG_VERSION_LT(conn, ver) (! PG_VERSION_GE(conn, ver))

/*	This is used to store cached table information in the connection */
struct col_info
{
	QResultClass *result;
	char		*schema;
	char		name[MAX_TABLE_LEN + 1];
};

 /* Translation DLL entry points */
#ifdef WIN32
#define DLLHANDLE HINSTANCE
#else
#define WINAPI CALLBACK
#define DLLHANDLE void *
#define HINSTANCE void *
#endif

typedef BOOL (FAR WINAPI * DataSourceToDriverProc) (UDWORD,
																SWORD,
																PTR,
																SDWORD,
																PTR,
																SDWORD,
															SDWORD FAR *,
															 UCHAR FAR *,
																SWORD,
															SWORD FAR *);

typedef BOOL (FAR WINAPI * DriverToDataSourceProc) (UDWORD,
																SWORD,
																PTR,
																SDWORD,
																PTR,
																SDWORD,
															SDWORD FAR *,
															 UCHAR FAR *,
																SWORD,
															SWORD FAR *);

/*******	The Connection handle	************/
struct ConnectionClass_
{
	HENV		henv;			/* environment this connection was created
								 * on */
	StatementOptions stmtOptions;
	ARDFields	ardOptions;
	APDFields	apdOptions;
	char	   *errormsg;
	int			errornumber;
	CONN_Status status;
	ConnInfo	connInfo;
	StatementClass **stmts;
	int			num_stmts;
	SocketClass *sock;
	int			lobj_type;
	int			ntables;
	COL_INFO  **col_info;
	long		translation_option;
	HINSTANCE	translation_handle;
	DataSourceToDriverProc DataSourceToDriver;
	DriverToDataSourceProc DriverToDataSource;
	Int2		driver_version; /* prepared for ODBC3.0 */
	char		transact_status;/* Is a transaction is currently in
								 * progress */
	char		errormsg_created;		/* has an informative error msg
										 * been created?  */
	char		pg_version[MAX_INFO_STRING];	/* Version of PostgreSQL
												 * we're connected to -
												 * DJP 25-1-2001 */
	float		pg_version_number;
	Int2		pg_version_major;
	Int2		pg_version_minor;
	char		ms_jet;
	char		unicode;
	char		result_uncommitted;
	char		schema_support;
#ifdef	MULTIBYTE
	char		*client_encoding;
	char		*server_encoding;
#endif   /* MULTIBYTE */
	int		ccsc;
	int		be_pid;	/* pid returned by backend */
	int		be_key; /* auth code needed to send cancel */
	UInt4		isolation;
	char		*current_schema;
};


/* Accessor functions */
#define CC_get_socket(x)					(x->sock)
#define CC_get_database(x)					(x->connInfo.database)
#define CC_get_server(x)					(x->connInfo.server)
#define CC_get_DSN(x)						(x->connInfo.dsn)
#define CC_get_username(x)					(x->connInfo.username)
#define CC_is_onlyread(x)					(x->connInfo.onlyread[0] == '1')


/*	for CC_DSN_info */
#define CONN_DONT_OVERWRITE		0
#define CONN_OVERWRITE			1


/*	prototypes */
ConnectionClass *CC_Constructor(void);
void		CC_conninfo_init(ConnInfo *conninfo);
char		CC_Destructor(ConnectionClass *self);
int			CC_cursor_count(ConnectionClass *self);
char		CC_cleanup(ConnectionClass *self);
char		CC_begin(ConnectionClass *self);
char		CC_commit(ConnectionClass *self);
char		CC_abort(ConnectionClass *self);
int			CC_set_translation(ConnectionClass *self);
char		CC_connect(ConnectionClass *self, char password_req, char *salt);
char		CC_add_statement(ConnectionClass *self, StatementClass *stmt);
char		CC_remove_statement(ConnectionClass *self, StatementClass *stmt);
char		CC_get_error(ConnectionClass *self, int *number, char **message);
QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag);
void		CC_clear_error(ConnectionClass *self);
char	   *CC_create_errormsg(ConnectionClass *self);
int			CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs);
char		CC_send_settings(ConnectionClass *self);
void		CC_lookup_lo(ConnectionClass *conn);
void		CC_lookup_pg_version(ConnectionClass *conn);
void		CC_initialize_pg_version(ConnectionClass *conn);
void		CC_log_error(const char *func, const char *desc, const ConnectionClass *self);
int			CC_get_max_query_len(const ConnectionClass *self);
int		CC_send_cancel_request(const ConnectionClass *conn);
void		CC_on_commit(ConnectionClass *conn);
void		CC_on_abort(ConnectionClass *conn, UDWORD opt);
void		ProcessRollback(ConnectionClass *conn, BOOL undo);
const char	*CC_get_current_schema(ConnectionClass *conn);

/* CC_send_query options */
#define	CLEAR_RESULT_ON_ABORT	1L
#define	CREATE_KEYSET		(1L << 1) /* create keyset for updatable curosrs */
#define	GO_INTO_TRANSACTION	(1L << 2) /* issue begin in advance */
/* CC_on_abort options */
#define	NO_TRANS		1L
#define	CONN_DEAD		(1L << 1) /* connection is no longer valid */

#endif /* __CONNECTION_H__ */
