/*--------------------------------------------------------------------
 * FILE:
 *     pool_process_query.c
 *
 * NOTE:
 *     query processing stuff
 *
 * Portions Copyright (c) 2003-2006, Atsushi Mitani
 * Portions Copyright (c) 2003-2006, Tatsuo Ishii
 *--------------------------------------------------------------------
 */
/*
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby
 * granted, provided that the above copyright notice appear in all
 * copies and that both that copyright notice and this permission
 * notice appear in supporting documentation, and that the name of the
 * author not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission. The author makes no representations about the
 * suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>

#include "postgres_fe.h"
#include "libpq/pqcomm.h"

#include "replicate_com.h"
#include "pglb.h"

POOL_STATUS pool_process_query(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, int connection_reuse);
POOL_STATUS ErrorResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
void pool_enable_timeout(void); 
void pool_disable_timeout(void);
int pool_check_fd(POOL_CONNECTION *cp, int notimeout);
void pool_send_frontend_exits(POOL_CONNECTION_POOL *backend);
POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
POOL_STATUS ParameterStatus(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
void pool_send_error_message(POOL_CONNECTION *frontend, int protoMajor, char *code, char *message, char *detail, char *hint, char *file, int line);


static POOL_STATUS Query(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, char *query);
static POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, int send_ready);
static POOL_STATUS CompleteCommandResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static int RowDescription(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS AsciiRow(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, short num_fields);
static POOL_STATUS BinaryRow(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, short num_fields);
static POOL_STATUS CursorResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS NoticeResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS CopyInResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS CopyOutResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS CopyDataRows(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, int copyin);
static POOL_STATUS EmptyQueryResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS FunctionCall(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS FunctionResultResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static POOL_STATUS ProcessFrontendResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static int synchronize(POOL_CONNECTION *cp);
static void process_reporting(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend);
static int reset_backend(POOL_CONNECTION_POOL *backend, int qcnt);
static int load_balance_enabled(POOL_CONNECTION_POOL *backend, char *sql);
static void start_load_balance(POOL_CONNECTION_POOL *backend);
static void end_load_balance(POOL_CONNECTION_POOL *backend);

static POOL_CONNECTION_POOL_SLOT *slots[MAX_CONNECTION_SLOTS];

POOL_STATUS pool_process_query(POOL_CONNECTION *frontend, 
							   POOL_CONNECTION_POOL *backend,
							   int connection_reuse)
{
	char * func = "pool_process_query()";
	char kind, kind1;	/* packet kind (backend) */
	char fkind;	/* packet kind (frontend) */
	short num_fields = 0;
	fd_set	readmask;
	fd_set	writemask;
	fd_set	exceptmask;
	int fds;
	POOL_STATUS status;
	int state;	/* 0: ok to issue commands 1: waiting for "ready for query" response */
	int qcnt;

	frontend->no_forward = connection_reuse;
	qcnt = 0;
	state = 0;

	for (;;)
	{
		kind = kind1 = 0;
		fkind = 0;

		if (state == 0 && connection_reuse)
		{
			int st = 0;

			/* send query for resetting connection such as "ROLLBACK" "RESET ALL"... */
			st = reset_backend(backend, qcnt);

			if (st < 0)		/* error? */
				return POOL_END;

			else if (st == 0)	/* no query issued? */
			{
				qcnt++;
				continue;
			}

			else if (st == 1)	/* more query remains */
			{
				state = 1;
				qcnt++;
				continue;
			}

			else if (st == 2)	/* no more qury */
			{
				frontend->no_forward = 0;
				return POOL_CONTINUE;
			}

		}

		if ((!REPLICATION && MASTER(backend)->len == 0 && frontend->len == 0) ||
			(REPLICATION && MASTER(backend)->len == 0 &&
			SECONDARY(backend)->len == 0
			 && frontend->len == 0))
		{

			struct timeval timeout;

			timeout.tv_sec = 1;
			timeout.tv_usec = 0;

			FD_ZERO(&readmask);
			FD_ZERO(&writemask);
			FD_ZERO(&exceptmask);
			if (!connection_reuse)
				FD_SET(frontend->fd, &readmask);
			FD_SET(MASTER(backend)->fd, &readmask);
			if (REPLICATION)
				FD_SET(SECONDARY(backend)->fd, &readmask);
			if (!connection_reuse)
				FD_SET(frontend->fd, &exceptmask);
			FD_SET(MASTER(backend)->fd, &exceptmask);

			if (connection_reuse)
			{
				if (REPLICATION)
					fds = select(Max(SECONDARY(backend)->fd, MASTER(backend)->fd) + 1,
								 &readmask, &writemask, &exceptmask, NULL);
				else
					fds = select(MASTER(backend)->fd+1, &readmask, &writemask, &exceptmask, NULL);
			}
			else
			{
				if (REPLICATION)
					fds = select(Max(SECONDARY(backend)->fd,
									 Max(frontend->fd, MASTER(backend)->fd)+1),
								 &readmask, &writemask, &exceptmask, NULL);
				else
					fds = select(Max(frontend->fd, MASTER(backend)->fd)+1,
								 &readmask, &writemask, &exceptmask, NULL);
			}

			if (fds == -1)
			{
				if (errno == EINTR)
					continue;

				show_error("%s:select() failed. reason: %s",func, strerror(errno));
				return POOL_ERROR;
			}

			if (fds == 0)
			{
				return POOL_CONTINUE;
			}

			if (FD_ISSET(MASTER(backend)->fd, &readmask))
			{
				pool_read(MASTER(backend), &kind, 1);
#ifdef PRINT_DEBUG
				show_debug("%s:read kind from backend %c", func,kind);
#endif			
			}

			if (REPLICATION && FD_ISSET(SECONDARY(backend)->fd, &readmask))
			{
				pool_read(SECONDARY(backend), &kind1, 1);
#ifdef PRINT_DEBUG
				show_debug("%s:read kind from secondary backend %c", func,kind1);
#endif			
			}

			if (!connection_reuse && FD_ISSET(frontend->fd, &exceptmask))
			{
				return POOL_END;
			}
			if (FD_ISSET(MASTER(backend)->fd, &exceptmask))
			{
				return POOL_ERROR;
			}

			if (!connection_reuse && FD_ISSET(frontend->fd, &readmask))
			{
				status = ProcessFrontendResponse(frontend, backend);
				if (status != POOL_CONTINUE)
					return status;

				continue;
			}
		}
		else
		{
			if (MASTER(backend)->len > 0)
			{
				pool_read(MASTER(backend), &kind, 1);
				if (REPLICATION)
				{
					pool_read(SECONDARY(backend), &kind1, 1);
					if (kind == '\0' || kind != kind1)
					{
						show_error("%s: kind does not match between backends master(%c) secondary(%c)",
								   func, kind, kind1);
						pool_send_error_message(frontend, MAJOR(backend), "XX000", 
												"kind mismatch between backends", "",
												"check data consistency between master and secondary", __FILE__, __LINE__);

						if (pool_config_replication_stop_on_mismatch)
							return POOL_FATAL;
						else
							return POOL_ERROR;
					}
				}
#ifdef PRINT_DEBUG
				show_debug("%s:read kind from backend pending data %c len: %d po: %d", func, kind, MASTER(backend)->len, MASTER(backend)->po);
#endif			
			}
			if (frontend->len > 0)
			{
				status = ProcessFrontendResponse(frontend, backend);
				if (status != POOL_CONTINUE)
					return status;

				continue;
			}
		}

		/* this is the synchronous point */
		if (REPLICATION)
		{
			if (kind == 0)
			{
				pool_read(MASTER(backend), &kind, 1);
			}
			if (kind1 == 0)
			{
				pool_read(SECONDARY(backend), &kind1, 1);
			}
			if (kind == '\0' || kind != kind1)
			{
				show_error("%s: kind does not match between backends master(%c) secondary(%c)",
						   func, kind, kind1);
				pool_send_error_message(frontend, MAJOR(backend), "XX000", 
										"kind mismatch between backends", "",
										"check data consistency between master and secondary", __FILE__, __LINE__);

				if (pool_config_replication_stop_on_mismatch)
					return POOL_FATAL;
				else
					return POOL_ERROR;
			}
		}

		/*
		 * Prrocess backend Response
		 */

		if (MAJOR(backend) == PROTO_MAJOR_V3)
		{
			switch (kind)
			{
				case 'G':
					/* CopyIn response */
					status = CopyInResponse(frontend, backend);
					break;
				case 'S':
					/* Paramter Status */
					status = ParameterStatus(frontend, backend);
					break;
				case 'Z':
					/* Ready for query */
					status = ReadyForQuery(frontend, backend, 1);
					break;
				default:
					status = SimpleForwardToFrontend(kind, frontend, backend);
					break;
			}
		}
		else
		{
			switch (kind)
			{
				case 'A':
					/* Notification  response */
					status = NotificationResponse(frontend, backend);
					break;

				case 'B':
					/* BinaryRow */
					status = BinaryRow(frontend, backend, num_fields);
					break;

				case 'C':
					/* Complete command response */
					status = CompleteCommandResponse(frontend, backend);
					break;

				case 'D':
					/* AsciiRow */
					status = AsciiRow(frontend, backend, num_fields);
					break;

				case 'E':
					/* Error Response */
					status = ErrorResponse(frontend, backend);
					break;

				case 'G':
					/* CopyIn Response */
					status = CopyInResponse(frontend, backend);
					break;

				case 'H':
					/* CopyOut Response */
					status = CopyOutResponse(frontend, backend);
					break;

				case 'I':
					/* Empty Query Response */
					status = EmptyQueryResponse(frontend, backend);
					break;

				case 'N':
					/* Notice Response */
					status = NoticeResponse(frontend, backend);
					break;

				case 'P':
					/* CursorResponse */
					status = CursorResponse(frontend, backend);
					break;

				case 'T':
					/* RowDescription */
					status = RowDescription(frontend, backend);
					if (status < 0)
						return POOL_ERROR;

					num_fields = status;
					status = POOL_CONTINUE;
					break;

				case 'V':
					/* FunctionResultResponse and FunctionVoidResponse */
					status = FunctionResultResponse(frontend, backend);
					break;
				
				case 'Z':
					/* Ready for query */
					status = ReadyForQuery(frontend, backend, 1);
					break;
				
				default:
					show_error("%s:Unknown message type %c(%02x)",func, kind, kind);
					exit(1);
			}
		}

		if (status != POOL_CONTINUE)
			return status;

		if (kind == 'Z' && frontend->no_forward && state == 1)
		{
			state = 0;
		}

	}
	return POOL_CONTINUE;
}

static POOL_STATUS Query(POOL_CONNECTION *frontend, 
						 POOL_CONNECTION_POOL *backend, char *query)
{
#ifdef PRINT_DEBUG
	char * func = "Query()";
#endif			
	char *string;
	int len;
	static char *sq = "show pool_status";

	if (query == NULL)
	{
		/* read actual query */
		if (MAJOR(backend) == PROTO_MAJOR_V3)
		{
			if (pool_read(frontend, &len, sizeof(len)) < 0)
				return POOL_END;
			len = ntohl(len) - 4;
			string = pool_read2(frontend, len);
		}
		else
			string = pool_read_string(frontend, &len, 0);

		if (string == NULL)
			return POOL_END;
	}
	else
	{
		len = strlen(query)+1;
		string = query;
	}

#ifdef PRINT_DEBUG
	show_debug("%s: %s", func,string);
#endif			

	/* process status reporting? */
	if (strncasecmp(sq, string, strlen(sq)) == 0)
	{
#ifdef PRINT_DEBUG
		show_debug("%s:process reporting",func);
#endif			
		process_reporting(frontend, backend);
		return POOL_CONTINUE;
	}

	/* load balance trick */
	if (load_balance_enabled(backend, string))
		start_load_balance(backend);

	/* forward the query to the backend */
	pool_write(MASTER(backend), "Q", 1);

	if (MAJOR(backend) == PROTO_MAJOR_V3)
	{
		int sendlen = htonl(len + 4);
		pool_write(MASTER(backend), &sendlen, sizeof(sendlen));
	}

	if (pool_write_and_flush(MASTER(backend), string, len) < 0)
	{
		return POOL_END;
	}

	if (REPLICATION)
	{
		/* in "strict mode" we need to wait for master completing the query */
		if (pool_config_replication_strict || STRICT_MODE(string))
			if (synchronize(MASTER(backend)))
				return POOL_END;

		pool_write(SECONDARY(backend), "Q", 1);
		if (MAJOR(backend) == PROTO_MAJOR_V3)
		{
			int sendlen = htonl(len + 4);
			pool_write(SECONDARY(backend), &sendlen, sizeof(sendlen));
		}

		if (pool_write_and_flush(SECONDARY(backend), string, len) < 0)
		{
			return POOL_END;
		}
	}
	return POOL_CONTINUE;
}

static POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend, 
								 POOL_CONNECTION_POOL *backend, int send_ready)
{
#ifdef PRINT_DEBUG
	char * func = "ReadyForQuery()";
#endif			

	pool_flush(frontend);

	if (send_ready)
	{
		pool_write(frontend, "Z", 1);

		if (MAJOR(backend) == PROTO_MAJOR_V3)
		{
			int len;
			signed char state;

			if ((len = pool_read_message_length(backend)) < 0)
				return POOL_END;

#ifdef PRINT_DEBUG
			show_debug("%s: message length: %d", func, len);
#endif			

			len = htonl(len);
			pool_write(frontend, &len, sizeof(len));

			state = pool_read_kind(backend);
			if (state < 0)
				return POOL_END;

			/* set transaction state */
#ifdef PRINT_DEBUG
			show_debug("%s: transaction state: %c", func, state);
#endif			
			MASTER(backend)->tstate = state;
			if (REPLICATION)
				SECONDARY(backend)->tstate = state;

			pool_write(frontend, &state, 1);
		}

		if (pool_flush(frontend))
			return POOL_END;
	}

	/* end load balance mode */
	if (IN_LOAD_BALANCE)
		end_load_balance(backend);

	return ProcessFrontendResponse(frontend, backend);
}

static POOL_STATUS CompleteCommandResponse(POOL_CONNECTION *frontend, 
										   POOL_CONNECTION_POOL *backend)
{
	char * func = "CompleteCommandResponse()";
	char *string, *string1;
	int len, len1;

	/* read command tag */
	string = pool_read_string(MASTER(backend), &len, 0);
	if (string == NULL)
		return POOL_END;

	if (REPLICATION)
	{
		string1 = pool_read_string(SECONDARY(backend), &len1, 0);
		if (string1 == NULL)
			return POOL_END;

		if (len != len1)
		{
			show_error("%s: message length does not match between master(%d \"%s\",) and secondary(%d \"%s\",)",
					 func, len, string, len1, string1);
		}
	}

	/* forward to the frontend */
	pool_write(frontend, "C", 1);
#ifdef PRINT_DEBUG
	show_debug("%s: string: \"%s\"",func, string);
#endif			
	if (pool_write(frontend, string, len) < 0)
	{
		return POOL_END;
	}
	return POOL_CONTINUE;
}

static int RowDescription(POOL_CONNECTION *frontend, 
						  POOL_CONNECTION_POOL *backend)
{
	char * func = "RowDescription()";
	short num_fields, num_fields1;
	int oid, mod;
	int oid1, mod1;
	short size, size1;
	char *string, *string1;
	int len, len1;
	int i;

	/* # of fields (could be 0) */
	pool_read(MASTER(backend), &num_fields, sizeof(short));
	if (REPLICATION)
	{
		pool_read(SECONDARY(backend), &num_fields1, sizeof(short));
		if (num_fields != num_fields1)
		{
			show_error("%s: num_fields deos not match between backends master(%d) and secondary(%d)",
					   func, num_fields, num_fields1);
			return POOL_FATAL;
		}
	}

	/* forward it to the frontend */
	pool_write(frontend, "T", 1);
	pool_write(frontend, &num_fields, sizeof(short));

	num_fields = ntohs(num_fields);
	for (i = 0;i<num_fields;i++)
	{
		/* field name */
		string = pool_read_string(MASTER(backend), &len, 0);
		if (string == NULL)
			return POOL_END;

		if (REPLICATION)
		{
			string1 = pool_read_string(SECONDARY(backend), &len1, 0);
			if (string == NULL)
				return POOL_END;
			if (len != len1)
			{
				show_error("%s: field length deos not match between backends master(%d) and secondary(%d)",
						   func, ntohl(len), ntohl(len1));
				return POOL_FATAL;
			}
		}

		pool_write(frontend, string, len);

		/* oid */
		pool_read(MASTER(backend), &oid, sizeof(int));
		if (REPLICATION)
		{
			pool_read(SECONDARY(backend), &oid1, sizeof(int));

			/* we do not regard oid mismatch as fatal */
			if (oid != oid1)
			{
				show_error("%s: field oid deos not match between backends master(%d) and secondary(%d)",
						 func, ntohl(oid), ntohl(oid1));
			}
		}
		pool_write(frontend, &oid, sizeof(int));

		/* size */
		pool_read(MASTER(backend), &size, sizeof(short));
		if (REPLICATION)
		{
			pool_read(SECONDARY(backend), &size1, sizeof(short));
			if (size1 != size1)
			{
				show_error("%s: field size deos not match between backends master(%d) and secondary(%d)",
						 func, ntohs(size), ntohs(size1));
				return POOL_FATAL;
			}
		}
#ifdef PRINT_DEBUG
		show_debug("%s: field size:%d", func, ntohs(size));
#endif			
		pool_write(frontend, &size, sizeof(short));

		/* modifier */
		pool_read(MASTER(backend), &mod, sizeof(int));
		if (REPLICATION)
		{
			pool_read(SECONDARY(backend), &mod1, sizeof(int));
			if (mod != mod1)
			{
				show_error("%s: modifier deos not match between backends master(%d) and secondary(%d)",
						 func, ntohl(mod), ntohl(mod1));
			}
		}
		pool_write(frontend, &mod, sizeof(int));
	}

	return num_fields;
}

static POOL_STATUS AsciiRow(POOL_CONNECTION *frontend, 
							POOL_CONNECTION_POOL *backend,
							short num_fields)
{
	char * func = "AsciiRow()";
	static char nullmap[8192], nullmap1[8192];
	int nbytes;
	int i;
	unsigned char mask;
	int size, size1;
	char *buf;
	char msgbuf[1024];

	pool_write(frontend, "D", 1);

	nbytes = (num_fields + 7)/8;

	if (nbytes <= 0)
		return POOL_CONTINUE;

	/* NULL map */
	pool_read(MASTER(backend), nullmap, nbytes);
	if (pool_write(frontend, nullmap, nbytes) < 0)
		return POOL_END;

	if (REPLICATION)
	{
		if (pool_read(SECONDARY(backend), nullmap1, nbytes) < 0)
			return POOL_END;

		if (memcmp(nullmap, nullmap1, nbytes))
		{
			/* XXX: NULLMAP maybe different among
			   backends. If we were a paranoid, we have to treat
			   this as a fatal error. However in the real world
			   we'd better to adapt this situation. Just throw a
			   log... */
			show_error("%s: NULLMAP differ between master and secondary",func);
		}
	}

	mask = 0;

	for (i = 0;i<num_fields;i++)
	{
		if (mask == 0)
			mask = 0x80;

		/* NOT NULL? */
		if (mask & nullmap[i/8])
		{
			/* field size */
			if (pool_read(MASTER(backend), &size, sizeof(int)) < 0)
				return POOL_END;
		}

		if (REPLICATION && (mask & nullmap1[i/8]))
		{
			/* XXX: field size maybe different among
			   backends. If we were a paranoid, we have to treat
			   this as a fatal error. However in the real world
			   we'd better to adapt this situation. Just throw a
			   log... */

			if (pool_read(SECONDARY(backend), &size1, sizeof(int)) < 0)
				return POOL_END;

			if (size != size1)
				show_error("%s: %d th field size does not match between master(%d) and secondary(%d)",
						 func, i, ntohl(size), ntohl(size1));
			size1 = ntohl(size1) - 4;
		}

		buf = NULL;

		if (mask & nullmap[i/8])
		{
			/* forward to frontend */
			pool_write(frontend, &size, sizeof(int));
			size = ntohl(size) - 4;

			/* read and send actual data only when size > 0 */
			if (size > 0)
			{
				buf = pool_read2(MASTER(backend), size);
				if (buf == NULL)
					return POOL_END;
			}
		}

		if (REPLICATION && size1 > 0 && (mask & nullmap1[i/8]))
		{
			/* read and discard secondary data */
			if (pool_read2(SECONDARY(backend), size1) == NULL)
				return POOL_END;
		}

		if (buf)
		{
			pool_write(frontend, buf, size);
			snprintf(msgbuf, Min(sizeof(msgbuf), size+1), "%s", buf);
#ifdef PRINT_DEBUG
			show_debug("%s: len: %d data: %s", func, size, msgbuf);
#endif			
		}

		mask >>= 1;
	}

	return POOL_CONTINUE;
}

static POOL_STATUS BinaryRow(POOL_CONNECTION *frontend, 
							 POOL_CONNECTION_POOL *backend,
							 short num_fields)
{
	char * func = "BinaryRow()";
	static char nullmap[8192], nullmap1[8192];
	int nbytes;
	int i;
	unsigned char mask;
	int size, size1;
	char *buf;

	pool_write(frontend, "B", 1);

	nbytes = (num_fields + 7)/8;

	if (nbytes <= 0)
		return POOL_CONTINUE;

	/* NULL map */
	pool_read(MASTER(backend), nullmap, nbytes);
	if (pool_write(frontend, nullmap, nbytes) < 0)
		return POOL_END;

	if (REPLICATION)
	{
		if (pool_read(SECONDARY(backend), nullmap1, nbytes) < 0)
			return POOL_END;

		if (memcmp(nullmap, nullmap1, nbytes))
		{
			/* XXX: NULLMAP maybe different among
			   backends. If we were a paranoid, we have to treat
			   this as a fatal error. However in the real world
			   we'd better to adapt this situation. Just throw a
			   log... */
			show_error("%s: NULLMAP differ between master and secondary",func);
		}
	}

	mask = 0;

	for (i = 0;i<num_fields;i++)
	{
		if (mask == 0)
			mask = 0x80;

		/* NOT NULL? */
		if (mask & nullmap[i/8])
		{
			/* field size */
			if (pool_read(MASTER(backend), &size, sizeof(int)) < 0)
				return POOL_END;
		}

		if (REPLICATION && (mask & nullmap1[i/8]))
		{
			/* XXX: field size maybe different among
			   backends. If we were a paranoid, we have to treat
			   this as a fatal error. However in the real world
			   we'd better to adapt this situation. Just throw a
			   log... */

			if (pool_read(SECONDARY(backend), &size1, sizeof(int)) < 0)
				return POOL_END;

			if (size != size1)
				show_error("%s: %d th field size does not match between master(%d) and secondary(%d)",
						 func, i, ntohl(size), ntohl(size1));
			size1 = ntohl(size1) - 4;
		}

		buf = NULL;

		if (mask & nullmap[i/8])
		{
			/* forward to frontend */
			pool_write(frontend, &size, sizeof(int));
			size = ntohl(size) - 4;

			/* read and send actual data only when size > 0 */
			if (size > 0)
			{
				buf = pool_read2(MASTER(backend), size);
				if (buf == NULL)
					return POOL_END;
			}
		}

		if (REPLICATION && size1 > 0 && (mask & nullmap1[i/8]))
		{
			/* read and discard secondary data */
			if (pool_read2(SECONDARY(backend), size1) == NULL)
				return POOL_END;
		}

		if (buf)
			pool_write(frontend, buf, size);

		mask >>= 1;
	}
	return POOL_CONTINUE;
}

static POOL_STATUS CursorResponse(POOL_CONNECTION *frontend, 
								  POOL_CONNECTION_POOL *backend)
{
	char * func = "CursorResponse()";
	char *string, *string1;
	int len, len1;

	/* read cursor name */
	string = pool_read_string(MASTER(backend), &len, 0);
	if (string == NULL)
		return POOL_END;
	if (REPLICATION)
	{
		string1 = pool_read_string(SECONDARY(backend), &len1, 0);
		if (string1 == NULL)
			return POOL_END;
		if (len != len1)
		{
			show_error("%s: length does not match between master(%d) and secondary(%d)",
					   func, len, len1);
			show_error("%s: master(%s) secondary(%s)", func, string, string1);
			return POOL_END;
		}
	}

	/* forward to the frontend */
	pool_write(frontend, "P", 1);
	if (pool_write(frontend, string, len) < 0)
	{
		return POOL_END;
	}
	return POOL_CONTINUE;
}

POOL_STATUS ErrorResponse(POOL_CONNECTION *frontend, 
						  POOL_CONNECTION_POOL *backend)
{
	char *string;
	int len;

	/* read error message */
	string = pool_read_string(MASTER(backend), &len, 0);
	if (string == NULL)
		return POOL_END;
	if (REPLICATION)
	{
		string = pool_read_string(SECONDARY(backend), &len, 0);
		if (string == NULL)
			return POOL_END;
	}

	/* forward to the frontend */
	pool_write(frontend, "E", 1);
	if (pool_write_and_flush(frontend, string, len) < 0)
		return POOL_END;
			
	return POOL_CONTINUE;
}

static POOL_STATUS NoticeResponse(POOL_CONNECTION *frontend, 
								  POOL_CONNECTION_POOL *backend)
{
	char *string, *string1;
	int len, len1;

	/* read notice message */
	string = pool_read_string(MASTER(backend), &len, 0);
	if (string == NULL)
		return POOL_END;
	if (REPLICATION)
	{
		string1 = pool_read_string(SECONDARY(backend), &len1, 0);
		if (string1 == NULL)
			return POOL_END;
	}

	/* forward to the frontend */
	pool_write(frontend, "N", 1);
	if (pool_write_and_flush(frontend, string, len) < 0)
	{
		return POOL_END;
	}
	return POOL_CONTINUE;
}

static POOL_STATUS CopyInResponse(POOL_CONNECTION *frontend, 
								  POOL_CONNECTION_POOL *backend)
{
	POOL_STATUS status;

	/* forward to the frontend */
	if (MAJOR(backend) == PROTO_MAJOR_V3)
	{
		if (SimpleForwardToFrontend('G', frontend, backend) != POOL_CONTINUE)
			return POOL_END;
		if (pool_flush(frontend) != POOL_CONTINUE)
			return POOL_END;
	}
	else
		if (pool_write_and_flush(frontend, "G", 1) < 0)
			return POOL_END;

	status = CopyDataRows(frontend, backend, 1);
	return status;
}

static POOL_STATUS CopyOutResponse(POOL_CONNECTION *frontend, 
								   POOL_CONNECTION_POOL *backend)
{
	POOL_STATUS status;

	/* forward to the frontend */
	if (MAJOR(backend) == PROTO_MAJOR_V3)
	{
		if (SimpleForwardToFrontend('H', frontend, backend) != POOL_CONTINUE)
			return POOL_END;
		if (pool_flush(frontend) != POOL_CONTINUE)
			return POOL_END;
	}
	else
		if (pool_write_and_flush(frontend, "H", 1) < 0)
			return POOL_END;

	status = CopyDataRows(frontend, backend, 0);
	return status;
}

static POOL_STATUS CopyDataRows(POOL_CONNECTION *frontend,
								POOL_CONNECTION_POOL *backend, int copyin)
{
#ifdef PRINT_DEBUG
	char * func = "CopyDataRows()";
#endif			
	char *string;
	int len;

#ifdef PRINT_DEBUG
	int i = 0;
	char *buf;
#endif

	for (;;)
	{
		if (copyin)
		{
			if (MAJOR(backend) == PROTO_MAJOR_V3)
			{
				char kind;
				POOL_STATUS status;

				if (pool_read(frontend, &kind, 1) < 0)
					return POOL_END;
				
				status = SimpleForwardToBackend(kind, frontend, backend);
				if (status == POOL_END)
					return status;

				/* CopyData? */
				if (kind == 'd')
					continue;
				else
					break;
			}
			else
			{
				string = pool_read_string(frontend, &len, 1);
				if (string == NULL)
					return POOL_END;
			}
		}
		else
		{
			/* CopyOut */
			if (MAJOR(backend) == PROTO_MAJOR_V3)
			{
				signed char kind;
				POOL_STATUS status;

				if ((kind = pool_read_kind(backend)) < 0)
					return POOL_END;
				
				status = SimpleForwardToFrontend(kind, frontend, backend);
				if (status == POOL_END)
					return status;

				/* CopyData? */
				if (kind == 'd')
					continue;
				else
					break;
			}
			else
			{
				string = pool_read_string(MASTER(backend), &len, 1);
				if (REPLICATION)
					string = pool_read_string(SECONDARY(backend), &len, 1);
			}
		}

		if (string == NULL)
			return POOL_END;

#ifdef PRINT_DEBUG
		buf = malloc(len + 1);
		if (buf == NULL)
		{
			show_error("CopyDataRows: malloc failed: %s", strerror(errno));
			return POOL_END;
		}
		strncpy(buf, string, len);
		buf[len] = '\0';
		show_debug("%s: copy line %d %d bytes :%s:",func, i++, len, buf);
		free(buf);
#endif

		if (copyin)
		{
			pool_write(MASTER(backend), string, len);
			if (REPLICATION)
				pool_write(SECONDARY(backend), string, len);
		}
		else
			pool_write(frontend, string, len);			

		if (len == PROTO_MAJOR_V3)
		{
			/* end of copy? */
			if (string[0] == '\\' &&
				string[1] == '.' &&
				string[2] == '\n')
			{
				break;
			}
		}
	}

	if (copyin)
	{
		if (pool_flush(MASTER(backend)) <0)
			return POOL_END;
		if (REPLICATION)
		{
			if (pool_flush(SECONDARY(backend)) <0)
				return POOL_END;
		}
	}
	else
		if (pool_flush(frontend) <0)
			return POOL_END;

	return POOL_CONTINUE;
}

static POOL_STATUS EmptyQueryResponse(POOL_CONNECTION *frontend,
									  POOL_CONNECTION_POOL *backend)
{
	char c;

	if (pool_read(MASTER(backend), &c, sizeof(c)) < 0)
		return POOL_END;

	if (REPLICATION)
	{
		if (pool_read(SECONDARY(backend), &c, sizeof(c)) < 0)
			return POOL_END;
	}

	pool_write(frontend, "I", 1);
	return pool_write_and_flush(frontend, "", 1);
}

static POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend, 
										POOL_CONNECTION_POOL *backend)
{
	int pid, pid1;
	char *condition, *condition1;
	int len, len1;

	pool_write(frontend, "A", 1);

	if (pool_read(MASTER(backend), &pid, sizeof(pid)) < 0)
		return POOL_ERROR;

	if (REPLICATION)
	{
		if (pool_read(SECONDARY(backend), &pid1, sizeof(pid1)) < 0)
			return POOL_ERROR;
	}

	condition = pool_read_string(MASTER(backend), &len, 0);
	if (condition == NULL)
		return POOL_END;
	if (REPLICATION)
	{
		condition1 = pool_read_string(SECONDARY(backend), &len1, 0);
		if (condition1 == NULL)
			return POOL_END;
	}

	pool_write(frontend, &pid, sizeof(pid));

	return pool_write_and_flush(frontend, condition, len);
}

static POOL_STATUS FunctionCall(POOL_CONNECTION *frontend, 
								POOL_CONNECTION_POOL *backend)
{
	char dummy[2];
	int oid;
	int argn;
	int i;

	pool_write(MASTER(backend), "F", 1);
	if (REPLICATION)
		pool_write(SECONDARY(backend), "F", 1);

	/* dummy */
	if (pool_read(frontend, dummy, sizeof(dummy)) < 0)
		return POOL_ERROR;
	pool_write(MASTER(backend), dummy, sizeof(dummy));
	if (REPLICATION)
		pool_write(SECONDARY(backend), dummy, sizeof(dummy));

	/* function object id */
	if (pool_read(frontend, &oid, sizeof(oid)) < 0)
		return POOL_ERROR;

	pool_write(MASTER(backend), &oid, sizeof(oid));
	if (REPLICATION)
		pool_write(SECONDARY(backend), &oid, sizeof(oid));

	/* number of arguments */
	if (pool_read(frontend, &argn, sizeof(argn)) < 0)
		return POOL_ERROR;
	pool_write(MASTER(backend), &argn, sizeof(argn));
	if (REPLICATION)
		pool_write(SECONDARY(backend), &argn, sizeof(argn));

	argn = ntohl(argn);

	for (i=0;i<argn;i++)
	{
		int len;
		char *arg;

		/* length of each argument in bytes */
		if (pool_read(frontend, &len, sizeof(len)) < 0)
			return POOL_ERROR;

		pool_write(MASTER(backend), &len, sizeof(len));
		if (REPLICATION)
			pool_write(SECONDARY(backend), &len, sizeof(len));

		len = ntohl(len);

		/* argument value itself */
		if ((arg = pool_read2(frontend, len)) == NULL)
			return POOL_ERROR;
		pool_write(MASTER(backend), arg, len);
		if (REPLICATION)
			pool_write(SECONDARY(backend), arg, len);
	}

	if (pool_flush(MASTER(backend)))
		return POOL_ERROR;
	if (REPLICATION)
		if (pool_flush(SECONDARY(backend)))
			return POOL_ERROR;
	return POOL_CONTINUE;
}

static POOL_STATUS FunctionResultResponse(POOL_CONNECTION *frontend, 
										  POOL_CONNECTION_POOL *backend)
{
	char dummy;
	int len;
	char *result;

	pool_write(frontend, "V", 1);

	if (pool_read(MASTER(backend), &dummy, 1) < 0)
		return POOL_ERROR;
	if (REPLICATION)
		if (pool_read(SECONDARY(backend), &dummy, 1) < 0)
			return POOL_ERROR;

	pool_write(frontend, &dummy, 1);

	/* non empty result? */
	if (dummy == 'G')
	{
		/* length of result in bytes */
		if (pool_read(MASTER(backend), &len, sizeof(len)) < 0)
			return POOL_ERROR;
		if (REPLICATION)
			if (pool_read(SECONDARY(backend), &len, sizeof(len)) < 0)
				return POOL_ERROR;

		pool_write(frontend, &len, sizeof(len));

		len = ntohl(len);

		/* result value itself */
		if ((result = pool_read2(MASTER(backend), len)) == NULL)
			return POOL_ERROR;
		if (REPLICATION)
			if (pool_read(SECONDARY(backend), result, len) < 0)
				return POOL_ERROR;

		pool_write(frontend, result, len);
	}

	/* unused ('0') */
	if (pool_read(MASTER(backend), &dummy, 1) < 0)
		return POOL_ERROR;
	if (REPLICATION)
		if (pool_read(SECONDARY(backend), &dummy, 1) < 0)
			return POOL_ERROR;

	pool_write(frontend, "0", 1);

	return pool_flush(frontend);
}

static POOL_STATUS ProcessFrontendResponse(POOL_CONNECTION *frontend, 
										   POOL_CONNECTION_POOL *backend)
{
	char * func = "ProcessFrontendResponse()";
	char fkind;
	POOL_STATUS status;

	if (frontend->len <= 0 && frontend->no_forward != 0)
		return POOL_CONTINUE;

	if (pool_read(frontend, &fkind, 1) < 0)
	{
		show_error("%s: failed to read kind",func);
		return POOL_END;
	}

#ifdef PRINT_DEBUG
	show_debug("%s:read kind from frontend %c(%02x)", func, fkind, fkind);
#endif			

	switch (fkind)
	{
		case 'X':
			if (MAJOR(backend) == PROTO_MAJOR_V3)
			{
				int len;
				pool_read(frontend, &len, sizeof(len));
			}
			status = POOL_END;
			break;

		case 'Q':
			status = Query(frontend, backend, NULL);
			break;

		default:
			if (MAJOR(backend) == PROTO_MAJOR_V3)
			{
				status = SimpleForwardToBackend(fkind, frontend, backend);
				if (pool_flush(MASTER(backend)))
					status = POOL_ERROR;
				if (REPLICATION)
					if (pool_flush(SECONDARY(backend)))
						status = POOL_ERROR;
			}
			else if (MAJOR(backend) == PROTO_MAJOR_V2 && fkind == 'F')
				status = FunctionCall(frontend, backend);
			else
			{
				show_error("%s: unknown message type %c(%02x)", func, fkind, fkind);
				status = POOL_ERROR;
			}
			break;
	}

	return status;
}

static int timeoutmsec;
/*
 * enable read timeout
 */
void pool_enable_timeout(void)
{
	timeoutmsec = pool_config_replication_timeout;
}

/*
 * disable read timeout
 */
void pool_disable_timeout(void)
{
	timeoutmsec = 0;
}

/*
 * wait until read data is ready
 */
static int synchronize(POOL_CONNECTION *cp)
{
	return pool_check_fd(cp, 1);
}

/*
 * wait until read data is ready
 * if notimeout is non 0, wait forever.
 */
int pool_check_fd(POOL_CONNECTION *cp, int notimeout)
{
	char * func = "pool_check_fd()";
	fd_set readmask;
	fd_set exceptmask;
	int fd;
	int fds;
	struct timeval timeout;
	struct timeval *tp;

	fd = cp->fd;

	for (;;)
	{
		FD_ZERO(&readmask);
		FD_ZERO(&exceptmask);
		FD_SET(fd, &readmask);
		FD_SET(fd, &exceptmask);

		if (notimeout || timeoutmsec == 0)
			tp = NULL;
		else
		{
			timeout.tv_sec = 0;
			timeout.tv_usec = pool_config_replication_timeout*1000;
			tp = &timeout;
		}

		fds = select(fd+1, &readmask, NULL, &exceptmask, tp);

		if (fds == -1)
		{
			if (errno == EAGAIN || errno == EINTR)
				continue;

			show_error("%s: select() failed. reason %s",func, strerror(errno));
			break;
		}

		if (FD_ISSET(fd, &exceptmask))
		{
			show_error("%s: exception occurred",func);
			break;
		}

		if (fds == 0)
		{
			show_error("%s: data is not ready tp->tv_sec %d tp->tp_usec %d", func, tp->tv_sec, tp->tv_usec);
			break;
		}
		return 0;
	}
	return -1;
}

static void process_reporting(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
{
	static char *cursorname = "blank";
	static short num_fields = 3;
	static char *field_names[] = {"item", "value", "description"};
	static int oid = 0;
	static short fsize = -1;
	static int mod = 0;
	short n;
	int i;
	short s;
	int len;
	short colnum;

	static char nullmap[2] = {0xff, 0xff};
	int nbytes = (num_fields + 7)/8;

#define MAXVALLEN 512

	typedef struct {
		char *name;
		char value[MAXVALLEN+1];
		char *desc;
	} POOL_REPORT_STATUS;

#define MAXITEMS 128

	POOL_REPORT_STATUS status[MAXITEMS];

	short nrows;
	int size;
	int hsize;

	i = 0;

	status[i].name = "inetdomain";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_inetdomain);
	status[i].desc = "1 if accepting TCP/IP connection";
	i++;

	status[i].name = "port";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_port);
	status[i].desc = "pgpool accepting port number";
	i++;

	status[i].name = "socket_dir";
	snprintf(status[i].value, MAXVALLEN, "%s", pool_config_socket_dir);
	status[i].desc = "pgpool socket directory";
	i++;

	status[i].name = "backend_host_name";
	snprintf(status[i].value, MAXVALLEN, "%s", pool_config_backend_host_name);
	status[i].desc = "master backend host name";
	i++;

	status[i].name = "backend_port";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_backend_port);
	status[i].desc = "master backend port number";
	i++;

	status[i].name = "secondary_backend_host_name";
	snprintf(status[i].value, MAXVALLEN, "%s", pool_config_secondary_backend_host_name);
	status[i].desc = "secondary backend host name";
	i++;

	status[i].name = "secondary_backend_port";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_secondary_backend_port);
	status[i].desc = "secondary backend port number";
	i++;

	status[i].name = "num_init_children";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_num_init_children);
	status[i].desc = "# of children initially pre-forked";
	i++;

	status[i].name = "child_life_time";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_child_life_time);
	status[i].desc = "if idle for this seconds, child exits (not implemented yet)";
	i++;

	status[i].name = "connection_life_time";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_connection_life_time);
	status[i].desc = "if idle for this seconds, connection closes";
	i++;

	status[i].name = "max_pool";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_max_pool);
	status[i].desc = "max # of connection pool per child";
	i++;

	status[i].name = "logdir";
	snprintf(status[i].value, MAXVALLEN, "%s", pool_config_logdir);
	status[i].desc = "logging directory";
	i++;

	status[i].name = "backend_socket_dir";
	snprintf(status[i].value, MAXVALLEN, "%s", pool_config_backend_socket_dir);
	status[i].desc = "Unix domain socket directory for the PostgreSQL server";
	i++;

	status[i].name = "replication_mode";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_replication_mode);
	status[i].desc = "non 0 if operating in replication mode";
	i++;

	status[i].name = "replication_strict";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_replication_strict);
	status[i].desc = "non 0 if operating in strict mode";
	i++;

	status[i].name = "replication_timeout";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_replication_timeout);
	status[i].desc = "if secondary does not respond in this milli seconds, abort the session";
	i++;

	status[i].name = "current_backend_host_name";
	snprintf(status[i].value, MAXVALLEN, "%s", pool_config_current_backend_host_name);
	status[i].desc = "current master host name";
	i++;

	status[i].name = "current_backend_port";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_current_backend_port);
	status[i].desc = "current master port #";
	i++;

	status[i].name = "replication_enabled";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_replication_enabled);
	status[i].desc = "non 0 if actually operating in replication mode";
	i++;

	status[i].name = "load_balance_mode";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_load_balance_mode);
	status[i].desc = "non 0 if operating in load balancing mode";
	i++;

	status[i].name = "replication_stop_on_mismatch";
	snprintf(status[i].value, MAXVALLEN, "%d", pool_config_replication_stop_on_mismatch);
	status[i].desc = "stop replication mode on fatal error";
	i++;

	nrows = i;

	if (MAJOR(backend) == PROTO_MAJOR_V2)
	{
		/* cursor response */
		pool_write(frontend, "P", 1);
		pool_write(frontend, cursorname, strlen(cursorname)+1);
	}

	/* row description */
	pool_write(frontend, "T", 1);

	if (MAJOR(backend) == PROTO_MAJOR_V3)
	{
		len = sizeof(num_fields) + sizeof(len);

		for (i=0;i<num_fields;i++)
		{
			char *f = field_names[i];
			len += strlen(f)+1;
			len += sizeof(oid);
			len += sizeof(colnum);
			len += sizeof(oid);
			len += sizeof(s);
			len += sizeof(mod);
			len += sizeof(s);
		}

		len = htonl(len);
		pool_write(frontend, &len, sizeof(len));
	}

	n = htons(num_fields);
	pool_write(frontend, &n, sizeof(short));

	for (i=0;i<num_fields;i++)
	{
		char *f = field_names[i];

		pool_write(frontend, f, strlen(f)+1);		/* field name */

		if (MAJOR(backend) == PROTO_MAJOR_V3)
		{
			pool_write(frontend, &oid, sizeof(oid));	/* table oid */
			colnum = htons(i);
			pool_write(frontend, &colnum, sizeof(colnum));	/* column number */
		}

		pool_write(frontend, &oid, sizeof(oid));		/* data type oid */
		s = htons(fsize);
		pool_write(frontend, &s, sizeof(fsize));		/* field size */
		pool_write(frontend, &mod, sizeof(mod));		/* modifier */

		if (MAJOR(backend) == PROTO_MAJOR_V3)
		{
			s = htons(0);
			pool_write(frontend, &s, sizeof(fsize));	/* field format (text) */
		}
	}
	pool_flush(frontend);

	if (MAJOR(backend) == PROTO_MAJOR_V2)
	{
		/* ascii row */
		for (i=0;i<nrows;i++)
		{
			pool_write(frontend, "D", 1);
			pool_write_and_flush(frontend, nullmap, nbytes);

			size = strlen(status[i].name);
			hsize = htonl(size+4);
			pool_write(frontend, &hsize, sizeof(hsize));
			pool_write(frontend, status[i].name, size);

			size = strlen(status[i].value);
			hsize = htonl(size+4);
			pool_write(frontend, &hsize, sizeof(hsize));
			pool_write(frontend, status[i].value, size);

			size = strlen(status[i].desc);
			hsize = htonl(size+4);
			pool_write(frontend, &hsize, sizeof(hsize));
			pool_write(frontend, status[i].desc, size);
		}
	}
	else
	{
		/* data row */
		for (i=0;i<nrows;i++)
		{
			pool_write(frontend, "D", 1);
			len = sizeof(len) + sizeof(nrows);
			len += sizeof(int) + strlen(status[i].name);
			len += sizeof(int) + strlen(status[i].value);
			len += sizeof(int) + strlen(status[i].desc);
			len = htonl(len);
			pool_write(frontend, &len, sizeof(len));
			s = htons(3);
			pool_write(frontend, &s, sizeof(s));

			len = htonl(strlen(status[i].name));
			pool_write(frontend, &len, sizeof(len));
			pool_write(frontend, status[i].name, strlen(status[i].name));

			len = htonl(strlen(status[i].value));
			pool_write(frontend, &len, sizeof(len));
			pool_write(frontend, status[i].value, strlen(status[i].value));
			
			len = htonl(strlen(status[i].desc));
			pool_write(frontend, &len, sizeof(len));
			pool_write(frontend, status[i].desc, strlen(status[i].desc));
		}
	}

	/* complete command response */
	pool_write(frontend, "C", 1);
	if (MAJOR(backend) == PROTO_MAJOR_V3)
	{
		len = htonl(sizeof(len) + strlen("SELECT")+1);
		pool_write(frontend, &len, sizeof(len));
	}
	pool_write(frontend, "SELECT", strlen("SELECT")+1);

	/* ready for query */
	pool_write(frontend, "Z", 1);
	if (MAJOR(backend) == PROTO_MAJOR_V3)
	{
		len = htonl(sizeof(len) + 1);
		pool_write(frontend, &len, sizeof(len));
		pool_write(frontend, "I", 1);
	}

	pool_flush(frontend);
}

void pool_send_frontend_exits(POOL_CONNECTION_POOL *backend)
{
	int len;

	pool_write(MASTER(backend), "X", 1);

	if (MAJOR(backend) == PROTO_MAJOR_V3)
	{
		len = htonl(4);
		pool_write(MASTER(backend), &len, sizeof(len));
	}

	/*
	 * XXX we cannot call pool_flush() here since backend may already
	 * close the socket and pool_flush() automatically invokes fail
	 * over handler. This could happen in copy command (remember the
	 * famouse "lostsynchronization with server, resettin g
	 * connection" message)
	 */
	fflush(MASTER(backend)->write_fd);

	if (REPLICATION)
	{
		pool_write(SECONDARY(backend), "X", 1);
		if (MAJOR(backend) == PROTO_MAJOR_V3)
		{
			len = htonl(4);
			pool_write(MASTER(backend), &len, sizeof(len));
		}
		fflush(SECONDARY(backend)->write_fd);
	}
}

/*
 * -------------------------------------------------------
 * V3 functions
 * -------------------------------------------------------
 */
POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
{
	char * func = "SimpleForwardToFrontend()";
	int len, len1;
	char *p;
	int status;

	pool_write(frontend, &kind, 1);

	status = pool_read(MASTER(backend), &len, sizeof(len));
	if (status < 0)
	{
		show_error("%s: error while reading message length",func);
		return POOL_END;
	}

	if (REPLICATION)
	{
		status = pool_read(SECONDARY(backend), &len1, sizeof(len1));
		if (status < 0)
		{
			show_error("%s: error while reading message length from secondary backend",func);
			return POOL_END;
		}

		if (len != len1)
		{
			show_error("%s: length does not match between backends master(%d) secondary(%d) kind:(%c)",
					 func, ntohl(len), ntohl(len1), kind);
		}
	}

	pool_write(frontend, &len, sizeof(len));

	len = ntohl(len);
	len -= 4;

	p = pool_read2(MASTER(backend), len);
	if (p == NULL)
		return POOL_END;

	if (REPLICATION)
	{
		len1 = ntohl(len1);
		len1 -= 4;
		if (pool_read2(SECONDARY(backend), len1) == NULL)
			return POOL_END;
	}

	return pool_write(frontend, p, len);
}

POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
{
	int len;
	int sendlen;
	char *p;

	if (pool_write(MASTER(backend), &kind, 1))
		return POOL_END;
	if (REPLICATION)
		if (pool_write(SECONDARY(backend), &kind, 1))
			return POOL_END;

	if (pool_read(frontend, &sendlen, sizeof(sendlen)))
	{
		return POOL_END;
	}

	len = ntohl(sendlen) - 4;

	p = pool_read2(frontend, len);
	if (p == NULL)
		return POOL_END;

	if (pool_write(MASTER(backend), &sendlen, sizeof(sendlen)))
		return POOL_END;
	if (pool_write(MASTER(backend), p, len))
		return POOL_END;

	if (REPLICATION)
	{
		if (pool_write(SECONDARY(backend), &sendlen, sizeof(sendlen)))
			return POOL_END;
		if (pool_write(SECONDARY(backend), p, len))
			return POOL_END;
	}

	return POOL_CONTINUE;
}

POOL_STATUS ParameterStatus(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
{
#ifdef PRINT_DEBUG
	char * func = "ParameterStatus()";
#endif			
	int len;
	int sendlen;
	char *p;
	char *name;
	char *value;

	pool_write(frontend, "S", 1);

	len = pool_read_message_length(backend);
	if (len < 0)
	{
		return POOL_END;
	}

	sendlen = htonl(len);
	pool_write(frontend, &sendlen, sizeof(sendlen));

	len -= 4;

	p = pool_read2(MASTER(backend), len);
	if (p == NULL)
		return POOL_END;

	name = p;
	value = p + strlen(name) + 1;

#ifdef PRINT_DEBUG
	show_debug("%s:name: %s value: %s",func, name, value);
#endif			

	pool_add_param(&MASTER(backend)->params, name, value);

#ifdef PRINT_DEBUG
	pool_param_debug_print(&MASTER(backend)->params);
#endif

	if (REPLICATION)
		if (pool_read2(SECONDARY(backend), len) == NULL)
			return POOL_END;

	return pool_write(frontend, p, len);

}

/*
 * reset backend status. return values are:
 * 0: no query was issued 1: a query was issued 2: no more queries remain -1: error
 */
static int reset_backend(POOL_CONNECTION_POOL *backend, int qcnt)
{
#ifdef NO_RESET_ALL
	static char *queries[] = {"ABORT"};
#else
	static char *queries[] = {"ABORT", "RESET ALL"};
#endif

	char *query;
	int qn = sizeof(queries)/sizeof(char *);

	/* for PGCluster */
	if (!Use_Connection_Pool)
		return 2;

	if (qcnt >= qn)
		return 2;

	query = queries[qcnt];

	/* if transaction state is idle, we don't need to issue ABORT */
	if (TSTATE(backend) == 'I' && !strcmp("ABORT", query))
		return 0;

	if (Query(NULL, backend, query) != POOL_CONTINUE)
		return -1;

	return 1;
}

/*
 * return non 0 if load balance is possible
 */
static int load_balance_enabled(POOL_CONNECTION_POOL *backend, char *sql)
{
	if (pool_config_load_balance_mode &&
		REPLICATION &&
		MAJOR(backend) == PROTO_MAJOR_V3 &&
		TSTATE(backend) == 'I' &&
		!strncasecmp(sql, "SELECT", 6))
		return 1;
	return 0;
}

/*
 * start load balance mode
 */
static void start_load_balance(POOL_CONNECTION_POOL *backend)
{
#ifdef PRINT_DEBUG
	char * func = "start_load_balance()";
#endif			
	int i;
	int master;

	/* save backend connection slots */
	for (i=0;i<backend->num;i++)
	{
		slots[i] = backend->slots[i];
	}

	/* temporary turn off replication mode */
	/*REPLICATION = 0; */

	/* choose a master in random manner */
	master = random() % backend->num;
	backend->slots[0] = slots[master];
#ifdef PRINT_DEBUG
	show_debug("%s: selected master is %d", func,master);
#endif			

	/* start load balancing */
	/*in_load_balance = 1;*/
}

/*
 * finish load balance mode
 */
static void end_load_balance(POOL_CONNECTION_POOL *backend)
{
	int i;

	/* restore backend connection slots */
	for (i=0;i<backend->num;i++)
	{
		backend->slots[i] = slots[i];
	}

	/* turn on replication mode */
	/* REPLICATION = 1; */

	/*in_load_balance = 0;*/
#ifdef PRINT_DEBUG
	show_debug("end_load_balance: end load balance mode");
#endif			
}

/*
 * send error message to frontend
 */
void pool_send_error_message(POOL_CONNECTION *frontend, int protoMajor,
							 char *code,
							 char *message,
							 char *detail,
							 char *hint,
							 char *file,
							 int line)
{
#define MAXDATA	1024
#define MAXMSGBUF 128
	char * func = "pool_send_error_message()";

	if (protoMajor == PROTO_MAJOR_V2)
	{
		pool_write(frontend, "E", 1);
		pool_write_and_flush(frontend, message, strlen(message)+1);
	}
	else if (protoMajor == PROTO_MAJOR_V3)
	{
		char data[MAXDATA];
		char msgbuf[MAXMSGBUF];
		int len;
		int thislen;
		int sendlen;

		len = 0;

		pool_write(frontend, "E", 1);

		/* error level */
		thislen = snprintf(msgbuf, MAXMSGBUF, "SERROR");
		memcpy(data +len, msgbuf, thislen+1);
		len += thislen + 1;

		/* code */
		thislen = snprintf(msgbuf, MAXMSGBUF, "C%s", code);
		memcpy(data +len, msgbuf, thislen+1);
		len += thislen + 1;

		/* message */
		thislen = snprintf(msgbuf, MAXMSGBUF, "M%s", message);
		memcpy(data +len, msgbuf, thislen+1);
		len += thislen + 1;

		/* detail */
		if (*detail != '\0')
		{
			thislen = snprintf(msgbuf, MAXMSGBUF, "D%s", detail);
			memcpy(data +len, msgbuf, thislen+1);
			len += thislen + 1;
		}

		/* hint */
		if (*hint != '\0')
		{
			thislen = snprintf(msgbuf, MAXMSGBUF, "H%s", hint);
			memcpy(data +len, msgbuf, thislen+1);
			len += thislen + 1;
		}

		/* file */
		thislen = snprintf(msgbuf, MAXMSGBUF, "F%s", file);
		memcpy(data +len, msgbuf, thislen+1);
		len += thislen + 1;

		/* line */
		thislen = snprintf(msgbuf, MAXMSGBUF, "L%d", line);
		memcpy(data +len, msgbuf, thislen+1);
		len += thislen + 1;

		/* stop null */
		len++;
		*(data + len) = '\0';

		sendlen = len;
		len = htonl(len + 4);
		pool_write(frontend, &len, sizeof(len));
		pool_write_and_flush(frontend, data, sendlen);
	}
	else
		show_error("%s: unknown protocol major %d",func, protoMajor);
}
