Skip to Content.
Sympa Menu

sm-commit - [SM-Commit] GIT changes to master grimoire by Arjan Bouter (3938db04ccd796caf6c0af01356e054cd355dc01)

sm-commit AT lists.ibiblio.org

Subject: Source Mage code commit list

List archive

Chronological Thread  
  • From: Arjan Bouter <scm AT mail.sourcemage.org>
  • To: sm-commit AT lists.ibiblio.org
  • Subject: [SM-Commit] GIT changes to master grimoire by Arjan Bouter (3938db04ccd796caf6c0af01356e054cd355dc01)
  • Date: Thu, 9 Nov 2006 12:30:31 -0600

GIT changes to master grimoire by Arjan Bouter <abouter AT sourcemage.org>:

database/postgresql/HISTORY | 4
database/postgresql/PREPARE | 1
database/postgresql/PRE_BUILD | 9
database/postgresql/pgcluster-1.5.0rc7-smgl-patch |30328
++++++++++++++++++++++
4 files changed, 30339 insertions(+), 3 deletions(-)

New commits:
commit 3938db04ccd796caf6c0af01356e054cd355dc01
Author: Arjan Bouter <abouter AT sourcemage.org>
Commit: Arjan Bouter <abouter AT sourcemage.org>

postgresql: added pgcluster support

diff --git a/database/postgresql/HISTORY b/database/postgresql/HISTORY
index 0866731..a0b739b 100644
--- a/database/postgresql/HISTORY
+++ b/database/postgresql/HISTORY
@@ -1,3 +1,7 @@
+2006-11-09 Arjan Bouter <abouter AT sourcemage.org>
+ * PREPARE, PRE_BUILD: added
+ * pgcluster-1.5.0rc7-smgl.patch: added
+
2006-10-14 Ladislav Hagara <hgr AT vabo.cz>
* DETAILS: 8.1.5
* FINAL: removed 8.0.3 related message
diff --git a/database/postgresql/PREPARE b/database/postgresql/PREPARE
new file mode 100755
index 0000000..4dd13d2
--- /dev/null
+++ b/database/postgresql/PREPARE
@@ -0,0 +1 @@
+config_query PGSQL_CLUSTER 'Do you want to apply the PGCluster patch?' n
diff --git a/database/postgresql/PRE_BUILD b/database/postgresql/PRE_BUILD
index 940e3da..05c5e75 100755
--- a/database/postgresql/PRE_BUILD
+++ b/database/postgresql/PRE_BUILD
@@ -1,3 +1,6 @@
- create_account postgres &&
- default_pre_build
-
+create_account postgres &&
+default_pre_build &&
+cd $SOURCE_DIRECTORY &&
+if [ "$PGSQL_CLUSTER" = "y" ]; then
+ patch -p1 < $SCRIPT_DIRECTORY/pgcluster-1.5.0rc7-smgl-patch
+fi
diff --git a/database/postgresql/pgcluster-1.5.0rc7-smgl-patch
b/database/postgresql/pgcluster-1.5.0rc7-smgl-patch
new file mode 100644
index 0000000..9a8f385
--- /dev/null
+++ b/database/postgresql/pgcluster-1.5.0rc7-smgl-patch
@@ -0,0 +1,30328 @@
+diff -aruN postgresql-8.1.2/GNUmakefile.in pgcluster-1.5.0rc7/GNUmakefile.in
+--- postgresql-8.1.2/GNUmakefile.in 2005-05-01 08:15:51.000000000 +0200
++++ pgcluster-1.5.0rc7/GNUmakefile.in 2006-01-10 17:39:30.000000000 +0100
+@@ -57,13 +57,13 @@
+
+ ##########################################################################
+
+-distdir := postgresql-$(VERSION)
++distdir := pgcluster-$(PGCLUSTER_VERSION)
+ dummy := =install=
+-garbage := =* "#"* ."#"* *~* *.orig *.rej core postgresql-*
++garbage := =* "#"* ."#"* *~* *.orig *.rej core pgcluster-*
+
+ dist: $(distdir).tar.gz
+ ifeq ($(split-dist), yes)
+-dist: postgresql-base-$(VERSION).tar.gz postgresql-docs-$(VERSION).tar.gz
postgresql-opt-$(VERSION).tar.gz postgresql-test-$(VERSION).tar.gz
++dist: pgcluster-base-$(PGCLUSTER_VERSION).tar.gz
pgcluster-docs-$(PGCLUSTER_VERSION).tar.gz
pgcluster-opt-$(PGCLUSTER_VERSION).tar.gz
pgcluster-test-$(PGCLUSTER_VERSION).tar.gz
+ endif
+ dist:
+ -rm -rf $(distdir)
+@@ -75,19 +75,19 @@
+ src/tools src/tutorial \
+ $(addprefix src/pl/, plperl plpython tcl)
+
+-docs_files := doc/postgres.tar.gz doc/src doc/TODO.detail
++docs_files := doc/pgcluster.tar.gz doc/src doc/TODO.detail
+
+-postgresql-base-$(VERSION).tar: distdir
++pgcluster-base-$(PGCLUSTER_VERSION).tar: distdir
+ $(TAR) -c $(addprefix --exclude $(distdir)/, $(docs_files)
$(opt_files) src/test) \
+ -f $@ $(distdir)
+
+-postgresql-docs-$(VERSION).tar: distdir
++pgcluster-docs-$(PGCLUSTER_VERSION).tar: distdir
+ $(TAR) cf $@ $(addprefix $(distdir)/, $(docs_files))
+
+-postgresql-opt-$(VERSION).tar: distdir
++pgcluster-opt-$(PGCLUSTER_VERSION).tar: distdir
+ $(TAR) cf $@ $(addprefix $(distdir)/, $(opt_files))
+
+-postgresql-test-$(VERSION).tar: distdir
++pgcluster-test-$(PGCLUSTER_VERSION).tar: distdir
+ $(TAR) cf $@ $(distdir)/src/test
+
+ distdir:
+diff -aruN postgresql-8.1.2/INSTALL_PGCLUSTER
pgcluster-1.5.0rc7/INSTALL_PGCLUSTER
+--- postgresql-8.1.2/INSTALL_PGCLUSTER 1970-01-01 01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/INSTALL_PGCLUSTER 2006-02-09 17:55:54.000000000
+0100
+@@ -0,0 +1,286 @@
++PGCluster Installation Instructions
++
++=============================================================
++1. Installation
++=============================================================
++
++1-1. Install Cluster DB Server, Replication Server & Load Balancer
++----------------------------------------------------------------
++$ cd $source_dir
++$ ./configure
++$ gmake
++$ su
++# gmake install
++# chown -R postgres /usr/local/pgsql
++----------------------------------------------------------------
++
++=============================================================
++2. Initialize DB
++=============================================================
++$ su
++# adduser postgres
++# mkdir /usr/local/pgsql/data
++# chown postgres /usr/local/pgsql/data
++# su - postgres
++$ /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
++
++
++=============================================================
++3. Configuration
++=============================================================
++(EX.System Composition)
++
++ |
++ ((Load Balance Server))
++ ( hostname: lb.hoo.com)
++ ( receive port:5432 )
++ ( recovery port:6001 )
++ |
++----------+-------------+------------+----------
++ | |
++ (( Cluster DB 1 )) (( Cluster DB 2 ))
++ ( hostname:c1.hoo.com) ( hostname:c2.hoo.com)
++ ( receive port: 5432 ) ( receive port:5432 )
++ ( recovery port:7001 ) ( recovery port 7002 )
++ | |
++----------+-------------+------------+----------
++ |
++ ((Replication Server))
++ ( hostname:pgr.hoo.com)
++ ( receive port:8001 )
++ ( recovery port:8101 )
++
++
++3-1. Load Balance Server
++
++The setup file of load balance server is copied from the sample file and
edited.
++(the sample file is installed '/usr/local/pgsql/share' in default)
++----------------------------------------------------------------
++$cd /usr/local/pgsql/share
++$cp pglb.conf.sample pglb.conf
++----------------------------------------------------------------
++
++In the case of the above system composition example,
++the setup example of pglb.conf file is as the following
++
++#============================================================
++# Load Balance Server configuration file
++#-------------------------------------------------------------
++# file: pglb.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts are db cluster server
++# o which port use connect to db cluster server
++# o how many connections are allowed on each DB server
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Max_Connection : maximun number of connection to postmaster
++#-------------------------------------------------------------
++<Cluster_Server_Info>
++ <Host_Name> c1.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Max_Connect> 32 </Max_Connect>
++</Cluster_Server_Info>
++<Cluster_Server_Info>
++ <Host_Name> c2.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Max_Connect> 32 </Max_Connect>
++</Cluster_Server_Info>
++#-------------------------------------------------------------
++# set Load Balance server information
++# o Receive_Port : connection from client
++# o Recovery_Port : connection for recovery process
++# o Max_Cluster_Num : maximun number of cluster DB servers
++# o Use_Connection_Pooling : use connection pool [yes/no]
++# o Max_Pool_Each_Server : number of pool connections/DB server
++#-------------------------------------------------------------
++<Receive_Port> 5432 </Receive_Port>
++<Recovery_Port> 6001 </Recovery_Port>
++<Max_Cluster_Num> 128 </Max_Cluster_Num>
++<Use_Connection_Pooling> yes </Use_Connection_Pooling>
++<Max_Pool_Each_Server> 1 </Max_Pool_Each_Server>
++
++3-2. Cluster DB Server
++
++The Cluster DB server need edit two configuration files
++('pg_hba.conf' and 'cluster.conf').
++These files are create under the $PG_DATA directory after 'initdb'.
++
++A. pg_hba.conf
++Permission to connect DB via IP connectoins is need for this system.
++
++B. cluster.conf
++In the case of the above system composition example,
++the setup example of cluster.conf file is as the following
++
++#============================================================
++# Cluster DB Server configuration file
++#-------------------------------------------------------------
++# file: cluster.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts & port are replication server
++# o which port use for replication request to replication server
++# o which command use for recovery function
++#
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Recovery_Port : connection for recovery process
++#-------------------------------------------------------------
++<Replicate_Server_Info>
++ <Host_Name> pgr.hoo.com </Host_Name>
++ <Port> 8001 </Port>
++ <Recovery_Port> 8101 </Recovery_Port>
++</Replicate_Server_Info>
++#-------------------------------------------------------------
++# set Cluster DB Server information
++# o Recovery_Port : connection for recovery
++# o Rsync_Path : path of rsync command
++# o Rsync_Option : file transfer option for rsync
++# o When_Stand_Alone : When all replication servers fell,
++# you can set up two kinds of permittion,
++# "real_only" or "read_write".
++#-------------------------------------------------------------
++<Recovery_Port> 7001 </Recovery_Port>
++<Rsync_Path> /usr/bin/rsync </Rsync_Path>
++<Rsync_Option> ssh -1 </Rsync_Option>
++<When_Stand_Alone> read_only </When_Stand_Alone>
++#-------------------------------------------------------------
++# set partitional replicate control information
++# set DB name and Table name to stop reprication
++# o DB_Name : DB name
++# o Table_Name : table name
++#-------------------------------------------------------------
++#<Not_Replicate_Info>
++# <DB_Name> test_db </DB_Name>
++# <Table_Name> log_table </Table_Name>
++#</Not_Replicate_Info>
++
++3-3. Replication Server
++
++The setup file of replication server is copied from the sample file and
edited.
++(the sample file is installed '/usr/local/pgsql/share' in default)
++----------------------------------------------------------------
++$cd /usr/local/pgsql/share
++$cp pgreplicate.conf.sample pgreplicate.conf
++----------------------------------------------------------------
++In the case of the above system composition example,
++the setup example of pgreplicate.conf file is as the following
++
++#============================================================
++# PGReplicate configuration file
++#-------------------------------------------------------------
++# file: pgreplicate.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts & port are cluster server
++# o which port use for replication request from cluster server
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<Cluster_Server_Info>
++ <Host_Name> c1.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Recovery_Port> 7001 </Recovery_Port>
++</Cluster_Server_Info>
++<Cluster_Server_Info>
++ <Host_Name> c2.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Recovery_Port> 7001 </Recovery_Port>
++</Cluster_Server_Info>
++#-------------------------------------------------------------
++# set Load Balance server information
++# o Host_Name : hostname
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<LoadBalance_Server_Info>
++ <Host_Name> lb.hoo.com </Host_Name>
++ <Recovery_Port> 6001 </Recovery_Port>
++</LoadBalance_Server_Info>
++#-------------------------------------------------------------
++# A setup of a replication server
++#
++# o Replicate_Port : connection for replication
++# o Recovery_Port : connection for recovery
++# o Response_mode : timing which returns a response
++# normal -- return result of DB which received the query
++# reliable -- return result after waiting for response of
++# all Cluster DBs.
++# o Use_Replication_Log : use replication log
++# [yes/no]. default : no
++# o RLOG_Port : connection for replication log
++#-------------------------------------------------------------
++<Replication_Port> 8001 </Replication_Port>
++<Recovery_Port> 8101 </Recovery_Port>
++<Response_Mode> normal </Response_Mode>
++<Use_Replication_Log> no </Use_Replication_Log>
++<RLOG_Port> 8301 </RLOG_Port>
++
++=============================================================
++4. Start Up / Stop
++=============================================================
++
++4-1. replication server
++
++A. Start replication server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pgreplicate -D /usr/local/pgsql/etc
++----------------------------------------------------------------
++
++B. Stop replication server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pgreplicate -D /usr/local/pgsql/etc stop
++----------------------------------------------------------------
++
++usage: pgreplicate [-D path_of_config_file] [-W path_of_work_files] [-U
login us
++er][-l][-n][-v][-h][stop]
++ -l: print error logs in the log file.
++ -n: don't run in daemon mode.
++ -v: debug mode. need '-n' flag
++ -h: print this help
++ stop: stop pgreplicate
++(config file default path: ./pgreplicate.conf)
++
++4-2. cluster DB server
++$PG_HOME = /usr/local/pgsql
++$PG_DATA = /usr/local/pgsql/data
++
++A. Start cluster DB server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -o "-i" start
++----------------------------------------------------------------
++
++B. Stop cluster DB server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data stop
++----------------------------------------------------------------
++
++4-3. load balance server
++
++A. Start load balance server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pglb -D /usr/local/pgsql/share
++----------------------------------------------------------------
++
++B. Stop load balance server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pglb -D /usr/local/pgsql/share stop
++----------------------------------------------------------------
++
++usage: pglb [-D path_of_config_file] [-W path_of_work_files]
[-n][-v][-h][stop]
++ -l: print error logs in the log file.
++ -n: don't run in daemon mode.
++ -v: debug mode. need '-n' flag
++ -h: print this help
++ stop: stop pglb
++ (config file default path: ./pglb.conf)
+diff -aruN postgresql-8.1.2/README_PGCLUSTER
pgcluster-1.5.0rc7/README_PGCLUSTER
+--- postgresql-8.1.2/README_PGCLUSTER 1970-01-01 01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/README_PGCLUSTER 2006-01-10 17:39:32.000000000
+0100
+@@ -0,0 +1,47 @@
++PGCluster: Multi-Master Synchronous Replication System for PostgreSQL
++===========================================================
++
++PGCluster is a multi-master and synchronous replication system that
supports load balancing of PostgreSQL.
++
++Changed:
++ src/Makefile.global.in
++ src/backend/Makefile
++ src/backend/libpq/Makefile
++ src/backend/access/transam/clog.c
++ src/backend/access/transam/xact.c
++ src/backend/catalog/pg_depend.c
++ src/backend/commands/analyze.c
++ src/backend/commands/copy.c
++ src/backend/commands/sequence.c
++ src/backend/executor/functions.c
++ src/backend/postmaster/postmaster.c
++ src/backend/storage/lmgr/lock.c
++ src/backend/tcop/postgres.c
++ src/backend/utils/adt/float.c
++ src/backend/utils/adt/misc.c
++ src/backend/utils/adt/nabstime.c
++ src/backend/utils/error/assert.c
++ src/backend/utils/error/elog.c
++ src/backend/utils/fmgr/dfmgr.c
++ src/backend/utils/mb/mbutils.c
++ src/bin/initdb/initdb.sh
++
++Added:
++ src/include/replicate.h
++ src/include/replicate_com.h
++ src/backend/libpq/recovery.c
++ src/backend/libpq/relicate.c
++ src/backend/libpq/relicate_com.c
++ src/backend/libpq/cluster.conf.sample
++ src/pgcluster/*
++
++The latest version of this software may be obtained at
++http://hiroshima.sraw.co.jp/people/mitani/jpug/pgcluster/en/current/last_night.html
++<for Japanese>
++http://hiroshima.sraw.co.jp/people/mitani/jpug/pgcluster/current/last_night.html
++
++For more information look at my web
++site located at
++http://hiroshima.sraw.co.jp/people/mitani/jpug/pgcluster/en/
++<for Japanese>
++http://hiroshima.sraw.co.jp/people/mitani/jpug/pgcluster/
+diff -aruN postgresql-8.1.2/configure pgcluster-1.5.0rc7/configure
+--- postgresql-8.1.2/configure 2006-01-05 05:02:18.000000000 +0100
++++ pgcluster-1.5.0rc7/configure 2006-02-09 17:47:19.000000000 +0100
+@@ -275,6 +275,8 @@
+ PACKAGE_STRING='PostgreSQL 8.1.2'
+ PACKAGE_BUGREPORT='pgsql-bugs AT postgresql.org'
+
++PGCLUSTER_VERSION='1.5.0rc7'
++
+ ac_unique_file="src/backend/access/common/heaptuple.c"
+ ac_default_prefix=/usr/local/pgsql
+ # Factoring default headers for most tests.
+@@ -314,7 +316,7 @@
+ # include <unistd.h>
+ #endif"
+
+-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME
PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix
program_transform_name bindir sbindir libexecdir datadir sysconfdir
sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir
build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS
configure_args build build_cpu build_vendor build_os host host_cpu
host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port
enable_shared enable_rpath enable_debug CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC
EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl
with_perl with_python with_krb5 krb_srvtab with_pam with_bonjour with_openssl
with_zlib EGREP ELF_SYS LDFLAGS_SL AWK FLEX FLEXFLAGS LN_S LD with_gnu_ld
ld_R_works RANLIB ac_ct_RANLIB LORDER TAR STRIP ac_ct_STRIP STRIP_STATIC_LIB
STRIP_SHARED_LIB YACC YFLAGS PERL perl_archlibexp perl_privlibexp
perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir
python_includespec python_libdir python_libspec python_additional_libs
HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS
HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH
TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD
TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL
vpath_build LTLIBOBJS'
++ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME
PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix
program_transform_name bindir sbindir libexecdir datadir sysconfdir
sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir
build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS
configure_args build build_cpu build_vendor build_os host host_cpu
host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port
enable_shared enable_rpath enable_debug CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC
EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl
with_perl with_python with_krb5 krb_srvtab with_pam with_bonjour with_openssl
with_zlib EGREP ELF_SYS LDFLAGS_SL AWK FLEX FLEXFLAGS LN_S LD with_gnu_ld
ld_R_works RANLIB ac_ct_RANLIB LORDER TAR STRIP ac_ct_STRIP STRIP_STATIC_LIB
STRIP_SHARED_LIB YACC YFLAGS PERL perl_archlibexp perl_privlibexp
perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir
python_includespec python_libdir python_libspec python_additional_libs
HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS
HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH
TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD
TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL
vpath_build LTLIBOBJS PGCLUSTER_VERSION'
+ ac_subst_files=''
+
+ # Initialize some variables set by options.
+@@ -1238,6 +1240,10 @@
+ #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+ _ACEOF
+
++cat >>confdefs.h <<_ACEOF
++#define PGCLUSTER_VERSION "$PGCLUSTER_VERSION"
++_ACEOF
++
+
+ # Let the site file select an alternate cache file if it wants to.
+ # Prefer explicitly selected file to automatically selected ones.
+@@ -1361,6 +1367,8 @@
+
+
+
++
++
+ ac_aux_dir=
+ for ac_dir in config $srcdir/config; do
+ if test -f $ac_dir/install-sh; then
+@@ -23204,6 +23212,7 @@
+ s,@host_os@,$host_os,;t t
+ s,@PORTNAME@,$PORTNAME,;t t
+ s,@docdir@,$docdir,;t t
++s,@PGCLUSTER_VERSION@,$PGCLUSTER_VERSION,;t t
+ s,@enable_nls@,$enable_nls,;t t
+ s,@WANTED_LANGUAGES@,$WANTED_LANGUAGES,;t t
+ s,@default_port@,$default_port,;t t
+diff -aruN postgresql-8.1.2/configure.in pgcluster-1.5.0rc7/configure.in
+--- postgresql-8.1.2/configure.in 2006-01-05 05:02:23.000000000 +0100
++++ pgcluster-1.5.0rc7/configure.in 2006-01-10 17:39:30.000000000 +0100
+@@ -27,6 +27,7 @@
+ AC_SUBST(configure_args, [$ac_configure_args])
+
+ AC_DEFINE_UNQUOTED(PG_VERSION, "$PACKAGE_VERSION", [PostgreSQL version])
++AC_DEFINE_UNQUOTED(PGCLUSTER_VERSION, "$PGCLUSTER_VERSION", [PGCluster
version])
+
+ AC_CANONICAL_HOST
+
+diff -aruN postgresql-8.1.2/pgcluster.sh.tmpl
pgcluster-1.5.0rc7/pgcluster.sh.tmpl
+--- postgresql-8.1.2/pgcluster.sh.tmpl 1970-01-01 01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/pgcluster.sh.tmpl 2006-01-10 17:39:30.000000000
+0100
+@@ -0,0 +1,56 @@
++#!/bin/sh
++#
++# $FreeBSD: ports/databases/pgcluster/files/pgcluster.sh.tmpl,v 1.1
2004/01/26 09:02:45 kuriyama Exp $
++#
++# PROVIDE: pgcluster
++# REQUIRE: DAEMON
++# BEFORE: pgreplicate
++# KEYWORD: FreeBSD
++#
++# Add the following line to /etc/rc.conf to enable pgcluster:
++#
++# pgcluster_enable="YES"
++# # optional
++# pgcluster_data="/home/pgsql/data"
++# pgcluster_flags="-w -s"
++#
++
++pgcluster_enable="NO"
++pgcluster_data="%%PREFIX%%/pgsql/data"
++pgcluster_flags="-w -s"
++
++. %%RC_SUBR%%
++
++load_rc_config pgcluster
++
++name=pgcluster
++command=%%PREFIX%%/bin/pg_ctl
++pgcluster_user=pgsql
++extra_commands="initdb recover"
++initdb_cmd="pgcluster_initdb"
++recover_cmd="pgcluster_recover"
++start_cmd="pgcluster_start"
++stop_cmd="pgcluster_stop"
++
++pgcluster_flags="${pgcluster_flags} -D ${pgcluster_data}"
++pidfile="${pgcluster_data}/postmaster.pid"
++
++pgcluster_start()
++{
++ su -m ${pgcluster_user} -c "exec ${command} start ${pgcluster_flags} -o
'-i'"
++}
++pgcluster_stop()
++{
++ su -m ${pgcluster_user} -c "exec ${command} stop ${pgcluster_flags} -m
i"
++}
++pgcluster_recover()
++{
++ su -m ${pgcluster_user} -c "exec ${command} start ${pgcluster_flags} -o
'-i -R'"
++}
++pgcluster_initdb()
++{
++ su -m ${pgcluster_user} -c "exec %%PREFIX%%/bin/initdb -D
${pgcluster_data}"
++}
++
++load_rc_config $name
++run_rc_command "$1"
+diff -aruN postgresql-8.1.2/src/Makefile pgcluster-1.5.0rc7/src/Makefile
+--- postgresql-8.1.2/src/Makefile 2005-01-13 19:23:21.000000000 +0100
++++ pgcluster-1.5.0rc7/src/Makefile 2006-01-10 17:39:48.000000000 +0100
+@@ -16,14 +16,15 @@
+ all install installdirs uninstall dep depend distprep:
+ $(MAKE) -C port $@
+ $(MAKE) -C timezone $@
++ $(MAKE) -C interfaces $@
+ $(MAKE) -C backend $@
+ $(MAKE) -C backend/utils/mb/conversion_procs $@
+ $(MAKE) -C include $@
+- $(MAKE) -C interfaces $@
+ $(MAKE) -C bin $@
+ $(MAKE) -C pl $@
+ $(MAKE) -C makefiles $@
+ $(MAKE) -C utils $@
++ $(MAKE) -C pgcluster $@
+
+ install: install-local
+
+@@ -44,6 +45,7 @@
+ rm -f $(addprefix $(DESTDIR)$(pgxsdir)/$(subdir), Makefile.global
Makefile.port Makefile.shlib nls-global.mk)
+
+ clean:
++ $(MAKE) -C pgcluster $@
+ $(MAKE) -C port $@
+ $(MAKE) -C timezone $@
+ $(MAKE) -C backend $@
+@@ -58,6 +60,7 @@
+ $(MAKE) -C tools/thread $@
+
+ distclean maintainer-clean:
++ -$(MAKE) -C pgcluster $@
+ -$(MAKE) -C port $@
+ -$(MAKE) -C timezone $@
+ -$(MAKE) -C backend $@
+diff -aruN postgresql-8.1.2/src/Makefile.global.in
pgcluster-1.5.0rc7/src/Makefile.global.in
+--- postgresql-8.1.2/src/Makefile.global.in 2005-09-27 19:39:32.000000000
+0200
++++ pgcluster-1.5.0rc7/src/Makefile.global.in 2006-01-10 17:39:48.000000000
+0100
+@@ -31,6 +31,9 @@
+ # PostgreSQL version number
+ VERSION = @PACKAGE_VERSION@
+
++# PGCluster version number
++PGCLUSTER_VERSION = @PGCLUSTER_VERSION@
++
+ # Support for VPATH builds
+ vpath_build = @vpath_build@
+ abs_top_srcdir = @abs_top_srcdir@
+@@ -204,6 +207,7 @@
+ GCC = @GCC@
+ CFLAGS = @CFLAGS@
+
++CFLAGS += -DUSE_REPLICATION -DPRINT_DEBUG
+ # Kind-of compilers
+
+ YACC = @YACC@
+diff -aruN postgresql-8.1.2/src/backend/Makefile
pgcluster-1.5.0rc7/src/backend/Makefile
+--- postgresql-8.1.2/src/backend/Makefile 2005-10-27 22:45:29.000000000
+0200
++++ pgcluster-1.5.0rc7/src/backend/Makefile 2006-01-10 17:39:39.000000000
+0100
+@@ -40,7 +40,7 @@
+ ifneq ($(PORTNAME), win32)
+ ifneq ($(PORTNAME), aix)
+
+-postgres: $(OBJS)
++postgres: $(OBJS) $(libpq_srcdir)/libpq.a
+ $(CC) $(CFLAGS) $(LDFLAGS) $(export_dynamic) $^ $(LIBS) -o $@
+
+ endif
+@@ -169,6 +169,7 @@
+ $(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample
$(DESTDIR)$(datadir)/pg_ident.conf.sample
+ $(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample
$(DESTDIR)$(datadir)/postgresql.conf.sample
+ $(INSTALL_DATA) $(srcdir)/access/transam/recovery.conf.sample
$(DESTDIR)$(datadir)/recovery.conf.sample
++ $(INSTALL_DATA) $(srcdir)/libpq/cluster.conf.sample
$(DESTDIR)$(datadir)/cluster.conf.sample
+
+ install-bin: postgres $(POSTGRES_IMP) installdirs
+ $(INSTALL_PROGRAM) postgres$(X) $(DESTDIR)$(bindir)/postgres$(X)
+@@ -222,7 +223,8 @@
+ rm -f $(DESTDIR)$(datadir)/pg_hba.conf.sample \
+ $(DESTDIR)$(datadir)/pg_service.conf.sample \
+ $(DESTDIR)$(datadir)/pg_ident.conf.sample \
+- $(DESTDIR)$(datadir)/postgresql.conf.sample
++ $(DESTDIR)$(datadir)/postgresql.conf.sample \
++ $(DESTDIR)$(datadir)/cluster.conf.sample
+
+
+ ##########################################################################
+diff -aruN postgresql-8.1.2/src/backend/access/transam/xact.c
pgcluster-1.5.0rc7/src/backend/access/transam/xact.c
+--- postgresql-8.1.2/src/backend/access/transam/xact.c 2005-11-22
19:23:05.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/access/transam/xact.c 2006-01-10
17:39:38.000000000 +0100
+@@ -46,6 +46,9 @@
+ #include "utils/resowner.h"
+ #include "pgstat.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * User-tweakable parameters
+@@ -4280,3 +4283,11 @@
+ else
+ strcat(buf, "UNKNOWN");
+ }
++
++#ifdef USE_REPLICATION
++void
++PGR_Reload_Start_Time(void)
++{
++ xactStartTimestamp = GetCurrentTimestamp();
++}
++#endif /* USE_REPLICATION */
+diff -aruN postgresql-8.1.2/src/backend/catalog/catalog.c
pgcluster-1.5.0rc7/src/backend/catalog/catalog.c
+--- postgresql-8.1.2/src/backend/catalog/catalog.c 2005-10-15
04:49:12.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/catalog/catalog.c 2006-01-10
17:39:39.000000000 +0100
+@@ -29,6 +29,9 @@
+ #include "utils/fmgroids.h"
+ #include "utils/relcache.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ #define OIDCHARS 10 /* max chars printed by %u */
+
+@@ -293,7 +296,7 @@
+ Oid
+ GetNewOidWithIndex(Relation relation, Relation indexrel)
+ {
+- Oid newOid;
++ Oid newOid = 0;
+ IndexScanDesc scan;
+ ScanKeyData key;
+ bool collides;
+@@ -301,8 +304,18 @@
+ /* Generate new OIDs until we find one not in the table */
+ do
+ {
++#ifdef USE_REPLICATION
++ if (PGR_Is_Sync_OID == true)
++ {
++ newOid = PGRGetNewObjectId(newOid);
++ }
++ else
++ {
++ newOid = GetNewObjectId();
++ }
++#else
+ newOid = GetNewObjectId();
+-
++#endif /* USE_REPLICATION */
+ ScanKeyInit(&key,
+ (AttrNumber) 1,
+ BTEqualStrategyNumber, F_OIDEQ,
+@@ -386,3 +399,4 @@
+
+ return rnode.relNode;
+ }
++
+diff -aruN postgresql-8.1.2/src/backend/commands/analyze.c
pgcluster-1.5.0rc7/src/backend/commands/analyze.c
+--- postgresql-8.1.2/src/backend/commands/analyze.c 2005-11-22
19:23:06.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/commands/analyze.c 2006-01-10
17:39:34.000000000 +0100
+@@ -39,6 +39,9 @@
+ #include "utils/syscache.h"
+ #include "utils/tuplesort.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /* Data structure for Algorithm S from Knuth 3.4.2 */
+ typedef struct
+@@ -936,7 +939,11 @@
+ /* random() can produce endpoint values, try again if so */
+ do
+ {
++#ifdef USE_REPLICATION
++ z = (long) PGR_Random();
++#else
+ z = random();
++#endif /* USE_REPLICATION */
+ } while (z <= 0 || z >= MAX_RANDOM_VALUE);
+ return (double) z / (double) MAX_RANDOM_VALUE;
+ }
+diff -aruN postgresql-8.1.2/src/backend/commands/copy.c
pgcluster-1.5.0rc7/src/backend/commands/copy.c
+--- postgresql-8.1.2/src/backend/commands/copy.c 2005-12-28
06:38:26.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/commands/copy.c 2006-01-10
17:39:34.000000000 +0100
+@@ -48,6 +48,9 @@
+ #include "utils/relcache.h"
+ #include "utils/syscache.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
+ #define OCTVALUE(c) ((c) - '0')
+@@ -421,6 +424,9 @@
+ CopyGetData(CopyState cstate, void *databuf, int minread, int maxread)
+ {
+ int bytesread = 0;
++#ifdef USE_REPLICATION
++ char * ptr = (char *)databuf;
++#endif
+
+ switch (cstate->copy_dest)
+ {
+@@ -511,6 +517,9 @@
+ }
+ break;
+ }
++#ifdef USE_REPLICATION
++ PGR_Set_Copy_Data(PGRCopyData,ptr,bytesread,0);
++#endif /* USE_REPLICATION */
+
+ return bytesread;
+ }
+@@ -1921,6 +1930,13 @@
+ }
+ }
+
++#ifdef USE_REPLICATION
++ if (done)
++ {
++ PGR_Set_Copy_Data(PGRCopyData,(char *)NULL,0,1);
++ }
++#endif /* USE_REPLICATION */
++
+ /* Done, clean up */
+ error_context_stack = errcontext.previous;
+
+@@ -2025,6 +2041,11 @@
+ break;
+ }
+ }
++#ifdef USE_REPLICATION
++ /*
++
PGR_Set_Copy_Data(PGRCopyData,cstate->line_buf.data,cstate->line_buf.len,0);
++ */
++#endif
+
+ /* Done reading the line. Convert it to server encoding. */
+ if (cstate->need_transcoding)
+diff -aruN postgresql-8.1.2/src/backend/commands/sequence.c
pgcluster-1.5.0rc7/src/backend/commands/sequence.c
+--- postgresql-8.1.2/src/backend/commands/sequence.c 2005-11-22
19:23:07.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/commands/sequence.c 2006-01-10
17:39:34.000000000 +0100
+@@ -26,6 +26,9 @@
+ #include "utils/resowner.h"
+ #include "utils/syscache.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * We don't want to log each fetching of a value from a sequence,
+@@ -385,6 +388,9 @@
+ RangeVar *sequence;
+ Oid relid;
+
++#ifdef USE_REPLICATION
++ Xlog_Check_Replicate(CMD_UTILITY);
++#endif /* USE_REPLICATION */
+ sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin));
+ relid = RangeVarGetRelid(sequence, false);
+
+@@ -610,6 +616,10 @@
+ SeqTable elm;
+ Relation seqrel;
+
++#ifdef USE_REPLICATION
++ Xlog_Check_Replicate(CMD_UTILITY);
++#endif /* USE_REPLICATION */
++
+ /* open and AccessShareLock sequence */
+ init_sequence(relid, &elm, &seqrel);
+
+diff -aruN postgresql-8.1.2/src/backend/executor/functions.c
pgcluster-1.5.0rc7/src/backend/executor/functions.c
+--- postgresql-8.1.2/src/backend/executor/functions.c 2005-11-22
19:23:08.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/executor/functions.c 2006-01-10
17:39:38.000000000 +0100
+@@ -32,6 +32,9 @@
+ #include "utils/syscache.h"
+ #include "utils/typcache.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * We have an execution_state record for each query in a function. Each
+@@ -462,6 +465,13 @@
+ Datum value;
+ MemoryContext oldcontext;
+
++#ifdef USE_REPLICATION
++ if ((es != NULL) && (es->qd != NULL))
++ {
++ Xlog_Check_Replicate(es->qd->operation);
++ }
++#endif /* USE_REPLICATION */
++
+ if (es->status == F_EXEC_START)
+ postquel_start(es, fcache);
+
+diff -aruN postgresql-8.1.2/src/backend/libpq/Makefile
pgcluster-1.5.0rc7/src/backend/libpq/Makefile
+--- postgresql-8.1.2/src/backend/libpq/Makefile 2003-11-29
20:51:49.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/libpq/Makefile 2006-01-10
17:39:39.000000000 +0100
+@@ -15,7 +15,8 @@
+ # be-fsstubs is here for historical reasons, probably belongs elsewhere
+
+ OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
+- pqformat.o pqsignal.o
++ pqformat.o pqsignal.o \
++ replicate.o replicate_com.o recovery.o
+
+
+ all: SUBSYS.o
+diff -aruN postgresql-8.1.2/src/backend/libpq/be-fsstubs.c
pgcluster-1.5.0rc7/src/backend/libpq/be-fsstubs.c
+--- postgresql-8.1.2/src/backend/libpq/be-fsstubs.c 2005-11-22
19:23:09.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/libpq/be-fsstubs.c 2006-01-10
17:39:39.000000000 +0100
+@@ -48,6 +48,9 @@
+ #include "storage/large_object.h"
+ #include "utils/memutils.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*#define FSDB 1*/
+ #define BUFSIZE 8192
+@@ -93,6 +96,19 @@
+ int fd;
+ MemoryContext currentContext;
+
++#ifdef USE_REPLICATION
++ if ((PGR_Stand_Alone != NULL) &&
++ (PGR_lo_open(lobjId,mode) != STATUS_OK))
++ {
++ if ((mode & INV_WRITE) &&
++ (PGR_Is_Stand_Alone() == true) &&
++ (PGR_Stand_Alone->permit == PERMIT_READ_ONLY) )
++ {
++ elog(WARNING, "This query is not permitted when all
replication servers fell down ");
++ PG_RETURN_INT32(-1);
++ }
++ }
++#endif /* USE_REPLICATION */
+ #if FSDB
+ elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
+ #endif
+@@ -132,6 +148,9 @@
+ errmsg("invalid large-object descriptor:
%d", fd)));
+ PG_RETURN_INT32(-1);
+ }
++#ifdef USE_REPLICATION
++ PGR_lo_close(fd);
++#endif
+ #if FSDB
+ elog(DEBUG4, "lo_close(%d)", fd);
+ #endif
+@@ -201,6 +220,18 @@
+ errmsg("large object descriptor %d was not opened
for writing",
+ fd)));
+
++#ifdef USE_REPLICATION
++ if ((PGR_Stand_Alone != NULL) &&
++ (PGR_lo_write(fd, buf, len) != STATUS_OK))
++ {
++ if ((PGR_Is_Stand_Alone() == true) &&
++ (PGR_Stand_Alone->permit == PERMIT_READ_ONLY) )
++ {
++ elog(WARNING, "This query is not permitted when all
replication servers fell down ");
++ return -1;
++ }
++ }
++#endif
+ Assert(fscxt != NULL);
+ currentContext = MemoryContextSwitchTo(fscxt);
+
+@@ -229,6 +260,10 @@
+ PG_RETURN_INT32(-1);
+ }
+
++#ifdef USE_REPLICATION
++ PGR_lo_lseek(fd, offset, whence);
++#endif /* USE_REPLICATION */
++
+ Assert(fscxt != NULL);
+ currentContext = MemoryContextSwitchTo(fscxt);
+
+@@ -250,6 +285,18 @@
+
+ currentContext = MemoryContextSwitchTo(fscxt);
+
++#ifdef USE_REPLICATION
++ if ((PGR_Stand_Alone != NULL) &&
++ (PGR_lo_create(InvalidOid) != STATUS_OK))
++ {
++ if ((PGR_Is_Stand_Alone() == true) &&
++ (PGR_Stand_Alone->permit == PERMIT_READ_ONLY) )
++ {
++ elog(WARNING, "This query is not permitted when all
replication servers fell down ");
++ PG_RETURN_INT32(-1);
++ }
++ }
++#endif /* USE_REPLICATION */
+ lobjId = inv_create(InvalidOid);
+
+ MemoryContextSwitchTo(currentContext);
+@@ -263,6 +310,18 @@
+ Oid lobjId = PG_GETARG_OID(0);
+ MemoryContext currentContext;
+
++#ifdef USE_REPLICATION
++ if ((PGR_Stand_Alone != NULL) &&
++ (PGR_lo_create(lobjId) != STATUS_OK))
++ {
++ if ((PGR_Is_Stand_Alone() == true) &&
++ (PGR_Stand_Alone->permit == PERMIT_READ_ONLY) )
++ {
++ elog(WARNING, "This query is not permitted when all
replication servers fell down ");
++ PG_RETURN_INT32(-1);
++ }
++ }
++#endif /* USE_REPLICATION */
+ /* do we actually need fscxt for this? */
+ CreateFSContext();
+
+@@ -300,6 +359,18 @@
+ {
+ Oid lobjId = PG_GETARG_OID(0);
+
++#ifdef USE_REPLICATION
++ if ((PGR_Stand_Alone != NULL) &&
++ (PGR_lo_unlink(lobjId) != STATUS_OK))
++ {
++ if ((PGR_Is_Stand_Alone() == true) &&
++ (PGR_Stand_Alone->permit == PERMIT_READ_ONLY) )
++ {
++ elog(WARNING, "This query is not permitted when all
replication servers fell down ");
++ return -1;
++ }
++ }
++#endif /* USE_REPLICATION */
+ /*
+ * If there are any open LO FDs referencing that ID, close 'em.
+ */
+@@ -391,6 +462,7 @@
+ errhint("Anyone can use the client-side
lo_import() provided by libpq.")));
+ #endif
+
++
+ /*
+ * We don't actually need to switch into fscxt, but create it anyway
to
+ * ensure that AtEOXact_LargeObject knows there is state to clean up
+@@ -405,6 +477,20 @@
+ nbytes = MAXPGPATH - 1;
+ memcpy(fnamebuf, VARDATA(filename), nbytes);
+ fnamebuf[nbytes] = '\0';
++
++#ifdef USE_REPLICATION
++ if ((PGR_Stand_Alone != NULL) &&
++ (PGR_lo_import((char*)fnamebuf) != STATUS_OK))
++ {
++ if ((PGR_Is_Stand_Alone() == true) &&
++ (PGR_Stand_Alone->permit == PERMIT_READ_ONLY) )
++ {
++ elog(WARNING, "This query is not permitted when all
replication servers fell down ");
++ return -1;
++ }
++ }
++#endif
++
+ fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666);
+ if (fd < 0)
+ ereport(ERROR,
+diff -aruN postgresql-8.1.2/src/backend/libpq/cluster.conf.sample
pgcluster-1.5.0rc7/src/backend/libpq/cluster.conf.sample
+--- postgresql-8.1.2/src/backend/libpq/cluster.conf.sample 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/libpq/cluster.conf.sample 2006-01-10
17:39:39.000000000 +0100
+@@ -0,0 +1,57 @@
++#============================================================
++# Cluster DB Server configuration file
++#------------------------------------------------------------
++# file: cluster.conf
++#------------------------------------------------------------
++# This file controls:
++# o which hosts & port are replication server
++# o which port use for replication request to replication server
++# o which command use for recovery function
++#============================================================
++#------------------------------------------------------------
++# set Replication Server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Recovery_Port : connection for recovery process
++#------------------------------------------------------------
++<Replicate_Server_Info>
++ <Host_Name> replicate1.postgres.jp </Host_Name>
++ <Port> 8001 </Port>
++ <Recovery_Port> 8101 </Recovery_Port>
++</Replicate_Server_Info>
++#<Replicate_Server_Info>
++# <Host_Name> replicate2.postgres.jp </Host_Name>
++# <Port> 8002 </Port>
++# <Recovery_Port> 8102 </Recovery_Port>
++#</Replicate_Server_Info>
++#<Replicate_Server_Info>
++# <Host_Name> replicate3.postgres.jp </Host_Name>
++# <Port> 8003 </Port>
++# <Recovery_Port> 8103 </Recovery_Port>
++#</Replicate_Server_Info>
++#-------------------------------------------------------------
++# set Cluster DB Server information
++# o Recovery_Port : connection for recovery
++# o Rsync_Path : path of rsync command
++# o Rsync_Option : file transfer option for rsync
++# o Rsync_Compress : use compression option for rsync
++# [yes/no]. default : yes
++# o When_Stand_Alone : When all replication servers fell,
++# you can set up two kinds of permittion,
++# "real_only" or "read_write".
++#-------------------------------------------------------------
++<Recovery_Port> 7001 </Recovery_Port>
++<Rsync_Path> /usr/bin/rsync </Rsync_Path>
++<Rsync_Option> ssh -1 </Rsync_Option>
++<Rsync_Compress> yes </Rsync_Compress>
++<When_Stand_Alone> read_only </When_Stand_Alone>
++#-------------------------------------------------------------
++# set partitional replicate control information
++# set DB name and Table name to stop reprication
++# o DB_Name : DB name
++# o Table_Name : table name
++#-------------------------------------------------------------
++#<Not_Replicate_Info>
++# <DB_Name> test_db </DB_Name>
++# <Table_Name> log_table </Table_Name>
++#</Not_Replicate_Info>
+diff -aruN postgresql-8.1.2/src/backend/libpq/recovery.c
pgcluster-1.5.0rc7/src/backend/libpq/recovery.c
+--- postgresql-8.1.2/src/backend/libpq/recovery.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/libpq/recovery.c 2006-02-08
22:17:50.000000000 +0100
+@@ -0,0 +1,1196 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * recovery.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at backend for the recovery.
++ * Low level I/O functions that called by in these functions are
++ * contained in 'replicate_com.c'.
++ *
++ *--------------------------------------------------------------------
++ */
++
++/*--------------------------------------
++ * INTERFACE ROUTINES
++ *
++ * I/O call:
++ * PGR_recovery_finish_send
++ * master module:
++ * PGR_Master_Main(void);
++ * recovery module:
++ * PGR_Recovery_Main
++ *-------------------------------------
++ */
++#ifdef USE_REPLICATION
++
++#include "postgres.h"
++
++#include <stdio.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <ctype.h>
++#include <time.h>
++#include <pwd.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <sys/select.h>
++#include <netinet/tcp.h>
++#include <arpa/inet.h>
++#include <sys/file.h>
++#include <dirent.h>
++
++#include "libpq/pqsignal.h"
++#include "utils/guc.h"
++#include "miscadmin.h"
++#include "nodes/nodes.h"
++#include "nodes/parsenodes.h"
++#include "access/xact.h"
++#include "access/xlog.h"
++#include "tcop/tcopprot.h"
++#include "postmaster/postmaster.h"
++
++#include "../interfaces/libpq/libpq-fe.h"
++#include "../interfaces/libpq/libpq-int.h"
++#include "../interfaces/libpq/fe-auth.h"
++
++#include "replicate.h"
++
++#ifdef WIN32
++#include "win32.h"
++#else
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++#include <arpa/inet.h>
++#endif
++
++#ifndef HAVE_STRDUP
++#include "strdup.h"
++#endif
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#ifdef MULTIBYTE
++#include "mb/pg_wchar.h"
++#endif
++
++#define RECOVERY_LOOP_END (0)
++#define RECOVERY_LOOP_CONTINUE (1)
++#define RECOVERY_LOOP_FAIL (2)
++char Local_Host_Name[HOSTNAME_MAX_LENGTH];
++
++static int read_packet(int sock,RecoveryPacket * packet);
++static int send_recovery_packet(int sock, RecoveryPacket * packet);
++static int send_packet(int * sock, RecoveryPacket * packet );
++static void master_loop(int fd);
++static int start_recovery_send(int * sock, ReplicateServerInfo * host);
++static int stop_recovery_send(int * sock, ReplicateServerInfo * host);
++static int rsync_pg_data(char * src , char * dest);
++static int remove_dir(char * dir_name);
++static int clear_bkup_dir(char * dir_name);
++static int bkup_dir(char * dir_name);
++static int restore_dir(char * dir_name);
++static int rsync_global_dir(char * src, char * dest, int stage);
++static int first_recovery(char * src, char * dest, char * dir);
++static int second_recovery(char * src, char * dest, char * dir);
++static int recovery_rsync(char * src , char * dest, int stage);
++static int recovery_loop(int fd);
++static void show_recovery_packet(RecoveryPacket * packet);
++static int direct_send_packet(int packet_no);
++static void set_recovery_packet(RecoveryPacket * packet, int packet_no);
++static int sync_table_space(char * hostName, uint16_t portNum, char *
userName, int stage);
++static PGresult * get_table_space_location(char * hostName, uint16_t
portNum, char * userName);
++static int rsync_table_space(char * hostName, char * location, int stage);
++
++int PGR_recovery_error_send(void);
++int PGR_recovery_finish_send(void);
++int PGR_Master_Main(void);
++int PGR_Recovery_Main(void);
++
++static int
++read_packet(int sock,RecoveryPacket * packet)
++{
++ int r;
++ char * read_ptr;
++ int read_size = 0;
++ int packet_size = 0;
++
++ read_ptr = (char*)packet;
++ packet_size = sizeof(RecoveryPacket);
++
++ for (;;){
++ r = recv(sock,read_ptr + read_size ,packet_size, MSG_WAITALL);
++ if (r < 0) {
++ if (errno == EINTR || errno == EAGAIN) {
++ continue;
++ } else {
++ elog(DEBUG1, "read_packet():recv failed");
++ return -1;
++ }
++ } else if (r == 0) {
++ elog(DEBUG1, "read_packet():unexpected EOF");
++ return -1;
++ } else /*if (r > 0)*/ {
++ read_size += r;
++ if (read_size == packet_size) {
++ show_recovery_packet(packet);
++ return read_size;
++ }
++ }
++ }
++ return -1;
++}
++
++static int
++send_recovery_packet(int sock, RecoveryPacket * packet)
++{
++ char * send_ptr;
++ int send_size= 0;
++ int buf_size = 0;
++ int s;
++ int rtn;
++ fd_set wmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = PGR_SEND_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ rtn = 1;
++ while (rtn)
++ {
++ for (;;)
++ {
++ timeout.tv_sec = PGR_SEND_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ FD_ZERO(&wmask);
++ FD_SET(sock,&wmask);
++ rtn = select(sock+1, (fd_set *)NULL, &wmask, (fd_set
*)NULL, &timeout);
++
++ if (rtn < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ {
++ continue;
++ }
++ else
++ {
++ rtn = 0;
++ break;
++ }
++ }
++ else if (rtn && FD_ISSET(sock, &wmask))
++ {
++ send_ptr = (char *)packet;
++ buf_size = sizeof(RecoveryPacket);
++
++ s = send(sock,send_ptr + send_size,buf_size -
send_size ,0);
++ if (s < 0) {
++ if (errno == EINTR || errno ==
EAGAIN) {
++ continue;
++ }
++ elog(DEBUG1,
"send_recovery_packet():send error");
++
++ /* EPIPE || ENCONNREFUSED || ENSOCK
|| EHOSTUNREACH */
++ return STATUS_ERROR;
++ } else if (s == 0) {
++ elog(DEBUG1,
"send_recovery_packet():unexpected EOF");
++ return STATUS_ERROR;
++ } else /*if (s > 0)*/ {
++ send_size += s;
++ if (send_size == buf_size)
++ {
++ return STATUS_OK;
++ }
++ }
++ }
++ }
++ }
++ return STATUS_ERROR;
++}
++
++static int
++send_packet(int * sock, RecoveryPacket * packet )
++{
++ int count = 0;
++ ReplicateServerInfo * host = NULL;
++
++ host = PGR_get_replicate_server_info();
++ if (host == (ReplicateServerInfo*)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ count = 0;
++ while (send_recovery_packet(*sock,packet) != STATUS_OK)
++ {
++ if (count < MAX_RETRY_TIMES )
++ {
++ count ++;
++ continue;
++ }
++ count = 0;
++ close(*sock);
++ PGR_Set_Replication_Server_Status(host,DATA_ERR);
++ host = PGR_get_replicate_server_info();
++ if (host == (ReplicateServerInfo*)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ PGR_Set_Replication_Server_Status(host,DATA_USE);
++ PGR_Create_Socket_Connect(sock, host->hostName ,
host->recoveryPortNumber);
++ }
++ return STATUS_OK;
++}
++
++static void
++master_loop(int fd)
++{
++ int count;
++ int sock;
++ int status = STATUS_OK;
++ RecoveryPacket packet;
++ int r_size = 0;
++ bool loop_end = false;
++
++ count = 0;
++ while ((status =
PGR_Create_Acception(fd,&sock,"",RecoveryPortNumber)) != STATUS_OK)
++ {
++ PGR_Close_Sock(&sock);
++ sock = -1;
++ if ( count > MAX_RETRY_TIMES)
++ {
++ return;
++ }
++ count ++;
++ }
++ for(;;)
++ {
++ int rtn;
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = PGR_RECV_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(sock,&rmask);
++ rtn = select(sock+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn && FD_ISSET(sock, &rmask))
++ {
++ r_size = read_packet(sock,&packet);
++ if (r_size == 0)
++ {
++ continue;
++ }
++ else if (r_size < 0)
++ {
++ loop_end=true;
++ break;
++ }
++ }
++ else
++ {
++ continue;
++ }
++ switch (ntohs(packet.packet_no))
++ {
++ case RECOVERY_PGDATA_REQ :
++ /*
++ * PGDATA information request
++ */
++ /*
++ * get master server information
++ */
++ memset(&packet,0,sizeof(packet));
++ set_recovery_packet(&packet,
RECOVERY_PGDATA_ANS) ;
++ status = send_packet(&sock,&packet);
++ PGR_Set_Cluster_Status(STATUS_RECOVERY);
++ break;
++ case RECOVERY_FSYNC_REQ :
++ /*
++ * get master server information
++ */
++ memset(&packet,0,sizeof(packet));
++ set_recovery_packet(&packet,
RECOVERY_FSYNC_ANS );
++ status = send_packet(&sock,&packet);
++ PGR_Set_Cluster_Status(STATUS_RECOVERY);
++ loop_end = true;
++ break;
++ case RECOVERY_ERROR_TARGET_ONLY:
++ memset(&packet,0,sizeof(packet));
++ set_recovery_packet(&packet,
RECOVERY_ERROR_ANS );
++ status = send_packet(&sock,&packet);
++ PGR_Set_Cluster_Status(STATUS_REPLICATE);
++ break;
++ case RECOVERY_ERROR_CONNECTION:
++ memset(&packet,0,sizeof(packet));
++ set_recovery_packet(&packet,
RECOVERY_ERROR_ANS );
++ status = send_packet(&sock,&packet);
++ PGR_Set_Cluster_Status(STATUS_REPLICATE);
++ /**
++ * kill broken cluster db.
++ * FIXME: missing MyProcPid here. It must be
postmaster's pid.
++ * but here's a bug MyProcPid doesn't
initialized properly , so MyProcPid = postmaster's pid.

++ * To fix this, define variable to set
posmaster's pid.
++ */
++ kill(MyProcPid,SIGQUIT);
++ loop_end = true;
++ break;
++ case RECOVERY_ERROR_ANS:
++ /* TODO: recovery failed. close this postmaster */
++ loop_end = true;
++ break;
++ case RECOVERY_FINISH:
++ PGR_Set_Cluster_Status(STATUS_REPLICATE);
++ loop_end = true;
++ break;
++ default:
++ loop_end = true;
++ break;
++ }
++ if (loop_end)
++ {
++ break;
++ }
++ }
++ PGR_Close_Sock(&sock);
++}
++
++int
++PGR_Master_Main(void)
++{
++ int status;
++ int fd = -1;
++ int rtn;
++ int pid;
++
++ if ((pid = fork()) != 0 )
++ {
++ return pid;
++ }
++
++ memset(Local_Host_Name,0,sizeof(Local_Host_Name));
++ gethostname(Local_Host_Name,sizeof(Local_Host_Name));
++ pqsignal(SIGHUP, authdie);
++ pqsignal(SIGTERM, authdie);
++ pqsignal(SIGINT, authdie);
++ pqsignal(SIGQUIT, authdie);
++ pqsignal(SIGALRM, authdie);
++ PG_SETMASK(&UnBlockSig);
++
++ status = STATUS_ERROR;
++ status = PGR_Create_Socket_Bind(&fd, "", RecoveryPortNumber);
++
++ if (status != STATUS_OK)
++ {
++ return pid;
++ }
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = 60;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(fd,&rmask);
++ rtn = select(fd+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn && FD_ISSET(fd, &rmask))
++ {
++ master_loop(fd);
++ }
++ }
++ return pid;
++}
++
++static int
++start_recovery_send(int * sock, ReplicateServerInfo * host)
++{
++ int status;
++ RecoveryPacket packet;
++ status = PGR_Create_Socket_Connect(sock, host->hostName,
host->recoveryPortNumber);
++ if (status != STATUS_OK)
++ {
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"connection error to replication server");
++ }
++ return STATUS_ERROR;
++ }
++
++ memset(&packet,0,sizeof(packet));
++ set_recovery_packet(&packet, RECOVERY_PREPARE_REQ );
++ status = send_packet(sock,&packet);
++
++ return status;
++}
++
++static int
++stop_recovery_send(int * sock, ReplicateServerInfo * host)
++{
++ int status;
++ RecoveryPacket packet;
++
++ memset(&packet,0,sizeof(packet));
++ set_recovery_packet(&packet, RECOVERY_ERROR_ANS );
++ status = send_packet(sock,&packet);
++ return status;
++}
++
++static int
++direct_send_packet(int packet_no)
++{
++
++ int status;
++ int fd = -1;
++ ReplicateServerInfo * host;
++ RecoveryPacket packet;
++
++ host = PGR_get_replicate_server_info();
++ if (host == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ status = PGR_Create_Socket_Connect(&fd, host->hostName,
host->recoveryPortNumber);
++ if (status != STATUS_OK)
++ {
++ PGR_Set_Replication_Server_Status(host,DATA_ERR);
++ return STATUS_ERROR;
++ }
++
++ memset(&packet,0,sizeof(packet));
++ set_recovery_packet(&packet, packet_no );
++ status = send_packet(&fd,&packet);
++
++ close(fd);
++
++ return status;
++}
++
++int
++PGR_recovery_error_send(void)
++{
++ return direct_send_packet(RECOVERY_ERROR_ANS);
++}
++
++int
++PGR_recovery_finish_send(void)
++{
++ return direct_send_packet(RECOVERY_FINISH);
++}
++
++static int
++rsync_pg_data(char * src, char * dest)
++{
++ int status;
++ char *args[12];
++ int pid, i = 0;
++
++ args[i++] = "rsync";
++ args[i++] = "-a";
++ args[i++] = "-r";
++ if (RsyncCompress)
++ args[i++] = "-z";
++ args[i++] = "--delete";
++ args[i++] = "-e";
++ args[i++] = RsyncOption;
++ args[i++] = src;
++ args[i++] = dest;
++ args[i++] = NULL;
++
++ pid = fork();
++ if (pid == 0)
++ {
++ status = execv(RsyncPath,args);
++ }
++ else
++ {
++ for (;;)
++ {
++ int result;
++ result = wait(&status);
++ if (result < 0)
++ {
++ if (errno == EINTR)
++ continue;
++ return STATUS_ERROR;
++ }
++
++ if (WIFEXITED(status) == 0 || WEXITSTATUS(status) !=
0)
++ return STATUS_ERROR;
++ else
++ break;
++ }
++ }
++ return STATUS_OK;
++}
++
++static int
++remove_dir(char * dir_name)
++{
++ DIR * dp = NULL;
++ struct dirent *dirp = NULL;
++ char fname[256];
++ int status = 0;
++
++ if ((dp = opendir(dir_name)) == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ while ((dirp = readdir(dp)) != NULL)
++ {
++ if ((!strcmp(dirp->d_name,".")) ||
++ (!strcmp(dirp->d_name,"..")))
++ {
++ continue;
++ }
++ sprintf(fname,"%s/%s",dir_name,dirp->d_name);
++ status = remove(fname);
++ if (status < 0)
++ {
++ remove_dir(fname);
++ }
++ }
++ closedir(dp);
++ if (remove(dir_name) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++clear_bkup_dir(char * dir_name)
++{
++ char bkp_dir[256];
++ pid_t pid = getpid();
++
++ sprintf(bkp_dir,"%s_%d",dir_name,pid);
++ return (remove_dir(bkp_dir));
++}
++
++static int
++bkup_dir(char * dir_name)
++{
++ int status;
++ char org_dir[256];
++ char bkp_dir[256];
++ pid_t pid = getpid();
++
++ sprintf(org_dir,"%s",dir_name);
++ sprintf(bkp_dir,"%s_%d",dir_name,pid);
++ status = rename(org_dir,bkp_dir);
++ if (status < 0)
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++restore_dir(char * dir_name)
++{
++ int status;
++ char org_dir[256];
++ char bkp_dir[256];
++ pid_t pid = getpid();
++
++ sprintf(org_dir,"%s",dir_name);
++ sprintf(bkp_dir,"%s_%d",dir_name,pid);
++ status = rename(bkp_dir,org_dir);
++ if (status < 0)
++ {
++ remove_dir(org_dir);
++ status = rename(bkp_dir,org_dir);
++ if (status < 0)
++ {
++ return STATUS_ERROR;
++ }
++ }
++ return STATUS_OK;
++}
++
++static int
++rsync_global_dir(char * src, char * dest, int stage)
++{
++ int status;
++ char control_file[256];
++ char org_dir[256];
++ char src_dir[256];
++ struct stat fstat;
++ int cnt;
++
++ sprintf(org_dir,"%s/global",dest);
++ sprintf(control_file,"%s/global/pg_control",dest);
++ if (stage == PGR_1ST_RECOVERY)
++ {
++ if (bkup_dir(org_dir) != STATUS_OK)
++ {
++ return STATUS_ERROR;
++ }
++ }
++ sprintf(src_dir,"%s/global",src);
++ status = rsync_pg_data(src_dir, dest);
++ if (status != STATUS_OK )
++ {
++ restore_dir(org_dir);
++ return STATUS_ERROR;
++ }
++ /* check pg_control file */
++ cnt = 0;
++ while (stat(control_file, &fstat) < 0)
++ {
++ if (cnt > MAX_RETRY_TIMES )
++ {
++ restore_dir(org_dir);
++ return STATUS_ERROR;
++ }
++ cnt ++;
++ sleep(1);
++ }
++ if (stage == PGR_2ND_RECOVERY)
++ {
++ clear_bkup_dir(org_dir);
++ }
++ return STATUS_OK;
++}
++
++static int
++first_recovery(char * src, char * dest, char * dir)
++{
++ int status = STATUS_OK;
++ char src_dir[256];
++ char dest_dir[256];
++
++ memset(src_dir,0,sizeof(src_dir));
++ memset(dest_dir,0,sizeof(dest_dir));
++ sprintf(src_dir,"%s/%s",src,dir);
++ sprintf(dest_dir,"%s/%s",dest,dir);
++ status = bkup_dir(dest_dir);
++ if (status < 0)
++ {
++ return STATUS_ERROR;
++ }
++ status = rsync_pg_data(src_dir, dest);
++ if (status != STATUS_OK )
++ {
++ restore_dir(dest_dir);
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++second_recovery(char * src, char * dest, char * dir)
++{
++ int status = STATUS_OK;
++ char src_dir[256];
++ char dest_dir[256];
++
++ memset(src_dir,0,sizeof(src_dir));
++ memset(dest_dir,0,sizeof(dest_dir));
++ sprintf(src_dir,"%s/%s",src,dir);
++ sprintf(dest_dir,"%s/%s",dest,dir);
++
++ status = rsync_pg_data(src_dir, dest);
++ if (status != STATUS_OK )
++ {
++ restore_dir(dest_dir);
++ return STATUS_ERROR;
++ }
++ clear_bkup_dir(dest_dir);
++
++ return STATUS_OK;
++}
++
++static int
++recovery_rsync(char * src , char * dest, int stage)
++{
++ if ((src== NULL) || ( dest == NULL))
++ {
++ return STATUS_ERROR;
++ }
++
++ /* recovery step of "global" directory */
++ fprintf(stderr,"%s recovery step of [global] directory...",
++ ((stage == 1)?"1st":"2nd"));
++ if (rsync_global_dir(src, dest, stage) != STATUS_OK)
++ {
++ fprintf(stderr,"NG\n");
++ return STATUS_ERROR;
++ }
++ fprintf(stderr,"OK\n");
++
++ if (stage == PGR_1ST_RECOVERY)
++ {
++ /* 1st recovery step of "base" directory */
++ fprintf(stderr,"1st recovery step of [base] directory...");
++ if (first_recovery(src,dest,"base") != STATUS_OK)
++ {
++ fprintf(stderr,"NG\n");
++ return STATUS_ERROR;
++ }
++ fprintf(stderr,"OK\n");
++
++ fprintf(stderr,"1st recovery step of [pg_clog] directory...");
++ /* 1st recovery step of "pg_clog" directory */
++ if (first_recovery(src,dest,"pg_clog") != STATUS_OK)
++ {
++ fprintf(stderr,"NG\n");
++ return STATUS_ERROR;
++ }
++ fprintf(stderr,"OK\n");
++
++ /* 1st recovery step of "pg_xlog" directory */
++ fprintf(stderr,"1st recovery step of [pg_xlog] directory...");
++ if (first_recovery(src,dest,"pg_xlog") != STATUS_OK)
++ {
++ fprintf(stderr,"NG\n");
++ return STATUS_ERROR;
++ }
++ fprintf(stderr,"OK\n");
++ }
++ else
++ {
++ /* 2nd recovery step of "base" directory */
++ fprintf(stderr,"2nd recovery step of [base] directory...");
++ if (second_recovery(src,dest,"base") != STATUS_OK)
++ {
++ fprintf(stderr,"NG\n");
++ return STATUS_ERROR;
++ }
++ fprintf(stderr,"OK\n");
++
++ /* 2nd recovery step of "pg_clog" directory */
++ fprintf(stderr,"2nd recovery step of [pg_clog] directory...");
++ if (second_recovery(src,dest,"pg_clog") != STATUS_OK)
++ {
++ fprintf(stderr,"NG\n");
++ return STATUS_ERROR;
++ }
++ fprintf(stderr,"OK\n");
++
++ /* 2nd recovery step of "pg_xlog" directory */
++ fprintf(stderr,"2nd recovery step of [pg_xlog] directory...");
++ if (second_recovery(src,dest,"pg_xlog") != STATUS_OK)
++ {
++ fprintf(stderr,"NG\n");
++ return STATUS_ERROR;
++ }
++ fprintf(stderr,"OK\n");
++ }
++
++ return STATUS_OK;
++}
++
++static int
++recovery_loop(int fd)
++{
++
++ int status = STATUS_OK;
++ RecoveryPacket packet;
++ int r_size = 0;
++ int rtn = RECOVERY_LOOP_END;
++ char src[256];
++ bool need_sync_table_space = false;
++
++ r_size = read_packet(fd,&packet);
++ if (r_size <= 0)
++ {
++ rtn = RECOVERY_LOOP_FAIL;
++ }
++ switch (ntohs(packet.packet_no))
++ {
++ case RECOVERY_PREPARE_ANS :
++ /*
++ * get master information
++ */
++ /*
++ * rsync master data before recovery
++ */
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"local host : %s
master:%s",Local_Host_Name,packet.hostName);
++ }
++ if
(!memcmp(Local_Host_Name,packet.hostName,strlen(packet.hostName)))
++ {
++ strcpy(src,packet.pg_data);
++ need_sync_table_space = false;
++ }
++ else
++ {
++
sprintf(src,"%s:%s",packet.hostName,packet.pg_data);
++ need_sync_table_space = true;
++ }
++ status = recovery_rsync(src,DataDir,PGR_1ST_RECOVERY);
++ if (status != STATUS_OK)
++ {
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"1st rsync error");
++ }
++ rtn = RECOVERY_LOOP_FAIL;
++ break;
++ }
++ if (need_sync_table_space == true)
++ {
++ fprintf(stderr,"1st sync_table_space ");
++ status = sync_table_space(packet.hostName,
ntohs(packet.port), packet.userName, PGR_1ST_RECOVERY);
++ if (status != STATUS_OK)
++ {
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"1st sync table
space error");
++ }
++ rtn = RECOVERY_LOOP_FAIL;
++ fprintf(stderr,"NG\n");
++ break;
++ }
++ fprintf(stderr,"OK\n");
++ }
++ /*
++ * send recovery start request
++ */
++ PGRset_recovery_packet_no(&packet, RECOVERY_START_REQ
);
++ status = send_packet(&fd,&packet);
++ if (status != STATUS_OK)
++ {
++ rtn = RECOVERY_LOOP_FAIL;
++ break;
++ }
++ rtn = RECOVERY_LOOP_CONTINUE;
++ break;
++ case RECOVERY_START_ANS :
++ /*
++ * rsync master data before recovery
++ */
++ if
(!memcmp(Local_Host_Name,packet.hostName,strlen(packet.hostName)))
++ {
++ strcpy(src,packet.pg_data);
++ need_sync_table_space = false;
++ }
++ else
++ {
++
sprintf(src,"%s:%s",packet.hostName,packet.pg_data);
++ need_sync_table_space = true;
++ }
++ status = recovery_rsync(src,DataDir,PGR_2ND_RECOVERY);
++ if (status != STATUS_OK)
++ {
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"2nd rsync error");
++ }
++ rtn = RECOVERY_LOOP_FAIL;
++ break;
++ }
++ if (need_sync_table_space == true)
++ {
++ fprintf(stderr,"2nd sync_table_space ");
++ status = sync_table_space(packet.hostName,
ntohs(packet.port), packet.userName, PGR_2ND_RECOVERY);
++ if (status != STATUS_OK)
++ {
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"2nd sync table
space error");
++ }
++ rtn = RECOVERY_LOOP_FAIL;
++ fprintf(stderr,"NG\n");
++ break;
++ }
++ fprintf(stderr,"OK\n");
++ }
++ rtn = RECOVERY_LOOP_END;
++ break;
++ case RECOVERY_ERROR_OCCUPIED:
++ case RECOVERY_ERROR_CONNECTION:
++ default:
++ rtn = RECOVERY_LOOP_FAIL;
++ break;
++ }
++
++ return rtn;
++}
++
++int
++PGR_Recovery_Main(void)
++{
++ int status;
++ int fd = -1;
++ int rtn;
++ ReplicateServerInfo * host;
++
++ memset(Local_Host_Name,0,sizeof(Local_Host_Name));
++ gethostname(Local_Host_Name,sizeof(Local_Host_Name));
++
++ status = STATUS_ERROR;
++
++Retry_Start_Recovery:
++ host = PGR_get_replicate_server_info();
++ if (host == NULL)
++ {
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"not found replication server");
++ }
++ return STATUS_ERROR;
++ }
++
++ status = start_recovery_send(&fd,host);
++ if (status != STATUS_OK)
++ {
++ PGR_Set_Replication_Server_Status(host,DATA_ERR);
++ close(fd);
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"start recovery packet send error");
++ }
++ goto Retry_Start_Recovery;
++ }
++
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = RECOVERY_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(fd,&rmask);
++ rtn = select(fd+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn && FD_ISSET(fd, &rmask))
++ {
++ status = recovery_loop(fd);
++ if (status == RECOVERY_LOOP_CONTINUE)
++ {
++ continue;
++ }
++ else if (status == RECOVERY_LOOP_END)
++ {
++ close(fd);
++ break;
++ }
++ else if (status == RECOVERY_LOOP_FAIL)
++ {
++ status = stop_recovery_send(&fd,host);
++ if (status != STATUS_OK)
++ {
++ close(fd);
++ return STATUS_ERROR;
++ }
++ close(fd);
++ return STATUS_ERROR;
++ }
++ else
++ {
++ close(fd);
++ return STATUS_ERROR;
++ }
++ }
++ }
++ return STATUS_OK;
++}
++
++static void
++show_recovery_packet(RecoveryPacket * packet)
++{
++
++ if (Debug_pretty_print)
++ {
++ elog(DEBUG1,"no = %d",ntohs(packet->packet_no));
++ elog(DEBUG1,"max_connect = %d",ntohs(packet->max_connect));
++ elog(DEBUG1,"port = %d",ntohs(packet->port));
++ elog(DEBUG1,"recoveryPort = %d",ntohs(packet->recoveryPort));
++ if (packet->hostName != NULL)
++ elog(DEBUG1,"hostName = %s",packet->hostName);
++ if (packet->pg_data != NULL)
++ elog(DEBUG1,"pg_data = %s",packet->pg_data);
++ }
++}
++
++static void
++set_recovery_packet(RecoveryPacket * packet, int packet_no)
++{
++ struct passwd * pw = NULL;
++
++ if (packet == NULL)
++ {
++ return;
++ }
++ PGRset_recovery_packet_no(packet, packet_no );
++ packet->max_connect = htons(MaxBackends);
++ packet->port = htons(PostPortNumber);
++ packet->recoveryPort = htons(RecoveryPortNumber);
++ gethostname(packet->hostName,sizeof(packet->hostName));
++ memcpy(packet->pg_data,DataDir,sizeof(packet->pg_data));
++ memset(packet->userName,0,sizeof(packet->userName));
++ if ((pw = getpwuid(geteuid())) != NULL)
++ {
++
strncpy(packet->userName,pw->pw_name,sizeof(packet->userName));
++ }
++ else
++ {
++ cuserid(packet->userName);
++ }
++}
++
++static int
++sync_table_space(char * hostName, uint16_t portNum, char * userName, int
stage)
++{
++ PGresult * res = (PGresult *)NULL;
++ int i = 0;
++ int row_num = 0;
++ char * location = NULL;
++ int rtn = STATUS_OK;
++
++ res = get_table_space_location(hostName, portNum, userName);
++ if (res == (PGresult *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ row_num = PQntuples(res);
++ for ( i = 0 ; i < row_num ; i ++)
++ {
++ location = PQgetvalue(res,i,0);
++ if (strlen(location) > 0 )
++ {
++ rtn = rsync_table_space(hostName, location, stage);
++ fprintf(stderr,".");
++ }
++ }
++ if (res != (PGresult *)NULL)
++ {
++ PQclear(res);
++ }
++
++ return STATUS_OK;
++}
++
++static PGresult *
++get_table_space_location(char * hostName, uint16_t portNum, char * userName)
++{
++ PGresult * res = (PGresult *)NULL;
++ int cnt = 0;
++ PGconn * conn = (PGconn *)NULL;
++ char port[8];
++ char *database = "template1";
++ char * query = "select spclocation from pg_tablespace";
++
++ if ( (hostName == NULL) ||
++ (portNum <= 0) ||
++ (userName == NULL))
++ {
++ return (PGresult *)NULL;
++ }
++ snprintf(port,sizeof(port),"%d", portNum);
++
++ /* create connection to master */
++ conn = PQsetdbLogin(hostName, port, NULL, NULL, database, userName,
NULL);
++ if (conn == NULL)
++ {
++ return (PGresult *)NULL;
++ }
++ /* check to see that the backend Connection was successfully made */
++ cnt = 0;
++ while (PQstatus(conn) == CONNECTION_BAD)
++ {
++ if (conn != NULL)
++ {
++ PQfinish(conn);
++ }
++ if (cnt > MAX_RETRY_TIMES )
++ {
++ return (PGresult *)NULL;
++ }
++ conn = PQsetdbLogin(hostName, port, NULL, NULL, database,
userName, NULL);
++ cnt ++;
++ }
++ res = PQexec(conn , query);
++ if ((res == NULL) ||
++ (PQresultStatus(res) != PGRES_TUPLES_OK))
++ {
++ PQclear(res);
++ res = (PGresult *)NULL;
++ }
++ if (conn != NULL)
++ {
++ PQfinish(conn);
++ }
++
++ return res;
++}
++
++static int
++rsync_table_space(char * hostName, char * location, int stage)
++{
++ int status = STATUS_OK;
++ char src_dir[256];
++ char dest_dir[256];
++ struct stat fstat;
++ int cnt = 0;
++ char * term = NULL;
++
++ sprintf(src_dir,"%s:%s",hostName,location);
++ strncpy(dest_dir,location,sizeof(dest_dir));
++ term = strrchr(dest_dir,'/');
++ if (term != NULL)
++ {
++ *term = '\0';
++ }
++
++ if (stage == PGR_1ST_RECOVERY)
++ {
++ status = bkup_dir(location);
++ }
++ status = rsync_pg_data(src_dir, dest_dir);
++ if (status != STATUS_OK )
++ {
++ restore_dir(location);
++ return STATUS_ERROR;
++ }
++ /* check pg_control file */
++ cnt = 0;
++ while (stat(location,&fstat) < 0)
++ {
++ if (cnt > MAX_RETRY_TIMES )
++ {
++ restore_dir(location);
++ return STATUS_ERROR;
++ }
++ cnt ++;
++ sleep(1);
++ }
++ if (stage == PGR_2ND_RECOVERY)
++ {
++ clear_bkup_dir(location);
++ }
++ return STATUS_OK;
++}
++#endif /* USE_REPLICATION */
+diff -aruN postgresql-8.1.2/src/backend/libpq/replicate.c
pgcluster-1.5.0rc7/src/backend/libpq/replicate.c
+--- postgresql-8.1.2/src/backend/libpq/replicate.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/libpq/replicate.c 2006-02-09
11:51:50.000000000 +0100
+@@ -0,0 +1,3552 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * replicate.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at backend for the replication.
++ * Low level I/O functions that called by in these functions are
++ * contained in 'replicate_com.c'.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++
++/*--------------------------------------
++ * INTERFACE ROUTINES
++ *
++ * setup/teardown:
++ * PGR_Init_Replicate_Server_Data
++ * PGR_Set_Replicate_Server_Socket
++ * PGR_delete_shm
++ * I/O call:
++ * PGR_Send_Replicate_Command
++ * table handling:
++ * PGR_get_replicate_server_info
++ * status distinction:
++ * PGR_Is_Replicated_Command
++ * Xlog_Check_Replicatec
++ * replicateion main:
++ * PGR_replication
++ *-------------------------------------
++ */
++#ifdef USE_REPLICATION
++
++#include "postgres.h"
++
++#include <stdio.h>
++#include <strings.h>
++#include <signal.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <grp.h>
++#include <unistd.h>
++#include <ctype.h>
++#include <time.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++#include <arpa/inet.h>
++#include <sys/file.h>
++#include <netdb.h>
++
++#include "libpq/libpq.h"
++#include "libpq/pqformat.h"
++#include "miscadmin.h"
++#include "nodes/nodes.h"
++#include "nodes/print.h"
++#include "utils/guc.h"
++#include "parser/parser.h"
++#include "access/xact.h"
++#include "storage/proc.h"
++#include "postmaster/postmaster.h"
++
++#include "replicate.h"
++
++/* the source of this value is 'access/transam/varsup.c' */
++#define VAR_OID_PREFETCH (8192)
++
++PGR_ReplicationLog_Info ReplicationLog_Info;
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++static int set_command_args(char argv[PGR_CMD_ARG_NUM][256],char *str);
++static bool is_same_replication_server(ReplicateServerInfo * sp1,
ReplicateServerInfo * sp2 );
++static ReplicateServerInfo * search_new_replication_server (
ReplicateServerInfo * sp , int socket_type );
++
++static int close_replicate_server_socket ( ReplicateServerInfo * sp , int
socket_type );
++static int recv_replicate_result(int sock,char * result,int user_timeout);
++static int recv_message(int sock,char * buf,int flag);
++static int send_replicate_packet(int sock,ReplicateHeader * header, char *
query_string);
++static int get_replicate_server_socket ( ReplicateServerInfo * sp , int
socket_type );
++static bool is_copy_from(char * query);
++//static int is_session_authorization(char * query);
++static int get_words( char words[MAX_WORDS][MAX_WORD_LETTERS] ,char *
string,int length,int upper);
++static int get_table_name(char * table_name, char * query, int position );
++static bool is_not_replication_query(char * query_string, int query_len,
char cmdType);
++static int Comp_Not_Replicate(PGR_Not_Replicate_Type *
nrp1,PGR_Not_Replicate_Type* nrp2);
++static bool is_serial_control_query(char cmdType,char * query);
++static bool is_select_into_query(char cmdType,char * query);
++static int send_response_to_replication_server(const char * notice);
++static bool do_not_replication_command(const char * commandTag);
++static bool is_create_temp_table(char * query);
++static int add_replication_server(char * hostname,char * port, char *
recovery_port);
++static int change_replication_server(char * hostname,char * port, char *
recovery_port);
++static int get_new_replication_socket( ReplicateServerInfo * base,
ReplicateServerInfo * sp, int socket_type);
++static char * get_hostName(char * str);
++static void set_response_mode(char * mode);
++static void PGR_Set_Current_Replication_Query_ID(char *id);
++#ifdef CONTROL_LOCK_CONFLICT
++static int wait_lock_answer(void);
++static int read_trigger(char * result, int buf_size);
++#endif /* CONTROL_LOCK_CONFLICT */
++
++
++static unsigned int get_next_request_id(void);
++static bool is_this_query_replicated(char * id);
++static int set_replication_id(char * id);
++static int return_current_oid(void);
++static int sync_oid(char * oid);
++
++extern ssize_t secure_read(Port *, void *, size_t);
++
++bool autocommit = true;
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGR_Init_Replicate_Server_Data()
++ * NOTES
++ * Read Configuration file and create ReplicateServerData table
++ * ARGS
++ * void
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGR_Init_Replicate_Server_Data(void)
++{
++ int table_size,str_size;
++ ReplicateServerInfo *sp;
++ PGR_Not_Replicate_Type * nrp;
++ ConfDataType * conf;
++ int rec_no,cnt;
++ unsigned int ip;
++ char HostName[128];
++
++
++ if (ConfData_Top == (ConfDataType *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++
++ /* allocate replication server information table */
++ table_size = sizeof(ReplicateServerInfo) * MAX_SERVER_NUM;
++ ReplicateServerShmid = shmget(IPC_PRIVATE,table_size,IPC_CREAT |
IPC_EXCL | 0600);
++ if (ReplicateServerShmid < 0)
++ {
++ return STATUS_ERROR;
++ }
++ ReplicateServerData = (ReplicateServerInfo
*)shmat(ReplicateServerShmid,0,0);
++ if (ReplicateServerData == (ReplicateServerInfo *)-1)
++ {
++ return STATUS_ERROR;
++ }
++ memset(ReplicateServerData,0,table_size);
++ sp = ReplicateServerData;
++
++ /* allocate cluster db information table */
++ ClusterDBShmid = shmget(IPC_PRIVATE,sizeof(ClusterDBInfo),IPC_CREAT |
IPC_EXCL | 0600);
++ if (ClusterDBShmid < 0)
++ {
++ return STATUS_ERROR;
++ }
++ ClusterDBData = (ClusterDBInfo *)shmat(ClusterDBShmid,0,0);
++ if (ClusterDBData == (ClusterDBInfo *)-1)
++ {
++ return STATUS_ERROR;
++ }
++ memset(ClusterDBData,0,sizeof(ClusterDBInfo));
++ PGR_Set_Cluster_Status(STATUS_REPLICATE);
++
++ /* allocate partial replicate table */
++ table_size = sizeof(PGR_Not_Replicate_Type) * MAX_SERVER_NUM;
++ PGR_Not_Replicate = malloc(table_size);
++ if (PGR_Not_Replicate == (PGR_Not_Replicate_Type*)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(PGR_Not_Replicate, 0, table_size);
++ nrp = PGR_Not_Replicate;
++ cnt = 0;
++ conf = ConfData_Top;
++ while ((conf != (ConfDataType *)NULL) && (cnt < MAX_SERVER_NUM))
++ {
++ /* set replication server table */
++ if (!strcmp(conf->table,REPLICATION_SERVER_INFO_TAG))
++ {
++ rec_no = conf->rec_no;
++ cnt = rec_no;
++ if (!strcmp(conf->key,HOST_NAME_TAG))
++ {
++ strncpy((sp +
rec_no)->hostName,conf->value,sizeof(sp->hostName));
++ conf = (ConfDataType *)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,PORT_TAG))
++ {
++ (sp + rec_no)->portNumber = atoi(conf->value);
++ (sp + rec_no)->sock = -1;
++ if ((sp + rec_no)->useFlag != DATA_USE)
++ {
++
PGR_Set_Replication_Server_Status((sp+rec_no), DATA_INIT);
++ }
++ memset((sp + rec_no +
1)->hostName,0,sizeof(sp->hostName));
++ (sp + rec_no + 1)->useFlag = DATA_END;
++ conf = (ConfDataType *)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,RECOVERY_PORT_TAG))
++ {
++ (sp + rec_no)->recoveryPortNumber =
atoi(conf->value);
++ if ((sp + rec_no)->useFlag != DATA_USE)
++ {
++
PGR_Set_Replication_Server_Status((sp+rec_no), DATA_INIT);
++ }
++ memset((sp + rec_no +
1)->hostName,0,sizeof(sp->hostName));
++ (sp + rec_no + 1)->useFlag = DATA_END;
++ conf = (ConfDataType *)conf->next;
++ continue;
++ }
++ }
++ /* set part replication table */
++ if (!strcmp(conf->table,NOT_REPLICATE_INFO_TAG))
++ {
++ rec_no = conf->rec_no;
++ cnt = rec_no;
++ if (PGR_Not_Replicate_Rec_Num < rec_no +1)
++ {
++ PGR_Not_Replicate_Rec_Num = rec_no +1;
++ }
++ if (!strcmp(conf->key,DB_NAME_TAG))
++ {
++ strncpy((nrp +
rec_no)->db_name,conf->value,sizeof(nrp->db_name));
++ conf = (ConfDataType *)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,TABLE_NAME_TAG))
++ {
++ strncpy((nrp +
rec_no)->table_name,conf->value,sizeof(nrp->table_name));
++ conf = (ConfDataType *)conf->next;
++ continue;
++ }
++ }
++ if (!strcmp(conf->key,RECOVERY_PORT_TAG))
++ {
++ RecoveryPortNumber = atoi(conf->value);
++ }
++ if (!strcmp(conf->key,RSYNC_PATH_TAG))
++ {
++ str_size = strlen(conf->value) ;
++ RsyncPath = malloc(str_size + 1);
++ if (RsyncPath == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(RsyncPath,0,str_size + 1);
++ strncpy(RsyncPath,conf->value,str_size);
++ }
++ if (!strcmp(conf->key,RSYNC_OPTION_TAG))
++ {
++ str_size = strlen(conf->value) ;
++ RsyncOption = malloc(str_size + 1);
++ if (RsyncOption == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(RsyncOption,0,str_size + 1);
++ strncpy(RsyncOption,conf->value,str_size);
++ }
++ if (!strcmp(conf->key,RSYNC_COMPRESS_TAG))
++ {
++ if (!strcmp(conf->value, "yes"))
++ RsyncCompress = true;
++ else if (!strcmp(conf->value, "no"))
++ RsyncCompress = false;
++ }
++ if (!strcmp(conf->key,STAND_ALONE_TAG))
++ {
++ PGR_Stand_Alone =
(PGR_Stand_Alone_Type*)malloc(sizeof(PGR_Stand_Alone_Type));
++ if (PGR_Stand_Alone == (PGR_Stand_Alone_Type *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ PGR_Stand_Alone->is_stand_alone = false;
++ if (!strcmp(conf->value,READ_WRITE_IF_STAND_ALONE))
++ {
++ PGR_Stand_Alone->permit = PERMIT_READ_WRITE;
++ }
++ else
++ {
++ PGR_Stand_Alone->permit = PERMIT_READ_ONLY;
++ }
++ }
++ conf = (ConfDataType *)conf->next;
++ }
++ TransactionSock = -1;
++ ReplicateCurrentTime = (ReplicateNow *)malloc(sizeof(ReplicateNow));
++ if (ReplicateCurrentTime == (ReplicateNow *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(ReplicateCurrentTime,0,sizeof(ReplicateNow));
++
++ PGRCopyData = (CopyData *)malloc(sizeof(CopyData));
++ if (PGRCopyData == (CopyData *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(PGRCopyData,0,sizeof(CopyData));
++
++ if (PGR_Not_Replicate_Rec_Num == 0)
++ {
++ free(PGR_Not_Replicate);
++ PGR_Not_Replicate = NULL;
++ }
++ else
++ {
++ qsort((char
*)PGR_Not_Replicate,PGR_Not_Replicate_Rec_Num,sizeof(PGR_Not_Replicate_Type),
(int (*)(const void*,const void*))Comp_Not_Replicate);
++ }
++
++ PGRSelfHostName = malloc(HOSTNAME_MAX_LENGTH);
++ if (PGRSelfHostName == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(PGRSelfHostName,0,HOSTNAME_MAX_LENGTH);
++ if (gethostname(HostName,HOSTNAME_MAX_LENGTH) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ ip=PGRget_ip_by_name(HostName);
++
++ sprintf(PGRSelfHostName,
++ "%d.%d.%d.%d",
++ (ip ) & 0xff ,
++ (ip >> 8) & 0xff ,
++ (ip >> 16) & 0xff ,
++ (ip >> 24) & 0xff );
++
++ return STATUS_OK;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGR_Set_Replicate_Server_Socket()
++ * NOTES
++ * Create new socket and set ReplicateServerData table
++ * ARGS
++ * void
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGR_Set_Replicate_Server_Socket(void)
++{
++ ReplicateServerInfo * sp;
++ if (ReplicateServerData == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ sp = ReplicateServerData;
++ while (sp->useFlag != DATA_END){
++ sp->sock = -1;
++
PGR_Create_Socket_Connect(&(sp->sock),sp->hostName,sp->portNumber);
++ sp ++;
++ }
++ return STATUS_OK;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * get_replicate_server_socket()
++ * NOTES
++ * search or create a socket to connect with the replication server
++ * ARGS
++ * ReplicateServerInfo * sp: replication server data (I)
++ * int socket_type: socket type (I)
++ * -PGR_TRANSACTION_SOCKET:
++ * -PGR_QUERY_SOCKET:
++ * RETURN
++ * OK: >0(socket)
++ * NG: -1
++ *--------------------------------------------------------------------
++ */
++static int
++get_replicate_server_socket ( ReplicateServerInfo * sp , int socket_type )
++{
++ ReplicateServerInfo * tmp;
++ tmp = sp;
++ if (tmp == (ReplicateServerInfo *) NULL)
++ {
++ return -1;
++ }
++ if (tmp->hostName[0] == '\0')
++ {
++ return -1;
++ }
++
++ if (TransactionSock != -1)
++ {
++ return TransactionSock;
++ }
++
++
while(PGR_Create_Socket_Connect(&TransactionSock,tmp->hostName,tmp->portNumber)
!= STATUS_OK)
++ {
++ close(TransactionSock);
++ TransactionSock = -1;
++ PGR_Set_Replication_Server_Status(tmp, DATA_ERR);
++ usleep(20);
++ tmp = PGR_get_replicate_server_info();
++ if (tmp == (ReplicateServerInfo *)NULL)
++ {
++ return -1;
++ }
++ PGR_Set_Replication_Server_Status(tmp, DATA_USE);
++ usleep(10);
++ }
++ return TransactionSock;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * close_replicate_server_socket()
++ * NOTES
++ * close the socket connected with the replication server
++ * ARGS
++ * ReplicateServerInfo * sp: replication server data (I)
++ * int socket_type: socket type (I)
++ * -PGR_TRANSACTION_SOCKET:
++ * -PGR_QUERY_SOCKET:
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++static int
++close_replicate_server_socket ( ReplicateServerInfo * sp , int socket_type )
++{
++ if (sp == (ReplicateServerInfo *)NULL )
++ {
++ return STATUS_ERROR;
++ }
++ if (sp->hostName[0] == '\0')
++ {
++ return STATUS_ERROR;
++ }
++ if (TransactionSock != -1)
++ {
++ PGR_Close_Sock(&(TransactionSock));
++ TransactionSock = -1;
++ }
++ switch (socket_type)
++ {
++ case PGR_TRANSACTION_SOCKET:
++ if (TransactionSock != -1)
++ {
++ PGR_Close_Sock(&(TransactionSock));
++ }
++ TransactionSock = -1;
++ sp->sock = -1;
++ break;
++ case PGR_QUERY_SOCKET:
++ if (sp->sock != -1)
++ {
++ PGR_Close_Sock(&(sp->sock));
++ }
++ sp->sock = -1;
++ break;
++ }
++ PGR_Set_Replication_Server_Status(sp, DATA_INIT);
++ return STATUS_OK;
++}
++
++static bool
++is_same_replication_server(ReplicateServerInfo * sp1, ReplicateServerInfo *
sp2 )
++{
++ if ((sp1 == NULL) || (sp2 == NULL))
++ {
++ return false;
++ }
++ if ((!strcmp(sp1->hostName,sp2->hostName)) &&
++ (sp1->portNumber == sp2->portNumber) &&
++ (sp1->recoveryPortNumber == sp2->recoveryPortNumber))
++ {
++ return true;
++ }
++ return false;
++}
++
++static ReplicateServerInfo *
++search_new_replication_server ( ReplicateServerInfo * sp , int socket_type )
++{
++ ReplicateHeader dummy_header;
++ ReplicateServerInfo * rs_tbl;
++ char command[256];
++ int sock = -1;
++ int cnt = 0;
++
++ if ((ReplicateServerData == NULL) || ( sp == NULL))
++ {
++ return NULL;
++ }
++ rs_tbl = sp;
++ close_replicate_server_socket ( sp , socket_type);
++ sp ++;
++ while (is_same_replication_server(sp,rs_tbl) != true)
++ {
++ if (sp->useFlag == DATA_END)
++ {
++ sp = ReplicateServerData;
++ }
++ sock = get_replicate_server_socket( sp , socket_type);
++ if (sock < 0 )
++ {
++ if (is_same_replication_server(sp,rs_tbl) == true)
++ {
++ return NULL;
++ }
++ else
++ {
++ sp++;
++ }
++ continue;
++ }
++ memset(&dummy_header, 0, sizeof(ReplicateHeader));
++ memset(command,0,sizeof(command));
++ snprintf(command,sizeof(command)-1,"SELECT %s(%d,%s,%d,%d)",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_CHANGE_REPLICATION_SERVER_FUNC_NO,
++ sp->hostName,
++ sp->portNumber,
++ sp->recoveryPortNumber);
++ dummy_header.cmdSys = CMD_SYS_CALL;
++ dummy_header.cmdSts = CMD_STS_NOTICE;
++ dummy_header.query_size = htonl(strlen(command));
++ if (send_replicate_packet(sock,&dummy_header,command) !=
STATUS_OK)
++ {
++ cnt ++;
++ close_replicate_server_socket ( sp , socket_type);
++ PGR_Set_Replication_Server_Status(sp, DATA_ERR);
++ }
++ else
++ {
++ PGR_Set_Replication_Server_Status(sp, DATA_USE);
++ return sp;
++ }
++ if (cnt > MAX_RETRY_TIMES )
++ {
++ sp++;
++ cnt = 0;
++ }
++ else
++ {
++ continue;
++ }
++ }
++ return NULL;
++}
++
++static int
++get_table_name(char * table_name, char * query, int position )
++{
++
++ int i,wc;
++ char * p;
++ char * sp;
++ int length;
++
++ if ((table_name == NULL) || (query == NULL) || (position < 1))
++ {
++ return STATUS_ERROR;
++ }
++ length = strlen(query);
++ p = query;
++ wc = 1;
++ sp = table_name;
++ for (i = 0 ; i < length ; i ++)
++ {
++ while(isspace(*p))
++ {
++ p++;
++ i++;
++ }
++ while((*p != '\0') && (! isspace(*p)))
++ {
++ if ((*p == ';') || (*p == '('))
++ break;
++ if (wc == position)
++ {
++ *sp = *p;
++ sp++;
++ }
++ p++;
++ i++;
++ }
++ if (wc == position)
++ {
++ *sp = '\0';
++ break;
++ }
++ wc++;
++ }
++ return STATUS_OK;
++}
++
++static bool
++is_not_replication_query(char * query_string, int query_len, char cmdType)
++{
++ PGR_Not_Replicate_Type key;
++ PGR_Not_Replicate_Type * ptr = NULL;
++
++ if (PGR_Not_Replicate_Rec_Num <= 0)
++ return false;
++ if (query_string == NULL)
++ return true;
++ memset(&key,0,sizeof(PGR_Not_Replicate_Type));
++ strncpy(key.db_name ,(char
*)(MyProcPort->database_name),sizeof(key.db_name)-1);
++ switch (cmdType)
++ {
++ case CMD_TYPE_INSERT:
++ get_table_name(key.table_name,query_string,3);
++ break;
++ case CMD_TYPE_UPDATE:
++ get_table_name(key.table_name,query_string,2);
++ break;
++ case CMD_TYPE_DELETE:
++ get_table_name(key.table_name,query_string,3);
++ break;
++ case CMD_TYPE_COPY:
++ get_table_name(key.table_name,query_string,2);
++ break;
++ default:
++ return false;
++ }
++ ptr =
(PGR_Not_Replicate_Type*)bsearch((void*)&key,(void*)PGR_Not_Replicate,PGR_Not_Replicate_Rec_Num,sizeof(PGR_Not_Replicate_Type),
(int (*)(const void*,const void*))Comp_Not_Replicate);
++ if (ptr == NULL)
++ {
++ return false;
++ }
++ return true;
++
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGR_Send_Replicate_Command()
++ * NOTES
++ * create new socket
++ * ARGS
++ * char * query_string: query strings (I)
++ * char cmdSts:
++ * char cmdType:
++ * RETURN
++ * OK: result
++ * NG: NULL
++ *--------------------------------------------------------------------
++ */
++char *
++PGR_Send_Replicate_Command(char * query_string, int query_len, char cmdSts
,char cmdType)
++{
++ int sock = -1;
++ int cnt = 0;
++ ReplicateHeader header;
++ char * serverName = NULL;
++ int portNumber=0;
++ char * result = NULL;
++ ReplicateServerInfo * sp = NULL;
++ ReplicateServerInfo * base = NULL;
++ int socket_type = 0;
++ char argv[ PGR_CMD_ARG_NUM ][256];
++ int argc = 0;
++ int func_no = 0;
++ int check_flag =0;
++
++
++ /*
++ * check query string
++ */
++ if ((query_string == NULL) ||
++ (query_len < 0))
++ {
++ return NULL;
++ }
++ /* check not replication query */
++ if (is_not_replication_query(query_string, query_len, cmdType) ==
true)
++ {
++ PGR_Copy_Data_Need_Replicate = false;
++ return NULL;
++ }
++
++ if ((cmdSts == CMD_STS_TRANSACTION ) ||
++ (cmdSts == CMD_STS_SET_SESSION_AUTHORIZATION ) ||
++ (cmdSts == CMD_STS_TEMP_TABLE ))
++ {
++ socket_type = PGR_TRANSACTION_SOCKET ;
++ }
++ else
++ {
++ socket_type = PGR_QUERY_SOCKET ;
++ }
++ sp = PGR_get_replicate_server_info();
++ if (sp == NULL)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"PGR_get_replicate_server_info get
error");
++ return NULL;
++ }
++ sock = get_replicate_server_socket( sp , socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"get_replicate_server_socket fail");
++ return NULL;
++ }
++ result = malloc(PGR_MESSAGE_BUFSIZE + 4);
++ if (result == NULL)
++ {
++ return NULL;
++ }
++
++ serverName = sp->hostName;
++ portNumber = (int)sp->portNumber;
++ memset(&header,0,sizeof(ReplicateHeader));
++
++ header.cmdSts = cmdSts;
++ header.cmdType = cmdType;
++ header.port = htons(PostPortNumber);
++ header.pid = htons(getpid());
++ header.query_size = htonl(query_len);
++
++ strncpy(header.dbName ,(char
*)(MyProcPort->database_name),sizeof(header.dbName)-1);
++ strncpy(header.userName , (char
*)(MyProcPort->user_name),sizeof(header.userName)-1);
++ header.request_id = htonl(get_next_request_id());
++ header.rlog = 0;
++
++ if (PGRSelfHostName != NULL)
++ {
++ strncpy(header.from_host, PGRSelfHostName,
HOSTNAME_MAX_LENGTH);
++ }
++
++ base = sp;
++ PGR_Sock_To_Replication_Server = sock;
++
++retry_send_prereplicate_packet:
++
++ memset(result,0,PGR_MESSAGE_BUFSIZE + 4);
++ cnt = 0;
++ header.cmdSys=CMD_SYS_PREREPLICATE;
++
++ while (send_replicate_packet(sock,&header,query_string) != STATUS_OK)
++ {
++ cnt++;
++ if (cnt >= MAX_RETRY_TIMES )
++ {
++ sock = get_new_replication_socket( base, sp,
socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all replication servers
may be down");
++ PGR_Stand_Alone->is_stand_alone = true;
++ if (cmdSts == CMD_STS_TRANSACTION )
++ {
++
strcpy(result,PGR_REPLICATION_ABORT_MSG);
++ return result;
++ }
++ free(result);
++ result = NULL;
++ return NULL;
++
++ }
++ if(cmdSts==CMD_STS_TRANSACTION )
++ {
++ if (cmdType!=CMD_TYPE_BEGIN
++ && cmdType!=CMD_TYPE_ROLLBACK)
++ {

++ elog(ERROR,"replicate server down
during replicating transaction. aborted.");
++ free(result);
++ return NULL;
++ }
++ }
++ PGR_Sock_To_Replication_Server = sock;
++ cnt = 0;
++ }
++ }
++
++ memset(result,0,PGR_MESSAGE_BUFSIZE);
++ if (recv_replicate_result(sock,result,0) < 0)
++ {
++
++ sock = get_new_replication_socket( base, sp,
socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all
replication servers may be down");
++ PGR_Stand_Alone->is_stand_alone =
true;
++
++ if (cmdSts == CMD_STS_TRANSACTION )
++ {
++
strcpy(result,PGR_REPLICATION_ABORT_MSG);
++ return result;
++ }
++ if(result!=NULL) {
++ free(result);
++ result = NULL;
++ }
++ return NULL;
++ }
++ PGR_Sock_To_Replication_Server = sock;
++ /* replication server should be down */
++
++ if(cmdSts==CMD_STS_TRANSACTION
++ && (cmdType!=CMD_TYPE_BEGIN &&
cmdType!=CMD_TYPE_ROLLBACK))
++ {
++ elog(ERROR,"replicate server down
during replicating transaction. aborted.");
++ free(result);
++ return NULL;
++ }
++
++ goto retry_send_prereplicate_packet;
++ }
++
++
++ argc = set_command_args(argv,result);
++ func_no=atoi(argv[0]);
++ if(func_no==0) {
++ /* this server is not primary replicate server*/
++ sock=-1;
++ goto retry_send_prereplicate_packet;
++ }
++retry_send_replicate_packet:
++
++ memset(result,0,PGR_MESSAGE_BUFSIZE + 4);
++ cnt = 0;
++ header.cmdSys = CMD_SYS_REPLICATE;
++ while (send_replicate_packet(sock,&header,query_string) != STATUS_OK)
++ {
++ if (cnt > MAX_RETRY_TIMES )
++ {
++ sock = get_new_replication_socket( base, sp,
socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all replication servers may be
down");
++ PGR_Stand_Alone->is_stand_alone = true;
++ if (cmdSts == CMD_STS_TRANSACTION )
++ {
++
strcpy(result,PGR_REPLICATION_ABORT_MSG);
++ return result;
++ }
++ free(result);
++ result = NULL;
++ return NULL;
++
++ }
++ PGR_Sock_To_Replication_Server = sock;
++ header.rlog = CONNECTION_SUSPENDED_TYPE;
++ cnt = 0;
++ }
++ cnt ++;
++ }
++
++ memset(result,0,PGR_MESSAGE_BUFSIZE);
++ if (recv_replicate_result(sock,result,0) < 0)
++ {
++ /* replication server should be down */
++ sock = get_new_replication_socket( base, sp, socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all replication servers may be
down");
++ PGR_Stand_Alone->is_stand_alone = true;
++
++ if (cmdSts == CMD_STS_TRANSACTION )
++ {
++ strcpy(result,PGR_REPLICATION_ABORT_MSG);
++ return result;
++ }
++ if(result!=NULL) {
++ free(result);
++ result = NULL;
++ }
++ return NULL;
++ }
++ PGR_Sock_To_Replication_Server = sock;
++ header.rlog = CONNECTION_SUSPENDED_TYPE;
++
++ goto retry_send_replicate_packet;
++ }
++
++ argc = set_command_args(argv,result);
++ if (argc >= 1)
++ {
++ func_no = atoi(argv[0]);
++ if (func_no == PGR_SET_CURRENT_TIME_FUNC_NO)
++ {
++ PGR_Set_Current_Time(argv[1],argv[2]);
++ set_replication_id(argv[3]);
++ set_response_mode(argv[4]);
++ PGR_Set_Current_Replication_Query_ID(argv[5]);
++ }
++ else if (func_no == PGR_NOTICE_DEADLOCK_DETECTION_FUNC_NO)
++ {
++ memset(result,0,PGR_MESSAGE_BUFSIZE);
++ strcpy(result,PGR_DEADLOCK_DETECTION_MSG);
++ }
++ else if (func_no ==
PGR_SET_CURRENT_REPLICATION_QUERY_ID_NO)
++ {
++ PGR_Set_Current_Replication_Query_ID(argv[1]);
++ }
++ else if (func_no == PGR_QUERY_CONFIRM_ANSWER_FUNC_NO)
++ {
++ check_flag = atoi(argv[1]);
++ if (check_flag == PGR_ALREADY_COMMITTED )
++ {
++ PGR_Set_Current_Time(argv[2],argv[3]);
++ set_replication_id(argv[4]);
++ }
++ else
++ {
++ PGR_Set_Current_Time(argv[1],argv[2]);
++ set_replication_id(argv[3]);
++ /* this query is not replicated */
++ /*
++ free(result);
++ return NULL;
++ */
++ }
++ }
++ }
++ return result;
++}
++
++uint32_t
++PGRget_replication_id(void)
++{
++ return (ReplicationLog_Info.PGR_Replicate_ID);
++}
++
++static int
++set_replication_id(char * id)
++{
++ uint32_t rid=0;
++ uint32_t saved_id;
++ if (id == NULL)
++ {
++ return STATUS_ERROR;
++ }
++
++ rid=(uint32_t)atol(id);
++ if(rid==0)
++ return STATUS_OK;
++
++ needToUpdateReplicateIdOnNextQueryIsDone=true;
++ saved_id=ReplicationLog_Info.PGR_Replicate_ID;
++
++ ReplicationLog_Info.PGR_Replicate_ID =rid;
++
++
++ /*set replicate id in this process */
++
++
++ if (CurrentReplicateServer == NULL)
++ {
++ PGR_get_replicate_server_info();
++ }
++ if (CurrentReplicateServer != NULL)
++ {
++ /* set replicate id in this system */
++ saved_id=CurrentReplicateServer->replicate_id;
++ elog(DEBUG1, "replication id set from %d to
%d", saved_id, rid);
++
++ CurrentReplicateServer->replicate_id =
(uint32_t)(atol(id));
++ }
++
++ return STATUS_OK;
++}
++
++
++static unsigned int
++get_next_request_id(void)
++{
++ if (ReplicationLog_Info.PGR_Request_ID +1 < PGR_MAX_COUNTER)
++ {
++ ReplicationLog_Info.PGR_Request_ID ++;
++ }
++ else
++ {
++ ReplicationLog_Info.PGR_Request_ID = 0;
++ }
++ return ReplicationLog_Info.PGR_Request_ID ;
++
++}
++
++static bool
++is_this_query_replicated(char * id)
++{
++ uint32_t replicate_id = 0;
++ uint32_t saved_id = 0;
++ int32_t diff=0;
++ ReplicateServerInfo * replicate_server_info = NULL;
++
++ if (id == NULL)
++ {
++ return false;
++ }
++ replicate_id = (uint32_t)atol(id);
++ elog(DEBUG1, "check for replication id , input=%u", replicate_id);
++
++ if (CurrentReplicateServer == NULL)
++ {
++ PGR_get_replicate_server_info();
++ }
++
++ if (CurrentReplicateServer != NULL)
++ {
++ replicate_server_info = CurrentReplicateServer;
++ }
++ else if (LastReplicateServer != NULL)
++ {
++ replicate_server_info = LastReplicateServer;
++ }
++ if (replicate_server_info != NULL)
++ {
++
++ saved_id=replicate_server_info->replicate_id;
++ saved_id = saved_id < ReplicationLog_Info.PGR_Replicate_ID
++ ? ReplicationLog_Info.PGR_Replicate_ID
++ : saved_id;
++
++ elog(DEBUG1, "check for replication id , now=%u", saved_id);
++ /* check replicate_id < saved_id logically
++ *
++ * see also:
++ * backend/transam/transam.c#TransactionIdPrecedes
++ */
++ diff = (int32) ( replicate_id - saved_id );
++ return (diff < 0);
++ }
++ elog(DEBUG1, "check for replication id check failed. no replication
server");
++ return false;
++}
++
++
++static int
++get_new_replication_socket( ReplicateServerInfo * base, ReplicateServerInfo
* sp, int socket_type)
++{
++ int sock = -1;
++
++ if (( base == NULL) ||
++ ( sp == NULL))
++ {
++ return -1;
++ }
++ close_replicate_server_socket ( sp , socket_type);
++ PGR_Set_Replication_Server_Status(sp, DATA_ERR);
++ sp = search_new_replication_server(base, socket_type);
++ if (sp == NULL)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all replication servers may be down");
++ PGR_Stand_Alone->is_stand_alone = true;
++ return -1;
++ }
++ sock = get_replicate_server_socket( sp , socket_type);
++ return sock;
++}
++
++
++static int
++recv_replicate_result(int sock,char * result,int user_timeout)
++{
++ fd_set rmask;
++ struct timeval timeout;
++ int rtn;
++
++ if (result == NULL)
++ {
++ return -1;
++ }
++
++ /*
++ * Wait for something to happen.
++ */
++ for (;;)
++ {
++ if (user_timeout == 0)
++ timeout.tv_sec = PGR_RECV_TIMEOUT * 3;
++ else
++ timeout.tv_sec = user_timeout;
++
++ timeout.tv_usec = 0;
++
++ FD_ZERO(&rmask);
++ FD_SET(sock,&rmask);
++ rtn = select(sock+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn < 0)
++ {
++ if (errno != EINTR)
++ return -1;
++ }
++
++ else if ((rtn > 0) && (FD_ISSET(sock, &rmask)))
++ {
++ return (recv_message(sock, result,0));
++ }
++ }
++ return -1;
++}
++
++static int
++recv_message(int sock,char * buf,int flag)
++{
++ int cnt = 0;
++ int r = 0;
++ char * read_ptr;
++ int read_size = 0;
++ cnt = 0;
++ read_ptr = buf;
++
++ for (;;)
++ {
++ r = recv(sock,read_ptr + read_size ,PGR_MESSAGE_BUFSIZE -
read_size, flag);
++ if (r < 0) {
++ if (errno == EINTR || errno == EAGAIN) {
++ continue;
++ } else {
++ elog(DEBUG1, "recv_message():recv failed");
++ return -1;
++ }
++ } else if (r == 0) {
++ elog(DEBUG1, "recv_message():unexpected EOF");
++ return -1;
++ } else /*if (r > 0)*/ {
++ read_size += r;
++ if (read_size == PGR_MESSAGE_BUFSIZE)
++ {
++ return read_size;
++ }
++ }
++ }
++ return -1;
++}
++
++static int
++send_replicate_packet(int sock,ReplicateHeader * header, char *
query_string)
++{
++ int s = 0;
++ char * send_ptr = NULL;
++ char * buf = NULL;
++ int send_size = 0;
++ int buf_size = 0;
++ int header_size = 0;
++ int rtn = 0;
++ fd_set wmask;
++ struct timeval timeout;
++ int query_size = 0;
++
++ /* check parameter */
++ if ((sock < 0) || (header == NULL))
++ {
++ return STATUS_ERROR;
++ }
++
++ query_size = ntohl(header->query_size);
++ header_size = sizeof(ReplicateHeader);
++ buf_size = header_size + query_size + 4;
++ buf = malloc(buf_size);
++ if (buf == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(buf,0,buf_size);
++ buf_size -= 4;
++ memcpy(buf,header,header_size);
++ if (query_string != NULL)
++ {
++ memcpy((char *)(buf+header_size),query_string,query_size+1);
++ }
++ send_ptr = buf;
++
++ /*
++ * Wait for something to happen.
++ */
++ rtn = 1;
++ for (;;)
++ {
++ timeout.tv_sec = PGR_SEND_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ FD_ZERO(&wmask);
++ FD_SET(sock,&wmask);
++ rtn = select(sock+1, (fd_set *)NULL, &wmask, (fd_set *)NULL,
&timeout);
++ if (rtn < 0)
++ {
++ if (errno == EINTR)
++ continue;
++ else
++ {
++ elog(DEBUG1,
"send_replicate_packet():select() failed");
++ return STATUS_ERROR;
++ }
++ }
++ else if (rtn && FD_ISSET(sock, &wmask))
++ {
++
++
++ s = send(sock,send_ptr + send_size,buf_size -
send_size ,0);
++ if (s < 0){
++ if (errno == EINTR || errno == EAGAIN)
++ {
++ continue;
++ }
++ elog(DEBUG1, "send_replicate_packet():send
error");
++
++ /* EPIPE || ENCONNREFUSED || ENSOCK ||
EHOSTUNREACH */
++ return STATUS_ERROR;
++ } else if (s == 0) {
++ free(buf);
++ buf = NULL;
++ elog(DEBUG1,
"send_replicate_packet():unexpected EOF");
++ return STATUS_ERROR;
++ } else /*if (s > 0)*/ {
++ send_size += s;
++ if (send_size == buf_size)
++ {
++ free(buf);
++ buf = NULL;
++ return STATUS_OK;
++ }
++ }
++ }
++ }
++ if (buf != NULL)
++ {
++ free(buf);
++ buf = NULL;
++ }
++ return STATUS_ERROR;
++}
++
++bool
++PGR_Is_Replicated_Command(char * query)
++{
++
++ return (PGR_Is_System_Command(query));
++}
++
++int
++Xlog_Check_Replicate(int operation)
++{
++ if ((PGR_Get_Cluster_Status() == STATUS_RECOVERY) &&
++ (Transaction_Mode == 0 ) )
++ {
++ elog(WARNING, "This query is not permitted while recovery db
");
++ }
++ else if ((operation == CMD_UTILITY ) ||
++ (operation == CMD_INSERT ) ||
++ (operation == CMD_UPDATE ) ||
++ (operation == CMD_DELETE ))
++ {
++ return (PGR_Replicate_Function_Call());
++ }
++ return STATUS_OK;
++}
++
++int
++PGR_Replicate_Function_Call(void)
++{
++ char *result = NULL;
++ int status = STATUS_OK;
++
++ if (PGR_Stand_Alone == NULL)
++ {
++ return STATUS_OK;
++ }
++ if (Query_String != NULL)
++ {
++ if (PGR_Is_Stand_Alone() == true)
++ {
++ if (PGR_Stand_Alone->permit == PERMIT_READ_ONLY)
++ {
++ Query_String = NULL;
++ return STATUS_ERROR;
++ }
++ }
++ PGR_Need_Notice = true;
++ PGR_Check_Lock.check_lock_conflict = true;
++ result =
PGR_Send_Replicate_Command(Query_String,strlen(Query_String),
CMD_STS_QUERY,CMD_TYPE_SELECT);
++ if (result != NULL)
++ {
++ PGR_Reload_Start_Time();
++ if
(!strncmp(result,PGR_DEADLOCK_DETECTION_MSG,strlen(PGR_DEADLOCK_DETECTION_MSG)))
++ {
++ status = STATUS_DEADLOCK_DETECT;
++ }
++ free(result);
++ result = NULL;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ Query_String = NULL;
++ }
++ return status;
++}
++
++void
++PGR_delete_shm(void)
++{
++
++ if (ReplicateServerData != NULL)
++ {
++ shmdt(ReplicateServerData);
++ ReplicateServerData = NULL;
++ shmctl(ReplicateServerShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ }
++ if (ClusterDBData != NULL)
++ {
++ shmdt(ClusterDBData);
++ ClusterDBData = NULL;
++ shmctl(ClusterDBShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ }
++
++ if (TransactionSock != -1)
++ {
++ close(TransactionSock);
++ }
++
++ if (RsyncPath != NULL)
++ {
++ free(RsyncPath);
++ RsyncPath = NULL;
++ }
++ if (RsyncOption != NULL)
++ {
++ free(RsyncOption);
++ RsyncOption = NULL;
++ }
++
++ if (ReplicateCurrentTime != NULL)
++ {
++ free(ReplicateCurrentTime);
++ ReplicateCurrentTime = NULL;
++ }
++
++ if (PGRCopyData != NULL)
++ {
++ free (PGRCopyData);
++ PGRCopyData = NULL;
++ }
++
++ if (PGR_Stand_Alone != NULL)
++ {
++ free(PGR_Stand_Alone);
++ PGR_Stand_Alone = NULL;
++ }
++
++ if (PGR_Not_Replicate != NULL)
++ {
++ free(PGR_Not_Replicate);
++ PGR_Not_Replicate = NULL;
++ }
++ if (PGRSelfHostName != NULL)
++ {
++ free(PGRSelfHostName);
++ PGRSelfHostName = NULL;
++ }
++}
++
++ReplicateServerInfo *
++PGR_get_replicate_server_info(void)
++{
++
++ ReplicateServerInfo * sp;
++
++ if (ReplicateServerData == NULL)
++ {
++ return (ReplicateServerInfo *)NULL;
++ }
++ sp = ReplicateServerData;
++ while (sp->useFlag != DATA_END)
++ {
++ if (sp->useFlag == DATA_USE )
++ {
++ if (CurrentReplicateServer != NULL)
++ {
++ LastReplicateServer = CurrentReplicateServer;
++ CurrentReplicateServer->replicate_id =
LastReplicateServer->replicate_id;
++ }
++ CurrentReplicateServer = sp;
++ return sp;
++ }
++ sp++;
++ }
++ sp = ReplicateServerData;
++ while (sp->useFlag != DATA_END)
++ {
++ if (sp->useFlag != DATA_ERR )
++ {
++ if (CurrentReplicateServer != NULL)
++ {
++ LastReplicateServer = CurrentReplicateServer;
++ CurrentReplicateServer->replicate_id =
LastReplicateServer-> replicate_id;
++ }
++ CurrentReplicateServer = sp;
++ PGR_Set_Replication_Server_Status(sp, DATA_USE);
++ return sp;
++ }
++ sp++;
++ }
++ PGR_Stand_Alone->is_stand_alone = true;
++ if (CurrentReplicateServer != NULL)
++ {
++ LastReplicateServer = CurrentReplicateServer;
++ CurrentReplicateServer->replicate_id = LastReplicateServer->
replicate_id;
++ }
++ CurrentReplicateServer = NULL;
++ return (ReplicateServerInfo *)NULL;
++}
++
++int
++PGR_Send_Copy(CopyData * copy,int end )
++{
++ char cmdSts,cmdType;
++ char * p = NULL;
++ char *result = NULL;
++ char term[8];
++
++ if (copy == NULL)
++ {
++ return STATUS_ERROR;
++ }
++
++ cmdSts = CMD_STS_COPY;
++
++ if (Transaction_Mode)
++ {
++ cmdSts = CMD_STS_TRANSACTION ;
++ }
++ if (Session_Authorization_Mode)
++ {
++ cmdSts = CMD_STS_SET_SESSION_AUTHORIZATION ;
++ }
++ cmdType = CMD_TYPE_COPY_DATA;
++
++ copy->copy_data[copy->cnt] = '\0';
++ if (end)
++ {
++ memset(term,0,sizeof(term));
++ term[0]='\\';
++ term[1]='.';
++ term[2]='\n';
++ cmdType = CMD_TYPE_COPY_DATA_END;
++ p = NULL;
++ if (copy->cnt > 0)
++ {
++ copy->copy_data[copy->cnt] = '\0';
++ p = strstr(copy->copy_data,term);
++ if (p == NULL)
++ {
++ p = &(copy->copy_data[copy->cnt-1]);
++ copy->cnt--;
++ }
++ else
++ {
++ p = NULL;
++ }
++ }
++ if (p != NULL)
++ {
++ strncpy(p,term,sizeof(term));
++ copy->cnt += 4;
++ }
++ }
++
++ result = PGR_Send_Replicate_Command(copy->copy_data, copy->cnt,
cmdSts, cmdType);
++
++ if (result != NULL)
++ {
++ PGR_Reload_Start_Time();
++ free(result);
++ result = NULL;
++ return STATUS_OK;
++ }
++ else
++ {
++ return STATUS_ERROR;
++ }
++}
++
++CopyData *
++PGR_Set_Copy_Data(CopyData * copy, char *str, int len,int end)
++{
++ CopyData save;
++ int save_len = 0;
++ int read_index = 0;
++ int send_size = 0;
++ int buf_size = 0;
++ int rest_len = 0;
++ int rest_buf_size = 0;
++ int status = STATUS_OK;
++ char * ep = NULL;
++ char term[4];
++
++ #define BUFF_OFFSET (8)
++
++ if ((PGR_Copy_Data_Need_Replicate == false) ||
++ (copy == NULL))
++ {
++ return (CopyData *)NULL;
++ }
++ memset(term,0,sizeof(term));
++ term[0]='\n';
++ term[1]='\\';
++ term[2]='.';
++ buf_size = COPYBUFSIZ - BUFF_OFFSET;
++ read_index = 0;
++ rest_len = len;
++ rest_buf_size = buf_size - copy->cnt;
++ while ((rest_len > 0) && (rest_buf_size > 0))
++ {
++ if (rest_buf_size < rest_len)
++ {
++ send_size = rest_buf_size;
++ rest_len -= send_size;
++ }
++ else
++ {
++ send_size = rest_len;
++ rest_len = 0;
++ }
++ memcpy(&(copy->copy_data[copy->cnt]) ,str + read_index
,send_size);
++ copy->cnt += send_size;
++ read_index += send_size;
++ rest_buf_size = buf_size - copy->cnt;
++ if (strstr(copy->copy_data,term) != NULL)
++ {
++ break;
++ }
++ if (rest_buf_size <= 0)
++ {
++ ep = strrchr(copy->copy_data,'\n');
++ if (ep != NULL)
++ {
++ *ep = '\0';
++ save_len = copy->cnt -
strlen(copy->copy_data) -1;
++ copy->cnt -= save_len ;
++ memset(&save,0,sizeof(CopyData));
++ memcpy(save.copy_data,(ep+1),save_len+1);
++ save.cnt = save_len;
++ *ep = '\n';
++ *(ep+1) = '\0';
++ status = PGR_Send_Copy(copy,0);
++ memset(copy,0,sizeof(CopyData));
++ if (save_len > 0)
++ {
++ memcpy(copy,&save,sizeof(CopyData));
++ }
++ rest_buf_size = buf_size - copy->cnt;
++
++ }
++ else
++ {
++ /* one record is bigger than COPYBUFSIZ */
++ /* buffer would be over flow*/
++ status = PGR_Send_Copy(copy,0);
++ memset(copy,0,sizeof(CopyData));
++ rest_buf_size = buf_size - copy->cnt;
++ }
++ }
++ }
++ if (end)
++ {
++ status = PGR_Send_Copy(copy,end);
++ memset(copy,0,sizeof(CopyData));
++ }
++ if (status != STATUS_OK)
++ {
++ return (CopyData *)NULL;
++ }
++ return copy;
++}
++
++int
++PGR_replication(char * query_string, CommandDest dest, Node *parsetree,
const char * commandTag)
++{
++ char *result = NULL;
++ char cmdSts = CMD_STS_OTHER;
++ char cmdType = CMD_TYPE_OTHER;
++ int query_len = 0;
++
++ if ((query_string == NULL) ||
++ (commandTag == NULL))
++ {
++ return STATUS_ERROR;
++ }
++
++ Query_String = NULL;
++ query_len = strlen(query_string);
++
++ /* save query data for retry */
++ PGR_Retry_Query.query_string = query_string;
++ PGR_Retry_Query.query_len = query_len;
++ PGR_Retry_Query.cmdSts = cmdSts;
++ PGR_Retry_Query.cmdType = cmdType;
++ PGR_Retry_Query.useFlag = DATA_USE;
++ /* set cmdType */
++ if (!strcmp(commandTag,"BEGIN")) cmdType = CMD_TYPE_BEGIN ;
++ else if (!strcmp(commandTag,"COMMIT")) cmdType = CMD_TYPE_COMMIT ;
++ else if (!strcmp(commandTag,"SELECT")) cmdType = CMD_TYPE_SELECT ;
++ else if (!strcmp(commandTag,"INSERT")) cmdType = CMD_TYPE_INSERT ;
++ else if (!strcmp(commandTag,"UPDATE")) cmdType = CMD_TYPE_UPDATE ;
++ else if (!strcmp(commandTag,"DELETE")) cmdType = CMD_TYPE_DELETE ;
++ else if (!strcmp(commandTag,"VACUUM")) cmdType = CMD_TYPE_VACUUM ;
++ else if (!strcmp(commandTag,"ANALYZE")) cmdType = CMD_TYPE_ANALYZE ;
++ else if (!strcmp(commandTag,"REINDEX")) cmdType = CMD_TYPE_REINDEX ;
++ else if (!strcmp(commandTag,"ROLLBACK")) cmdType = CMD_TYPE_ROLLBACK ;
++ else if (!strcmp(commandTag,"RESET")) cmdType = CMD_TYPE_RESET ;
++ else if (!strcmp(commandTag,"START TRANSACTION")) cmdType =
CMD_TYPE_BEGIN ;
++
++ /* only "replication_server" statement-name is replicated for SHOW. */
++ /* see CreateCommandTag() @ backend/tcop/postgres.c */
++ /*
++ else if (!strcmp(commandTag,"SHOW"))
++ {
++ }
++ */
++
++ else if (!strcmp(commandTag,"COPY"))
++ {
++ cmdType = CMD_TYPE_COPY ;
++ if (is_copy_from(query_string))
++ {
++ PGR_Copy_Data_Need_Replicate = true;
++ }
++ else
++ {
++ PGR_Copy_Data_Need_Replicate = false;
++ return STATUS_NOT_REPLICATE;
++ }
++ }
++ else if (!strcmp(commandTag,"SET"))
++ {
++ /* set pgr_force_loadbalance to on */
++ if (strstr(query_string,SYS_QUERY_1) != NULL)
++ {
++ return STATUS_NOT_REPLICATE;
++ }
++ cmdType = CMD_TYPE_SET;
++ }
++ else if (!strcmp(commandTag,"CREATE TABLE"))
++ {
++ if (is_create_temp_table(query_string))
++ {
++ Create_Temp_Table_Mode = true;
++ }
++ }
++ if (Create_Temp_Table_Mode)
++ {
++ cmdSts = CMD_STS_TEMP_TABLE ;
++ }
++ if (Transaction_Mode > 0)
++ {
++ cmdSts = CMD_STS_TRANSACTION ;
++ }
++ else if ((cmdType == CMD_TYPE_COMMIT ) ||
++ (cmdType == CMD_TYPE_ROLLBACK ))
++ {
++ cmdSts = CMD_STS_TRANSACTION ;
++ if (ReplicateCurrentTime != NULL)
++ {
++ ReplicateCurrentTime->useFlag = DATA_INIT;
++ ReplicateCurrentTime->use_seed = 0;
++ }
++ }
++ if (Session_Authorization_Mode)
++ {
++ cmdSts = CMD_STS_SET_SESSION_AUTHORIZATION ;
++ if (cmdType == CMD_TYPE_SESSION_AUTHORIZATION_END)
++ {
++ Session_Authorization_Mode = false;
++ }
++ }
++ if ((cmdSts == CMD_STS_TRANSACTION ) ||
++ (cmdSts == CMD_STS_SET_SESSION_AUTHORIZATION ) ||
++ (cmdSts == CMD_STS_TEMP_TABLE ))
++ {
++ /* check partitional replication table */
++ if (is_not_replication_query(query_string, query_len,
cmdType)== true )
++ {
++ PGR_Copy_Data_Need_Replicate = false;
++ return STATUS_NOT_REPLICATE;
++ }
++ Query_String = NULL;
++ if (( do_not_replication_command(commandTag) == true) &&
++ (strcmp(commandTag,"SELECT")))
++ {
++ return STATUS_NOT_REPLICATE;
++ }
++
++ if (Debug_pretty_print)
++ elog(DEBUG1,"transaction query send :%s",(char
*)query_string);
++ PGR_Retry_Query.cmdSts = cmdSts;
++ PGR_Retry_Query.cmdType = cmdType;
++ result = PGR_Send_Replicate_Command(query_string,query_len,
cmdSts,cmdType);
++ if (result != NULL)
++ {
++ if
(!strncmp(result,PGR_DEADLOCK_DETECTION_MSG,strlen(PGR_DEADLOCK_DETECTION_MSG)))
++ {
++ /*
++ PGR_Send_Message_To_Frontend(result);
++ */
++ free(result);
++ result = NULL;
++ return STATUS_DEADLOCK_DETECT;
++ }
++ else if
(!strncmp(result,PGR_REPLICATION_ABORT_MSG,strlen(PGR_REPLICATION_ABORT_MSG)))
++ {
++ free(result);
++ result = NULL;
++ return STATUS_REPLICATION_ABORT;
++ }
++ free(result);
++ result = NULL;
++ return STATUS_CONTINUE;
++ }
++ else
++ {
++ return STATUS_ERROR;
++ }
++ }
++ else
++ {
++ cmdSts = CMD_STS_QUERY ;
++ if ( do_not_replication_command(commandTag) == false)
++ {
++ Query_String = NULL;
++ /* check partitional replication table */
++ if (is_not_replication_query(query_string, query_len,
cmdType)== true )
++ {
++ PGR_Copy_Data_Need_Replicate = false;
++ return STATUS_NOT_REPLICATE;
++ }
++ result =
PGR_Send_Replicate_Command(query_string,query_len,cmdSts,cmdType);
++ if (result != NULL)
++ {
++ if
(!strncmp(result,PGR_DEADLOCK_DETECTION_MSG,strlen(PGR_DEADLOCK_DETECTION_MSG)))
++ {
++ free(result);
++ result = NULL;
++ return STATUS_DEADLOCK_DETECT;
++ }
++ else if
(!strncmp(result,PGR_REPLICATION_ABORT_MSG,strlen(PGR_REPLICATION_ABORT_MSG)))
++ {
++ free(result);
++ result = NULL;
++ return STATUS_REPLICATION_ABORT;
++ }
++ /*
++ PGR_Send_Message_To_Frontend(result);
++ */
++ free(result);
++ result = NULL;
++ return STATUS_CONTINUE;
++ }
++ else
++ {
++ return STATUS_ERROR;
++ }
++ }
++ else
++ {
++ if (( is_serial_control_query(cmdType,query_string)
== true) ||
++ ( is_select_into_query(cmdType,query_string)
== true))
++ {
++ Query_String = NULL;
++ PGR_Need_Notice = true;
++ PGR_Check_Lock.check_lock_conflict = true;
++ result =
PGR_Send_Replicate_Command(query_string,query_len,cmdSts,cmdType);
++ if (result != NULL)
++ {
++ /*
++ PGR_Send_Message_To_Frontend(result);
++ */
++ if
(!strncmp(result,PGR_DEADLOCK_DETECTION_MSG,strlen(PGR_DEADLOCK_DETECTION_MSG)))
++ {
++ free(result);
++ return STATUS_DEADLOCK_DETECT;
++ }
++ free(result);
++ result = NULL;
++ return STATUS_CONTINUE;
++ }
++ else
++ {
++ return STATUS_ERROR;
++ }
++ }
++ else
++ {
++ Query_String = query_string;
++ /*PGR_Sock_To_Replication_Server = -1;*/
++ }
++ return STATUS_CONTINUE_SELECT;
++ }
++ }
++ return STATUS_CONTINUE;
++}
++
++
++bool
++PGR_Is_System_Command(char * query)
++{
++ char * ptr;
++
++ if (query == NULL)
++ {
++ return false;
++ }
++ ptr = strstr(query,PGR_SYSTEM_COMMAND_FUNC);
++ if (ptr != NULL)
++ {
++ ptr = strchr(ptr,'(');
++ if (ptr == NULL)
++ return false;
++ return true;
++ }
++ return false;
++}
++
++static int
++set_command_args(char argv[ PGR_CMD_ARG_NUM ][256],char *str)
++{
++ int i,j,cnt,len;
++ char * ptr = str;
++
++ if (str == NULL)
++ {
++ return 0;
++ }
++ len = strlen(str);
++ cnt = j = 0;
++ for ( i = 0 ; i < len ; i++,ptr++)
++ {
++ if (cnt >= PGR_CMD_ARG_NUM)
++ break;
++ if (( *ptr == ',') || (*ptr == ')'))
++ {
++ argv[cnt][j] = '\0';
++ cnt ++;
++ j = 0;
++ continue;
++ }
++ argv[cnt][j] = *ptr;
++ j++;
++ }
++ if (cnt < PGR_CMD_ARG_NUM)
++ argv[cnt][j] = '\0';
++ cnt ++;
++
++ return cnt;
++}
++
++static int
++add_replication_server(char * hostname,char * port, char * recovery_port)
++{
++ int cnt;
++ int portNumber;
++ int recoveryPortNumber;
++ ReplicateServerInfo * sp;
++
++ if ((hostname == NULL) ||
++ (port == NULL ) ||
++ (recovery_port == NULL ))
++ {
++ return STATUS_ERROR;
++ }
++ if (ReplicateServerData == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ portNumber = atoi(port);
++ recoveryPortNumber = atoi(recovery_port);
++ cnt = 0;
++ sp = ReplicateServerData;
++ while (sp->useFlag != DATA_END){
++ if((!strncmp(sp->hostName,hostname,sizeof(sp->hostName))) &&
++ (sp->portNumber == portNumber) &&
++ (sp->recoveryPortNumber == recoveryPortNumber))
++ {
++ if (sp->useFlag != DATA_USE)
++ {
++ PGR_Set_Replication_Server_Status(sp,
DATA_INIT);
++ }
++ return STATUS_OK;
++ }
++ sp ++;
++ cnt ++;
++ }
++ if (cnt < MAX_SERVER_NUM)
++ {
++ strncpy(sp->hostName,hostname,sizeof(sp->hostName));
++ sp->portNumber = portNumber;
++ sp->recoveryPortNumber = recoveryPortNumber;
++ PGR_Set_Replication_Server_Status(sp, DATA_INIT);
++ memset((sp+1),0,sizeof(ReplicateServerInfo));
++ (sp + 1)->useFlag = DATA_END;
++ }
++ else
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++change_replication_server(char * hostname,char * port, char * recovery_port)
++{
++ int cnt;
++ int portNumber;
++ int recoveryPortNumber;
++ ReplicateServerInfo * sp;
++
++ if ((hostname == NULL) ||
++ (port == NULL ) ||
++ (recovery_port == NULL ))
++ {
++ return STATUS_ERROR;
++ }
++ if (ReplicateServerData == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ portNumber = atoi(port);
++ recoveryPortNumber = atoi(recovery_port);
++ cnt = 0;
++ sp = ReplicateServerData;
++ while (sp->useFlag != DATA_END){
++ if((!strcmp(sp->hostName,hostname)) &&
++ (sp->portNumber == portNumber) &&
++ (sp->recoveryPortNumber == recoveryPortNumber))
++ {
++ PGR_Set_Replication_Server_Status(sp, DATA_USE);
++ }
++ else
++ {
++ if (sp->useFlag == DATA_USE)
++ {
++ PGR_Set_Replication_Server_Status(sp,
DATA_INIT);
++ }
++ }
++ sp ++;
++ cnt ++;
++ }
++ return STATUS_OK;
++}
++
++int
++PGR_Set_Current_Time(char * sec, char * usec)
++{
++ int rtn = 0;
++ struct timeval local_tp;
++ struct timezone local_tpz;
++ struct timeval tv;
++
++ if ((sec == NULL) ||
++ (usec == NULL))
++ {
++ return STATUS_ERROR;
++ }
++ rtn = gettimeofday(&local_tp, &local_tpz);
++ tv.tv_sec = atol(sec);
++ tv.tv_usec = atol(usec);
++ ReplicateCurrentTime->offset_sec = local_tp.tv_sec - tv.tv_sec;
++ ReplicateCurrentTime->offset_usec = local_tp.tv_usec - tv.tv_usec;
++ ReplicateCurrentTime->tp.tv_sec = tv.tv_sec;
++ ReplicateCurrentTime->tp.tv_usec = tv.tv_usec;
++ ReplicateCurrentTime->useFlag = DATA_USE;
++ ReplicateCurrentTime->use_seed = 0;
++
++ return STATUS_OK;
++}
++
++static void
++PGR_Set_Current_Replication_Query_ID(char *id) {
++ MyProc->replicationId=atol(id);
++ return;
++}
++
++static void
++set_response_mode(char * mode)
++{
++ int response_mode = 0;
++
++ if (mode == NULL)
++ return;
++ response_mode = atoi(mode);
++ if (response_mode < 0)
++ return;
++ if (CurrentReplicateServer == NULL)
++ {
++ PGR_get_replicate_server_info();
++ if (CurrentReplicateServer == NULL)
++ {
++ return;
++ }
++ }
++ if (CurrentReplicateServer->response_mode != response_mode)
++ {
++ CurrentReplicateServer->response_mode = response_mode;
++ }
++}
++
++int
++PGR_Call_System_Command(char * command)
++{
++ char * ptr;
++ char * args;
++ char argv[ PGR_CMD_ARG_NUM ][256];
++ int argc = 0;
++ int func_no;
++ char * hostName = NULL;
++
++ if ((command == NULL) || (ReplicateCurrentTime == NULL))
++ {
++ return STATUS_ERROR;
++ }
++ ptr = strstr(command,PGR_SYSTEM_COMMAND_FUNC);
++ if (ptr == NULL)
++ return STATUS_ERROR;
++ ptr = strchr(ptr,'(');
++ if (ptr == NULL)
++ return STATUS_ERROR;
++ args = ptr+1;
++ ptr = strchr(ptr,')');
++ if (ptr == NULL)
++ return STATUS_ERROR;
++ *ptr = '\0';
++ argc = set_command_args(argv,args);
++ if (argc < 1)
++ return STATUS_ERROR;
++ func_no = atoi(argv[0]);
++ switch (func_no)
++ {
++ /* set current system time */
++ case PGR_SET_CURRENT_TIME_FUNC_NO:
++ if (atol(argv[1]) == 0)
++ {
++ CreateCheckPoint(false,true);
++ }
++ else
++ {
++ /*
++ if ((atoi(argv[3]) > 0) &&
++ (is_this_query_replicated(argv[3]) == true))
++ {
++ return STATUS_SKIP_QUERY;
++ }
++ */
++ PGR_Set_Current_Time(argv[1],argv[2]);
++ set_replication_id(argv[3]);
++ set_response_mode(argv[4]);
++ PGR_Set_Current_Replication_Query_ID(argv[5]);
++
++ }
++ break;
++ /* add new replication server data */
++ case PGR_STARTUP_REPLICATION_SERVER_FUNC_NO:
++ hostName = get_hostName(argv[1]);
++ add_replication_server(hostName,argv[2],argv[3]);
++ break;
++ /* change new replication server */
++ case PGR_CHANGE_REPLICATION_SERVER_FUNC_NO:
++ hostName = get_hostName(argv[1]);
++ change_replication_server(hostName,argv[2],argv[3]);
++ break;
++ case PGR_SET_CURRENT_REPLICATION_QUERY_ID_NO:
++ PGR_Set_Current_Replication_Query_ID(argv[1]);
++ break;
++ /* check replication id */
++ case PGR_QUERY_CONFIRM_ANSWER_FUNC_NO:
++ if ((atoi(argv[3]) > 0) &&
++ (is_this_query_replicated(argv[3]) == true))
++ {
++ /* skip this query */
++ return STATUS_SKIP_QUERY;
++ }
++ else
++ {
++ PGR_Set_Current_Time(argv[1],argv[2]);
++ set_replication_id(argv[3]);
++ }
++ break;
++ /* get current oid */
++ case PGR_GET_OID_FUNC_NO:
++ return_current_oid();
++ break;
++ /* set current oid */
++ case PGR_SET_OID_FUNC_NO:
++ sync_oid(argv[1]);
++ break;
++ /* set noticed session abort */
++ case PGR_NOTICE_ABORT_FUNC_NO:
++ PGR_Noticed_Abort = 1;
++ break;
++ }
++ return STATUS_OK;
++}
++
++int
++PGR_GetTimeOfDay(struct timeval *tp, struct timezone *tpz)
++{
++
++ int rtn;
++
++ rtn = gettimeofday(tp, tpz);
++ if (ReplicateCurrentTime == NULL)
++ {
++ return rtn;
++ }
++ if (ReplicateCurrentTime->useFlag == DATA_USE)
++ {
++ if (ReplicateCurrentTime->use_seed != 0)
++ {
++ tp->tv_sec -= ReplicateCurrentTime->offset_sec;
++ if (tp->tv_usec < ReplicateCurrentTime->offset_usec)
++ {
++ tp->tv_usec += (1000000 -
ReplicateCurrentTime->offset_usec);
++ tp->tv_sec -= 1;
++ }
++ else
++ {
++ tp->tv_usec -=
ReplicateCurrentTime->offset_usec;
++ }
++ }
++ else
++ {
++ tp->tv_sec = ReplicateCurrentTime->tp.tv_sec;
++ tp->tv_usec = ReplicateCurrentTime->tp.tv_usec;
++ }
++ rtn = 0;
++ }
++ return rtn;
++}
++
++long
++PGR_Random(void)
++{
++ double rtn;
++ if (ReplicateCurrentTime != NULL)
++ {
++ if ( ReplicateCurrentTime->use_seed == 0)
++ {
++ srand( ReplicateCurrentTime->tp.tv_usec );
++ }
++ }
++ rtn = random();
++ return rtn;
++}
++
++char *
++PGR_scan_terminate( char * str)
++{
++ char * p;
++ int sflag = 0;
++ int dflag = 0;
++ int lflag = 0;
++ int i = 0;
++ char tag[256];
++
++ if (str == NULL)
++ return NULL;
++ p = str;
++ memset(tag,0,sizeof(tag));
++ while ( *p != '\0' )
++ {
++ if ((!strncmp(p,"--",2)) ||
++ (!strncmp(p,"//",2)))
++ {
++ while (( *p != '\n') && (*p != '\0'))
++ {
++ p++;
++ }
++ continue;
++ }
++
++ switch (*p)
++ {
++ case '\'':
++ sflag ^= 1;
++ break;
++ case '\"':
++ dflag ^= 1;
++ break;
++ case '$':
++ i = 0;
++ p++;
++ while (( *p != '\n') && (*p != '\0'))
++ {
++ if (isalnum(*p) == 0)
++ {
++ if (*p == '$')
++ {
++ lflag ^= 1;
++ }
++ break;
++ }
++ else
++ {
++ if (i >= sizeof(tag))
++ break;
++ if (lflag == 0)
++ {
++ tag[i] = *p;
++ }
++ else
++ {
++ if (tag[i] != *p)
++ {
++ break;
++ }
++ }
++ i++;
++ }
++ p++;
++ }
++ break;
++ case '\\':
++ p +=2;
++ continue;
++ break;
++ case ';':
++ if ((!sflag) && (!dflag) && (!lflag))
++ return p;
++ break;
++ }
++ p++;
++ }
++ return NULL;
++}
++
++static bool
++is_copy_from(char * query)
++{
++ char * p;
++ int i;
++ char buf[12];
++ int c_flag = 0;
++ if (query == NULL)
++ return false;
++ p = query;
++ for ( i = 0 ; i <= 1 ; i ++)
++ {
++ /* get 'copy table_name' string */
++ while(isspace(*p))
++ p++;
++ while ((*p != '\0') && (*p != '(') && (!isspace(*p)))
++ p++;
++ }
++ while(isspace(*p))
++ p++;
++ /* skip table column */
++ if (*p == '(')
++ {
++ c_flag = 1;
++ p++;
++ while (*p != '\0')
++ {
++ if (*p == '(')
++ c_flag ++;
++ if (*p == ')')
++ c_flag --;
++ if (c_flag == 0)
++ {
++ p++;
++ break;
++ }
++ p++;
++ }
++ while(isspace(*p))
++ p++;
++ }
++ /* get 'from' or 'to' */
++ i = 0;
++ memset(buf,0,sizeof(buf));
++ while ((*p != '\0') && (!isspace(*p)) && ( i < sizeof(buf)-1))
++ {
++ buf[i] = (char)toupper(*p);
++ p++;
++ i++;
++ }
++ if (!strcmp(buf,"FROM"))
++ {
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++static bool
++is_create_temp_table(char * query)
++{
++ int len,wc;
++ char buf[MAX_WORDS][MAX_WORD_LETTERS];
++
++ if (query == NULL)
++ return false;
++ len = strlen(query);
++ wc = get_words(buf,query,len,1);
++ if (wc < 4)
++ return false;
++ if ((!strncmp(buf[0],"CREATE", strlen("CREATE"))) &&
++ (!strncmp(buf[1],"TEMP",strlen("TEMP"))) &&
++ (!strncmp(buf[2],"TABLE",strlen("TABLE"))))
++ {
++ return true;
++ }
++ return false;
++}
++
++static int
++get_words( char words[MAX_WORDS][MAX_WORD_LETTERS] ,char * string,int
length,int upper)
++{
++ int i,wc,lc;
++ char * p = NULL;
++ char * buf = NULL;
++
++ if (string == NULL)
++ return STATUS_ERROR;
++ buf = malloc(length);
++ if (buf == NULL)
++ return STATUS_ERROR;
++
++ memset(buf,0,length);
++ p = string;
++ wc = 0;
++ for (i = 0 ; i < length ; i ++)
++ {
++ if ((*p == '\0') || (wc >= MAX_WORDS))
++ break;
++ while (isspace(*p))
++ {
++ p++;
++ i++;
++ }
++ lc = 0;
++ while ((*p != '\0') && (! isspace(*p)))
++ {
++ if (upper)
++ *(buf+lc) = (char)toupper(*p);
++ else
++ *(buf+lc) = *p;
++
++ p++;
++ i++;
++ lc++;
++ }
++ memset(words[wc],0,MAX_WORD_LETTERS);
++ memcpy(words[wc],buf,lc);
++ memset(buf,0,length);
++ wc++;
++ }
++ free(buf);
++ buf = NULL;
++ return wc;
++}
++
++static int
++Comp_Not_Replicate(PGR_Not_Replicate_Type * nrp1,PGR_Not_Replicate_Type*
nrp2)
++{
++ int rtn;
++
++ if ((nrp1 == NULL) ||
++ (nrp2 == NULL))
++ {
++ return 0;
++ }
++ rtn = strcasecmp(nrp1->table_name,nrp2->table_name);
++ if (rtn == 0)
++ {
++ rtn = strcasecmp(nrp1->db_name,nrp2->db_name);
++ }
++ return rtn;
++}
++
++bool
++PGR_Is_Stand_Alone(void)
++{
++ ReplicateServerInfo * sp = NULL;
++
++ if (PGR_Stand_Alone == NULL)
++ return true;
++ if (PGR_Stand_Alone->is_stand_alone == true)
++ {
++ sp = PGR_get_replicate_server_info();
++ if (sp == NULL)
++ {
++ return true;
++ }
++ }
++ return false;
++}
++
++void
++PGR_Send_Message_To_Frontend(const char * msg)
++{
++ StringInfoData msgbuf;
++
++ pq_beginmessage(&msgbuf, 'N');
++
++ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
++ {
++ /* New style with separate fields */
++ char tbuf[12];
++ int ssval;
++ int i;
++
++ pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
++ pq_sendstring(&msgbuf, "NOTICE" );
++
++ /* unpack MAKE_SQLSTATE code */
++ ssval = ERRCODE_WARNING ;
++ for (i = 0; i < 5; i++)
++ {
++ tbuf[i] = PGUNSIXBIT(ssval);
++ ssval >>= 6;
++ }
++ tbuf[i] = '\0';
++
++ pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE);
++ pq_sendstring(&msgbuf, tbuf);
++
++ /* M field is required per protocol, so always send something
*/
++ pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
++ if (msg)
++ pq_sendstring(&msgbuf, msg);
++ else
++ pq_sendstring(&msgbuf, _("missing error text"));
++
++ pq_sendbyte(&msgbuf, '\0'); /* terminator */
++ }
++ else
++ {
++ /* Old style --- gin up a backwards-compatible message */
++ StringInfoData buf;
++
++ initStringInfo(&buf);
++
++ appendStringInfo(&buf, "%s: ", "NOTICE");
++
++ if (msg)
++ appendStringInfoString(&buf, msg);
++ else
++ appendStringInfoString(&buf, _("missing error text"));
++
++ appendStringInfoChar(&buf, '\n');
++
++ pq_sendstring(&msgbuf, buf.data);
++
++ pfree(buf.data);
++ }
++
++ pq_endmessage(&msgbuf);
++
++ /*
++ * This flush is normally not necessary, since postgres.c will flush
out
++ * waiting data when control returns to the main loop. But it seems
best
++ * to leave it here, so that the client has some clue what happened
if the
++ * backend dies before getting back to the main loop ... error/notice
++ * messages should not be a performance-critical path anyway, so an
extra
++ * flush won't hurt much ...
++ */
++ pq_flush();
++}
++
++static bool
++is_serial_control_query(char cmdType,char * query)
++{
++ char * buf = NULL;
++ int len = 0;
++ int i = 0;
++ char * p = NULL;
++
++ if ((cmdType != CMD_TYPE_SELECT ) ||
++ ( query == NULL))
++ {
++ return false;
++ }
++
++ p = query;
++ len = strlen(query) +1;
++ buf = malloc(len);
++ if (buf == NULL)
++ return false;
++
++ memset(buf,0,len);
++ for ( i = 0 ; i < len ; i ++)
++ {
++ *(buf+i) = toupper(*(query+i));
++ }
++ if ((strstr(buf,"NEXTVAL") != NULL) ||
++ (strstr(buf,"SETVAL") != NULL))
++ {
++ free(buf);
++ buf = NULL;
++ return true;
++ }
++ free(buf);
++ buf = NULL;
++ return false;
++}
++
++static bool
++is_select_into_query(char cmdType,char * query)
++{
++ char * buf = NULL;
++ int len = 0;
++ int i = 0;
++ char * p = NULL;
++
++ if ((cmdType != CMD_TYPE_SELECT ) ||
++ ( query == NULL))
++ {
++ return false;
++ }
++
++ p = query;
++ len = strlen(query) +1;
++ buf = malloc(len);
++ if (buf == NULL)
++ return false;
++
++ memset(buf,0,len);
++ for ( i = 0 ; i < len ; i ++)
++ {
++ *(buf+i) = toupper(*(query+i));
++ }
++ if (strstr(buf,"INTO") != NULL)
++ {
++ free(buf);
++ buf = NULL;
++ return true;
++ }
++ if (strstr(buf,"CREATE") != NULL)
++ {
++ free(buf);
++ buf = NULL;
++ return true;
++ }
++ free(buf);
++ buf = NULL;
++ return false;
++}
++
++static int
++send_response_to_replication_server(const char * notice)
++{
++ ReplicateHeader header;
++ int status;
++ if (PGR_Lock_Noticed)
++ {
++ return STATUS_OK;
++ }
++ if ((notice == NULL) ||
++ (PGR_Sock_To_Replication_Server < 0))
++ {
++ return STATUS_ERROR;
++ }
++
++ memset(&header,0,sizeof(ReplicateHeader));
++ header.cmdSys = CMD_SYS_CALL;
++ header.cmdSts = CMD_STS_RESPONSE;
++ if (!strcmp(notice,PGR_QUERY_ABORTED_NOTICE_CMD))
++ {
++ header.cmdType = CMD_TYPE_FRONTEND_CLOSED;
++ }
++ header.query_size = htonl(strlen(notice));
++ status =
send_replicate_packet(PGR_Sock_To_Replication_Server,&header,(char *)notice);
++ return status;
++}
++
++void
++PGR_Notice_Transaction_Query_Done(void)
++{
++ send_response_to_replication_server(PGR_QUERY_DONE_NOTICE_CMD);
++}
++
++void
++PGR_Notice_Transaction_Query_Aborted(void)
++{
++ send_response_to_replication_server(PGR_QUERY_ABORTED_NOTICE_CMD);
++}
++
++int
++PGR_Notice_Conflict(void)
++{
++ const char * msg = NULL ;
++ int rtn = STATUS_OK;
++
++ msg = PGR_LOCK_CONFLICT_NOTICE_CMD ;
++ if (PGR_Check_Lock.deadlock == true)
++ {
++ msg = PGR_DEADLOCK_DETECT_NOTICE_CMD ;
++ }
++ if (PGR_Check_Lock.dest == TO_FRONTEND)
++ {
++ ReadyForQuery(DestRemote);
++ EndCommand(msg,DestRemote);
++#ifdef CONTROL_LOCK_CONFLICT
++ rtn = wait_lock_answer();
++#endif /* CONTROL_LOCK_CONFLICT */
++ }
++ else
++ {
++ send_response_to_replication_server(msg);
++#ifdef CONTROL_LOCK_CONFLICT
++ rtn = PGR_Recv_Trigger (PGR_RECV_TIMEOUT);
++#endif /* CONTROL_LOCK_CONFLICT */
++ }
++ return rtn;
++}
++
++#ifdef CONTROL_LOCK_CONFLICT
++static int
++wait_lock_answer(void)
++{
++ char result[PGR_MESSAGE_BUFSIZE+4];
++ int rtn = 0;
++
++ memset(result,0,sizeof(result));
++ rtn = read_trigger(result, PGR_MESSAGE_BUFSIZE);
++ if (rtn < 0)
++ return STATUS_ERROR;
++ return STATUS_OK;
++}
++
++static int
++read_trigger(char * result, int buf_size)
++{
++ int i = 0;
++ char c;
++ int r = 0;
++
++ if ((result == NULL) || (buf_size <= 0 ))
++ {
++ return EOF;
++ }
++ while ((r = pq_getbytes(&c,1)) == 0)
++ {
++ if (i < buf_size -1)
++ {
++ *(result + i) = c;
++ }
++ else
++ {
++ break;
++ }
++ if (c == '\0')
++ break;
++ i++;
++ }
++
++ return r;
++}
++#endif /* CONTROL_LOCK_CONFLICT */
++
++int
++PGR_Recv_Trigger (int user_timeout)
++{
++ char result[PGR_MESSAGE_BUFSIZE];
++ int rtn = 0;
++ int func_no = 0;
++
++
++ if (PGR_Lock_Noticed)
++ {
++ return STATUS_OK;
++ }
++ if (PGR_Sock_To_Replication_Server < 0)
++ return STATUS_ERROR;
++ memset(result,0,sizeof(result));
++ rtn =
recv_replicate_result(PGR_Sock_To_Replication_Server,result,user_timeout);
++ if (rtn > 0)
++ {
++ func_no = atoi(result);
++ if (func_no <= 0)
++ {
++ func_no = STATUS_OK;
++ }
++ return func_no;
++ }
++ else
++ {
++ if (user_timeout == 0)
++ {
++
PGR_Set_Replication_Server_Status(CurrentReplicateServer, DATA_ERR);
++ }
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++int
++PGR_Set_Transaction_Mode(int mode,const char * commandTag)
++{
++ if (commandTag == NULL)
++ {
++ return mode;
++ }
++ if ((!strcmp(commandTag,"BEGIN")) ||
++ (!strcmp(commandTag,"START TRANSACTION")) )
++ {
++ return (++mode);
++ }
++ if (mode > 0)
++ {
++ if ((!strncmp(commandTag,"COMMIT",strlen("COMMIT"))) ||
++ (!strncmp(commandTag,"ROLLBACK",strlen("ROLLBACK"))))
++ {
++ return (--mode);
++ }
++ }
++ /*
++ if (autocommit == false)
++ {
++ return mode;
++ }
++ */
++ return mode;
++}
++
++
++
++static bool
++do_not_replication_command(const char * commandTag)
++{
++ if (commandTag == NULL)
++ {
++ return true;
++ }
++ if ((!strcmp(commandTag,"SELECT")) ||
++ (!strcmp(commandTag,"CLOSE CURSOR")) ||
++ (!strcmp(commandTag,"MOVE")) ||
++ (!strcmp(commandTag,"FETCH")) ||
++ (!strcmp(commandTag,"EXPLAIN")))
++ {
++ return true;
++ }
++ else
++ {
++ return false;
++ }
++}
++
++void
++PGR_Set_Replication_Server_Status( ReplicateServerInfo * sp, int status)
++{
++ if (sp == NULL)
++ {
++ return;
++ }
++ if (sp->useFlag != status)
++ {
++ sp->useFlag = status;
++ }
++}
++
++int
++PGR_Is_Skip_Replication(char * query)
++{
++ char skip_2[256];
++
++ if ((query == NULL) ||
++ (MyProcPort == NULL))
++ {
++ return -1;
++ }
++ snprintf(skip_2,sizeof(skip_2),SKIP_QUERY_2,MyProcPort->user_name);
++ if ((strncmp(query,SKIP_QUERY_1,strlen(SKIP_QUERY_1)) == 0) ||
++ (strncmp(query,skip_2,strlen(skip_2)) == 0))
++ {
++ return 3;
++ }
++ if ((strncmp(query,SKIP_QUERY_3,strlen(SKIP_QUERY_3)) == 0) ||
++ (strncmp(query,SKIP_QUERY_4,strlen(SKIP_QUERY_4)) == 0))
++ {
++ return 1;
++ }
++ return 0;
++}
++
++bool
++PGR_Did_Commit_Transaction(void)
++{
++ int sock = -1;
++ int cnt = 0;
++ ReplicateHeader header;
++ char * serverName = NULL;
++ int portNumber=0;
++ char * result = NULL;
++ ReplicateServerInfo * sp = NULL;
++ ReplicateServerInfo * base = NULL;
++ int socket_type = 0;
++ char argv[ PGR_CMD_ARG_NUM ][256];
++ int argc = 0;
++ int func_no = 0;
++
++ if (ReplicateCurrentTime->useFlag != DATA_USE)
++ {
++ return false;
++ }
++ sp = PGR_get_replicate_server_info();
++ if (sp == NULL)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"PGR_get_replicate_server_info get
error");
++ return false;
++ }
++ sock = get_replicate_server_socket( sp , PGR_QUERY_SOCKET);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"get_replicate_server_socket fail");
++ return false;
++ }
++ result = malloc(PGR_MESSAGE_BUFSIZE);
++ if (result == NULL)
++ {
++ return false;
++ }
++ memset(result,0,PGR_MESSAGE_BUFSIZE);
++
++ serverName = sp->hostName;
++ portNumber = (int)sp->portNumber;
++ header.cmdSys = CMD_SYS_CALL;
++ header.cmdSts = CMD_STS_TRANSACTION_ABORT;
++ header.cmdType = CMD_TYPE_COMMIT_CONFIRM;
++ header.port = htons(PostPortNumber);
++ header.pid = htons(getpid());
++ header.tv.tv_sec = htonl(ReplicateCurrentTime->tp.tv_sec);
++ header.tv.tv_usec = htonl(ReplicateCurrentTime->tp.tv_usec);
++ header.query_size = htonl(0);
++ strncpy(header.dbName ,(char
*)(MyProcPort->database_name),sizeof(header.dbName)-1);
++ strncpy(header.userName , (char
*)(MyProcPort->user_name),sizeof(header.userName)-1);
++ if (PGRSelfHostName != NULL)
++ {
++ strncpy(header.from_host, PGRSelfHostName,
HOSTNAME_MAX_LENGTH);
++ }
++ header.replicate_id = htonl(ReplicationLog_Info.PGR_Replicate_ID);
++ header.request_id = 0;
++
++ base = sp;
++ PGR_Sock_To_Replication_Server = sock;
++
++retry_send_confirm_packet:
++
++ cnt = 0;
++ while (send_replicate_packet(sock,&header,"") != STATUS_OK)
++ {
++ if (cnt > MAX_RETRY_TIMES )
++ {
++ sock = get_new_replication_socket( base, sp,
socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all replication servers
may be down");
++ PGR_Stand_Alone->is_stand_alone = true;
++ free(result);
++ result = NULL;
++ return false;
++ }
++ PGR_Sock_To_Replication_Server = sock;
++ cnt = 0;
++ }
++ cnt ++;
++ }
++
++ if (recv_replicate_result(sock,result,0) < 0)
++ {
++ /* replication server should be down */
++ sock = get_new_replication_socket( base, sp, socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all replication servers may be
down");
++ PGR_Stand_Alone->is_stand_alone = true;
++ free(result);
++ result = NULL;
++ return false;
++ }
++ PGR_Sock_To_Replication_Server = sock;
++ goto retry_send_confirm_packet;
++ }
++ /* read answer */
++ argc = set_command_args(argv,result);
++ if (argc >= 1)
++ {
++ func_no = atoi(argv[0]);
++ if (func_no == PGR_TRANSACTION_CONFIRM_ANSWER_FUNC_NO)
++ {
++ /* the transaction was commited in other server */
++ if (atoi(argv[1]) == PGR_ALREADY_COMMITTED)
++ {
++ free(result);
++ result = NULL;
++ return true;
++ }
++ }
++ }
++ free(result);
++ result = NULL;
++ return false;
++}
++
++int
++PGRsend_system_command(char cmdSts, char cmdType)
++{
++ ReplicateServerInfo * sp = NULL;
++ int sock = -1;
++ int socket_type = 0;
++ char * result = NULL;
++ char * serverName = NULL;
++ int portNumber=0;
++ ReplicateHeader header;
++ int cnt = 0;
++ ReplicateServerInfo * base = NULL;
++
++ sp = PGR_get_replicate_server_info();
++ if (sp == NULL)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"PGR_get_replicate_server_info get
error");
++ return STATUS_ERROR;
++ }
++ sock = get_replicate_server_socket( sp , PGR_QUERY_SOCKET);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"get_replicate_server_socket fail");
++ return STATUS_ERROR;
++ }
++ result = malloc(PGR_MESSAGE_BUFSIZE);
++ if (result == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(result,0,PGR_MESSAGE_BUFSIZE);
++
++ serverName = sp->hostName;
++ portNumber = (int)sp->portNumber;
++ header.cmdSys = CMD_SYS_CALL;
++ header.cmdSts = cmdSts;
++ header.cmdType = cmdType;
++ header.port = htons(PostPortNumber);
++ header.pid = htons(getpid());
++ header.tv.tv_sec = htonl(ReplicateCurrentTime->tp.tv_sec);
++ header.tv.tv_usec = htonl(ReplicateCurrentTime->tp.tv_usec);
++ header.query_size = htonl(0);
++ strncpy(header.dbName ,(char
*)(MyProcPort->database_name),sizeof(header.dbName)-1);
++ strncpy(header.userName , (char
*)(MyProcPort->user_name),sizeof(header.userName)-1);
++ if (PGRSelfHostName != NULL)
++ {
++ strncpy(header.from_host, PGRSelfHostName,
HOSTNAME_MAX_LENGTH);
++ }
++ header.replicate_id = htonl(ReplicationLog_Info.PGR_Replicate_ID);
++ header.request_id = 0;
++
++ base = sp;
++ PGR_Sock_To_Replication_Server = sock;
++ cnt = 0;
++ while (send_replicate_packet(sock,&header,"") != STATUS_OK)
++ {
++ if (cnt > MAX_RETRY_TIMES )
++ {
++ sock = get_new_replication_socket( base, sp,
socket_type);
++ if (sock < 0)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"all replication servers
may be down");
++ PGR_Stand_Alone->is_stand_alone = true;
++ free(result);
++ result = NULL;
++ return STATUS_ERROR;
++ }
++ PGR_Sock_To_Replication_Server = sock;
++ cnt = 0;
++ }
++ cnt ++;
++ }
++ free(result);
++ result = NULL;
++ return STATUS_OK;
++}
++
++static char *
++get_hostName(char * str)
++{
++ char * top = NULL;
++ char * p = NULL;
++
++ p = str;
++ while ( *p != '\0')
++ {
++ if (*p == '\'')
++ {
++ *p = '\0';
++ p++;
++ if (top == NULL)
++ {
++ top = p;
++ }
++ }
++ p++;
++ }
++ return top;
++}
++
++char *
++PGR_Remove_Comment(char * str)
++{
++ char * p = NULL;
++ p = str;
++ while( *p != '\0')
++ {
++ while(isspace(*p))
++ {
++ p++;
++ }
++ if ((!memcmp(p,"--",2)) ||
++ (!memcmp(p,"//",2)))
++ {
++ while((*p != '\n') && (*p != '\0'))
++ {
++ p++;
++ }
++ continue;
++ }
++ break;
++ }
++ return p;
++}
++
++void
++PGR_Force_Replicate_Query(void)
++{
++ if (PGR_Retry_Query.useFlag == DATA_USE)
++ {
++ PGR_Send_Replicate_Command(PGR_Retry_Query.query_string,
++ PGR_Retry_Query.query_len,
++ PGR_Retry_Query.cmdSts,
++ PGR_Retry_Query.cmdType);
++ }
++}
++
++void
++PGR_Notice_DeadLock(void)
++{
++ ReplicateHeader header;
++
++ memset(&header,0,sizeof(ReplicateHeader));
++ header.cmdSys = CMD_SYS_CALL;
++ header.cmdSts = CMD_STS_NOTICE;
++ header.cmdType = CMD_TYPE_DEADLOCK_DETECT;
++ header.query_size = 0;
++ send_replicate_packet(PGR_Sock_To_Replication_Server,&header,(char
*)NULL);
++}
++
++void
++PGR_Set_Cluster_Status(int status)
++{
++ if (ClusterDBData != NULL)
++ {
++ if (ClusterDBData->status != status)
++ {
++ ClusterDBData->status = status;
++ }
++ }
++}
++
++int
++PGR_Get_Cluster_Status(void)
++{
++ if (ClusterDBData != NULL)
++ {
++ return (ClusterDBData->status);
++ }
++ return 0;
++}
++
++int
++PGR_Check_Replicate_Server_Status(ReplicateServerInfo * sp)
++{
++ ReplicateHeader header;
++ char * result = NULL;
++ int status;
++ int fdP;
++
++ result = malloc(PGR_MESSAGE_BUFSIZE + 4);
++ if (result == NULL)
++ {
++ if (Debug_pretty_print)
++ elog(DEBUG1,"malloc failed in
PGR_Check_Replicate_Server_Status()");
++ return STATUS_ERROR;
++ }
++
++ memset(&header, 0, sizeof(ReplicateHeader));
++ memset(result, 0, PGR_MESSAGE_BUFSIZE + 4);
++
++ header.cmdSys = CMD_SYS_PREREPLICATE;
++ header.cmdSts = CMD_STS_OTHER;
++ header.cmdType = CMD_TYPE_OTHER;
++ header.port = htons(PostPortNumber);
++ header.pid = htons(getpid());
++ header.query_size = 0;
++ strncpy(header.dbName ,(char
*)(MyProcPort->database_name),sizeof(header.dbName)-1);
++ strncpy(header.userName , (char
*)(MyProcPort->user_name),sizeof(header.userName)-1);
++ header.request_id = htonl(get_next_request_id());
++ header.rlog = 0;
++ if (PGRSelfHostName != NULL) {
++ strncpy(header.from_host, PGRSelfHostName,
HOSTNAME_MAX_LENGTH);
++ }
++
++ /* open a new socket for lifecheck */
++ if ((status = PGR_Create_Socket_Connect(&fdP, sp->hostName,
sp->portNumber)) == STATUS_ERROR) {
++ if (Debug_pretty_print) {
++ elog(DEBUG1,"create socket failed in
PGR_Create_Socket_Connect()");
++ }
++
++ /* status = STATUS_OK */
++ } else {
++ if ((status = send_replicate_packet(fdP, &header, (char
*)NULL)) == STATUS_OK) {
++ /* receive result to check for possible deadlock */
++ status = (0 < recv_replicate_result(fdP, result ,0))
++ ? STATUS_OK : STATUS_ERROR;
++ }
++ }
++
++ free(result);
++ PGR_Close_Sock(&fdP);
++
++ return status;
++}
++
++static int
++return_current_oid(void)
++{
++ char msg[PGR_MESSAGE_BUFSIZE];
++
++ LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
++
++ if (ShmemVariableCache->nextOid < ((Oid) FirstBootstrapObjectId))
++ {
++ ShmemVariableCache->nextOid = FirstBootstrapObjectId;
++ ShmemVariableCache->oidCount = 0;
++ }
++
++ if (ShmemVariableCache->oidCount == 0)
++ {
++ XLogPutNextOid(ShmemVariableCache->nextOid +
VAR_OID_PREFETCH);
++ ShmemVariableCache->oidCount = VAR_OID_PREFETCH;
++ }
++ LWLockRelease(OidGenLock);
++
++ memset(msg,0,sizeof(msg));
++ snprintf(msg, sizeof(msg), "%u", ShmemVariableCache->nextOid);
++ if (PGR_Check_Lock.dest == TO_FRONTEND)
++ {
++ /*
++ PGR_Send_Message_To_Frontend(msg);
++ */
++ pq_puttextmessage('C',msg);
++ pq_flush();
++ }
++ else
++ {
++ send_response_to_replication_server(msg);
++ }
++ return STATUS_OK;
++}
++
++static int
++sync_oid(char * oid)
++{
++ uint32_t next_oid = 0;
++ int offset = 0;
++ char msg[PGR_MESSAGE_BUFSIZE];
++
++ LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
++
++ next_oid = strtoul(oid, NULL, 10);
++ if (next_oid <= 0)
++ return STATUS_ERROR;
++ next_oid ++;
++ offset = next_oid - ShmemVariableCache->nextOid ;
++ if (offset <= 0)
++ return STATUS_ERROR;
++
++ if (next_oid < FirstBootstrapObjectId)
++ {
++ ShmemVariableCache->nextOid = FirstBootstrapObjectId;
++ ShmemVariableCache->oidCount = 0;
++ }
++
++ /* If we run out of logged for use oids then we must log more */
++ while (ShmemVariableCache->oidCount - offset <= 0)
++ {
++ offset -= (ShmemVariableCache->oidCount) ;
++ (ShmemVariableCache->nextOid) +=
(ShmemVariableCache->oidCount);
++ XLogPutNextOid(ShmemVariableCache->nextOid +
VAR_OID_PREFETCH);
++ ShmemVariableCache->oidCount = VAR_OID_PREFETCH;
++ }
++
++ (ShmemVariableCache->nextOid) += offset;
++ (ShmemVariableCache->oidCount) -= offset;
++
++ LWLockRelease(OidGenLock);
++
++ memset(msg,0,sizeof(msg));
++ snprintf(msg, sizeof(msg), "%u", ShmemVariableCache->nextOid);
++ if (PGR_Check_Lock.dest == TO_FRONTEND)
++ {
++ /*
++ PGR_Send_Message_To_Frontend(msg);
++ */
++ pq_puttextmessage('C',msg);
++ pq_flush();
++ }
++ else
++ {
++ send_response_to_replication_server(msg);
++ }
++ return STATUS_OK;
++}
++
++int
++PGR_lo_import(char * filename)
++{
++ char * result = NULL;
++ LOArgs *lo_args;
++ int len = 0;
++ int buf_size = 0;
++
++ if ((PGR_Is_Replicated_Query == true) ||
++ (PGR_Retry_Query.cmdSts == CMD_STS_TRANSACTION))
++ {
++ return STATUS_OK;
++ }
++ if ((PGR_Retry_Query.cmdSts != CMD_STS_QUERY) ||
++ (PGR_Retry_Query.cmdType != CMD_TYPE_SELECT))
++ {
++ return STATUS_OK;
++ }
++
++ len = strlen(filename);
++ buf_size = sizeof(LOArgs) + len;
++ lo_args = (LOArgs *)malloc(buf_size + 4);
++ if (lo_args == (LOArgs *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(lo_args, 0, buf_size + 4);
++ lo_args->arg1 = htonl((uint32_t)len);
++ memcpy(lo_args->buf, filename, len);
++
++ result = PGR_Send_Replicate_Command((char *)lo_args,
++ buf_size,
++ CMD_STS_LARGE_OBJECT,
++ CMD_TYPE_LO_IMPORT);
++
++ free(lo_args);
++ if (result != NULL)
++ {
++ free(result);
++ return STATUS_OK;
++ }
++
++ return STATUS_ERROR;
++}
++
++int
++PGR_lo_create(int flags)
++{
++ char * result = NULL;
++ LOArgs lo_args;
++
++ if ((PGR_Is_Replicated_Query == true) ||
++ (PGR_Retry_Query.cmdSts == CMD_STS_TRANSACTION))
++ {
++ return STATUS_OK;
++ }
++ if ((PGR_Retry_Query.cmdSts != CMD_STS_QUERY) ||
++ (PGR_Retry_Query.cmdType != CMD_TYPE_SELECT))
++ {
++ return STATUS_OK;
++ }
++ memset(&lo_args, 0, sizeof(LOArgs));
++ lo_args.arg1 = htonl(flags);
++
++ result = PGR_Send_Replicate_Command((char *)&lo_args,
++ sizeof(LOArgs),
++ CMD_STS_LARGE_OBJECT,
++ CMD_TYPE_LO_CREATE);
++
++ if (result != NULL)
++ {
++ free(result);
++ return STATUS_OK;
++ }
++
++ return STATUS_ERROR;
++}
++
++int
++PGR_lo_open(Oid lobjId,int32 mode)
++{
++ char * result = NULL;
++ LOArgs lo_args;
++
++ if ((PGR_Is_Replicated_Query == true) ||
++ (PGR_Retry_Query.cmdSts == CMD_STS_TRANSACTION))
++ {
++ return STATUS_OK;
++ }
++ if ((PGR_Retry_Query.cmdSts != CMD_STS_QUERY) ||
++ (PGR_Retry_Query.cmdType != CMD_TYPE_SELECT))
++ {
++ return STATUS_OK;
++ }
++ memset(&lo_args, 0, sizeof(LOArgs));
++ lo_args.arg1 = htonl((uint32_t)lobjId);
++ lo_args.arg2 = htonl((uint32_t)mode);
++
++ result = PGR_Send_Replicate_Command((char *)&lo_args,
++ sizeof(LOArgs),
++ CMD_STS_LARGE_OBJECT,
++ CMD_TYPE_LO_OPEN);
++
++ if (result != NULL)
++ {
++ free(result);
++ return STATUS_OK;
++ }
++
++ return STATUS_ERROR;
++}
++
++int
++PGR_lo_close(int32 fd)
++{
++ char * result = NULL;
++ LOArgs lo_args;
++
++ if ((PGR_Is_Replicated_Query == true) ||
++ (PGR_Retry_Query.cmdSts == CMD_STS_TRANSACTION))
++ {
++ return STATUS_OK;
++ }
++ if ((PGR_Retry_Query.cmdSts != CMD_STS_QUERY) ||
++ (PGR_Retry_Query.cmdType != CMD_TYPE_SELECT))
++ {
++ return STATUS_OK;
++ }
++ memset(&lo_args, 0, sizeof(LOArgs));
++ lo_args.arg1 = htonl((uint32_t)fd);
++
++ result = PGR_Send_Replicate_Command((char *)&lo_args,
++ sizeof(LOArgs),
++ CMD_STS_LARGE_OBJECT,
++ CMD_TYPE_LO_CLOSE);
++
++ if (result != NULL)
++ {
++ free(result);
++ return STATUS_OK;
++ }
++
++ return STATUS_ERROR;
++}
++
++int
++PGR_lo_write(int fd, char *buf, int len)
++{
++ char * result = NULL;
++ LOArgs *lo_args = NULL;
++ int buf_size = 0;
++
++ if ((PGR_Is_Replicated_Query == true) ||
++ (PGR_Retry_Query.cmdSts == CMD_STS_TRANSACTION))
++ {
++ return STATUS_OK;
++ }
++ if ((PGR_Retry_Query.cmdSts != CMD_STS_QUERY) ||
++ (PGR_Retry_Query.cmdType != CMD_TYPE_SELECT))
++ {
++ return STATUS_OK;
++ }
++ buf_size = sizeof(LOArgs) + len;
++ lo_args = malloc(buf_size + 4);
++ if (lo_args == (LOArgs *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(lo_args, 0, buf_size + 4);
++ lo_args->arg1 = htonl((uint32_t)fd);
++ lo_args->arg2 = htonl((uint32_t)len);
++ memcpy(lo_args->buf, buf, len);
++ result = PGR_Send_Replicate_Command((char *)lo_args,
++ buf_size,
++ CMD_STS_LARGE_OBJECT,
++ CMD_TYPE_LO_WRITE);
++
++ free(lo_args);
++ if (result != NULL)
++ {
++ free(result);
++ return STATUS_OK;
++ }
++
++ return STATUS_ERROR;
++}
++
++int
++PGR_lo_lseek(int32 fd, int32 offset, int32 whence)
++{
++ char * result = NULL;
++ LOArgs lo_args;
++
++ if ((PGR_Is_Replicated_Query == true) ||
++ (PGR_Retry_Query.cmdSts == CMD_STS_TRANSACTION))
++ {
++ return STATUS_OK;
++ }
++ if ((PGR_Retry_Query.cmdSts != CMD_STS_QUERY) ||
++ (PGR_Retry_Query.cmdType != CMD_TYPE_SELECT))
++ {
++ return STATUS_OK;
++ }
++ memset(&lo_args, 0, sizeof(LOArgs));
++ lo_args.arg1 = htonl((uint32_t)fd);
++ lo_args.arg2 = htonl((uint32_t)offset);
++ lo_args.arg3 = htonl((uint32_t)whence);
++
++ result = PGR_Send_Replicate_Command((char *)&lo_args,
++ sizeof(LOArgs),
++ CMD_STS_LARGE_OBJECT,
++ CMD_TYPE_LO_LSEEK);
++
++ if (result != NULL)
++ {
++ free(result);
++ return STATUS_OK;
++ }
++
++ return STATUS_ERROR;
++}
++
++int
++PGR_lo_unlink(Oid lobjId)
++{
++ char * result = NULL;
++ LOArgs lo_args;
++
++ if ((PGR_Is_Replicated_Query == true) ||
++ (PGR_Retry_Query.cmdSts == CMD_STS_TRANSACTION))
++ {
++ return STATUS_OK;
++ }
++ if ((PGR_Retry_Query.cmdSts != CMD_STS_QUERY) ||
++ (PGR_Retry_Query.cmdType != CMD_TYPE_SELECT))
++ {
++ return STATUS_OK;
++ }
++ memset(&lo_args, 0, sizeof(LOArgs));
++ lo_args.arg1 = htonl((uint32_t)lobjId);
++
++ result = PGR_Send_Replicate_Command((char *)&lo_args,
++ sizeof(LOArgs),
++ CMD_STS_LARGE_OBJECT,
++ CMD_TYPE_LO_UNLINK);
++
++ if (result != NULL)
++ {
++ free(result);
++ return STATUS_OK;
++ }
++
++ return STATUS_ERROR;
++}
++
++Oid
++PGRGetNewObjectId(Oid last_id)
++{
++ Oid newId = 0;
++
++ if (last_id == 0)
++ {
++ newId = (Oid)PGRget_replication_id();
++ }
++ else
++ {
++ newId = last_id + 1;
++ }
++ return newId;
++}
++
++int
++PGR_Send_Input_Message(char cmdType,StringInfo input_message)
++{
++ int len = 0;
++ char * ptr = NULL;
++ char * result = NULL;
++
++ if (input_message == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ if (PGR_Is_Replicated_Query == true)
++ {
++ return STATUS_OK;
++ }
++ len = input_message->len+1;
++ ptr = input_message->data;
++ result = PGR_Send_Replicate_Command(ptr,len, CMD_STS_PREPARE,cmdType);
++ if (result != NULL)
++ {
++ PGR_Reload_Start_Time();
++ free(result);
++ result = NULL;
++ return STATUS_OK;
++ }
++ else
++ {
++ return STATUS_ERROR;
++ }
++}
++
++#endif /* USE_REPLICATION */
+diff -aruN postgresql-8.1.2/src/backend/libpq/replicate_com.c
pgcluster-1.5.0rc7/src/backend/libpq/replicate_com.c
+--- postgresql-8.1.2/src/backend/libpq/replicate_com.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/libpq/replicate_com.c 2006-01-10
17:39:39.000000000 +0100
+@@ -0,0 +1,671 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * replicate_com.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at backend for the replication.
++ * Low level I/O functions that called by in these functions are
++ * contained in 'replicate_com.c'.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++
++/*--------------------------------------
++ * INTERFACE ROUTINES
++ *
++ * setup/teardown:
++ * PGR_Close_Sock
++ * PGR_Free_Conf_Data
++ * I/O call:
++ * PGR_Create_Socket_Connect
++ * PGR_Create_Socket_Bind
++ * PGR_Create_Acception
++ * table handling:
++ * PGR_Get_Conf_Data
++ *-------------------------------------
++ */
++#ifdef USE_REPLICATION
++
++#include "postgres.h"
++
++#include <signal.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <grp.h>
++#include <unistd.h>
++#include <ctype.h>
++#include <time.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++#include <arpa/inet.h>
++#include <sys/file.h>
++#include <netdb.h>
++
++#include "libpq/libpq.h"
++#include "miscadmin.h"
++#include "nodes/print.h"
++#include "utils/guc.h"
++#include "parser/parser.h"
++#include "access/xact.h"
++#include "replicate_com.h"
++
++int PGR_Create_Socket_Connect(int * fdP, char * hostName , unsigned short
portNumber);
++void PGR_Close_Sock(int * sock);
++int PGR_Create_Socket_Bind(int * fdP, char * hostName , unsigned short
portNumber);
++int PGR_Create_Acception(int fd, int * sockP, char * hostName , unsigned
short portNumber);
++int PGR_Free_Conf_Data(void);
++int PGR_Get_Conf_Data(char * dir , char * fname);
++void PGRset_recovery_packet_no(RecoveryPacket * packet, int packet_no);
++unsigned int PGRget_ip_by_name(char * host);
++
++static char * get_string(char * buf);
++static bool is_start_tag(char * ptr);
++static bool is_end_tag(char * ptr);
++static void init_conf_data(ConfDataType *conf);
++static int get_key(char * key, char * str);
++static int get_conf_key_value(char * key, char * value , char * str);
++static int add_conf_data(char *table,int rec_no, char *key,char * value);
++static int get_table_data(FILE * fp,char * table, int rec_no);
++static int get_single_data(char * str);
++static int get_conf_file(char * fname);
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGR_Create_Socket_Connect()
++ * NOTES
++ * create new socket
++ * ARGS
++ * int * fdP:
++ * char * hostName:
++ * unsigned short portNumber:
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGR_Create_Socket_Connect(int * fdP, char * hostName , unsigned short
portNumber)
++{
++
++ int sock;
++ size_t len = 0;
++ struct sockaddr_in addr;
++ int one = 1;
++
++ if ((*hostName == '\0') || (portNumber < 1000))
++ {
++ * fdP = -1;
++ return STATUS_ERROR;
++ }
++ if ((*fdP = socket(AF_INET, SOCK_STREAM, 0)) < 0)
++ {
++ * fdP = -1;
++ return STATUS_ERROR;
++ }
++ if ((setsockopt(*fdP, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
sizeof(one))) == -1)
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++ if (setsockopt(*fdP, IPPROTO_TCP, TCP_NODELAY, (char *) &one,
sizeof(one)) < 0)
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++
++ addr.sin_family = AF_INET;
++ if (hostName[0] == '\0')
++ addr.sin_addr.s_addr = htonl(INADDR_ANY);
++ else
++ {
++ struct hostent *hp;
++
++ hp = gethostbyname(hostName);
++ if ((hp == NULL) || (hp->h_addrtype != AF_INET))
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++ memmove((char *) &(addr.sin_addr), (char *) hp->h_addr,
hp->h_length);
++ }
++
++ addr.sin_port = htons(portNumber);
++ len = sizeof(struct sockaddr_in);
++
++ if ((sock = connect(*fdP,(struct sockaddr*)&addr,len)) < 0)
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++
++ return STATUS_OK;
++}
++
++int
++PGR_Create_Socket_Bind(int * fdP, char * hostName , unsigned short
portNumber)
++{
++
++ int err;
++ size_t len = 0;
++ struct sockaddr_in addr;
++ int one = 1;
++
++ if ((*fdP = socket(AF_INET, SOCK_STREAM, 0)) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ if ((setsockopt(*fdP, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
sizeof(one))) == -1)
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++ addr.sin_family = AF_INET;
++ if (hostName[0] == '\0')
++ addr.sin_addr.s_addr = htonl(INADDR_ANY);
++ else
++ {
++ struct hostent *hp;
++
++ hp = gethostbyname(hostName);
++ if ((hp == NULL) || (hp->h_addrtype != AF_INET))
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++ memmove((char *) &(addr.sin_addr), (char *) hp->h_addr,
hp->h_length);
++ }
++
++ addr.sin_port = htons(portNumber);
++ len = sizeof(struct sockaddr_in);
++
++ err = bind(*fdP, (struct sockaddr *) & addr, len);
++ if (err < 0)
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++ err = listen(*fdP, MAX_SOCKET_QUEUE );
++ if (err < 0)
++ {
++ PGR_Close_Sock(fdP);
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++int
++PGR_Create_Acception(int fd, int * sockP, char * hostName , unsigned short
portNumber)
++{
++ int sock;
++ struct sockaddr addr;
++ size_t len = 0;
++ int one = 1;
++
++ len = sizeof(struct sockaddr);
++ if ((sock = accept(fd, &addr, &len)) < 0)
++ {
++ *sockP = -1;
++ return STATUS_ERROR;
++ }
++
++ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &one,
sizeof(one)) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
sizeof(one)) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ *sockP = sock;
++
++ return STATUS_OK;
++}
++
++void
++PGR_Close_Sock(int * sock)
++{
++ close( (int)*sock);
++ *sock = -1;
++}
++
++static char *
++get_string(char * buf)
++{
++ int i,len1,len2,start_flag;
++ char *readp, *writep;
++
++ writep = readp = buf;
++ i = len1 = 0;
++ while (*(readp +i) != '\0')
++ {
++ if (!isspace(*(readp+ i)))
++ {
++ len1 ++;
++ }
++ i++;
++ }
++ start_flag = len2 = 0;
++ while (*readp != '\0')
++ {
++ if (*readp == '#')
++ {
++ *writep = '\0';
++ break;
++ }
++ if (isspace(*readp))
++ {
++ if ((len2 >= len1) || (!start_flag))
++ {
++ readp++;
++ continue;
++ }
++ *writep = *readp;
++ }
++ else
++ {
++ start_flag = 1;
++ *writep = *readp;
++ len2 ++;
++ }
++ readp ++;
++ writep ++;
++ }
++ *writep = '\0';
++ return buf;
++}
++
++static bool
++is_start_tag(char * ptr)
++{
++ if ((*ptr == '<') && (*(ptr+1) != '/'))
++ {
++ return true;
++ }
++ return false;
++}
++
++static bool
++is_end_tag(char * ptr)
++{
++ if ((*ptr == '<') && (*(ptr+1) == '/'))
++ {
++ return true;
++ }
++ return false;
++}
++
++static void
++init_conf_data(ConfDataType *conf)
++{
++ memset(conf->table,0,sizeof(conf->table));
++ memset(conf->key,0,sizeof(conf->key));
++ memset(conf->value,0,sizeof(conf->value));
++ conf->rec_no = 0;
++ conf->last = NULL;
++ conf->next = NULL;
++}
++
++static int
++get_key(char * key, char * str)
++{
++ int offset = 1;
++ char * ptr_s,*ptr_e;
++
++ ptr_s = strchr(str,'<');
++ if (ptr_s == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ if (*(ptr_s+1) == '/')
++ {
++ offset = 2;
++ }
++ ptr_e = strchr(str,'>');
++ if (ptr_e == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ *ptr_e = '\0';
++ strcpy(key,ptr_s + offset);
++ *ptr_e = '>';
++ return STATUS_OK;
++}
++
++static int
++get_conf_key_value(char * key, char * value , char * str)
++{
++ int i;
++ int len1,len2,start_flag;
++ char * ptr_s,*ptr_e;
++
++ if(get_key(key,str) == STATUS_ERROR)
++ {
++ return STATUS_ERROR;
++ }
++ ptr_e = strchr(str,'>');
++ if (ptr_e == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ ptr_s = ptr_e + 1;
++
++ len1 = 0;
++ while ((*ptr_s != '<') && (*ptr_s != '\0'))
++ {
++ if (! isspace(*ptr_s))
++ {
++ len1 ++;
++ }
++ ptr_s ++;
++ }
++ ptr_s = ptr_e + 1;
++ i = len2 = start_flag = 0;
++ while ((*ptr_s != '<') && (*ptr_s != '\0'))
++ {
++ if (isspace(*ptr_s))
++ {
++ if ((len2 >= len1) || (!start_flag))
++ {
++ ptr_s ++;
++ continue;
++ }
++ *(value + i) = *ptr_s;
++ }
++ else
++ {
++ start_flag = 1;
++ *(value + i) = *ptr_s;
++ len2 ++;
++ }
++ i++;
++ ptr_s ++;
++ }
++ *(value + i) = '\0';
++ return STATUS_OK;
++}
++
++static int
++add_conf_data(char *table,int rec_no, char *key,char * value)
++{
++ ConfDataType * conf_data;
++
++ conf_data = (ConfDataType *)malloc(sizeof(ConfDataType));
++ if (conf_data == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ init_conf_data(conf_data);
++ if (table != NULL)
++ {
++ memcpy(conf_data->table,table,sizeof(conf_data->table));
++ }
++ else
++ {
++ memset(conf_data->table,0,sizeof(conf_data->table));
++ }
++ memcpy(conf_data->key,key,sizeof(conf_data->key));
++ memcpy(conf_data->value,value,sizeof(conf_data->value));
++ conf_data->rec_no = rec_no;
++ if (ConfData_Top == (ConfDataType *)NULL)
++ {
++ ConfData_Top = conf_data;
++ conf_data->last = (char *)NULL;
++ }
++ if (ConfData_End == (ConfDataType *)NULL)
++ {
++ conf_data->last = (char *)NULL;
++ }
++ else
++ {
++ conf_data->last = (char *)ConfData_End;
++ ConfData_End->next = (char *)conf_data;
++ }
++ ConfData_End = conf_data;
++ conf_data->next = (char *)NULL;
++ return STATUS_OK;
++}
++
++static int
++get_table_data(FILE * fp,char * table, int rec_no)
++{
++ char buf[1024];
++ char key_buf[1024];
++ char value_buf[1024];
++ int len = 0;
++ char * ptr;
++
++ while (fgets(buf,sizeof(buf),fp) != NULL)
++ {
++ /*
++ * pic up a data string
++ */
++ ptr = get_string(buf);
++ len = strlen(ptr);
++ if (len == 0)
++ {
++ continue;
++ }
++ if (is_end_tag(ptr))
++ {
++ if(get_key(key_buf,ptr) == STATUS_ERROR)
++ {
++ return STATUS_ERROR;
++ }
++ if (!strcmp(key_buf,table))
++ {
++ return STATUS_OK;
++ }
++ }
++ if (is_start_tag(ptr))
++ {
++ if(get_conf_key_value(key_buf,value_buf,ptr) ==
STATUS_ERROR)
++ {
++ return STATUS_ERROR;
++ }
++ add_conf_data(table,rec_no,key_buf,value_buf);
++ }
++ }
++ return STATUS_ERROR;
++}
++
++static int
++get_single_data(char * str)
++{
++ char key_buf[1024];
++ char value_buf[1024];
++ if(get_conf_key_value(key_buf,value_buf,str) == STATUS_ERROR)
++ {
++ return STATUS_ERROR;
++ }
++ add_conf_data(NULL,0,key_buf,value_buf);
++ return STATUS_OK;
++}
++
++
++static int
++get_conf_file(char * fname)
++{
++ FILE * fp = NULL;
++ int len;
++ char buf[1024];
++ char key_buf[1024];
++ char last_key_buf[1024];
++ char *ptr;
++ int rec_no = 0;
++
++ /*
++ * configuration file open
++ */
++ if ((fp = fopen(fname,"r")) == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ /*
++ * configuration file read
++ */
++ memset(last_key_buf,0,sizeof(last_key_buf));
++ memset(key_buf,0,sizeof(key_buf));
++ while (fgets(buf,sizeof(buf),fp) != NULL)
++ {
++ /*
++ * pic up a data string
++ */
++ ptr = get_string(buf);
++ len = strlen(ptr);
++ if (len == 0)
++ {
++ continue;
++ }
++ if (is_start_tag(ptr))
++ {
++ if(get_key(key_buf,ptr) == STATUS_ERROR)
++ {
++ fclose(fp);
++ return STATUS_ERROR;
++ }
++ if (strstr(ptr,"</") == NULL)
++ {
++ if (strcmp(last_key_buf,key_buf))
++ {
++ rec_no = 0;
++ strcpy(last_key_buf,key_buf);
++ }
++ get_table_data(fp,key_buf,rec_no);
++ rec_no ++;
++ }
++ else
++ {
++ get_single_data(ptr);
++ }
++ }
++ }
++ fclose(fp);
++ return STATUS_OK;
++}
++
++int
++PGR_Free_Conf_Data(void)
++{
++ ConfDataType * conf, *nextp;
++
++ if (ConfData_Top == (ConfDataType *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ conf = ConfData_Top;
++
++ while (conf != (ConfDataType *)NULL)
++ {
++ nextp = (ConfDataType*)conf->next;
++ free (conf);
++ conf = nextp;
++ }
++ ConfData_Top = ConfData_End = (ConfDataType *)NULL;
++ return STATUS_OK;
++}
++
++int
++PGR_Get_Conf_Data(char * dir , char * fname)
++{
++
++ int status;
++
++ char * conf_file;
++ if ((dir == NULL) || ( fname == NULL))
++ {
++ return STATUS_ERROR;
++ }
++ conf_file = malloc(strlen(dir) + strlen(fname) + 2);
++ if (conf_file == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ sprintf(conf_file,"%s/%s",dir,fname);
++
++ ConfData_Top = ConfData_End = (ConfDataType * )NULL;
++ status = get_conf_file(conf_file);
++ free (conf_file);
++ conf_file = NULL;
++
++ return status;
++}
++
++void
++PGRset_recovery_packet_no(RecoveryPacket * packet, int packet_no)
++{
++ if (packet == NULL)
++ {
++ return;
++ }
++ packet->packet_no = htons(packet_no) ;
++
++}
++
++unsigned int
++PGRget_ip_by_name(char * host)
++{
++ struct hostent *hp = NULL;
++ unsigned int ip = 0;
++ unsigned char uc = 0;
++ int i;
++
++ if ((host == NULL) || (*host == '\0'))
++ {
++ return 0;
++ }
++ hp = gethostbyname( host );
++ if (hp == NULL)
++ {
++ return 0;
++ }
++ for (i = 3 ; i>= 0 ; i --)
++ {
++ uc = (unsigned char)hp->h_addr_list[0][i];
++ ip = ip | uc;
++ if (i > 0)
++ ip = ip << 8;
++ }
++ return ip;
++}
++
++#if 0
++void
++PGR_Init_Com_Info(PGR_Com_Info * p)
++{
++ if (p != (PGR_Com_Info *)NULL)
++ {
++ /* init PGR_ConfData_Info data */
++ ConfData_Info = &(p->ConfData_Info);
++ ConfData_Info->ConfData_Top = (ConfDataType*)NULL;
++ ConfData_Info->ConfData_End = (ConfDataType*)NULL;
++ ConfData_Info->LifeCheck_Port_Number = 0;
++ ConfData_Info->Recovery_Port_Number = 0;
++
++ /* init PGR_Log_Info data */
++ Log_Info = &(p->Log_Info);
++ Log_Info->StatusFp = (FILE *)NULL;
++ Log_Info->LogFp = (FILE *)NULL;
++ Log_Info->PGRStatusFileName = NULL;
++ Log_Info->PGRLogFileName = NULL;
++ Log_Info->Log_Print = 0;
++ Log_Info->Debug_Print = 0;
++ }
++ else
++ {
++ ConfData_Info = (PGR_ConfData_Info *)NULL;
++ Log_Info = (PGR_Log_Info *)NULL;
++ }
++}
++#endif
++
++#endif /* USE_REPLICATION */
+diff -aruN postgresql-8.1.2/src/backend/parser/parse_clause.c
pgcluster-1.5.0rc7/src/backend/parser/parse_clause.c
+--- postgresql-8.1.2/src/backend/parser/parse_clause.c 2005-11-22
19:23:13.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/parser/parse_clause.c 2006-01-23
07:10:23.000000000 +0100
+@@ -34,6 +34,10 @@
+ #include "utils/builtins.h"
+ #include "utils/guc.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++extern bool auto_lock_table;
++#endif /* USE_REPLICATION */
+
+ #define ORDER_CLAUSE 0
+ #define GROUP_CLAUSE 1
+@@ -154,7 +158,18 @@
+ * analyze.c will eventually do the corresponding heap_close(), but
*not*
+ * release the lock.
+ */
++#ifdef USE_REPLICATION
++ if ((auto_lock_table == true) && (Transaction_Mode < 1))
++ {
++ pstate->p_target_relation = heap_openrv(relation,
ShareRowExclusiveLock);
++ }
++ else
++ {
++ pstate->p_target_relation = heap_openrv(relation,
RowExclusiveLock);
++ }
++#else
+ pstate->p_target_relation = heap_openrv(relation, RowExclusiveLock);
++#endif /* USE_REPLICATION */
+
+ /*
+ * Now build an RTE.
+diff -aruN postgresql-8.1.2/src/backend/parser/parse_relation.c
pgcluster-1.5.0rc7/src/backend/parser/parse_relation.c
+--- postgresql-8.1.2/src/backend/parser/parse_relation.c 2005-11-22
19:23:14.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/parser/parse_relation.c 2006-01-23
07:12:19.000000000 +0100
+@@ -30,6 +30,10 @@
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++extern bool auto_lock_table;
++#endif /* USE_REPLICATION */
+
+ /* GUC parameter */
+ bool add_missing_from;
+@@ -587,7 +591,14 @@
+ * to a rel in a statement, be careful to get the right access level
+ * depending on whether we're doing SELECT FOR UPDATE/SHARE.
+ */
++#ifdef USE_REPLICATION
++ if ((auto_lock_table == true) && (Transaction_Mode < 2))
++ lockmode = isLockedRel(pstate, refname) ?
ShareRowExclusiveLock : AccessShareLock;
++ else
++ lockmode = isLockedRel(pstate, refname) ? RowShareLock :
AccessShareLock;
++#else
+ lockmode = isLockedRel(pstate, refname) ? RowShareLock :
AccessShareLock;
++#endif /* USE_REPLICATION */
+ rel = heap_openrv(relation, lockmode);
+ rte->relid = RelationGetRelid(rel);
+
+diff -aruN postgresql-8.1.2/src/backend/postmaster/postmaster.c
pgcluster-1.5.0rc7/src/backend/postmaster/postmaster.c
+--- postgresql-8.1.2/src/backend/postmaster/postmaster.c 2006-01-06
03:58:32.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/postmaster/postmaster.c 2006-01-31
13:12:17.000000000 +0100
+@@ -128,6 +128,9 @@
+ #include "storage/spin.h"
+ #endif
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * List of active backends (or child processes anyway; we don't actually
+@@ -370,6 +373,49 @@
+ #define StartupDataBase() StartChildProcess(BS_XLOG_STARTUP)
+ #define StartBackgroundWriter() StartChildProcess(BS_XLOG_BGWRITER)
+
++#ifdef USE_REPLICATION
++char * Query_String = NULL;
++ReplicateServerInfo * ReplicateServerData = NULL;
++ReplicateServerInfo * CurrentReplicateServer = NULL;
++ReplicateServerInfo * LastReplicateServer = NULL;
++int ReplicateServerShmid = -1;
++int TransactionQuery = 0;
++int TransactionSock = -1;
++int Transaction_Mode = 0;
++int PGR_Noticed_Abort = 0;
++bool Session_Authorization_Mode = false;
++bool Create_Temp_Table_Mode = false;
++ConfDataType * ConfData_Top = (ConfDataType *)NULL;
++ConfDataType * ConfData_End = (ConfDataType *)NULL;
++int RecoveryPortNumber = 0;
++int Master_Pid = 0;
++char * RsyncPath = NULL;
++char * RsyncOption = NULL;
++bool RsyncCompress = true;
++ReplicateNow * ReplicateCurrentTime = NULL;
++CopyData * PGRCopyData = NULL;
++bool PGR_Copy_Data_Need_Replicate = false;
++PGR_Stand_Alone_Type * PGR_Stand_Alone = NULL;
++PGR_Not_Replicate_Type * PGR_Not_Replicate = NULL;
++int PGR_Not_Replicate_Rec_Num = 0;
++bool PGR_Is_Replicated_Query = false;
++PGR_Check_Lock_Type PGR_Check_Lock;
++int PGR_Sock_To_Replication_Server = -1;
++bool PGR_Need_Notice = false;
++bool PGR_Lock_Noticed = false;
++bool PGR_Recovery_Option = false;
++char * PGRSelfHostName = NULL;
++int PGR_Pending_Sem_Num = 0;
++bool PGR_Reliable_Mode_Wait = true;
++PGR_Retry_Query_Type PGR_Retry_Query;
++int ClusterDBShmid = -1;
++ClusterDBInfo * ClusterDBData = NULL;
++bool PGRforceLoadBalance=false;
++
++bool needToUpdateReplicateIdOnNextQueryIsDone=false;
++bool PGR_Is_Sync_OID = false;
++
++#endif /* USE_REPLICATION */
+
+ /*
+ * Postmaster main entry point
+@@ -382,6 +428,11 @@
+ char *userDoption = NULL;
+ int i;
+
++#ifdef USE_REPLICATION
++ PGR_Check_Lock.check_lock_conflict = false;
++ PGR_Check_Lock.status_lock_conflict = STATUS_OK;
++#endif /* USE REPLICATION */
++
+ /* This will call exit() if strdup() fails. */
+ progname = get_progname(argv[0]);
+
+@@ -450,10 +501,15 @@
+
+ opterr = 1;
+
+- while ((opt = getopt(argc, argv,
"A:a:B:b:c:D:d:Fh:ik:lm:MN:no:p:Ss-:")) != -1)
++ while ((opt = getopt(argc, argv,
"A:a:B:b:c:D:d:Fh:ik:lm:MN:no:p:Ss-:R")) != -1)
+ {
+ switch (opt)
+ {
++ case 'R':
++#ifdef USE_REPLICATION
++ PGR_Recovery_Option = true;
++ break;
++#endif /* USE_REPLICATION */
+ case 'A':
+ #ifdef USE_ASSERT_CHECKING
+ SetConfigOption("debug_assertions", optarg,
PGC_POSTMASTER, PGC_S_ARGV);
+@@ -685,6 +741,23 @@
+ */
+ CreateDataDirLockFile(true);
+
++#ifdef USE_REPLICATION
++ if (PGR_Get_Conf_Data( DataDir, CLUSTER_CONF_FILE ) == STATUS_OK)
++ {
++ PGR_Init_Replicate_Server_Data();
++ PGR_Set_Replicate_Server_Socket();
++ PGR_Free_Conf_Data();
++ if (PGR_Recovery_Option)
++ {
++ fprintf(stderr,"Start in recovery mode! \n");
++ fprintf(stderr,"Please wait until a data
synchronization finishes from Master DB... \n");
++ if (PGR_Recovery_Main() != STATUS_OK)
++ {
++ ExitPostmaster(1);
++ }
++ }
++ }
++#endif /* USE_REPLICATION */
+ /*
+ * If timezone is not set, determine what the OS uses. (In theory
this
+ * should be done during GUC initialization, but because it can take
as
+@@ -944,6 +1017,21 @@
+ */
+ autovac_init();
+
++#ifdef USE_REPLICATION
++ Master_Pid = PGR_Master_Main();
++ if (Master_Pid < 0)
++ {
++ elog(DEBUG1,"PGR_Master_Main failed");
++ ExitPostmaster(1);
++ }
++ if (PGR_Recovery_Option)
++ {
++ PGR_recovery_finish_send();
++ PGR_Recovery_Option = false;
++ fprintf(stderr,"OK! The data synchronization with Master DB
was finished. \n");
++ }
++#endif /* USE_REPLICATION */
++
+ status = ServerLoop();
+
+ /*
+@@ -1607,7 +1695,10 @@
+ case CAC_TOOMANY:
+ ereport(FATAL,
+
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
+- errmsg("sorry, too many clients
already")));
++ errmsg("sorry, too many clients already")));
++#ifdef USE_REPLICATION
++ return STATUS_ERROR;
++#endif
+ break;
+ case CAC_OK:
+ default:
+@@ -1875,6 +1966,19 @@
+ (errmsg_internal("postmaster received signal %d",
+
postgres_signal_arg)));
+
++#ifdef USE_REPLICATION
++ if (PGR_Recovery_Option)
++ {
++ PGR_recovery_error_send();
++ PGR_Recovery_Option = false;
++ }
++ if (Master_Pid > 0)
++ {
++ kill (Master_Pid,postgres_signal_arg);
++ }
++ PGR_delete_shm();
++#endif /* USE_REPLICATION */
++
+ switch (postgres_signal_arg)
+ {
+ case SIGTERM:
+@@ -3348,6 +3452,16 @@
+ * MUST -- vadim 05-10-1999
+ */
+
++#ifdef USE_REPLICATION
++ if (PGR_Recovery_Option)
++ {
++ write_stderr("sorry, recovery failed.");
++ PGR_recovery_error_send();
++ PGR_Recovery_Option = false;
++ }
++ PGR_delete_shm();
++#endif /* USE_REPLICATION */
++
+ proc_exit(status);
+ }
+
+diff -aruN postgresql-8.1.2/src/backend/storage/large_object/inv_api.c
pgcluster-1.5.0rc7/src/backend/storage/large_object/inv_api.c
+--- postgresql-8.1.2/src/backend/storage/large_object/inv_api.c
2005-10-15 04:49:26.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/storage/large_object/inv_api.c
2006-01-10 17:39:39.000000000 +0100
+@@ -28,6 +28,10 @@
+ #include "utils/lsyscache.h"
+ #include "utils/resowner.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
++
+
+ /*
+ * All accesses to pg_largeobject and its index make use of a single
Relation
+@@ -39,7 +43,6 @@
+ static Relation lo_heap_r = NULL;
+ static Relation lo_index_r = NULL;
+
+-
+ /*
+ * Open pg_largeobject and its index, if not already done in current xact
+ */
+@@ -180,6 +183,9 @@
+ * use. We can use the index on pg_largeobject for checking OID
+ * uniqueness, even though it has additional columns besides OID.
+ */
++#ifdef USE_REPLICATION
++ PGR_Is_Sync_OID = true;
++#endif /* USE_REPLICATION */
+ if (!OidIsValid(lobjId))
+ {
+ open_lo_relation();
+@@ -198,6 +204,9 @@
+ */
+ CommandCounterIncrement();
+
++#ifdef USE_REPLICATION
++ PGR_Is_Sync_OID = false;
++#endif /* USE_REPLICATION */
+ return lobjId;
+ }
+
+diff -aruN postgresql-8.1.2/src/backend/storage/lmgr/deadlock.c
pgcluster-1.5.0rc7/src/backend/storage/lmgr/deadlock.c
+--- postgresql-8.1.2/src/backend/storage/lmgr/deadlock.c 2005-10-29
02:31:51.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/storage/lmgr/deadlock.c 2006-01-10
17:39:39.000000000 +0100
+@@ -31,6 +31,9 @@
+ #include "storage/proc.h"
+ #include "utils/memutils.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /* One edge in the waits-for graph */
+ typedef struct
+@@ -226,6 +229,13 @@
+ if (!FindLockCycle(proc, possibleConstraints, &nSoftEdges))
+ elog(FATAL, "deadlock seems to have disappeared");
+
++#ifdef USE_REPLICATION
++ if (PGR_Notice_Conflict() == STATUS_ERROR)
++ {
++ return FALSE;
++ }
++ PGR_Lock_Noticed =true;
++#endif
+ return true; /* cannot find a
non-deadlocked state */
+ }
+
+diff -aruN postgresql-8.1.2/src/backend/storage/lmgr/lmgr.c
pgcluster-1.5.0rc7/src/backend/storage/lmgr/lmgr.c
+--- postgresql-8.1.2/src/backend/storage/lmgr/lmgr.c 2005-10-15
04:49:26.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/storage/lmgr/lmgr.c 2006-01-10
17:39:39.000000000 +0100
+@@ -24,6 +24,9 @@
+ #include "storage/procarray.h"
+ #include "utils/inval.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * This conflict table defines the semantics of the various lock modes.
+@@ -466,10 +469,18 @@
+
+ SET_LOCKTAG_TRANSACTION(tag, xid);
+
++#ifdef USE_REPLICATION
++ if (!LockAcquire(LockTableId, &tag, false,
++ ExclusiveLock, false,false))
++ elog(ERROR, "XactLockTableWait: LockAcquire failed");
++
++ LockRelease(LockTableId, &tag, ExclusiveLock,false);
++#else
+ (void) LockAcquire(LockTableId, &tag, false,
+ ShareLock, false, false);
+
+ LockRelease(LockTableId, &tag, ShareLock, false);
++#endif /* USE_REPLICATION */
+
+ if (!TransactionIdIsInProgress(xid))
+ break;
+@@ -605,3 +616,38 @@
+
+ LockRelease(LockTableId, &tag, lockmode, false);
+ }
++
++#ifdef USE_REPLICATION
++/*
++ * XactLockTableWait
++ *
++ * Wait for the specified transaction to commit or abort.
++ */
++void
++XactLockTableWaitForCluster(TransactionId xid,Buffer buffer)
++{
++ LOCKTAG tag;
++ TransactionId myxid = GetCurrentTransactionId();
++
++ Assert(!TransactionIdEquals( xid, myxid ));
++
++ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
++
++ SET_LOCKTAG_TRANSACTION(tag, xid);
++
++ if (!LockAcquire(LockTableId, &tag, false,
++ ExclusiveLock, false,false))
++ elog(ERROR, "XactLockTableWait: LockAcquire failed");
++
++ LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
++
++ LockRelease(LockTableId, &tag, ExclusiveLock,false);
++
++ /*
++ * Transaction was committed/aborted/crashed - we have to update
++ * pg_clog if transaction is still marked as running.
++ */
++ if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
++ TransactionIdAbort(xid);
++}
++#endif /*USE_REPLICATION*/
+diff -aruN postgresql-8.1.2/src/backend/storage/lmgr/lock.c
pgcluster-1.5.0rc7/src/backend/storage/lmgr/lock.c
+--- postgresql-8.1.2/src/backend/storage/lmgr/lock.c 2005-11-22
19:23:18.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/storage/lmgr/lock.c 2006-01-10
17:39:39.000000000 +0100
+@@ -42,6 +42,10 @@
+ #include "utils/ps_status.h"
+ #include "utils/resowner.h"
+
++#ifdef USE_REPLICATION
++#include "storage/lmgr.h"
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /* This configuration variable is used to set the lock table size */
+ int max_locks_per_xact; /* set by guc.c */
+@@ -697,6 +701,10 @@
+ status = LockCheckConflicts(lockMethodTable, lockmode,
+ lock,
proclock, MyProc);
+
++#ifdef USE_REPLICATION
++ PGR_Check_Lock.status_lock_conflict = status;
++ PGR_Check_Lock.deadlock = false;
++#endif /* USE_REPLICATION */
+ if (status == STATUS_OK)
+ {
+ /* No conflict with held or previously requested locks */
+@@ -706,6 +714,17 @@
+ else
+ {
+ Assert(status == STATUS_FOUND);
++#ifdef USE_REPLICATION
++ if ((PGR_Need_Notice == true) &&
++ (PGR_Check_Lock.check_lock_conflict == true))
++ {
++ if (!PGR_Lock_Noticed && PGR_Notice_Conflict() ==
STATUS_ERROR)
++ {
++ return FALSE;
++ }
++ PGR_Lock_Noticed = true;
++ }
++#endif /* USE_REPLICATION */
+
+ /*
+ * We can't acquire the lock immediately. If caller
specified no
+diff -aruN postgresql-8.1.2/src/backend/storage/lmgr/proc.c
pgcluster-1.5.0rc7/src/backend/storage/lmgr/proc.c
+--- postgresql-8.1.2/src/backend/storage/lmgr/proc.c 2005-11-22
19:23:19.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/storage/lmgr/proc.c 2006-01-10
17:39:39.000000000 +0100
+@@ -44,6 +44,9 @@
+ #include "storage/procarray.h"
+ #include "storage/spin.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /* GUC variables */
+ int DeadlockTimeout = 1000;
+@@ -264,6 +267,9 @@
+ MyProc->lwWaitLink = NULL;
+ MyProc->waitLock = NULL;
+ MyProc->waitProcLock = NULL;
++#ifdef USE_REPLICATION
++ MyProc->replicationId = 0;
++#endif
+ SHMQueueInit(&(MyProc->procLocks));
+
+ /*
+@@ -360,6 +366,9 @@
+ MyProc->lwWaitLink = NULL;
+ MyProc->waitLock = NULL;
+ MyProc->waitProcLock = NULL;
++#ifdef USE_REPLICATION
++ MyProc->replicationId=0;
++#endif
+ SHMQueueInit(&(MyProc->procLocks));
+
+ /*
+@@ -691,6 +700,17 @@
+ GrantAwaitedLock();
+ return STATUS_OK;
+ }
++#ifdef USE_REPLICATION
++ if(proc->replicationId == 0 ||
++ (MyProc->replicationId >
proc->replicationId &&
++ proc->heldLocks & aheadRequests) ) {
++ elog(DEBUG1,"origin's RId = %d ,
MyProc->RId = %d , skip",proc->replicationId,MyProc->replicationId);
++ aheadRequests |= (1 <<
proc->waitLockMode);
++ proc = (PGPROC *)
MAKE_PTR(proc->links.next);
++ continue;
++ }
++
++#endif
+ /* Break out of loop to put myself before him
*/
+ break;
+ }
+@@ -706,8 +726,21 @@
+ }
+ else
+ {
++#ifdef USE_REPLICATION
++ proc = (PGPROC *) &(waitQueue->links);
++ for (i = 0; i < waitQueue->size+1; i++){
++ elog(DEBUG1,"origin's RId = %d , MyProc->RId =
%d",proc->replicationId,MyProc->replicationId);
++ if(proc->replicationId == 0 ||
++ MyProc->replicationId > proc->replicationId) {
++ proc= (PGPROC *) MAKE_PTR(proc->links.next);
++ }else {
++ break;
++ }
++ }
++#else
+ /* I hold no locks, so I can't push in front of anyone. */
+ proc = (PGPROC *) &(waitQueue->links);
++#endif
+ }
+
+ /*
+@@ -988,6 +1021,9 @@
+ * such processes.
+ */
+ LWLockRelease(LockMgrLock);
++#ifdef USE_REPLICATION
++ PGR_Notice_DeadLock();
++#endif
+ }
+
+
+diff -aruN postgresql-8.1.2/src/backend/tcop/postgres.c
pgcluster-1.5.0rc7/src/backend/tcop/postgres.c
+--- postgresql-8.1.2/src/backend/tcop/postgres.c 2005-12-14
18:06:37.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/tcop/postgres.c 2006-02-09
11:09:21.000000000 +0100
+@@ -64,6 +64,10 @@
+
+ #include "pgstat.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
++
+ extern int optind;
+ extern char *optarg;
+
+@@ -84,6 +88,9 @@
+ /* GUC variable for maximum stack depth (measured in kilobytes) */
+ int max_stack_depth = 2048;
+
++#ifdef USE_REPLICATION
++bool PGR_Not_Replication_Query = false;
++#endif /* USE_REPLICATION */
+
+ /* ----------------
+ * private variables
+@@ -820,6 +827,24 @@
+ char *prepare_string = NULL;
+ bool was_logged = false;
+
++#ifdef USE_REPLICATION
++ char * query_ptr = NULL;
++ char * null_ptr = NULL;
++ int skip_cnt = 0;
++ int status = 0;
++
++ PGR_Reliable_Mode_Wait = false;
++ query_ptr = (char *)query_string;
++ if (PGR_Is_Replicated_Query == false)
++ {
++ PGR_Is_Replicated_Query =
PGR_Is_Replicated_Command(query_ptr);
++ }
++ PGR_Retry_Query.query_string = (char *)query_string;
++ PGR_Retry_Query.query_len = strlen(query_string);
++ PGR_Retry_Query.cmdSts = CMD_STS_OTHER;
++ PGR_Retry_Query.cmdType = CMD_TYPE_OTHER;
++#endif /* USE_REPLICATION */
++
+ /*
+ * Report query to various monitoring facilities.
+ */
+@@ -898,6 +923,13 @@
+ DestReceiver *receiver;
+ int16 format;
+
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = false;
++ PGR_Reliable_Mode_Wait = false;
++ PGR_Retry_Query.useFlag = DATA_INIT;
++ PGR_Lock_Noticed = false;
++#endif /* USE_REPLICATION */
++
+ /*
+ * Get the command name for use in status display (it also
becomes the
+ * default completion tag, down inside PortalRun). Set
ps_status and
+@@ -905,7 +937,6 @@
+ * destination.
+ */
+ commandTag = CreateCommandTag(parsetree);
+-
+ set_ps_display(commandTag);
+
+ BeginCommand(commandTag, dest);
+@@ -920,10 +951,192 @@
+ */
+ if (IsAbortedTransactionBlockState() &&
+ !IsTransactionExitStmt(parsetree))
++ {
++#ifdef USE_REPLICATION
++ Transaction_Mode = 0;
++#endif
+ ereport(ERROR,
+
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is
aborted, "
+ "commands ignored until end
of transaction block")));
++ }
++#ifdef USE_REPLICATION
++ Query_String = NULL;
++ query_ptr = PGR_Remove_Comment(query_ptr);
++ PGR_Check_Lock.dest = TO_FRONTEND;
++ PGR_Need_Notice = false;
++ PGR_Check_Lock.check_lock_conflict = false;
++ if (!xact_started)
++ {
++ start_xact_command();
++ xact_started = true;
++ }
++ if (skip_cnt == 0)
++ {
++ skip_cnt = PGR_Is_Skip_Replication(query_ptr);
++ }
++ null_ptr = PGR_scan_terminate (query_ptr);
++ if(null_ptr != NULL)
++ {
++ *null_ptr = '\0';
++ }
++ Transaction_Mode =
PGR_Set_Transaction_Mode(Transaction_Mode,commandTag);
++ if ((PGR_Is_Replicated_Query ) ||
++ (skip_cnt != 0))
++ {
++ if (skip_cnt > 0)
++ {
++ skip_cnt --;
++ }
++ else
++ {
++ skip_cnt = 0;
++ }
++ PGR_Copy_Data_Need_Replicate = false;
++ if (!strncmp(commandTag,"SELECT",strlen("SELECT")))
++ {
++ if (PGR_Is_System_Command(query_ptr))
++ {
++ status =
PGR_Call_System_Command(query_ptr);
++ if (status == STATUS_SKIP_QUERY)
++ {
++
EndCommand(PGR_ALREADY_REPLICATED_NOTICE_CMD,dest);
++ break;
++ }
++ else
++ {
++
EndCommand("SYSTEM_COMMAND",dest);
++ continue;
++ }
++ }
++ }
++ PGR_Check_Lock.status_lock_conflict = STATUS_OK;
++ PGR_Check_Lock.dest = TO_FRONTEND;
++ }
++ else
++ {
++ PGR_Copy_Data_Need_Replicate = false;
++
++ /* check cluster db status */
++ if ((PGR_Get_Cluster_Status() == STATUS_RECOVERY)
&&
++ (PGR_Not_Replication_Query == false)
&&
++ (Transaction_Mode == 0 ) )
++ {
++ elog(WARNING, "This query is not permitted
while recovery db ");
++ if(null_ptr != NULL)
++ {
++ *null_ptr = ';';
++ query_ptr = null_ptr +1;
++ }
++ continue;
++ }
++ if (PGR_Is_Stand_Alone() == true)
++ {
++ if (PGR_Stand_Alone->permit ==
PERMIT_READ_ONLY)
++ {
++ if (!strcmp(commandTag, "SHOW")) {
++ VariableShowStmt *stmt =
(VariableShowStmt *)parsetree;
++ if (!strcmp(stmt->name,
"replication_server")) {
++
PGR_Not_Replication_Query = true;
++ }
++ }
++
++ if (PGR_Not_Replication_Query ==
false)
++ elog(ERROR, "This query is
not permitted when all replication servers fell down ");
++ }
++ }
++ else if ((PGRforceLoadBalance == false) &&
++ ((PGR_Not_Replication_Query == false
) ||
++ (!strcmp(commandTag,"SELECT"))))
++ {
++ status =
PGR_replication(query_ptr,dest,parsetree,commandTag);
++ if (status == STATUS_REPLICATED)
++ {
++ if (xact_started)
++ {
++ finish_xact_command();
++ xact_started = false;
++ }
++ CommandCounterIncrement();
++ continue;
++ }
++ else if (status == STATUS_ERROR)
++ {
++ if (!strcmp(commandTag, "SHOW")) {
++ VariableShowStmt *stmt =
(VariableShowStmt *)parsetree;
++ if (!strcmp(stmt->name,
"replication_server")) {
++
PGR_Not_Replication_Query = true;
++ }
++ }
++ else if (PGR_Stand_Alone->permit ==
PERMIT_READ_ONLY)
++ {
++ elog(ERROR, "This query is
not permitted when all replication servers fell down ");
++ }
++ }
++ else if (status == STATUS_DEADLOCK_DETECT)
++ {
++ PGR_Need_Notice = false;
++ elog(ERROR, "postmaster deadlock
detected");
++ continue;
++ }
++ else if (status == STATUS_REPLICATION_ABORT)
++ {
++ PGR_Need_Notice = false;
++ elog(ERROR, "replication server
should be down, transaction aborted.");
++ continue;
++ }
++ else if (status != STATUS_CONTINUE)
++ {
++ PGR_Check_Lock.dest = TO_FRONTEND;
++ }
++ else
++ {
++ PGR_Check_Lock.dest =
TO_REPLICATION_SERVER;
++ PGR_Reliable_Mode_Wait = true;
++ }
++ }
++ }
++ if(null_ptr != NULL)
++ {
++ *null_ptr = ';';
++ query_ptr = null_ptr +1;
++ }
++ if (!PGR_Is_Replicated_Query )
++ {
++ if ((!strcmp(commandTag,"BEGIN")) ||
++ (!strcmp(commandTag, "START TRANSACTION")) ||
++ (Transaction_Mode == 0 ) )
++ {
++ PGR_Reload_Start_Time();
++ }
++ }
++ if (((IsA(parsetree, TransactionStmt)) ||
++ (Transaction_Mode > 0) ||
++ (Create_Temp_Table_Mode == true) ||
++ (Session_Authorization_Mode == true)) ||
++ (!strcmp(commandTag,"COPY")))
++ {
++ PGR_Need_Notice = true;
++ PGR_Check_Lock.check_lock_conflict = true;
++ }
++ else
++ {
++ if (PGR_Not_Replication_Query == false)
++ {
++ PGR_Need_Notice = true;
++ PGR_Check_Lock.check_lock_conflict = true;
++ }
++ else
++ {
++ if ((PGR_Is_Replicated_Query ) &&
++ (!strncmp(commandTag,
"SELECT",strlen("SELECT"))))
++ {
++ PGR_Need_Notice = true;
++ PGR_Check_Lock.check_lock_conflict =
true;
++ }
++ }
++ }
++#endif /* USE_REPLICATION */
+
+ /* Make sure we are in a transaction command */
+ start_xact_command();
+@@ -1046,7 +1259,42 @@
+ * command the client sent, regardless of rewriting. (But a
command
+ * aborted by error will not send an EndCommand report at
all.)
+ */
++#ifdef USE_REPLICATION
++ /*
++ * In Non-CONTROL LOCK CONFLICT mode, we *MUST NOT* send
command tag twice.
++ * So , if it was already sent for lock notification , we
didn't send
++ * tag here. also ReadyForQuery,too.
++ */
++ if(!(PGR_Is_Replicated_Query && PGR_Lock_Noticed))
++#endif
+ EndCommand(completionTag, dest);
++#ifdef USE_REPLICATION
++ if(PGR_Is_Replicated_Query &&
++ needToUpdateReplicateIdOnNextQueryIsDone) {
++ ++(ReplicationLog_Info.PGR_Replicate_ID);
++
++ if (CurrentReplicateServer != NULL)
++ {
++ /* set replicate id in this system */
++ ++(CurrentReplicateServer->replicate_id);
++ }
++ elog(DEBUG1,"increased replicate_id to
%d",CurrentReplicateServer->replicate_id);
++ needToUpdateReplicateIdOnNextQueryIsDone=false;
++ }
++
++ if ((PGR_Need_Notice == true) &&
++ (PGRforceLoadBalance == false))
++ {
++ PGR_Notice_Transaction_Query_Done();
++ }
++ if (ReplicateCurrentTime != NULL)
++ {
++ if (Transaction_Mode == 0)
++ {
++ ReplicateCurrentTime->use_seed = 1;
++ }
++ }
++#endif
+ } /* end loop
over parsetrees */
+
+ /*
+@@ -1234,11 +1482,15 @@
+ */
+ if (IsAbortedTransactionBlockState() &&
+ !IsTransactionExitStmt(parsetree))
++ {
++#ifdef USE_REPLICATION
++ Transaction_Mode = 0;
++#endif
+ ereport(ERROR,
+
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is
aborted, "
+ "commands ignored until end
of transaction block")));
+-
++ }
+ /*
+ * OK to analyze, rewrite, and plan this query. Note that the
+ * originally specified parameter set is not required to be
complete,
+@@ -1432,11 +1684,15 @@
+ if (IsAbortedTransactionBlockState() &&
+ (!IsTransactionExitStmtList(pstmt->query_list) ||
+ numParams != 0))
++ {
++#ifdef USE_REPLICATION
++ Transaction_Mode = 0;
++#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is aborted, "
+ "commands ignored until end
of transaction block")));
+-
++ }
+ /*
+ * Create the portal. Allow silent replacement of an existing portal
only
+ * if the unnamed portal is specified.
+@@ -1737,11 +1993,15 @@
+ */
+ if (IsAbortedTransactionBlockState() &&
+ !IsTransactionExitStmtList(portal->parseTrees))
++ {
++#ifdef USE_REPLICATION
++ Transaction_Mode = 0;
++#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is aborted, "
+ "commands ignored until end
of transaction block")));
+-
++ }
+ /* Check for cancel signal before we start execution */
+ CHECK_FOR_INTERRUPTS();
+
+@@ -1882,11 +2142,15 @@
+ */
+ if (IsAbortedTransactionBlockState() &&
+ PreparedStatementReturnsTuples(pstmt))
++ {
++#ifdef USE_REPLICATION
++ Transaction_Mode = 0;
++#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is aborted, "
+ "commands ignored until end
of transaction block")));
+-
++ }
+ if (whereToSendOutput != DestRemote)
+ return; /* can't actually do
anything... */
+
+@@ -1952,11 +2216,15 @@
+ */
+ if (IsAbortedTransactionBlockState() &&
+ portal->tupDesc)
++ {
++#ifdef USE_REPLICATION
++ Transaction_Mode = 0;
++#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
+ errmsg("current transaction is aborted, "
+ "commands ignored until end
of transaction block")));
+-
++ }
+ if (whereToSendOutput != DestRemote)
+ return; /* can't actually do
anything... */
+
+@@ -2112,6 +2380,9 @@
+ * backend. This is necessary precisely because we don't clean up our
+ * shared memory state.
+ */
++#ifdef USE_REPLICATION
++ PGR_delete_shm();
++#endif /* USE_REPLICATION */
+ exit(1);
+ }
+
+@@ -2149,6 +2420,9 @@
+ }
+ }
+
++#ifdef USE_REPLICATION
++ PGR_delete_shm();
++#endif /* USE_REPLICATION */
+ errno = save_errno;
+ }
+
+@@ -2163,6 +2437,9 @@
+ void
+ authdie(SIGNAL_ARGS)
+ {
++#ifdef USE_REPLICATION
++ PGR_delete_shm();
++#endif /* USE_REPLICATION */
+ exit(0);
+ }
+
+@@ -3165,6 +3442,14 @@
+ pgstat_report_activity("<IDLE>");
+ }
+
++#ifdef USE_REPLICATION
++ /*
++ * In Non-CONTROL LOCK CONFLICT mode, we *MUST NOT*
send command tag twice.
++ * So , if it was already sent for lock notification
, we didn't send
++ * tag here. also ReadyForQuery,too.
++ */
++ if(!(PGR_Is_Replicated_Query && PGR_Lock_Noticed))
++#endif
+ ReadyForQuery(whereToSendOutput);
+ send_rfq = false;
+ }
+@@ -3204,7 +3489,25 @@
+ */
+ if (ignore_till_sync && firstchar != EOF)
+ continue;
+-
++#ifdef USE_REPLICATION
++ if ((firstchar == CMD_TYPE_P_PARSE) ||
++ (firstchar == CMD_TYPE_P_BIND) ||
++ (firstchar == CMD_TYPE_P_DESCRIBE) ||
++ (firstchar == CMD_TYPE_P_EXECUTE) ||
++ (firstchar == CMD_TYPE_P_SYNC) ||
++ (firstchar == CMD_TYPE_P_CLOSE))
++ {
++ if (PGR_Send_Input_Message(firstchar, &input_message)
!= STATUS_OK)
++ {
++ if ((PGR_Is_Stand_Alone() == true) &&
++ (PGR_Stand_Alone->permit ==
PERMIT_READ_ONLY))
++ {
++ elog(WARNING, "This query is not
permitted when all replication servers fell down ");
++ break;
++ }
++ }
++ }
++#endif /* USE_REPLICATION */
+ switch (firstchar)
+ {
+ case 'Q': /* simple query */
+@@ -3213,9 +3516,7 @@
+
+ query_string =
pq_getmsgstring(&input_message);
+ pq_getmsgend(&input_message);
+-
+ exec_simple_query(query_string);
+-
+ send_rfq = true;
+ }
+ break;
+@@ -3239,7 +3540,6 @@
+ paramTypes[i] =
pq_getmsgint(&input_message, 4);
+ }
+ pq_getmsgend(&input_message);
+-
+ exec_parse_message(query_string,
stmt_name,
+
paramTypes, numParams);
+ }
+@@ -3262,7 +3562,6 @@
+ portal_name =
pq_getmsgstring(&input_message);
+ max_rows =
pq_getmsgint(&input_message, 4);
+ pq_getmsgend(&input_message);
+-
+ exec_execute_message(portal_name,
max_rows);
+ }
+ break;
+@@ -3356,7 +3655,6 @@
+ describe_type =
pq_getmsgbyte(&input_message);
+ describe_target =
pq_getmsgstring(&input_message);
+ pq_getmsgend(&input_message);
+-
+ switch (describe_type)
+ {
+ case 'S':
+@@ -3395,6 +3693,27 @@
+ case 'X':
+ case EOF:
+
++#ifdef USE_REPLICATION
++ if (PGRforceLoadBalance == false)
++ {
++ if (PGR_Is_Replicated_Query == false)
++ {
++ PGR_Noticed_Abort = 1;
++
PGRsend_system_command(CMD_STS_TRANSACTION_ABORT, CMD_TYPE_FRONTEND_CLOSED);
++ }
++ else if ((Transaction_Mode == 1) &&
(PGR_Noticed_Abort == 0))
++ {
++ if
(PGR_Did_Commit_Transaction() == true)
++ {
++
pgstat_report_activity("commit");
++
exec_simple_query("commit");
++ }
++ }
++ }
++ /*
++ PGR_Notice_Transaction_Query_Aborted();
++ */
++#endif /* USE_REPLICATION */
+ /*
+ * Reset whereToSendOutput to prevent ereport
from attempting
+ * to send any more messages to client.
+diff -aruN postgresql-8.1.2/src/backend/tcop/pquery.c
pgcluster-1.5.0rc7/src/backend/tcop/pquery.c
+--- postgresql-8.1.2/src/backend/tcop/pquery.c 2005-11-22 19:23:20.000000000
+0100
++++ pgcluster-1.5.0rc7/src/backend/tcop/pquery.c 2006-01-23
04:56:04.000000000 +0100
+@@ -25,6 +25,9 @@
+ #include "utils/guc.h"
+ #include "utils/memutils.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * ActivePortal is the currently executing Portal (the most closely nested,
+@@ -143,6 +146,18 @@
+ */
+ dest = None_Receiver;
+ }
++#ifdef USE_REPLICATION
++ if (PGR_Is_Replicated_Query == true )
++ {
++ /*
++ * Replicated *SELECT* query is used to replicate
++ * ONLY lock and function execution , results . All of
++ * them will be discarded by pgrp processes.
++ * So , we don't need to send it.
++ */
++ dest = None_Receiver;
++ }
++#endif /*USE_REPLICATION */
+ }
+
+ /*
+diff -aruN postgresql-8.1.2/src/backend/tcop/utility.c
pgcluster-1.5.0rc7/src/backend/tcop/utility.c
+--- postgresql-8.1.2/src/backend/tcop/utility.c 2005-10-15
04:49:27.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/tcop/utility.c 2006-01-23
22:51:52.000000000 +0100
+@@ -57,6 +57,9 @@
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * Error-checking support for DROP commands
+@@ -1198,29 +1201,48 @@
+
+ case T_SelectStmt:
+ tag = "SELECT";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_TransactionStmt:
+ {
+ TransactionStmt *stmt = (TransactionStmt *)
parsetree;
+
++#ifdef USE_REPLICATION
++ bool isInTransaction=IsTransactionBlock();
++#endif /* USE_REPLICATION */
++
+ switch (stmt->kind)
+ {
+ case TRANS_STMT_BEGIN:
+ tag = "BEGIN";
++#ifdef USE_REPLICATION
++
PGR_Not_Replication_Query=isInTransaction;
++#endif /* USE_REPLICATION */
+ break;
+
+ case TRANS_STMT_START:
+ tag = "START TRANSACTION";
++#ifdef USE_REPLICATION
++
PGR_Not_Replication_Query=isInTransaction;
++#endif /* USE_REPLICATION */
+ break;
+
+ case TRANS_STMT_COMMIT:
+ tag = "COMMIT";
++#ifdef USE_REPLICATION
++
PGR_Not_Replication_Query=!isInTransaction;
++#endif /* USE_REPLICATION */
+ break;
+
+ case TRANS_STMT_ROLLBACK:
+ case TRANS_STMT_ROLLBACK_TO:
+ tag = "ROLLBACK";
++#ifdef USE_REPLICATION
++
PGR_Not_Replication_Query=!isInTransaction;
++#endif /* USE_REPLICATION */
+ break;
+
+ case TRANS_STMT_SAVEPOINT:
+@@ -1252,10 +1274,16 @@
+
+ case T_DeclareCursorStmt:
+ tag = "DECLARE CURSOR";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_ClosePortalStmt:
+ tag = "CLOSE CURSOR";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_FetchStmt:
+@@ -1264,6 +1292,9 @@
+
+ tag = (stmt->ismove) ? "MOVE" : "FETCH";
+ }
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_CreateDomainStmt:
+@@ -1581,10 +1612,16 @@
+ tag = "VACUUM";
+ else
+ tag = "ANALYZE";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_ExplainStmt:
+ tag = "EXPLAIN";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_VariableSetStmt:
+@@ -1593,6 +1630,14 @@
+
+ case T_VariableShowStmt:
+ tag = "SHOW";
++#ifdef USE_REPLICATION
++ {
++ VariableShowStmt *stmt = (VariableShowStmt
*)parsetree;
++ if (strcmp(stmt->name, "replication_server"))
{
++ PGR_Not_Replication_Query = true;
++ }
++ }
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_VariableResetStmt:
+@@ -1651,10 +1696,16 @@
+
+ case T_CheckPointStmt:
+ tag = "CHECKPOINT";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_ReindexStmt:
+ tag = "REINDEX";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+
+ case T_CreateConversionStmt:
+@@ -1696,6 +1747,13 @@
+ break;
+ }
+
++#ifdef USE_REPLICATION
++ if(PGRforceLoadBalance == true)
++ {
++ PGR_Not_Replication_Query = true;
++ }
++#endif /* USE_REPLICATION */
++
+ return tag;
+ }
+
+@@ -1729,7 +1787,12 @@
+ tag = "SELECT FOR SHARE";
+ }
+ else
++ {
+ tag = "SELECT";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
++ }
+ break;
+ case CMD_UPDATE:
+ tag = "UPDATE";
+@@ -1747,6 +1810,9 @@
+ elog(WARNING, "unrecognized commandType: %d",
+ (int) parsetree->commandType);
+ tag = "???";
++#ifdef USE_REPLICATION
++ PGR_Not_Replication_Query = true;
++#endif /* USE_REPLICATION */
+ break;
+ }
+
+diff -aruN postgresql-8.1.2/src/backend/utils/adt/datetime.c
pgcluster-1.5.0rc7/src/backend/utils/adt/datetime.c
+--- postgresql-8.1.2/src/backend/utils/adt/datetime.c 2005-12-01
18:56:43.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/utils/adt/datetime.c 2006-01-18
23:42:03.000000000 +0100
+@@ -25,7 +25,6 @@
+ #include "utils/datetime.h"
+ #include "utils/guc.h"
+
+-
+ static int DecodeNumber(int flen, char *field, bool haveTextMonth,
+ int fmask, int *tmask,
+ struct pg_tm * tm, fsec_t *fsec, int *is2digits);
+diff -aruN postgresql-8.1.2/src/backend/utils/adt/float.c
pgcluster-1.5.0rc7/src/backend/utils/adt/float.c
+--- postgresql-8.1.2/src/backend/utils/adt/float.c 2005-10-15
04:49:28.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/utils/adt/float.c 2006-01-10
17:39:38.000000000 +0100
+@@ -68,6 +68,9 @@
+ #include "utils/array.h"
+ #include "utils/builtins.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ #ifndef M_PI
+ /* from my RH5.2 gcc math.h file - thomas 2000-04-03 */
+@@ -1834,7 +1837,11 @@
+ float8 result;
+
+ /* result 0.0-1.0 */
++#ifdef USE_REPLICATION
++ result = ((double) PGR_Random()) / ((double) MAX_RANDOM_VALUE);
++#else
+ result = ((double) random()) / ((double) MAX_RANDOM_VALUE);
++#endif /* USE_REPLICATION */
+
+ PG_RETURN_FLOAT8(result);
+ }
+diff -aruN postgresql-8.1.2/src/backend/utils/adt/nabstime.c
pgcluster-1.5.0rc7/src/backend/utils/adt/nabstime.c
+--- postgresql-8.1.2/src/backend/utils/adt/nabstime.c 2005-10-22
16:27:29.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/utils/adt/nabstime.c 2006-01-18
23:58:50.000000000 +0100
+@@ -29,6 +29,10 @@
+ #include "utils/builtins.h"
+ #include "utils/nabstime.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
++
+ #define MIN_DAYNUM (-24856) /* December 13, 1901 */
+ #define MAX_DAYNUM 24854 /* January 18, 2038 */
+
+@@ -94,7 +98,13 @@
+ {
+ time_t now;
+
++#ifdef USE_REPLICATION
++ struct timeval tp;
++ PGR_GetTimeOfDay(&tp,NULL);
++ now = tp.tv_sec;
++#else
+ now = time(NULL);
++#endif /* USE_REPLICATION */
+ return (AbsoluteTime) now;
+ }
+
+@@ -1033,9 +1043,14 @@
+ {
+ time_t sec;
+
++#ifdef USE_REPLICATION
++ struct timeval tp;
++ PGR_GetTimeOfDay(&tp,NULL);
++ sec = tp.tv_sec;
++#else
+ if (time(&sec) < 0)
+ PG_RETURN_ABSOLUTETIME(INVALID_ABSTIME);
+-
++#endif
+ PG_RETURN_ABSOLUTETIME((AbsoluteTime) sec);
+ }
+
+@@ -1590,7 +1605,11 @@
+ int len;
+ pg_time_t tt;
+
++#ifdef USE_REPLICATION
++ PGR_GetTimeOfDay(&tp,NULL);
++#else
+ gettimeofday(&tp, NULL);
++#endif /* USE_REPLICATION */
+ tt = (pg_time_t) tp.tv_sec;
+ pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
+ pg_localtime(&tt, global_timezone));
+diff -aruN postgresql-8.1.2/src/backend/utils/adt/ri_triggers.c
pgcluster-1.5.0rc7/src/backend/utils/adt/ri_triggers.c
+--- postgresql-8.1.2/src/backend/utils/adt/ri_triggers.c 2005-11-22
19:23:21.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/utils/adt/ri_triggers.c 2006-01-10
17:39:38.000000000 +0100
+@@ -45,6 +45,10 @@
+ #include "utils/typcache.h"
+ #include "miscadmin.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++extern bool check_constraint_with_lock;
++#endif /* USE_REPLICATION */
+
+ /* ----------
+ * Local definitions
+@@ -266,8 +270,18 @@
+ * ----------
+ */
+ quoteRelationName(pkrelname, pk_rel);
++#ifdef USE_REPLICATION
++ if (check_constraint_with_lock)
++ snprintf(querystr, sizeof(querystr), "SELECT
1 FROM ONLY %s x FOR UPDATE OF x",
++ pkrelname);
++ else
++ snprintf(querystr, sizeof(querystr), "SELECT
1 FROM ONLY %s x ",
++ pkrelname);
++
++#else
+ snprintf(querystr, sizeof(querystr), "SELECT 1 FROM
ONLY %s x FOR SHARE OF x",
+ pkrelname);
++#endif /* USE_REPLICATION */
+
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querystr, 0, NULL,
+@@ -411,6 +425,9 @@
+ queryoids[i] = SPI_gettypeid(fk_rel->rd_att,
+
qkey.keypair[i][RI_KEYPAIR_FK_IDX]);
+ }
++#ifdef USE_REPLICATION
++ if (check_constraint_with_lock)
++#endif /* USE_REPLICATION */
+ strcat(querystr, " FOR SHARE OF x");
+
+ /* Prepare and save the plan */
+@@ -572,6 +589,9 @@
+ queryoids[i] = SPI_gettypeid(pk_rel->rd_att,
+
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ }
++#ifdef USE_REPLICATION
++ if (check_constraint_with_lock)
++#endif /* USE_REPLICATION */
+ strcat(querystr, " FOR SHARE OF x");
+
+ /* Prepare and save the plan */
+@@ -728,6 +748,9 @@
+ queryoids[i] =
SPI_gettypeid(pk_rel->rd_att,
+
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ }
++#ifdef USE_REPLICATION
++ if (check_constraint_with_lock)
++#endif /* USE_REPLICATION */
+ strcat(querystr, " FOR SHARE OF x");
+
+ /* Prepare and save the plan */
+@@ -917,6 +940,9 @@
+ queryoids[i] =
SPI_gettypeid(pk_rel->rd_att,
+
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ }
++#ifdef USE_REPLICATION
++ if (check_constraint_with_lock)
++#endif /* USE_REPLICATION */
+ strcat(querystr, " FOR SHARE OF x");
+
+ /* Prepare and save the plan */
+@@ -1423,6 +1449,9 @@
+ queryoids[i] =
SPI_gettypeid(pk_rel->rd_att,
+
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ }
++#ifdef USE_REPLICATION
++ if (check_constraint_with_lock)
++#endif /* USE_REPLICATION */
+ strcat(querystr, " FOR SHARE OF x");
+
+ /* Prepare and save the plan */
+@@ -1602,6 +1631,9 @@
+ queryoids[i] =
SPI_gettypeid(pk_rel->rd_att,
+
qkey.keypair[i][RI_KEYPAIR_PK_IDX]);
+ }
++#ifdef USE_REPLICATION
++ if (check_constraint_with_lock)
++#endif /* USE_REPLICATION */
+ strcat(querystr, " FOR SHARE OF x");
+
+ /* Prepare and save the plan */
+diff -aruN postgresql-8.1.2/src/backend/utils/adt/timestamp.c
pgcluster-1.5.0rc7/src/backend/utils/adt/timestamp.c
+--- postgresql-8.1.2/src/backend/utils/adt/timestamp.c 2005-11-22
19:23:22.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/utils/adt/timestamp.c 2006-01-18
23:59:10.000000000 +0100
+@@ -31,6 +31,9 @@
+ #include "utils/builtins.h"
+ #include "utils/datetime.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /*
+ * gcc's -ffast-math switch breaks routines that expect exact results from
+@@ -937,7 +940,11 @@
+ TimestampTz result;
+ struct timeval tp;
+
++#ifdef USE_REPLICATION
++ PGR_GetTimeOfDay(&tp,NULL);
++#else
+ gettimeofday(&tp, NULL);
++#endif
+
+ result = (TimestampTz) tp.tv_sec -
+ ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
+diff -aruN postgresql-8.1.2/src/backend/utils/error/assert.c
pgcluster-1.5.0rc7/src/backend/utils/error/assert.c
+--- postgresql-8.1.2/src/backend/utils/error/assert.c 2005-10-15
04:49:32.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/utils/error/assert.c 2006-01-10
17:39:34.000000000 +0100
+@@ -19,6 +19,10 @@
+
+ #include <unistd.h>
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
++
+ /*
+ * ExceptionalCondition - Handles the failure of an Assert()
+ */
+@@ -39,6 +43,18 @@
+ fileName, lineNumber);
+ }
+
++#ifdef USE_REPLICATION
++ if ((PGR_Check_Lock.dest == TO_REPLICATION_SERVER ) &&
++ (PGR_Need_Notice == true))
++ {
++ PGR_Notice_Transaction_Query_Aborted();
++ }
++ if (PGR_Copy_Data_Need_Replicate)
++ {
++ PGR_Set_Copy_Data(PGRCopyData,NULL,0,1);
++ }
++#endif /* USE_REPLICATION */
++
+ #ifdef SLEEP_ON_ASSERT
+
+ /*
+diff -aruN postgresql-8.1.2/src/backend/utils/error/elog.c
pgcluster-1.5.0rc7/src/backend/utils/error/elog.c
+--- postgresql-8.1.2/src/backend/utils/error/elog.c 2005-11-22
19:23:23.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/utils/error/elog.c 2006-01-10
17:39:34.000000000 +0100
+@@ -69,6 +69,9 @@
+ #include "utils/guc.h"
+ #include "utils/ps_status.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+ /* Global variables */
+ ErrorContextCallback *error_context_stack = NULL;
+@@ -313,6 +316,16 @@
+ MemoryContext oldcontext;
+ ErrorContextCallback *econtext;
+
++#ifdef USE_REPLICATION
++ int status = 0;
++ bool parse_error_flag = false;
++
++ if ((edata->message) && (strstr(edata->message,"parse error") !=
NULL))
++ {
++ parse_error_flag = true;
++ }
++#endif /* USE_REPLICATION */
++
+ recursion_depth++;
+ CHECK_STACK_DEPTH();
+
+@@ -362,6 +375,24 @@
+ * handler should reset it to something else soon.
+ */
+
++#ifdef USE_REPLICATION
++ if (parse_error_flag)
++ {
++ if ((PGR_Check_Lock.dest != TO_FRONTEND) &&
++ (Transaction_Mode > 0))
++ {
++ PGR_Force_Replicate_Query();
++ }
++ }
++ if (PGR_Copy_Data_Need_Replicate)
++ {
++ PGR_Set_Copy_Data(PGRCopyData,NULL,0,1);
++ }
++ else if (PGR_Need_Notice == true)
++ {
++ PGR_Notice_Transaction_Query_Done();
++ }
++#endif /* USE_REPLICATION */
+ recursion_depth--;
+ PG_RE_THROW();
+ }
+@@ -376,7 +407,16 @@
+ * client_min_messages above FATAL, so don't look at output_to_client.
+ */
+ if (elevel >= FATAL && whereToSendOutput == DestRemote)
++ {
++#ifdef USE_REPLICATION
++ if (PGR_Copy_Data_Need_Replicate)
++ {
++ PGR_Set_Copy_Data(PGRCopyData,NULL,0,1);
++ }
++#endif /* USE_REPLICATION */
+ pq_endcopyout(true);
++ }
++
+
+ /* Emit the message to the right places */
+ EmitErrorReport();
+@@ -416,6 +456,34 @@
+ if (PG_exception_stack == NULL && whereToSendOutput ==
DestRemote)
+ whereToSendOutput = DestNone;
+
++#ifdef USE_REPLICATION
++ if (CurrentReplicateServer != NULL)
++ {
++ if (PGR_Need_Notice == true)
++ {
++ PGR_Notice_Transaction_Query_Aborted();
++ }
++ if (PGR_Copy_Data_Need_Replicate)
++ {
++ PGR_Set_Copy_Data(PGRCopyData,NULL,0,1);
++ }
++ else
++ {
++ if ((!PGR_Is_Replicated_Query ) &&
++ (PGR_Check_Lock.dest != TO_FRONTEND)
&&
++ (PGR_Reliable_Mode_Wait == true) &&
++
(CurrentReplicateServer->response_mode == PGR_RELIABLE_MODE))
++ {
++ status = PGR_Recv_Trigger(0);
++ }
++ }
++ }
++ if (TransactionSock != -1)
++ {
++ close (TransactionSock);
++ TransactionSock = -1;
++ }
++#endif /* USE_REPLICATION */
+ /*
+ * fflush here is just to improve the odds that we get to see
the
+ * error message, in case things are so hosed that proc_exit
crashes.
+@@ -437,6 +505,34 @@
+
+ if (elevel >= PANIC)
+ {
++#ifdef USE_REPLICATION
++ if (CurrentReplicateServer != NULL)
++ {
++ if (PGR_Need_Notice == true)
++ {
++ PGR_Notice_Transaction_Query_Aborted();
++ }
++ if (PGR_Copy_Data_Need_Replicate)
++ {
++ PGR_Set_Copy_Data(PGRCopyData,NULL,0,1);
++ }
++ else
++ {
++ if ((!PGR_Is_Replicated_Query ) &&
++ (PGR_Check_Lock.dest != TO_FRONTEND)
&&
++ (PGR_Reliable_Mode_Wait == true) &&
++
(CurrentReplicateServer->response_mode == PGR_RELIABLE_MODE))
++ {
++ status =
PGR_Recv_Trigger(PGR_RECV_TIMEOUT);
++ }
++ }
++ }
++ if (TransactionSock != -1)
++ {
++ close (TransactionSock);
++ TransactionSock = -1;
++ }
++#endif /* USE_REPLICATION */
+ /*
+ * Serious crash time. Postmaster will observe nonzero
process exit
+ * status and kill the other backends too.
+diff -aruN postgresql-8.1.2/src/backend/utils/fmgr/dfmgr.c
pgcluster-1.5.0rc7/src/backend/utils/fmgr/dfmgr.c
+--- postgresql-8.1.2/src/backend/utils/fmgr/dfmgr.c 2005-10-15
04:49:32.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/utils/fmgr/dfmgr.c 2006-01-10
17:39:34.000000000 +0100
+@@ -20,6 +20,9 @@
+ #include "dynloader.h"
+ #include "miscadmin.h"
+ #include "utils/dynamic_loader.h"
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+
+
+ /*
+@@ -82,6 +85,9 @@
+ char *load_error;
+ struct stat stat_buf;
+ char *fullname;
++#ifdef USE_REPLICATION
++ int status = STATUS_OK;
++#endif /* USE_REPLICATION */
+
+ fullname = expand_dynamic_library_name(filename);
+ if (!fullname)
+@@ -176,6 +182,13 @@
+ funcname, fullname)));
+
+ pfree(fullname);
++#ifdef USE_REPLICATION
++ status = PGR_Replicate_Function_Call();
++ if (status != STATUS_OK)
++ {
++ return NULL;
++ }
++#endif /* USE_REPLICATION */
+ return retval;
+ }
+
+diff -aruN postgresql-8.1.2/src/backend/utils/mb/mbutils.c
pgcluster-1.5.0rc7/src/backend/utils/mb/mbutils.c
+--- postgresql-8.1.2/src/backend/utils/mb/mbutils.c 2005-10-15
04:49:33.000000000 +0200
++++ pgcluster-1.5.0rc7/src/backend/utils/mb/mbutils.c 2006-01-10
17:39:34.000000000 +0100
+@@ -16,6 +16,9 @@
+ #include "utils/syscache.h"
+ #include "catalog/namespace.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
+ /*
+ * We handle for actual FE and BE encoding setting encoding-identificator
+ * and encoding-name too. It prevent searching and conversion from encoding
+@@ -404,6 +407,11 @@
+ dest_encoding;
+ FmgrInfo *flinfo;
+
++#ifdef USE_REPLICATION
++ if (PGR_Is_Replicated_Query)
++ return src;
++#endif /* USE_REPLICATION */
++
+ if (len <= 0)
+ return (char *) src;
+
+diff -aruN postgresql-8.1.2/src/backend/utils/misc/guc.c
pgcluster-1.5.0rc7/src/backend/utils/misc/guc.c
+--- postgresql-8.1.2/src/backend/utils/misc/guc.c 2005-11-22
19:23:24.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/utils/misc/guc.c 2006-01-23
04:55:40.000000000 +0100
+@@ -64,6 +64,14 @@
+ #include "utils/pg_locale.h"
+ #include "pgstat.h"
+
++#ifdef USE_REPLICATION
++#include "replicate.h"
++#endif /* USE_REPLICATION */
++
++#ifdef USE_REPLICATION
++bool check_constraint_with_lock = false;
++bool auto_lock_table = true;
++#endif /* USE_REPLICATION */
+
+ #ifndef PG_KRB_SRVTAB
+ #define PG_KRB_SRVTAB ""
+@@ -225,6 +233,9 @@
+ char *role_string;
+ char *session_authorization_string;
+
++#ifdef USE_REPLICATION
++static void ShowReplicationServerConfig(DestReceiver *dest);
++#endif /* USE_REPLICATION */
+
+ /*
+ * Displayable names for context types (enum GucContext)
+@@ -947,6 +958,32 @@
+ &pg_krb_caseins_users,
+ false, NULL, NULL
+ },
++#ifdef USE_REPLICATION
++ {
++ {"pgr_force_loadbalance", PGC_USERSET, CLIENT_CONN_STATEMENT,
++ gettext_noop("force loadbalance mode"),
++ NULL
++ },
++ &PGRforceLoadBalance,
++ false, NULL, NULL
++ },
++ {
++ {"check_constraint_with_lock", PGC_USERSET,
CLIENT_CONN_STATEMENT,
++ gettext_noop("check constrain with lock"),
++ NULL
++ },
++ &check_constraint_with_lock,
++ false, NULL, NULL
++ },
++ {
++ {"auto_lock_table", PGC_USERSET, CLIENT_CONN_STATEMENT,
++ gettext_noop("auto lock table"),
++ NULL
++ },
++ &auto_lock_table,
++ true, NULL, NULL
++ },
++#endif
+
+ {
+ {"escape_string_warning", PGC_USERSET,
COMPAT_OPTIONS_PREVIOUS,
+@@ -4544,6 +4581,10 @@
+ {
+ if (pg_strcasecmp(name, "all") == 0)
+ ShowAllGUCConfig(dest);
++#ifdef USE_REPLICATION
++ else if (strcasecmp(name, "replication_server") == 0)
++ ShowReplicationServerConfig(dest);
++#endif
+ else
+ ShowGUCConfigOption(name, dest);
+ }
+@@ -5976,5 +6017,72 @@
+ return nbuf;
+ }
+
++#ifdef USE_REPLICATION
++/*
++ * SHOW REPLICATION SERVER command
++ */
++static void
++ShowReplicationServerConfig(DestReceiver *dest)
++{
++ TupOutputState *tstate;
++ TupleDesc tupdesc;
++ char *values[4];
++ char buffer[256];
++ ReplicateServerInfo *sp;
++
++ /* need a tuple descriptor representing two TEXT columns */
++ tupdesc = CreateTemplateTupleDesc(4, false);
++ TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
++ TEXTOID, -1, 0 );
++ TupleDescInitEntry(tupdesc, (AttrNumber) 2, "host_name",
++ TEXTOID, -1, 0 );
++ TupleDescInitEntry(tupdesc, (AttrNumber) 3, "port_num",
++ TEXTOID, -1, 0 );
++ TupleDescInitEntry(tupdesc, (AttrNumber) 4, "recovery_port_num",
++ TEXTOID, -1, 0 );
++
++ /* prepare for projection of tuples */
++ tstate = begin_tup_output_tupdesc(dest, tupdesc);
++
++ sp = ReplicateServerData;
++ while (sp->useFlag != DATA_END) {
++ if (PGR_Check_Replicate_Server_Status(sp) == STATUS_ERROR) {
++ PGR_Set_Replication_Server_Status(sp, DATA_ERR);
++ }
++
++ sp++;
++ }
++
++ sp = ReplicateServerData;
++ while (sp->useFlag != DATA_END) {
++ if (sp->useFlag == DATA_USE) {
++ values[0] = "ALIVE";
++ } else if (sp->useFlag == DATA_ERR) {
++ values[0] = "DEAD";
++ } else if (sp->useFlag == DATA_INIT) {
++ values[0] = "STANDBY";
++ } else {
++ values[0] = "UNKNOWN";
++ }
++
++ values[1] = (char *) sp->hostName;
++
++ snprintf(buffer, sizeof(buffer), "%d", sp->portNumber);
++ values[2] = pstrdup(buffer);
++
++ snprintf(buffer, sizeof(buffer), "%d",
sp->recoveryPortNumber);
++ values[3] = pstrdup(buffer);
++
++ do_tup_output(tstate, values);
++
++ pfree(values[2]);
++ pfree(values[3]);
++
++ sp++;
++ }
++
++ end_tup_output(tstate);
++}
++#endif /* USE_REPLICATION */
+
+ #include "guc-file.c"
+diff -aruN postgresql-8.1.2/src/backend/utils/misc/postgresql.conf.sample
pgcluster-1.5.0rc7/src/backend/utils/misc/postgresql.conf.sample
+--- postgresql-8.1.2/src/backend/utils/misc/postgresql.conf.sample
2005-11-10 15:02:59.000000000 +0100
++++ pgcluster-1.5.0rc7/src/backend/utils/misc/postgresql.conf.sample
2006-01-10 17:39:38.000000000 +0100
+@@ -428,3 +428,11 @@
+ #---------------------------------------------------------------------------
+
+ #custom_variable_classes = '' # list of custom variable class names
++
++
++#---------------------------------------------------------------------------
++# PGCluster
++#---------------------------------------------------------------------------
++
++# auto_lock_table = false
++# check_constraint_with_lock = false
+diff -aruN postgresql-8.1.2/src/bin/initdb/initdb.c
pgcluster-1.5.0rc7/src/bin/initdb/initdb.c
+--- postgresql-8.1.2/src/bin/initdb/initdb.c 2005-11-22 19:23:26.000000000
+0100
++++ pgcluster-1.5.0rc7/src/bin/initdb/initdb.c 2006-01-10 17:39:49.000000000
+0100
+@@ -107,6 +107,11 @@
+ static char *info_schema_file;
+ static char *features_file;
+ static char *system_views_file;
++#ifdef USE_REPLICATION
++static char *cluster_conf_file;
++static char *pgreplicate_conf_file;
++static char *pglb_conf_file;
++#endif /* USE_REPLICATION */
+ static char *effective_user;
+ static bool made_new_pgdata = false;
+ static bool found_existing_pgdata = false;
+@@ -1284,6 +1289,14 @@
+
+ free(conflines);
+
++#ifdef USE_REPLICATION
++ /* cluster.conf */
++ conflines = readfile(cluster_conf_file);
++ snprintf(path, sizeof(path), "%s/cluster.conf", pg_data);
++ writefile(path, conflines);
++ chmod(path, 0600);
++ free(conflines);
++#endif /* USE_REPLICATION */
+ check_ok();
+ }
+
+@@ -2468,6 +2481,11 @@
+ set_input(&info_schema_file, "information_schema.sql");
+ set_input(&features_file, "sql_features.txt");
+ set_input(&system_views_file, "system_views.sql");
++#ifdef USE_REPLICATION
++ set_input(&cluster_conf_file, "cluster.conf.sample");
++ set_input(&pgreplicate_conf_file, "pgreplicate.conf.sample");
++ set_input(&pglb_conf_file, "pglb.conf.sample");
++#endif /* USE_REPLICATION */
+
+ set_info_version();
+
+@@ -2484,6 +2502,16 @@
+ effective_user, bki_file,
+ desc_file, conf_file,
+ hba_file, ident_file);
++#ifdef USE_REPLICATION
++ fprintf(stderr,
++ "PGCLUSTER_VERSION=%s\n"
++
"CLUSTER_CONF_SAMPLE=%s\nPGREPLICATE_CONF_SAMPLE=%s\n"
++ "PGLB_CONF_SAMPLE=%s\n",
++ PGCLUSTER_VERSION,
++ cluster_conf_file,
++ pgreplicate_conf_file,
++ pglb_conf_file);
++#endif /* USE_REPLICATION */
+ if (show_setting)
+ exit(0);
+ }
+@@ -2497,6 +2525,11 @@
+ check_input(info_schema_file);
+ check_input(features_file);
+ check_input(system_views_file);
++#ifdef USE_REPLICATION
++ check_input(cluster_conf_file);
++ check_input(pgreplicate_conf_file);
++ check_input(pglb_conf_file);
++#endif /* USE_REPLICATION */
+
+ setlocales();
+
+diff -aruN postgresql-8.1.2/src/bin/pg_dump/pg_dump.c
pgcluster-1.5.0rc7/src/bin/pg_dump/pg_dump.c
+--- postgresql-8.1.2/src/bin/pg_dump/pg_dump.c 2005-11-22 19:23:26.000000000
+0100
++++ pgcluster-1.5.0rc7/src/bin/pg_dump/pg_dump.c 2006-01-23
21:35:54.000000000 +0100
+@@ -111,6 +111,9 @@
+ /* flag to turn on/off dollar quoting */
+ static int disable_dollar_quoting = 0;
+
++#ifdef USE_REPLICATION
++ bool nonReplicate=true;
++#endif
+
+ static void help(const char *progname);
+ static NamespaceInfo *findNamespace(Oid nsoid, Oid objoid);
+@@ -220,6 +223,9 @@
+ {"column-inserts", no_argument, NULL, 'D'},
+ {"host", required_argument, NULL, 'h'},
+ {"ignore-version", no_argument, NULL, 'i'},
++#ifdef USE_REPLICATION
++ {"non-replicate", no_argument ,NULL, 'r'},
++#endif
+ {"no-reconnect", no_argument, NULL, 'R'},
+ {"oids", no_argument, NULL, 'o'},
+ {"no-owner", no_argument, NULL, 'O'},
+@@ -280,7 +286,7 @@
+ }
+ }
+
+- while ((c = getopt_long(argc, argv,
"abcCdDE:f:F:h:in:oOp:RsS:t:uU:vWxX:Z:",
++ while ((c = getopt_long(argc, argv,
"abcCdDE:f:F:h:in:oOp:rRsS:t:uU:vWxX:Z:",
+ long_options,
&optindex)) != -1)
+ {
+ switch (c)
+@@ -347,6 +353,12 @@
+ pgport = optarg;
+ break;
+
++#ifdef USE_REPLICATION
++ case 'r':
++ nonReplicate = true;
++ break;
++#endif
++
+ case 'R':
+ /* no-op, still accepted for backwards
compatibility */
+ break;
+@@ -514,6 +526,11 @@
+ /*
+ * Start serializable transaction to dump consistent data.
+ */
++#ifdef USE_REPLICATION
++ if(nonReplicate) {
++ do_sql_command(g_conn, "set pgr_force_loadbalance to on");
++ }
++#endif /* USE_REPLICATION */
+ do_sql_command(g_conn, "BEGIN");
+
+ do_sql_command(g_conn, "SET TRANSACTION ISOLATION LEVEL
SERIALIZABLE");
+@@ -685,6 +702,9 @@
+ printf(_(" -o, --oids include OIDs in dump\n"));
+ printf(_(" -O, --no-owner skip restoration of object
ownership\n"
+ " in plain text
format\n"));
++#ifdef USE_REPLICATION
++ printf(_(" -r, --non-replicate No queries replicate. Available
only in pgcluster.\n"));
++#endif
+ printf(_(" -s, --schema-only dump only the schema, no
data\n"));
+ printf(_(" -S, --superuser=NAME specify the superuser user name
to use in\n"
+ " plain text format\n"));
+diff -aruN postgresql-8.1.2/src/bin/pg_dump/pg_dumpall.c
pgcluster-1.5.0rc7/src/bin/pg_dump/pg_dumpall.c
+--- postgresql-8.1.2/src/bin/pg_dump/pg_dumpall.c 2005-10-15
04:49:39.000000000 +0200
++++ pgcluster-1.5.0rc7/src/bin/pg_dump/pg_dumpall.c 2006-01-10
17:39:48.000000000 +0100
+@@ -99,6 +99,9 @@
+ {"oids", no_argument, NULL, 'o'},
+ {"no-owner", no_argument, NULL, 'O'},
+ {"port", required_argument, NULL, 'p'},
++#ifdef USE_REPLICATION
++ {"non-replicate", no_argument ,NULL, 'r'},
++#endif
+ {"password", no_argument, NULL, 'W'},
+ {"schema-only", no_argument, NULL, 's'},
+ {"superuser", required_argument, NULL, 'S'},
+@@ -164,7 +167,7 @@
+
+ pgdumpopts = createPQExpBuffer();
+
+- while ((c = getopt_long(argc, argv, "acdDgh:ioOp:sS:U:vWxX:",
long_options, &optindex)) != -1)
++ while ((c = getopt_long(argc, argv, "acdDgh:ioOp:rsS:U:vWxX:",
long_options, &optindex)) != -1)
+ {
+ switch (c)
+ {
+@@ -218,6 +221,11 @@
+ #endif
+ break;
+
++#ifdef USE_REPLICATION
++ case 'r':
++ appendPQExpBuffer(pgdumpopts, " -r");
++ break;
++#endif /* USE_REPLICATION */
+ case 's':
+ schema_only = true;
+ appendPQExpBuffer(pgdumpopts, " -s");
+@@ -383,6 +391,9 @@
+ printf(_("\nConnection options:\n"));
+ printf(_(" -h, --host=HOSTNAME database server host or socket
directory\n"));
+ printf(_(" -p, --port=PORT database server port number\n"));
++#ifdef USE_REPLICATION
++ printf(_(" -r, --non-replicate No queries replicate. Available
only in pgcluster.\n"));
++#endif /* USE_REPLICATION */
+ printf(_(" -U, --username=NAME connect as specified database
user\n"));
+ printf(_(" -W, --password force password prompt (should
happen automatically)\n"));
+
+diff -aruN postgresql-8.1.2/src/bin/psql/sql_help.h
pgcluster-1.5.0rc7/src/bin/psql/sql_help.h
+--- postgresql-8.1.2/src/bin/psql/sql_help.h 2006-01-06 05:09:44.000000000
+0100
++++ pgcluster-1.5.0rc7/src/bin/psql/sql_help.h 2006-01-10 17:39:49.000000000
+0100
+@@ -3,7 +3,7 @@
+ * *** generated from the DocBook documentation.
+ *
+ * generated by
+- * /usr/local/bin/perl create_help.pl ../../../doc/src/sgml/ref
sql_help.h
++ * /usr/bin/perl create_help.pl ../../../doc/src/sgml/ref sql_help.h
+ *
+ */
+
+diff -aruN postgresql-8.1.2/src/include/catalog/catalog.h
pgcluster-1.5.0rc7/src/include/catalog/catalog.h
+--- postgresql-8.1.2/src/include/catalog/catalog.h 2005-10-15
04:49:42.000000000 +0200
++++ pgcluster-1.5.0rc7/src/include/catalog/catalog.h 2006-01-10
17:39:43.000000000 +0100
+@@ -35,5 +35,4 @@
+ extern Oid GetNewOidWithIndex(Relation relation, Relation indexrel);
+ extern Oid GetNewRelFileNode(Oid reltablespace, bool relisshared,
+ Relation pg_class);
+-
+ #endif /* CATALOG_H */
+diff -aruN postgresql-8.1.2/src/include/pg_config.h.in
pgcluster-1.5.0rc7/src/include/pg_config.h.in
+--- postgresql-8.1.2/src/include/pg_config.h.in 2005-12-06
03:29:26.000000000 +0100
++++ pgcluster-1.5.0rc7/src/include/pg_config.h.in 2006-01-10
17:39:43.000000000 +0100
+@@ -676,3 +676,7 @@
+ /* Define to empty if the keyword `volatile' does not work. Warning: valid
+ code using `volatile' can become incorrect without. Disable with care. */
+ #undef volatile
++
++/* PGCluster version */
++#undef PGCLUSTER_VERSION
++
+diff -aruN postgresql-8.1.2/src/include/pg_config.h.win32
pgcluster-1.5.0rc7/src/include/pg_config.h.win32
+--- postgresql-8.1.2/src/include/pg_config.h.win32 2006-01-05
05:02:26.000000000 +0100
++++ pgcluster-1.5.0rc7/src/include/pg_config.h.win32 2006-01-10
17:39:43.000000000 +0100
+@@ -5,6 +5,7 @@
+ */
+ #define PG_VERSION "8.1.2"
+ #define PG_VERSION_STR "8.1.2 (win32)"
++#define PGCLUSTER_VERSION "1.5.0"
+
+ #define DEF_PGPORT 5432
+ #define DEF_PGPORT_STR "5432"
+diff -aruN postgresql-8.1.2/src/include/replicate.h
pgcluster-1.5.0rc7/src/include/replicate.h
+--- postgresql-8.1.2/src/include/replicate.h 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/include/replicate.h 2006-01-31 13:12:04.000000000
+0100
+@@ -0,0 +1,198 @@
++/*-------------------------------------------------------------------------
++ *
++ * replicate.h
++ * Primary include file for replicate server .c files
++ *
++ * This should be the first file included by replicate modules.
++ *
++ *-------------------------------------------------------------------------
++ */
++#ifndef REPLICATE_H
++#define REPLICATE_H
++
++#ifndef _SYS_TIME_H
++#include <sys/time.h>
++#endif
++#include "tcop/dest.h"
++#include "storage/proc.h"
++#include "lib/stringinfo.h"
++#include "replicate_com.h"
++
++#define STAND_ALONE_TAG "When_Stand_Alone"
++#define NOT_REPLICATE_INFO_TAG "Not_Replicate_Info"
++#define DB_NAME_TAG "DB_Name"
++#define TABLE_NAME_TAG "Table_Name"
++
++#define CLUSTER_CONF_FILE "cluster.conf"
++#define NOT_SESSION_AUTHORIZATION (0)
++#define SESSION_AUTHORIZATION_BEGIN (1)
++#define SESSION_AUTHORIZATION_END (2)
++
++#define READ_ONLY_IF_STAND_ALONE "read_only"
++#define READ_WRITE_IF_STAND_ALONE "read_write"
++#define PERMIT_READ_ONLY (1)
++#define PERMIT_READ_WRITE (2)
++#define STATUS_REPLICATED (3)
++#define STATUS_CONTINUE (4)
++#define STATUS_CONTINUE_SELECT (5)
++#define STATUS_NOT_REPLICATE (6)
++#define STATUS_SKIP_QUERY (7)
++#define STATUS_REPLICATE (10)
++#define STATUS_RECOVERY (11)
++#define STATUS_REPLICATION_ABORT (98)
++#define STATUS_DEADLOCK_DETECT (99)
++
++#define TO_REPLICATION_SERVER (0)
++#define TO_FRONTEND (1)
++
++#define PGR_DEADLOCK_DETECTION_MSG "deadlock detected!"
++#define PGR_REPLICATION_ABORT_MSG "replication aborted!"
++#define SKIP_QUERY_1 "begin; select getdatabaseencoding(); commit"
++#define SKIP_QUERY_2 "BEGIN; SELECT usesuper FROM pg_catalog.pg_user WHERE
usename = '%s'; COMMIT"
++#define SKIP_QUERY_3 "SET autocommit TO 'on'"
++#define SKIP_QUERY_4 "SET search_path = public"
++#define SYS_QUERY_1 "set pgr_force_loadbalance to on"
++
++#define PGR_SEND_RETRY_CNT (3)
++#define PGR_SEND_WAIT_MSEC (150)
++#define PGR_RECV_RETRY_CNT (3)
++#define PGR_RECV_WAIT_MSEC (150)
++#define PGR_SEND_TIMEOUT (5)
++#define PGR_RECV_TIMEOUT (5)
++#define PGR_LOCK_RETRY_CNT (50)
++#define PGR_LOCK_WAIT_MSEC (20)
++
++#define PGR_1ST_RECOVERY (1)
++#define PGR_2ND_RECOVERY (2)
++
++typedef struct
++{
++ bool is_stand_alone;
++ int permit;
++} PGR_Stand_Alone_Type;
++
++typedef struct
++{
++ char db_name[DBNAME_MAX_LENGTH];
++ char table_name[TABLENAME_MAX_LENGTH];
++} PGR_Not_Replicate_Type;
++
++typedef struct
++{
++ bool check_lock_conflict;
++ bool deadlock;
++ int status_lock_conflict;
++ int dest;
++} PGR_Check_Lock_Type;
++
++typedef struct
++{
++ char * query_string;
++ int query_len;
++ char cmdSts;
++ char cmdType;
++ char useFlag;
++} PGR_Retry_Query_Type;
++
++
++/* replicaition log */
++typedef struct {
++ uint32_t PGR_Replicate_ID;
++ uint32_t PGR_Request_ID;
++} PGR_ReplicationLog_Info;
++
++
++extern char * Query_String;
++extern int TransactionQuery;
++extern int Transaction_Mode;
++extern int PGR_Noticed_Abort;
++extern bool Session_Authorization_Mode;
++extern bool Create_Temp_Table_Mode;
++extern int RecoveryPortNumber;
++extern char * RsyncPath;
++extern char * RsyncOption;
++extern bool RsyncCompress;
++extern int TransactionSock;
++extern ReplicateNow * ReplicateCurrentTime;
++extern CopyData * PGRCopyData;
++extern bool PGR_Copy_Data_Need_Replicate;
++extern PGR_Stand_Alone_Type * PGR_Stand_Alone;
++extern PGR_Not_Replicate_Type * PGR_Not_Replicate;
++extern int PGR_Not_Replicate_Rec_Num;
++extern bool autocommit;
++extern bool PGR_Is_Replicated_Query;
++extern PGR_Check_Lock_Type PGR_Check_Lock;
++extern int PGR_Sock_To_Replication_Server;
++extern bool PGR_Need_Notice;
++extern bool PGR_Lock_Noticed;
++extern ReplicateServerInfo * CurrentReplicateServer;
++extern ReplicateServerInfo * LastReplicateServer;
++extern char * PGRSelfHostName;
++extern int PGR_Pending_Sem_Num;
++extern int PGR_Response_Mode;
++extern bool PGR_Reliable_Mode_Wait;
++extern PGR_Retry_Query_Type PGR_Retry_Query;
++extern bool PGRforceLoadBalance;
++extern bool needToUpdateReplicateIdOnNextQueryIsDone;
++extern PGR_ReplicationLog_Info ReplicationLog_Info;
++extern bool PGR_Not_Replication_Query;
++extern bool PGR_Is_Sync_OID;
++
++/* in backend/libpq/replicate.c */
++extern int PGR_Init_Replicate_Server_Data(void);
++extern int PGR_Set_Replicate_Server_Socket(void);
++extern ReplicateServerInfo * PGR_get_replicate_server_info(void);
++extern char * PGR_Send_Replicate_Command(char * query_string, int
query_len, char cmdSts ,char cmdType);
++extern bool PGR_Is_Replicated_Command(char * query);
++extern int Xlog_Check_Replicate(int operation);
++extern int PGR_Replicate_Function_Call(void);
++extern void PGR_delete_shm(void);
++extern int PGR_replication(char * query_string, CommandDest dest, Node
*parsetree, const char * commandTag);
++extern bool PGR_Is_System_Command(char * query);
++extern int PGR_Call_System_Command(char * command);
++extern int PGR_GetTimeOfDay(struct timeval *tp,struct timezone *tpz);
++extern long PGR_Random(void);
++extern int PGR_Set_Current_Time(char * sec, char * usec);
++extern int PGR_Send_Copy(CopyData * copy, int end);
++extern CopyData * PGR_Set_Copy_Data(CopyData * copy, char *str, int len,
int end);
++extern char * PGR_scan_terminate( char * str);
++extern bool PGR_Is_Stand_Alone(void);
++extern void PGR_Send_Message_To_Frontend(const char * msg);
++extern void PGR_Notice_Transaction_Query_Done(void);
++extern void PGR_Notice_Transaction_Query_Aborted(void);
++extern int PGRsend_system_command(char cmdSts, char cmdType);
++extern int PGR_Notice_Conflict(void);
++extern int PGR_Recv_Trigger (int user_timeout);
++#if 0
++extern bool PGR_Check_DeadLock(LOCKMETHODTABLE *lockMethodTable, LOCKMODE
lockmode, LOCK *lock, PROCLOCK *holder);
++#endif
++extern void PGR_Set_Replication_Server_Status( ReplicateServerInfo * sp,
int status);
++extern int PGR_Is_Skip_Replication(char * query);
++extern bool PGR_Did_Commit_Transaction(void);
++extern int PGR_Set_Transaction_Mode(int mode,const char * commandTag);
++extern char * PGR_Remove_Comment(char * str);
++extern void PGR_Force_Replicate_Query(void);
++extern void PGR_Notice_DeadLock(void);
++extern void PGR_Set_Cluster_Status(int status);
++extern int PGR_Get_Cluster_Status(void);
++extern int PGR_Check_Replicate_Server_Status(ReplicateServerInfo * sp);
++extern int PGR_lo_import(char * filename);
++extern int PGR_lo_create(int flags);
++extern int PGR_lo_open(Oid lobjId,int32 mode);
++extern int PGR_lo_close(int32 fd);
++extern int PGR_lo_write(int fd, char *buf, int len);
++extern int PGR_lo_lseek(int32 fd, int32 offset, int32 whence);
++extern int PGR_lo_unlink(Oid lobjId);
++extern uint32_t PGRget_replication_id(void);
++extern Oid PGRGetNewObjectId(Oid last_id);
++extern int PGR_Send_Input_Message(char cmdType,StringInfo input_message);
++
++/* in backend/libpq/recovery.c */
++extern int PGR_Master_Main(void);
++extern int PGR_Recovery_Main(void);
++extern int PGR_recovery_error_send(void);
++extern int PGR_recovery_finish_send(void);
++
++/* in backend/access/transam/xact.c */
++extern void PGR_Reload_Start_Time(void);
++#endif /* REPLICATE_H */
+diff -aruN postgresql-8.1.2/src/include/replicate_com.h
pgcluster-1.5.0rc7/src/include/replicate_com.h
+--- postgresql-8.1.2/src/include/replicate_com.h 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/include/replicate_com.h 2006-01-29
22:45:17.000000000 +0100
+@@ -0,0 +1,388 @@
++/*-------------------------------------------------------------------------
++ *
++ * replicate.h
++ * Primary include file for replicate server .c files
++ *
++ * This should be the first file included by replicate modules.
++ *
++ *-------------------------------------------------------------------------
++ */
++#ifndef REPLICATE_COM_H
++#define REPLICATE_COM_H 1
++
++#ifndef _SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#ifndef _INTTYPES_H
++#include <inttypes.h>
++#endif
++#ifndef _NETINET_IN_H
++#include <netinet/in.h>
++#endif
++
++#include "pg_config.h"
++
++/* default values */
++#define DEFAULT_PGLB_PORT (6001)
++#define DEFAULT_PGLB_RECOVERY_PORT (6101)
++#define DEFAULT_PGLB_LIFECHECK_PORT (6201)
++#define DEFAULT_CLUSTER_PORT (5432)
++#define DEFAULT_CLUSTER_RECOVERY_PORT (7101)
++#define DEFAULT_CLUSTER_LIFECHECK_PORT (7201)
++#define DEFAULT_PGRP_PORT (8001)
++#define DEFAULT_PGRP_RECOVERY_PORT (8101)
++#define DEFAULT_PGRP_LIFECHECK_PORT (8201)
++#define DEFAULT_PGRP_RLOG_PORT (8301)
++#define MAX_DB_SERVER (32)
++
++/**************************
++* *
++* Packet ID definition *
++* *
++***************************/
++/*=========================
++ Replication packet id
++===========================*/
++#define CMD_SYS_REPLICATE 'R'
++/*-------------------------
++ Simple Query
++--------------------------*/
++#define CMD_STS_SET_SESSION_AUTHORIZATION 'S'
++#define CMD_STS_TRANSACTION 'T'
++#define CMD_STS_TEMP_TABLE 'E'
++#define CMD_STS_QUERY 'Q'
++#define CMD_STS_OTHER 'O'
++
++#define CMD_TYPE_VACUUM 'V'
++#define CMD_TYPE_ANALYZE 'A'
++#define CMD_TYPE_REINDEX 'N'
++#define CMD_TYPE_SELECT 'S'
++#define CMD_TYPE_EXPLAIN 'X'
++#define CMD_TYPE_SET 'T'
++#define CMD_TYPE_RESET 't'
++#define CMD_TYPE_INSERT 'I'
++#define CMD_TYPE_DELETE 'D'
++#define CMD_TYPE_EXECUTE 'U'
++#define CMD_TYPE_UPDATE 'U'
++#define CMD_TYPE_BEGIN 'B'
++#define CMD_TYPE_COMMIT 'E'
++#define CMD_TYPE_ROLLBACK 'R'
++#define CMD_TYPE_CONNECTION_CLOSE 'x'
++#define CMD_TYPE_SESSION_AUTHORIZATION_BEGIN 'a'
++#define CMD_TYPE_SESSION_AUTHORIZATION_END 'b'
++#define CMD_TYPE_SAVEPOINT 's'
++#define CMD_TYPE_ROLLBACK_TO_SAVEPOINT 'r'
++#define CMD_TYPE_RELEASE_SAVEPOINT 'l'
++#define CMD_TYPE_OTHER 'O'
++
++/*=========================
++ System call packet id
++===========================*/
++#define CMD_SYS_CALL 'S'
++#define CMD_SYS_PREREPLICATE 'Z'
++
++#define CMD_STS_NOTICE 'N'
++#define CMD_STS_RESPONSE 'R'
++#define CMD_STS_TRANSACTION_ABORT 'A'
++#define CMD_STS_QUERY_SUSPEND 'P'
++#define CMD_STS_QUERY_DONE 'D'
++
++#define CMD_TYPE_COMMIT_CONFIRM 'c'
++#define CMD_TYPE_QUERY_CONFIRM 'q'
++#define CMD_TYPE_DEADLOCK_DETECT 'd'
++#define CMD_TYPE_FRONTEND_CLOSED 'x'
++
++/*----------------------------
++ Copy Command
++------------------------------*/
++#define CMD_STS_COPY 'C'
++
++#define CMD_TYPE_COPY 'C'
++#define CMD_TYPE_COPY_DATA 'd'
++#define CMD_TYPE_COPY_DATA_END 'e'
++
++/*----------------------------
++ Large Object
++------------------------------*/
++#define CMD_STS_LARGE_OBJECT 'L'
++
++#define CMD_TYPE_LO_IMPORT 'I'
++#define CMD_TYPE_LO_CREATE 'C'
++#define CMD_TYPE_LO_OPEN 'O'
++#define CMD_TYPE_LO_WRITE 'W'
++#define CMD_TYPE_LO_LSEEK 'S'
++#define CMD_TYPE_LO_CLOSE 'X'
++#define CMD_TYPE_LO_UNLINK 'U'
++
++/*-------------------------
++ Prepare/Params Query
++--------------------------*/
++#define CMD_STS_PREPARE 'P'
++
++#define CMD_TYPE_P_PARSE 'P'
++#define CMD_TYPE_P_BIND 'B'
++#define CMD_TYPE_P_EXECUTE 'E'
++#define CMD_TYPE_P_FASTPATH 'F'
++#define CMD_TYPE_P_CLOSE 'C'
++#define CMD_TYPE_P_DESCRIBE 'D'
++#define CMD_TYPE_P_FLUSH 'H'
++#define CMD_TYPE_P_SYNC 'S'
++
++#define PGR_TRANSACTION_SOCKET (0)
++#define PGR_QUERY_SOCKET (1)
++
++#define DATA_FREE (0)
++#define DATA_INIT (1)
++#define DATA_USE (2)
++#define DATA_ERR (90)
++#define DATA_END (-1)
++#define HOSTNAME_MAX_LENGTH (128)
++#define DBNAME_MAX_LENGTH (128)
++#define USERNAME_MAX_LENGTH (128)
++#define TABLENAME_MAX_LENGTH (128)
++#define PATH_MAX_LENGTH (256)
++#define MAX_SERVER_NUM (128)
++#define MAX_RETRY_TIMES (3)
++#define MAX_SOCKET_QUEUE (100000)
++#define TRANSACTION_ERROR_RESULT "TRANSACTION_ERROR"
++#define REPLICATE_SERVER_SHM_KEY (1020)
++/* target -> replicate */
++#define RECOVERY_PREPARE_REQ (1)
++/* replicate -> master */
++#define RECOVERY_PGDATA_REQ (2)
++/* master -> replicate */
++#define RECOVERY_PGDATA_ANS (3)
++/* replicate -> target */
++#define RECOVERY_PREPARE_ANS (4)
++/* target -> replicate */
++#define RECOVERY_START_REQ (5)
++/* replicate -> master */
++#define RECOVERY_FSYNC_REQ (6)
++/* master -> replicate */
++#define RECOVERY_FSYNC_ANS (7)
++/* replicate -> target */
++#define RECOVERY_START_ANS (8)
++/* target -> replicate */
++#define RECOVERY_FINISH (9)
++#define RECOVERY_ERROR_OCCUPIED (100)
++#define RECOVERY_ERROR_CONNECTION (101)
++#define RECOVERY_ERROR_TARGET_ONLY (102)
++#define RECOVERY_ERROR_ANS (200)
++#define REPLICATION_SERVER_INFO_TAG "Replicate_Server_Info"
++#define HOST_NAME_TAG "Host_Name"
++#define PORT_TAG "Port"
++#define RECOVERY_PORT_TAG "Recovery_Port"
++#define LIFECHECK_PORT_TAG "LifeCheck_Port"
++#define RSYNC_PATH_TAG "Rsync_Path"
++#define RSYNC_OPTION_TAG "Rsync_Option"
++#define RSYNC_COMPRESS_TAG "Rsync_Compress"
++#define RECOVERY_INIT (0)
++#define RECOVERY_PREPARE_START (1)
++#define RECOVERY_START (2)
++#define RECOVERY_ERROR (99)
++
++/* response mode */
++#define PGR_FAST_MODE (0)
++#define PGR_NORMAL_MODE (1)
++#define PGR_RELIABLE_MODE (2)
++
++#define RECOVERY_TIMEOUT (600)
++#ifndef COMPLETION_TAG_BUFSIZE
++#define COMPLETION_TAG_BUFSIZE (128)
++#endif
++
++/* replicate log type */
++#define FROM_R_LOG_TYPE (1)
++#define FROM_C_DB_TYPE (2)
++#define CONNECTION_SUSPENDED_TYPE (3)
++
++#define PGR_SYSTEM_COMMAND_FUNC "PGR_SYSTEM_COMMAND_FUNCTION"
++#define PGR_STARTUP_REPLICATION_SERVER_FUNC_NO (1)
++#define PGR_CHANGE_REPLICATION_SERVER_FUNC_NO (2)
++#define PGR_SET_CURRENT_TIME_FUNC_NO (3)
++#define PGR_NOTICE_DEADLOCK_DETECTION_FUNC_NO (4)
++#define PGR_TRANSACTION_CONFIRM_ANSWER_FUNC_NO (5)
++#define PGR_RELIABLE_MODE_DONE_FUNC_NO (6)
++#define PGR_NOTICE_ABORT_FUNC_NO (7)
++#define PGR_SET_CURRENT_REPLICATION_QUERY_ID_NO (8)
++#define PGR_QUERY_CONFIRM_ANSWER_FUNC_NO (9)
++#define PGR_GET_OID_FUNC_NO (10)
++#define PGR_SET_OID_FUNC_NO (11)
++
++#define PGR_CMD_ARG_NUM (10)
++#define PGR_LOCK_CONFLICT_NOTICE_CMD "PGR_LOCK_CONFLICT_NOTICE_CMD"
++#define PGR_DEADLOCK_DETECT_NOTICE_CMD
"PGR_DEADLOCK_DETECT_NOTICE_CMD"
++#define PGR_QUERY_DONE_NOTICE_CMD "PGR_QUERY_DONE_NOTICE_CMD"
++#define PGR_QUERY_ABORTED_NOTICE_CMD "PGR_QUERY_ABORTED_NOTICE_CMD"
++#define PGR_RETRY_LOCK_QUERY_CMD "PGR_RETRY_LOCK_QUERY_CMD"
++#define PGR_NOT_YET_REPLICATE_NOTICE_CMD
"PGR_NOT_YET_REPLICATE_NOTICE_CMD"
++#define PGR_ALREADY_REPLICATED_NOTICE_CMD
"PGR_ALREADY_REPLICATED_NOTICE_CMD"
++#define PGR_NOT_YET_COMMIT (0)
++#define PGR_ALREADY_COMMITTED (1)
++
++#define COPYBUFSIZ (8192)
++#define MAX_WORDS (24)
++#define MAX_WORD_LETTERS (48)
++#define PGR_MESSAGE_BUFSIZE (128)
++#define INT_LENGTH (12)
++#define PGR_MAX_COUNTER (0x0FFFFFFF)
++#define PGR_GET_OVER_FLOW_FILTER (0xF0000000)
++#define PGR_GET_DATA_FILTER (0x0FFFFFFF)
++#define PGR_SET_OVER_FLOW (0x10000000)
++#define PGR_MIN_COUNTER (0x0000000F)
++
++#define STRCMP(x,y) (strncmp(x,y,strlen(y)))
++
++/* life check target */
++#define SYN_TO_LOAD_BALANCER (0)
++#define SYN_TO_CLUSTER_DB (1)
++#define SYN_TO_REPLICATION_SERVER (2)
++#define LIFE_CHECK_TRY_COUNT (2)
++#define LIFE_CHECK_STOP (0)
++#define LIFE_CHECK_START (1)
++
++#ifndef HAVE_UNION_SEMUN
++union semun {
++ int val;
++ struct semid_ds *buf;
++ unsigned short int *array;
++ struct seminfo *__buf;
++};
++#endif
++
++typedef struct ReplicateHeaderType
++{
++ char cmdSys;
++ char cmdSts; /*
++ Q:query
++ T:transaction
++ */
++ char cmdType; /*
++ S:select
++ I:insert
++ D:delete
++ U:update
++ B:begin
++ E:commit/rollback/end
++ O:others
++ */
++ char rlog; /*
++ -- kind of replication log --
++ 1: send from replication log
++ 2: send from cluster db
(should be retry)
++ 3: connection suspended
++ */
++ uint16_t port;
++ uint16_t pid;
++ uint32_t query_size;
++ char from_host[HOSTNAME_MAX_LENGTH];
++ char dbName[DBNAME_MAX_LENGTH];
++ char userName[USERNAME_MAX_LENGTH];
++ struct timeval tv;
++ uint32_t query_id;
++ int isAutoCommit; /* 0 if autocommit is off. 1 if autocommit is on */
++ uint32_t request_id;
++ uint32_t replicate_id;
++} ReplicateHeader;
++
++typedef struct RecoveryPacketType
++{
++ uint16_t packet_no; /*
++ 1:start recovery prepare
++ 2:ask pgdata
++ 3:ans pgdata
++ 4:send master info
++ 5:start queueing query
++ 6:requst fsync
++ 7:ready to fsync
++ 8:pepared master
++ 9:finished rsync
++ */
++ uint16_t max_connect;
++ uint16_t port;
++ uint16_t recoveryPort;
++ char hostName[HOSTNAME_MAX_LENGTH];
++ char pg_data[PATH_MAX_LENGTH];
++ char userName[USERNAME_MAX_LENGTH];
++} RecoveryPacket;
++
++typedef struct
++{
++ char table[128];
++ int rec_no;
++ char key[128];
++ char value[128];
++ char * last;
++ char * next;
++} ConfDataType;
++
++
++typedef struct ReplicateServerInfoType
++{
++ uint32_t useFlag;
++ char hostName[HOSTNAME_MAX_LENGTH];
++ uint16_t portNumber;
++ uint16_t recoveryPortNumber;
++ uint16_t lifecheckPortNumber;
++ uint16_t RLogPortNumber;
++ uint32_t sock;
++ uint32_t rlog_sock;
++ uint32_t replicate_id;
++ uint16_t response_mode;
++} ReplicateServerInfo;
++
++
++typedef struct ReplicateNowType
++{
++ uint32_t replicate_id;
++ int useFlag;
++ int use_seed;
++ int use_time;
++ int offset_sec;
++ int offset_usec;
++ struct timeval tp;
++} ReplicateNow;
++
++typedef struct CopyDataType
++{
++ int cnt;
++ char copy_data[COPYBUFSIZ];
++} CopyData;
++
++typedef struct ClusterDBInfoType
++{
++ int status;
++} ClusterDBInfo;
++
++typedef struct
++{
++ uint32_t arg1;
++ uint32_t arg2;
++ uint32_t arg3;
++ char buf[1];
++} LOArgs;
++
++extern ConfDataType * ConfData_Top;
++extern ConfDataType * ConfData_End;
++extern ReplicateServerInfo * ReplicateServerData;
++extern ClusterDBInfo * ClusterDBData;
++extern int ReplicateServerShmid;
++extern int ClusterDBShmid;
++
++
++/* in backend/libpq/replicate_com.c */
++extern int PGR_Create_Socket_Connect(int * fdP, char * hostName , unsigned
short portNumber);
++extern void PGR_Close_Sock(int * sock);
++extern int PGR_Create_Socket_Bind(int * fdP, char * hostName , unsigned
short portNumber);
++extern int PGR_Create_Acception(int fd, int * sockP, char * hostName ,
unsigned short portNumber);
++extern int PGR_Free_Conf_Data(void);
++extern int PGR_Get_Conf_Data(char * dir , char * fname);
++extern void PGRset_recovery_packet_no(RecoveryPacket * packet, int
packet_no);
++extern unsigned int PGRget_ip_by_name(char * host);
++extern void PGRwrite_log_file(FILE * fp, const char * fmt,...);
++extern void show_debug(const char * fmt,...);
++extern void show_error(const char * fmt,...);
++
++
++
++#endif /* REPLICATE_COM_H */
+diff -aruN postgresql-8.1.2/src/include/storage/lmgr.h
pgcluster-1.5.0rc7/src/include/storage/lmgr.h
+--- postgresql-8.1.2/src/include/storage/lmgr.h 2005-10-15
04:49:46.000000000 +0200
++++ pgcluster-1.5.0rc7/src/include/storage/lmgr.h 2006-01-10
17:39:43.000000000 +0100
+@@ -15,6 +15,7 @@
+ #define LMGR_H
+
+ #include "storage/lock.h"
++#include "storage/bufmgr.h"
+ #include "utils/rel.h"
+
+
+@@ -85,4 +86,5 @@
+ extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
+ LOCKMODE lockmode);
+
++extern void XactLockTableWaitForCluster(TransactionId xid,Buffer buffer);
+ #endif /* LMGR_H */
+diff -aruN postgresql-8.1.2/src/include/storage/proc.h
pgcluster-1.5.0rc7/src/include/storage/proc.h
+--- postgresql-8.1.2/src/include/storage/proc.h 2005-10-15
04:49:46.000000000 +0200
++++ pgcluster-1.5.0rc7/src/include/storage/proc.h 2006-01-10
17:39:43.000000000 +0100
+@@ -90,6 +90,9 @@
+ * awaited by
this backend */
+
+ struct XidCache subxids; /* cache for subtransaction XIDs */
++#ifdef USE_REPLICATION
++ unsigned int replicationId; /* id for replication. */
++#endif
+ };
+
+ /* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */
+diff -aruN postgresql-8.1.2/src/interfaces/libpq/Makefile
pgcluster-1.5.0rc7/src/interfaces/libpq/Makefile
+--- postgresql-8.1.2/src/interfaces/libpq/Makefile 2005-08-29
02:47:35.000000000 +0200
++++ pgcluster-1.5.0rc7/src/interfaces/libpq/Makefile 2006-01-10
17:39:43.000000000 +0100
+@@ -30,7 +30,7 @@
+
+ OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
+ fe-protocol2.o fe-protocol3.o pqexpbuffer.o pqsignal.o fe-secure.o \
+- md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o thread.o \
++ dllist.o md5.o ip.o wchar.o encnames.o noblock.o pgstrcasecmp.o
thread.o \
+ $(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o
strerror.o, $(LIBOBJS))
+
+ ifeq ($(PORTNAME), cygwin)
+@@ -86,6 +86,9 @@
+ encnames.c wchar.c : % : $(backend_src)/utils/mb/%
+ rm -f $@ && $(LN_S) $< .
+
++dllist.c : % : $(backend_src)/lib/dllist.c
++ rm -f $@ && $(LN_S) $< .
++
+
+ # We need several not-quite-identical variants of .DEF files to build libpq
+ # DLLs for Windows. These are made from the single source file exports.txt.
+@@ -144,7 +147,7 @@
+ rm -f $(DESTDIR)$(includedir)/libpq-fe.h
$(DESTDIR)$(includedir_internal)/libpq-int.h
$(DESTDIR)$(includedir_internal)/pqexpbuffer.h
+
+ clean distclean: clean-lib
+- rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c
noblock.c pgstrcasecmp.c snprintf.c strerror.c open.c thread.c md5.c ip.c
encnames.c wchar.c pthread.h
++ rm -f $(OBJS) pg_config_paths.h crypt.c getaddrinfo.c inet_aton.c
noblock.c pgstrcasecmp.c snprintf.c strerror.c open.c thread.c md5.c ip.c
encnames.c wchar.c pthread.h dllist.c
+ rm -f pg_config_paths.h # Might be left over from a Win32 client-only
build
+
+ maintainer-clean: distclean
+diff -aruN postgresql-8.1.2/src/interfaces/libpq/libpq.rc
pgcluster-1.5.0rc7/src/interfaces/libpq/libpq.rc
+--- postgresql-8.1.2/src/interfaces/libpq/libpq.rc 2006-01-06
05:09:37.000000000 +0100
++++ pgcluster-1.5.0rc7/src/interfaces/libpq/libpq.rc 2006-02-09
23:03:29.000000000 +0100
+@@ -1,8 +1,8 @@
+ #include <winver.h>
+
+ VS_VERSION_INFO VERSIONINFO
+- FILEVERSION 8,1,2,6006
+- PRODUCTVERSION 8,1,2,6006
++ FILEVERSION 8,1,2,6040
++ PRODUCTVERSION 8,1,2,6040
+ FILEFLAGSMASK 0x3fL
+ FILEFLAGS 0
+ FILEOS VOS__WINDOWS32
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.aix
pgcluster-1.5.0rc7/src/makefiles/Makefile.aix
+--- postgresql-8.1.2/src/makefiles/Makefile.aix 2005-10-28
19:32:22.000000000 +0200
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.aix 2006-01-10
17:39:48.000000000 +0100
+@@ -36,3 +36,5 @@
+ $(CC) $(LDFLAGS) $(LDFLAGS_SL) -o $@ $*.o -Wl,-bE:$*$(EXPSUFF)
$(SHLIB_LINK)
+
+ sqlmansect = 7
++CFLAGS += -pthread
++LDFLAGS += -L/usr/lib/threads
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.freebsd
pgcluster-1.5.0rc7/src/makefiles/Makefile.freebsd
+--- postgresql-8.1.2/src/makefiles/Makefile.freebsd 2005-08-24
22:20:38.000000000 +0200
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.freebsd 2006-01-10
17:39:48.000000000 +0100
+@@ -28,3 +28,5 @@
+ endif
+
+ sqlmansect = 7
++
++LIBS += -lc_r
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.hpux
pgcluster-1.5.0rc7/src/makefiles/Makefile.hpux
+--- postgresql-8.1.2/src/makefiles/Makefile.hpux 2004-12-21
19:47:42.000000000 +0100
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.hpux 2006-01-10
17:39:48.000000000 +0100
+@@ -16,6 +16,9 @@
+ # correctly in the LP64 data model.
+ LIBS := -lxnet $(LIBS)
+
++# add thread lib for PGCluster
++LIBS := -lpthread $(LIBS)
++
+ # Set up rpath so that the executables don't need SHLIB_PATH to be set.
+ # (Note: --disable-rpath is a really bad idea on this platform...)
+ ifeq ($(with_gnu_ld), yes)
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.linux
pgcluster-1.5.0rc7/src/makefiles/Makefile.linux
+--- postgresql-8.1.2/src/makefiles/Makefile.linux 2004-12-21
19:47:42.000000000 +0100
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.linux 2006-01-10
17:39:48.000000000 +0100
+@@ -14,3 +14,4 @@
+ $(CC) -shared -o $@ $<
+
+ sqlmansect = 7
++LIBS += -lpthread
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.netbsd
pgcluster-1.5.0rc7/src/makefiles/Makefile.netbsd
+--- postgresql-8.1.2/src/makefiles/Makefile.netbsd 2004-12-21
19:47:42.000000000 +0100
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.netbsd 2006-01-10
17:39:48.000000000 +0100
+@@ -30,3 +30,4 @@
+ endif
+
+ sqlmansect = 7
++LIBS += -lpthread
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.openbsd
pgcluster-1.5.0rc7/src/makefiles/Makefile.openbsd
+--- postgresql-8.1.2/src/makefiles/Makefile.openbsd 2004-12-21
19:47:42.000000000 +0100
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.openbsd 2006-01-10
17:39:48.000000000 +0100
+@@ -28,3 +28,4 @@
+ endif
+
+ sqlmansect = 7
++LIBS += -lc_r
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.solaris
pgcluster-1.5.0rc7/src/makefiles/Makefile.solaris
+--- postgresql-8.1.2/src/makefiles/Makefile.solaris 2004-12-21
19:47:42.000000000 +0100
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.solaris 2006-01-10
17:39:48.000000000 +0100
+@@ -20,3 +20,4 @@
+ $(LD) -G -Bdynamic -o $@ $<
+
+ sqlmansect = 5sql
++LIBS += -lpthread
+diff -aruN postgresql-8.1.2/src/makefiles/Makefile.sunos4
pgcluster-1.5.0rc7/src/makefiles/Makefile.sunos4
+--- postgresql-8.1.2/src/makefiles/Makefile.sunos4 2002-09-05
00:54:18.000000000 +0200
++++ pgcluster-1.5.0rc7/src/makefiles/Makefile.sunos4 2006-01-10
17:39:48.000000000 +0100
+@@ -11,3 +11,4 @@
+ $(LD) -assert pure-text -Bdynamic -o $@ $<
+
+ sqlmansect = 7
++LIBS += -lpthread
+diff -aruN postgresql-8.1.2/src/pgcluster/Makefile
pgcluster-1.5.0rc7/src/pgcluster/Makefile
+--- postgresql-8.1.2/src/pgcluster/Makefile 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/Makefile 2006-01-10 17:39:44.000000000
+0100
+@@ -0,0 +1,17 @@
++#-------------------------------------------------------------------------
++#
++# Makefile for src/pgcluster (client programs)
++#
++#-------------------------------------------------------------------------
++
++subdir = src/pgcluster
++top_builddir = ../..
++include $(top_builddir)/src/Makefile.global
++
++DIRS := libpgc pgrp pglb tool
++
++all install installdirs uninstall depend distprep:
++ @for dir in $(DIRS); do $(MAKE) -C $$dir $@ || exit; done
++
++clean distclean maintainer-clean:
++ -@for dir in $(DIRS); do $(MAKE) -C $$dir $@; done
+diff -aruN postgresql-8.1.2/src/pgcluster/libpgc/Makefile
pgcluster-1.5.0rc7/src/pgcluster/libpgc/Makefile
+--- postgresql-8.1.2/src/pgcluster/libpgc/Makefile 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/libpgc/Makefile 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,32 @@
++#-------------------------------------------------------------------------
++#
++# Makefile--
++# Makefile for libpq subsystem (backend half of libpq interface)
++#
++# IDENTIFICATION
++# $Header: /cvsroot/pgsql-server/src/backend/libpq/Makefile,v 1.33
2002/06/14 04:23:17 momjian Exp $
++#
++#-------------------------------------------------------------------------
++
++subdir = src/pgcluster/libpgc
++top_builddir = ../../..
++include $(top_builddir)/src/Makefile.global
++
++OBJS = sem.o show.o
++
++all: SUBSYS.o
++
++SUBSYS.o: $(OBJS)
++ $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS)
++
++depend dep:
++ $(CC) -MM $(CFLAGS) *.c >depend
++
++distclean: clean
++
++clean:
++ rm -f SUBSYS.o $(OBJS)
++
++ifeq (depend,$(wildcard depend))
++include depend
++endif
+diff -aruN postgresql-8.1.2/src/pgcluster/libpgc/libpgc.h
pgcluster-1.5.0rc7/src/pgcluster/libpgc/libpgc.h
+--- postgresql-8.1.2/src/pgcluster/libpgc/libpgc.h 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/libpgc/libpgc.h 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,22 @@
++/*-------------------------------------------------------------------------
++ *
++ * lilbpgc.h
++ * external definition of the function for pgreplicate and pglb
++ *
++ * This should be the first file included by replicate modules.
++ *
++ *-------------------------------------------------------------------------
++ */
++#ifndef LIBPGC_H
++#define LIBPGC_H
++
++/* external definition of the function in sem.c */
++extern void PGRsem_unlock( int semid, short sem_num );
++extern void PGRsem_lock( int semid, short sem_num );
++
++/* external definition of the function in show.c */
++extern void show_debug(const char * fmt,...);
++extern void show_error(const char * fmt,...);
++extern void PGRwrite_log_file(FILE * fp, const char * fmt,...);
++
++#endif /* LIBPGC_H */
+diff -aruN postgresql-8.1.2/src/pgcluster/libpgc/sem.c
pgcluster-1.5.0rc7/src/pgcluster/libpgc/sem.c
+--- postgresql-8.1.2/src/pgcluster/libpgc/sem.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/libpgc/sem.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,67 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * sem.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at pgreplicate for the semapho control.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#include <stdio.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <errno.h>
++#include <sys/ipc.h>
++#include <sys/sem.h>
++#include <signal.h>
++
++extern void show_debug(const char * fmt,...);
++
++void PGRsem_unlock( int semid, short sem_num );
++void PGRsem_lock( int semid, short sem_num );
++
++#define PGR_SEM_UNLOCK_WAIT_MSEC (100)
++#define PGR_SEM_LOCK_WAIT_MSEC (500)
++
++void
++PGRsem_unlock( int semid, short sem_num )
++{
++ int status = 0;
++ struct sembuf sops;
++
++ sops.sem_num = sem_num;
++ sops.sem_op = 1;
++ /*sops.sem_flg = IPC_NOWAIT;*/
++ sops.sem_flg = 0;
++ do
++ {
++ status = semop(semid, &sops, 1);
++ if ((status == -1) && (errno != EINTR))
++ {
++ usleep(PGR_SEM_UNLOCK_WAIT_MSEC);
++ }
++ } while (status == -1);
++}
++
++void
++PGRsem_lock( int semid, short sem_num )
++{
++ int status = 0;
++ struct sembuf sops;
++
++ sops.sem_num = sem_num;
++ sops.sem_op = -1;
++ /*sops.sem_flg = IPC_NOWAIT;*/
++ sops.sem_flg = 0;
++ do
++ {
++ status = semop(semid, &sops, 1);
++ if ((status == -1) && (errno != EINTR))
++ {
++ usleep(PGR_SEM_LOCK_WAIT_MSEC);
++ }
++ } while (status == -1);
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/libpgc/show.c
pgcluster-1.5.0rc7/src/pgcluster/libpgc/show.c
+--- postgresql-8.1.2/src/pgcluster/libpgc/show.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/libpgc/show.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,119 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * show.c
++ *
++ * NOTE:
++ * This file is composed of the logging and debug functions
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#include <stdio.h>
++#include <stdarg.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <string.h>
++#include <unistd.h>
++#include <time.h>
++#include <sys/time.h>
++#include <sys/types.h>
++
++#define TIMESTAMP_SIZE 19 /* format `YYYY-MM-DD HH:MM:SS' */
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++void show_debug(const char * fmt,...);
++void show_error(const char * fmt,...);
++void PGRwrite_log_file(FILE * fp, const char * fmt,...);
++
++extern int Debug_Print;
++extern int Log_Print;
++extern FILE * LogFp;
++
++static char*
++get_current_timestamp(void)
++{
++ time_t now;
++ static char buf[TIMESTAMP_SIZE + 1];
++
++ now = time(NULL);
++ strftime(buf, sizeof(buf),
++ "%Y-%m-%d %H:%M:%S", localtime(&now));
++ return buf;
++}
++
++void
++show_debug(const char * fmt,...)
++{
++ va_list ap;
++ char *timestamp;
++
++ if (Debug_Print)
++ {
++ timestamp = get_current_timestamp();
++ fprintf(stdout,"%s [%d] DEBUG:",timestamp, getpid());
++ va_start(ap,fmt);
++ vfprintf(stdout,fmt,ap);
++ va_end(ap);
++ fprintf(stdout,"\n");
++ fflush(stdout);
++ }
++}
++
++void
++show_error(const char * fmt,...)
++{
++ va_list ap;
++ char buf[256], *timestamp;
++
++ if (Debug_Print)
++ {
++ timestamp = get_current_timestamp();
++ fprintf(stderr,"%s [%d] ERROR:",timestamp, getpid());
++ va_start(ap,fmt);
++ vfprintf(stderr,fmt,ap);
++ va_end(ap);
++ fprintf(stderr,"\n");
++ fflush(stderr);
++ }
++ if ((Log_Print) && (LogFp != NULL))
++ {
++ va_start(ap,fmt);
++ vsnprintf(buf,sizeof(buf),fmt,ap);
++ va_end(ap);
++ PGRwrite_log_file(LogFp,buf);
++ }
++}
++
++void
++PGRwrite_log_file(FILE * fp, const char * fmt,...)
++{
++ char buf[256];
++ char log[288];
++ char * timestamp;
++ va_list ap;
++ time_t t;
++
++ if (fp == NULL)
++ {
++ return;
++ }
++ if (time(&t) < 0)
++ {
++ return;
++ }
++ timestamp = get_current_timestamp();
++ snprintf(log,sizeof(log),"%s ",timestamp);
++ va_start(ap,fmt);
++ vsnprintf(buf,sizeof(buf),fmt,ap);
++ va_end(ap);
++ strcat(log,buf);
++ strcat(log,"\n");
++ if (fputs(log,fp) >= 0)
++ {
++ fflush(fp);
++ }
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/AUTHORS
pgcluster-1.5.0rc7/src/pgcluster/pglb/AUTHORS
+--- postgresql-8.1.2/src/pgcluster/pglb/AUTHORS 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/AUTHORS 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,3 @@
++Authors of pglb
++
++pglb was written by Atsushi Mitani
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/COPYING
pgcluster-1.5.0rc7/src/pgcluster/pglb/COPYING
+--- postgresql-8.1.2/src/pgcluster/pglb/COPYING 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/COPYING 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,14 @@
++Copyright (c) 2003 Atsushi Mitani
++
++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.
++
++Portions copyright (c) 2003, Tatsuo Ishii
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/Makefile
pgcluster-1.5.0rc7/src/pgcluster/pglb/Makefile
+--- postgresql-8.1.2/src/pgcluster/pglb/Makefile 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/Makefile 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,38 @@
++#-------------------------------------------------------------------------
++#
++# Makefile for src/pgcluster/pgrp
++#
++#-------------------------------------------------------------------------
++
++subdir = src/pgcluster/pglb
++top_builddir = ../../..
++include $(top_builddir)/src/Makefile.global
++
++OBJS= child.o cluster_table.o load_balance.o main.o pool_auth.o \
++ pool_connection_pool.o pool_process_query.o pool_stream.o \
++ pool_params.o recovery.o socket.o
++
++EXTRA_OBJS = $(top_builddir)/src/backend/libpq/replicate_com.o
../libpgc/SUBSYS.o
++
++CFLAGS += -DPRINT_DEBUG
++override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) -DBINDIR=\"$(bindir)\"
++
++all: pglb
++
++pglb: $(OBJS) $(libpq_builddir)/libpq.a
++ $(CC) $(CFLAGS) $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o
$@
++
++install: all installdirs
++ $(INSTALL_PROGRAM) pglb$(X) $(DESTDIR)$(bindir)/pglb$(X)
++ $(INSTALL_DATA) pglb.conf.sample
$(DESTDIR)$(datadir)/pglb.conf.sample
++
++installdirs:
++ $(mkinstalldirs) $(DESTDIR)$(bindir)
++ $(mkinstalldirs) $(DESTDIR)$(datadir)
++
++uninstall:
++ rm -f $(addprefix $(DESTDIR)$(bindir)/, pglb$(X))
++ rm -f $(DESTDIR)$(datadir)/pglb.conf.sample
++
++clean distclean maintainer-clean:
++ rm -f pglb$(X) $(OBJS)
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/README
pgcluster-1.5.0rc7/src/pgcluster/pglb/README
+--- postgresql-8.1.2/src/pgcluster/pglb/README 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/README 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,276 @@
++PGCluster Installation Instructions
++
++=============================================================
++1. Installation
++=============================================================
++
++1-1. Install Cluster DB Server, Replication Server & Load Balancer
++----------------------------------------------------------------
++$ cd $source_dir
++$ ./configure
++$ gmake
++$ su
++# gmake install
++# chown -R postgres /usr/local/pgsql
++----------------------------------------------------------------
++
++=============================================================
++2. Initialize DB
++=============================================================
++$ su
++# adduser postgres
++# mkdir /usr/local/pgsql/data
++# chown postgres /usr/local/pgsql/data
++# su - postgres
++$ /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
++
++
++=============================================================
++3. Configuration
++=============================================================
++(EX.System Composition)
++
++ |
++ ((Load Balance Server))
++ ( hostname: lb.hoo.com)
++ ( receive port:5432 )
++ ( recovery port:6001 )
++ |
++----------+-------------+------------+----------
++ | |
++ (( Cluster DB 1 )) (( Cluster DB 2 ))
++ ( hostname:c1.hoo.com) ( hostname:c2.hoo.com)
++ ( receive port: 5432 ) ( receive port:5432 )
++ ( recovery port:7001 ) ( recovery port 7002 )
++ | |
++----------+-------------+------------+----------
++ |
++ ((Replication Server))
++ ( hostname:pgr.hoo.com)
++ ( receive port:8001 )
++ ( recovery port:8101 )
++
++
++3-1. Load Balance Server
++
++The setup file of load balance server is copied from the sample file and
edited.
++(the sample file is installed '/usr/local/pgsql/etc' in default)
++----------------------------------------------------------------
++$cd /usr/local/pgsql/etc
++$cp pglb.conf.sample pglb.conf
++----------------------------------------------------------------
++
++In the case of the above system composition example,
++the setup example of pglb.conf file is as the following
++
++#============================================================
++# Load Balance Server configuration file
++#-------------------------------------------------------------
++# file: pglb.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts are db cluster server
++# o which port use connect to db cluster server
++# o how many connections are allowed on each DB server
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Max_Connection : maximun number of connection to postmaster
++#-------------------------------------------------------------
++<Cluster_Server_Info>
++ <Host_Name> c1.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Max_Connect> 32 </Max_Connect>
++</Cluster_Server_Info>
++<Cluster_Server_Info>
++ <Host_Name> c2.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Max_Connect> 32 </Max_Connect>
++</Cluster_Server_Info>
++#-------------------------------------------------------------
++# set Load Balance server information
++# o Receive_Port : connection from client
++# o Recovery_Port : connection for recovery process
++# o Max_Cluster_Num : maximun number of cluster DB servers
++# o Use_Connection_Pooling : use connection pool [yes/no]
++# o Max_Pool_Each_Server : number of pool connections/DB server
++#-------------------------------------------------------------
++<Receive_Port> 5432 </Receive_Port>
++<Recovery_Port> 6001 </Recovery_Port>
++<Max_Cluster_Num> 128 </Max_Cluster_Num>
++<Use_Connection_Pooling> yes </Use_Connection_Pooling>
++<Max_Pool_Each_Server> 1 </Max_Pool_Each_Server>
++
++3-2. Cluster DB Server
++
++The Cluster DB server need edit two configuration files
++('pg_hba.conf' and 'cluster.conf').
++These files are create under the $PG_DATA directory after 'initdb'.
++
++A. pg_hba.conf
++Permission to connect DB via IP connectoins is need for this system.
++
++B. cluster.conf
++In the case of the above system composition example,
++the setup example of cluster.conf file is as the following
++
++#============================================================
++# Cluster DB Server configuration file
++#-------------------------------------------------------------
++# file: cluster.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts & port are replication server
++# o which port use for replication request to replication server
++# o which command use for recovery function
++#
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Recovery_Port : connection for recovery process
++#-------------------------------------------------------------
++<Replicate_Server_Info>
++ <Host_Name> pgr.hoo.com </Host_Name>
++ <Port> 8001 </Port>
++ <Recovery_Port> 8101 </Recovery_Port>
++</Replicate_Server_Info>
++#-------------------------------------------------------------
++# set Cluster DB Server information
++# o Recovery_Port : connection for recovery
++# o Rsync_Path : path of rsync command
++# o Rsync_Option : file transfer option for rsync
++# o When_Stand_Alone : When all replication servers fell,
++# you can set up two kinds of permittion,
++# "real_only" or "read_write".
++#-------------------------------------------------------------
++<Recovery_Port> 7001 </Recovery_Port>
++<Rsync_Path> /usr/bin/rsync </Rsync_Path>
++<Rsync_Option> ssh -1 </Rsync_Option>
++<When_Stand_Alone> read_only </When_Stand_Alone>
++#-------------------------------------------------------------
++# set partitional replicate control information
++# set DB name and Table name to stop reprication
++# o DB_Name : DB name
++# o Table_Name : table name
++#-------------------------------------------------------------
++#<Not_Replicate_Info>
++# <DB_Name> test_db </DB_Name>
++# <Table_Name> log_table </Table_Name>
++#</Not_Replicate_Info>
++
++3-3. Replication Server
++
++The setup file of replication server is copied from the sample file and
edited.
++(the sample file is installed '/usr/local/pgsql/etc' in default)
++----------------------------------------------------------------
++$cd /usr/local/pgsql/etc
++$cp pgreplicate.conf.sample pgreplicate.conf
++----------------------------------------------------------------
++In the case of the above system composition example,
++the setup example of pgreplicate.conf file is as the following
++
++#============================================================
++# PGReplicate configuration file
++#-------------------------------------------------------------
++# file: pgreplicate.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts & port are cluster server
++# o which port use for replication request from cluster server
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<Cluster_Server_Info>
++ <Host_Name> c1.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Recovery_Port> 7001 </Recovery_Port>
++</Cluster_Server_Info>
++<Cluster_Server_Info>
++ <Host_Name> c2.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Recovery_Port> 7001 </Recovery_Port>
++</Cluster_Server_Info>
++#-------------------------------------------------------------
++# set Load Balance server information
++# o Host_Name : hostname
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<LoadBalance_Server_Info>
++ <Host_Name> lb.hoo.com </Host_Name>
++ <Recovery_Port> 6001 </Recovery_Port>
++</LoadBalance_Server_Info>
++#-------------------------------------------------------------
++# set PGReplicate server information
++# o Replicate_Port : connection for reprication
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<Replication_Port> 8001 </Replication_Port>
++<Recovery_Port> 8101 </Recovery_Port>
++
++
++=============================================================
++4. Start Up / Stop
++=============================================================
++
++4-1. replication server
++
++A. Start replication server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pgreplicate -D /usr/local/pgsql/etc
++----------------------------------------------------------------
++
++B. Stop replication server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pgreplicate -D /usr/local/pgsql/etc stop
++----------------------------------------------------------------
++
++usage: pgreplicate [-D path_of_config_file] [-W path_of_work_files] [-U
login us
++er][-l][-n][-v][-h][stop]
++ -l: print error logs in the log file.
++ -n: don't run in daemon mode.
++ -v: debug mode. need '-n' flag
++ -h: print this help
++ stop: stop pgreplicate
++(config file default path: ./pgreplicate.conf)
++
++4-2. cluster DB server
++$PG_HOME = /usr/local/pgsql
++$PG_DATA = /usr/local/pgsql/data
++
++A. Start cluster DB server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -o "-i" start
++----------------------------------------------------------------
++
++B. Stop cluster DB server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data stop
++----------------------------------------------------------------
++
++4-3. load balance server
++
++A. Start load balance server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pglb -D /usr/local/pgsql/etc
++----------------------------------------------------------------
++
++B. Stop load balance server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pglb -D /usr/local/pgsql/etc stop
++----------------------------------------------------------------
++
++usage: pglb [-D path_of_config_file] [-W path_of_work_files]
[-n][-v][-h][stop]
++ -l: print error logs in the log file.
++ -n: don't run in daemon mode.
++ -v: debug mode. need '-n' flag
++ -h: print this help
++ stop: stop pglb
++ (config file default path: ./pglb.conf)
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/child.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/child.c
+--- postgresql-8.1.2/src/pgcluster/pglb/child.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/child.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,1194 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * child.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at child process of pglb.
++ *
++ * 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 "postgres.h"
++
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <sys/time.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <arpa/inet.h>
++#include <sys/file.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#include "postgres_fe.h"
++#include "libpq/pqcomm.h"
++
++#include "replicate_com.h"
++#include "pglb.h"
++
++/*--------------------------------------
++ * GLOBAL VARIABLE DECLARATION
++ *--------------------------------------
++ */
++POOL_CONNECTION * Frontend = NULL;
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++int PGRpre_fork_children(ClusterTbl * ptr);
++int PGRpre_fork_child(ClusterTbl * ptr);
++int PGRdo_child( int use_pool);
++int PGRcreate_child(ClusterTbl * cluster_p);
++pid_t PGRscan_child_tbl(ClusterTbl * cluster_p);
++void notice_backend_error(void);
++void do_pooling_child(int sig);
++int PGRset_status_to_child_tbl(pid_t pid, int status);
++int PGRadd_child_tbl(ClusterTbl * cluster_p, pid_t pid, int status);
++int PGRget_child_status(pid_t pid);
++void PGRreturn_connection_full_error(void);
++void PGRreturn_no_connection_error(void);
++void PGRquit_children_on_cluster(int rec_no);
++
++#ifdef NONE_BLOCK
++static void set_nonblock(int fd);
++#endif
++static void unset_nonblock(int fd);
++static POOL_CONNECTION *do_accept(int unix_fd, int inet_fd);
++static PGR_StartupPacket *read_startup_packet(POOL_CONNECTION *cp);
++static int send_startup_packet(POOL_CONNECTION_POOL_SLOT *cp);
++static void cancel_request(CancelPacket *sp, int secondary_backend);
++static POOL_CONNECTION_POOL *connect_backend(PGR_StartupPacket *sp,
POOL_CONNECTION *frontend);
++static int send_params(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL
*backend);
++static void child_end(int sig);
++static void PGRreturn_with_error(char *msg);
++
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRpre_fork_children()
++ * NOTES
++ * pre forked child precesses
++ * ARGS
++ * ClusterTbl * ptr: pointer of cluster server table (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGRpre_fork_children(ClusterTbl * ptr)
++{
++ int cnt;
++
++ if (ptr == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ cnt = 0 ;
++ while ((ptr->useFlag != TBL_END) && (cnt < ClusterNum))
++ {
++ PGRpre_fork_child(ptr);
++ cnt ++;
++ ptr ++;
++ }
++ return STATUS_OK;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRpre_fork_child()
++ * NOTES
++ * pre forked child precess
++ * ARGS
++ * ClusterTbl * ptr: pointer of cluster server table (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGRpre_fork_child(ClusterTbl * ptr)
++{
++ pid_t pid = 0;
++ int i;
++
++ if (ptr == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ if (ptr->useFlag == TBL_END)
++ {
++ return STATUS_ERROR;
++ }
++ for ( i = 0 ; i < ptr->max_connect * Max_Pool ; i ++)
++ {
++ pid = PGRcreate_child(ptr);
++ }
++ return STATUS_OK;
++}
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRdo_child()
++ * NOTES
++ * execute child process
++ * ARGS
++ * int use_pool: usage flag of connection pooling (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGRdo_child( int use_pool)
++{
++ char * func = "PGRdo_child()";
++ pid_t pid = 0;
++ PGR_StartupPacket *sp = NULL;
++ POOL_CONNECTION *frontend = NULL;
++ POOL_CONNECTION_POOL *backend = NULL;
++ int status = 0;
++ int connection_reuse = 1;
++ int ssl_request = 0;
++ int count = 0;
++
++ pid = getpid();
++#ifdef PRINT_DEBUG
++ show_debug("%s:I am %d",func, pid);
++#endif
++
++ /* set up signal handlers */
++ signal(SIGALRM, SIG_DFL);
++ signal(SIGTERM, child_end);
++ signal(SIGHUP, child_end);
++ signal(SIGINT, child_end);
++ signal(SIGUSR1, SIG_IGN);
++ signal(SIGUSR2, SIG_IGN);
++
++#ifdef NONE_BLOCK
++ /* set listen fds to none block */
++ set_nonblock(Frontend_FD.unix_fd);
++ set_nonblock(Frontend_FD.inet_fd);
++#endif
++
++retry_accept:
++ /* perform accept() */
++ frontend = do_accept(Frontend_FD.unix_fd,Frontend_FD.inet_fd);
++ if (frontend == NULL)
++ {
++ /* accept() failed. return to the accept() loop */
++ PGRset_status_to_child_tbl(pid,TBL_FREE);
++ return STATUS_ERROR;
++ }
++
++ /* unset frontend fd tp none block */
++ unset_nonblock(frontend->fd);
++
++ /* read the startup packet */
++ sp = 0;
++retry_startup:
++ if (sp)
++ {
++ free(sp->startup_packet);
++ free(sp->database);
++ free(sp->user);
++ free(sp);
++ }
++
++ sp = read_startup_packet(frontend);
++ if (sp == NULL)
++ {
++ /* failed to read the startup packet. return to the
++ accept() loop */
++ pool_close(frontend);
++ PGRset_status_to_child_tbl(pid,TBL_FREE);
++ return STATUS_ERROR;
++ }
++ PGRset_status_to_child_tbl(pid,TBL_ACCEPT);
++
++ /* cancel request? */
++ if (sp->major == 1234 && sp->minor == 5678)
++ {
++ cancel_request((CancelPacket *)sp->startup_packet, 0);
++ pool_close(frontend);
++ return STATUS_ERROR;
++ }
++
++ /* SSL? */
++ if (sp->major == 1234 && sp->minor == 5679)
++ {
++ /* SSL not supported */
++#ifdef PRINT_DEBUG
++ show_debug("%s:SSLRequest: sent N; retry startup",func);
++#endif
++ if (ssl_request)
++ {
++ pool_close(frontend);
++ return STATUS_ERROR;
++ }
++
++ /*
++ * say to the frontend "we do not suppport SSL"
++ * note that this is not a NOTICE response despite it's an
'N'!
++ */
++ pool_write_and_flush(frontend, "N", 1);
++ ssl_request = 1;
++ goto retry_startup;
++ }
++
++ /*
++ * Ok, negotiaton with frontend has been done. Let's go to the next
step.
++ */
++ /*
++ * if there's no connection associated with user and database,
++ * we need to connect to the backend and send the startup packet.
++ */
++ count = 0;
++ if ((backend = pool_get_cp(sp->user, sp->database, sp->major)) ==
NULL)
++ {
++ connection_reuse = 0;
++
++ if ((backend = connect_backend(sp, frontend)) == NULL)
++ {
++ /*
++
PGRset_status_on_cluster_tbl(TBL_ERROR,CurrentCluster);
++ return STATUS_ERROR;
++ */
++ goto retry_accept;
++ }
++ }
++ else
++ {
++ /* reuse existing connection to backend */
++
++ if (pool_do_reauth(frontend, backend))
++ {
++ pool_close(frontend);
++ return STATUS_ERROR;
++ }
++
++ if (MAJOR(backend) == 3)
++ {
++ if (send_params(frontend, backend))
++ {
++ pool_close(frontend);
++ return STATUS_ERROR;
++ }
++ }
++
++ /* send ReadyForQuery to frontend */
++ pool_write(frontend, "Z", 1);
++
++ if (MAJOR(backend) == 3)
++ {
++ int len;
++ char tstate;
++
++ len = htonl(5);
++ pool_write(frontend, &len, sizeof(len));
++ tstate = TSTATE(backend);
++ pool_write(frontend, &tstate, 1);
++ }
++
++ if (pool_flush(frontend) < 0)
++ {
++ pool_close(frontend);
++ return STATUS_ERROR;
++ }
++
++ }
++
++ /* query process loop */
++ for (;;)
++ {
++ POOL_STATUS status;
++
++ status = pool_process_query(frontend, backend, 0);
++
++ switch (status)
++ {
++ /* client exits */
++ case POOL_END:
++ /* do not cache connection to template0,
template1, regression */
++ if (!strcmp(sp->database, "template0") ||
!strcmp(sp->database, "template1") ||
++ !strcmp(sp->database, "regression") ||
use_pool == NOT_USE_CONNECTION_POOL)
++ {
++ pool_close(frontend);
++ pool_send_frontend_exits(backend);
++ pool_discard_cp(sp->user,
sp->database, sp->major);
++ }
++ else
++ {
++ POOL_STATUS status1;
++
++ /* send reset request to backend */
++ status1 =
pool_process_query(frontend, backend, 1);
++ pool_close(frontend);
++
++ /* if we detect errors on resetting
connection, we need to discard
++ * this connection since it might be
in unknown status
++ */
++ if (status1 != POOL_CONTINUE)
++ pool_discard_cp(sp->user,
sp->database, sp->major);
++ else
++
pool_connection_pool_timer(backend);
++ }
++ break;
++
++ /* error occured. discard backend connection pool
++ and disconnect connection to the frontend */
++ case POOL_ERROR:
++ show_error("%s:do_child: exits with status 1
due to error",func);
++ break;
++
++ /* fatal error occured. just exit myself... */
++ case POOL_FATAL:
++ show_error("%s:do_child: fatal error
occured",func);
++ notice_backend_error();
++ break;
++
++ /* not implemented yet */
++ case POOL_IDLE:
++
do_accept(Frontend_FD.unix_fd,Frontend_FD.inet_fd);
++#ifdef PRINT_DEBUG
++ show_debug("%s:accept while idle",func);
++#endif
++ break;
++
++ default:
++ break;
++ }
++
++ if (status != POOL_CONTINUE)
++ break;
++ }
++ if ((status == POOL_ERROR) ||
++ (status == POOL_FATAL))
++ {
++ PGRset_status_to_child_tbl(pid,TBL_FREE);
++ return STATUS_ERROR;
++ }
++ PGRset_status_to_child_tbl(pid,TBL_INIT);
++ return STATUS_OK;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRcreate_child()
++ * NOTES
++ * create child process
++ * ARGS
++ * ClusterTbl * ptr: pointer of cluster server table (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGRcreate_child(ClusterTbl * cluster_p)
++{
++ char * func = "PGRcreate_child()";
++ pid_t pid,pgid;
++
++ if (cluster_p == NULL)
++ return STATUS_ERROR;
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:create child
[%d@%s]",func,cluster_p->port,cluster_p->hostName);
++#endif
++ signal(SIGCHLD,PGRrecreate_child);
++ pgid = getpgid((pid_t)0);
++ pid = fork();
++ if (pid < 0)
++ {
++ show_error("%s:fork() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ if (pid == 0)
++ {
++ CurrentCluster = cluster_p;
++ if (pool_init_cp())
++ {
++ show_error("%s:pool_init_cp failed",func);
++ exit(1);
++ }
++ signal(SIGCHLD,PGRchild_wait);
++ signal(SIGTERM, child_end);
++ signal(SIGHUP, child_end);
++ signal(SIGINT, child_end);
++ signal(SIGUSR1,do_pooling_child);
++ setpgid((pid_t)0,pgid);
++ for (;;)
++ {
++ pause();
++ signal(SIGUSR1,do_pooling_child);
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:create child end
[%d@%s]",func,cluster_p->port,cluster_p->hostName);
++#endif
++ child_end(SIGTERM);
++ }
++ else
++ {
++ PGRadd_child_tbl(cluster_p,pid,TBL_INIT);
++ }
++ return pid;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRscan_child_tbl()
++ * NOTES
++ * get a child process id that is waiting for connection
++ * with the cluster server
++ * ARGS
++ * ClusterTbl * ptr: pointer of cluster server table (I)
++ * RETURN
++ * OK: child process id
++ * NG: 0
++ *--------------------------------------------------------------------
++ */
++pid_t
++PGRscan_child_tbl(ClusterTbl * cluster_p)
++{
++ char * func = "PGRscan_child_tbl()";
++ ChildTbl * p;
++
++ if ( cluster_p == NULL)
++ {
++ show_error("%s:Cluster_Tbl is not initialize",func);
++ return STATUS_ERROR;
++ }
++ p = Child_Tbl;
++ if ( p == NULL)
++ {
++ show_error("%s:Child_Tbl is not initialize",func);
++ return STATUS_ERROR;
++ }
++ while(p->useFlag != TBL_END)
++ {
++ if (p->pid <= 0)
++ {
++ p++;
++ continue;
++ }
++ if ((p->useFlag == TBL_INIT) &&
++ (p->rec_no == cluster_p->rec_no))
++ {
++ p->useFlag = TBL_USE;
++ return (p->pid);
++ }
++ p++;
++ }
++ return 0;
++}
++
++/* notice backend connection error using SIGUSR2 */
++void
++notice_backend_error(void)
++{
++ pid_t pid = getpid();
++
++ PGRset_status_to_child_tbl(pid,TBL_ERROR);
++ PGRset_status_on_cluster_tbl(TBL_ERROR_NOTICE,CurrentCluster);
++
++ /*
++ kill(parent, SIGUSR2);
++ sleep(1);
++ */
++}
++
++
++/*
++ * start up pooling child process
++ */
++void
++do_pooling_child(int sig)
++{
++ char * func = "do_pooling_child()";
++ int rtn;
++ pid_t pid;
++
++ pid = getpid();
++ rtn = PGRdo_child(USE_CONNECTION_POOL);
++ PGRrelease_connection(CurrentCluster);
++ if (rtn != STATUS_OK)
++ {
++ show_error("%s:PGRdo_child failed",func);
++ child_end(SIGTERM);
++ }
++ return ;
++}
++
++/*
++ * set status in child process table
++ */
++int
++PGRset_status_to_child_tbl(pid_t pid, int status)
++{
++ char * func = "PGRset_status_to_child_tbl()";
++ ChildTbl * p;
++
++ p = Child_Tbl;
++ if ( p == NULL)
++ {
++ show_error("%s:Child_Tbl is not initialize",func);
++ return STATUS_ERROR;
++ }
++ while(p->useFlag != TBL_END)
++ {
++ if (p->pid == pid)
++ {
++ p->useFlag = status;
++ return STATUS_OK;
++ }
++ p++;
++ }
++ return STATUS_ERROR;
++}
++
++/*
++ * add child process data in child process table
++ */
++int
++PGRadd_child_tbl(ClusterTbl * cluster_p, pid_t pid, int status)
++{
++ char * func = "PGRadd_child_tbl()";
++ ChildTbl * p;
++
++ p = Child_Tbl;
++ if ( cluster_p == NULL)
++ {
++ show_error("%s:Cluster_Tbl is not initialize",func);
++ return STATUS_ERROR;
++ }
++ if ( p == NULL)
++ {
++ show_error("%s:Child_Tbl is not initialize",func);
++ return STATUS_ERROR;
++ }
++ while(p->useFlag != TBL_END)
++ {
++ if ((p->useFlag == TBL_FREE) ||
++ (p->useFlag == TBL_ERROR))
++ {
++ p->useFlag = status;
++ p->rec_no = cluster_p->rec_no;
++ p->pid = pid;
++ return STATUS_OK;
++ }
++ p++;
++ }
++ return STATUS_ERROR;
++}
++
++int
++PGRget_child_status(pid_t pid)
++{
++ char * func = "PGRget_child_status()";
++ ChildTbl * p;
++
++ p = Child_Tbl;
++ if ( p == NULL)
++ {
++ show_error("%s:Child_Tbl is not initialize",func);
++ return STATUS_ERROR;
++ }
++
++ while (p->useFlag != TBL_END)
++ {
++ if (p->pid == pid)
++ {
++ return p->useFlag;
++ }
++ p++;
++ }
++ return STATUS_ERROR;
++}
++
++void
++PGRreturn_connection_full_error(void)
++{
++ PGRreturn_with_error( "Sorry, backend connection is full\n");
++}
++
++void
++PGRreturn_no_connection_error(void) {
++ PGRreturn_with_error("pglb could not connect to server: no cluster
available.\n");
++}
++
++static void
++PGRreturn_with_error (char *msg)
++{
++ PGR_StartupPacket *sp = NULL;
++ POOL_CONNECTION *frontend = NULL;
++
++
++ /* perform accept() */
++ frontend = do_accept(Frontend_FD.unix_fd,Frontend_FD.inet_fd);
++ if (frontend == NULL)
++ {
++ /* accept() failed. return to the accept() loop */
++ return ;
++ }
++ sp = read_startup_packet(frontend);
++ if (sp == NULL)
++ {
++ /* failed to read the startup packet. return to the
++ accept() loop */
++ pool_close(frontend);
++ return ;
++ }
++ pool_write_and_flush(frontend, "E", 1);
++ pool_write_and_flush(frontend, msg, strlen(msg)+1);
++ pool_close(frontend);
++ return ;
++}
++
++void
++PGRquit_children_on_cluster(int rec_no)
++{
++ char * func = "PGRquit_children_on_cluster()";
++ ChildTbl * p;
++
++ if (Child_Tbl == NULL)
++ {
++ return;
++ }
++ signal(SIGCHLD,SIG_IGN);
++ p = Child_Tbl;
++ while(p->useFlag != TBL_END)
++ {
++ if (p->rec_no == rec_no)
++ {
++ if (kill (p->pid,SIGTERM) == -1)
++ {
++ show_error("%s:could not stop pid: %d
(%s)",func,p->pid,strerror(errno));
++ return;
++ }
++ PGRchild_wait(SIGTERM);
++ p->useFlag = DATA_FREE;
++ }
++ p++;
++ }
++ if (Use_Connection_Pool)
++ {
++ signal(SIGCHLD,PGRrecreate_child);
++ }
++ else
++ {
++ signal(SIGCHLD,PGRchild_wait);
++ }
++}
++
++/* -------------------------------------------------------------------
++ * private functions
++ * -------------------------------------------------------------------
++ */
++
++#ifdef NONE_BLOCK
++/*
++ * set non-block flag
++ */
++static void set_nonblock(int fd)
++{
++ char* func = "set_nonblock()";
++ int var;
++
++ /* set fd to none blocking */
++ var = fcntl(fd, F_GETFL, 0);
++ if (var == -1)
++ {
++ show_error("%s:fcntl failed. %s", func,strerror(errno));
++ child_end(SIGTERM);
++ }
++ if (fcntl(fd, F_SETFL, var | O_NONBLOCK) == -1)
++ {
++ show_error("%s:fcntl failed. %s", func,strerror(errno));
++ child_end(SIGTERM);
++ }
++}
++#endif
++
++/*
++ * unset non-block flag
++ */
++static void unset_nonblock(int fd)
++{
++ char * func = "unset_nonblock()";
++ int var;
++
++ /* set fd to none blocking */
++ var = fcntl(fd, F_GETFL, 0);
++ if (var == -1)
++ {
++ show_error("%s,fcntl failed. %s", func,strerror(errno));
++ child_end(SIGTERM);
++ }
++ if (fcntl(fd, F_SETFL, var & ~O_NONBLOCK) == -1)
++ {
++ show_error("%s,fcntl failed. %s", func,strerror(errno));
++ child_end(SIGTERM);
++ }
++}
++
++
++/*
++* perform accept() and returns new fd
++*/
++static POOL_CONNECTION *do_accept(int unix_fd, int inet_fd)
++{
++ char * func = "do_accept()";
++ fd_set readmask;
++ int fds;
++ struct sockaddr addr;
++ socklen_t addrlen;
++ int fd = 0;
++ int afd;
++ int inet = 0;
++ POOL_CONNECTION *cp;
++#ifdef ACCEPT_PERFORMANCE
++ struct timeval now1, now2;
++ static long atime;
++ static int cnt;
++#endif
++
++ FD_ZERO(&readmask);
++ FD_SET(unix_fd, &readmask);
++ if (inet_fd)
++ FD_SET(inet_fd, &readmask);
++
++ fds = select(Max(unix_fd, inet_fd)+1, &readmask, NULL, NULL, NULL);
++ if (fds == -1)
++ {
++ if (errno == EAGAIN || errno == EINTR)
++ return NULL;
++
++ show_error("%s:select() failed. reason %s",func,
strerror(errno));
++ return NULL;
++ }
++
++ if (fds == 0)
++ return NULL;
++
++ if (FD_ISSET(unix_fd, &readmask))
++ {
++ fd = unix_fd;
++ }
++
++ if (FD_ISSET(inet_fd, &readmask))
++ {
++ fd = inet_fd;
++ inet++;
++ }
++
++ /*
++ * Note that some SysV systems do not work here. For those
++ * systems, we need some locking mechanism for the fd.
++ */
++ addrlen = sizeof(addr);
++
++#ifdef ACCEPT_PERFORMANCE
++ gettimeofday(&now1,0);
++#endif
++ afd = accept(fd, &addr, &addrlen);
++ if (afd < 0)
++ {
++ /*
++ * "Resource temporarily unavailable" (EAGAIN or EWOULDBLOCK)
++ * can be silently ignored.
++ */
++ if (errno != EAGAIN && errno != EWOULDBLOCK)
++ show_error("%s:accept() failed. reason: %s",func,
strerror(errno));
++ return NULL;
++ }
++#ifdef ACCEPT_PERFORMANCE
++ gettimeofday(&now2,0);
++ atime += (now2.tv_sec - now1.tv_sec)*1000000 + (now2.tv_usec -
now1.tv_usec);
++ cnt++;
++ if (cnt % 100 == 0)
++ {
++ show_error("%s:cnt: %d atime: %ld",func, cnt, atime);
++ }
++#endif
++#ifdef PRINT_DEBUG
++ show_debug("%s:I am %d accept fd %d",func, getpid(), afd);
++#endif
++
++ /* set NODELAY and KEEPALIVE options if INET connection */
++ if (inet)
++ {
++ int on = 1;
++
++ if (setsockopt(afd, IPPROTO_TCP, TCP_NODELAY,
++ (char *) &on,
++ sizeof(on)) < 0)
++ {
++ show_error("%s:do_accept: setsockopt() failed:
%s",func, strerror(errno));
++ close(afd);
++ return NULL;
++ }
++ if (setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE,
++ (char *) &on,
++ sizeof(on)) < 0)
++ {
++ show_error("%s:do_accept: setsockopt() failed: %s",
func,strerror(errno));
++ close(afd);
++ return NULL;
++ }
++ }
++
++ if ((cp = pool_open(afd)) == NULL)
++ {
++ close(afd);
++ return NULL;
++ }
++ return cp;
++}
++
++/*
++* read startup packet
++*/
++static PGR_StartupPacket *read_startup_packet(POOL_CONNECTION *cp)
++{
++ char * func = "read_startup_packet()";
++ PGR_StartupPacket *sp;
++ PGR_StartupPacket_v2 *sp2;
++ int protov;
++ int len;
++ char *p;
++
++ sp = (PGR_StartupPacket *)malloc(sizeof(PGR_StartupPacket));
++ if (!sp)
++ {
++ show_error("%s:read_startup_packet: out of memory",func);
++ return NULL;
++ }
++
++ /* read startup packet length */
++ if (pool_read(cp, &len, sizeof(len)))
++ {
++ free(sp);
++ return NULL;
++ }
++ len = ntohl(len);
++ len -= sizeof(len);
++
++ if (len <= 0)
++ {
++ show_error("%s:read_startup_packet: incorrect packet length
(%d)", func,len);
++ free(sp);
++ return NULL;
++ }
++
++ sp->startup_packet = calloc(len, 1);
++ if (!sp->startup_packet)
++ {
++ show_error("%s:read_startup_packet: out of memory",func);
++ free(sp);
++ return NULL;
++ }
++
++ /* read startup packet */
++ if (pool_read(cp, sp->startup_packet, len))
++ {
++ free(sp);
++ return NULL;
++ }
++
++ sp->len = len;
++ memcpy(&protov, sp->startup_packet, sizeof(protov));
++ sp->major = ntohl(protov)>>16;
++ sp->minor = ntohl(protov) & 0x0000ffff;
++ p = sp->startup_packet;
++
++ switch(sp->major)
++ {
++ case PROTO_MAJOR_V2: /* V2 */
++ sp2 = (PGR_StartupPacket_v2 *)(sp->startup_packet);
++
++ sp->database = calloc(SM_DATABASE+1, 1);
++ if (!sp->database)
++ {
++ show_error("%s:read_startup_packet: out of
memory",func);
++ free(sp);
++ return NULL;
++ }
++ strncpy(sp->database, sp2->database, SM_DATABASE);
++
++ sp->user = calloc(SM_USER+1, 1);
++ if (!sp->user)
++ {
++ show_error("%s:read_startup_packet: out of
memory",func);
++ free(sp);
++ return NULL;
++ }
++ strncpy(sp->user, sp2->user, SM_USER);
++
++ break;
++
++ case PROTO_MAJOR_V3: /* V3 */
++ p += sizeof(int); /* skip protocol version info
*/
++
++ while(*p)
++ {
++ if (!strcmp("user", p))
++ {
++ p += (strlen(p) + 1);
++ sp->user = strdup(p);
++ if (!sp->user)
++ {
++
show_error("%s:read_startup_packet: out of memory",func);
++ free(sp);
++ return NULL;
++ }
++ }
++ else if (!strcmp("database", p))
++ {
++ p += (strlen(p) + 1);
++ sp->database = strdup(p);
++ if (!sp->database)
++ {
++
show_error("%s:read_startup_packet: out of memory",func);
++ free(sp);
++ return NULL;
++ }
++ }
++ p += (strlen(p) + 1);
++ }
++ break;
++
++ case 1234: /* cancel or SSL request */
++ /* set dummy database, user info */
++ sp->database = calloc(1, 1);
++ if (!sp->database)
++ {
++ show_error("%s:read_startup_packet: out of
memory",func);
++ free(sp);
++ return NULL;
++ }
++ sp->user = calloc(1, 1);
++ if (!sp->user)
++ {
++ show_error("%s:read_startup_packet: out of
memory",func);
++ free(sp);
++ return NULL;
++ }
++ break;
++
++ default:
++ show_error("%s:read_startup_packet: invalid major no:
%d",func, sp->major);
++ free(sp);
++ return NULL;
++ }
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:Protocol Major: %d Minor: %d database: %s user: %s",
++ func,sp->major, sp->minor, sp->database, sp->user);
++#endif
++
++ return sp;
++}
++
++/*
++* send startup packet
++*/
++static int send_startup_packet(POOL_CONNECTION_POOL_SLOT *cp)
++{
++ int len;
++
++ len = htonl(cp->sp->len + sizeof(len));
++ pool_write(cp->con, &len, sizeof(len));
++ return pool_write_and_flush(cp->con, cp->sp->startup_packet,
cp->sp->len);
++}
++
++/*
++ * process cancel request
++ */
++static void cancel_request(CancelPacket *sp, int secondary_backend)
++{
++ char * func = "cancel_request()";
++ int len;
++ int fd;
++ POOL_CONNECTION *con;
++ char hostName[128];
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:Cancel request received",func);
++#endif
++
++ if (CurrentCluster == NULL)
++ {
++ return;
++ }
++ if (gethostname(hostName,sizeof(hostName)) < 0)
++ {
++ show_error("%s:gethostname() failed.
(%s)",func,strerror(errno));
++ return ;
++ }
++ if (secondary_backend)
++ {
++ if (PGRis_same_host(hostName,CurrentCluster->hostName))
++ fd = connect_unix_domain_socket(1);
++ else
++ fd = connect_inet_domain_socket(1);
++ }
++ else
++ {
++ if (PGRis_same_host(hostName,CurrentCluster->hostName))
++ fd = connect_unix_domain_socket(0);
++ else
++ fd = connect_inet_domain_socket(0);
++ }
++
++ if (fd < 0)
++ {
++ show_error("%s:Could not create socket for sending cancel
request",func);
++ return;
++ }
++
++ con = pool_open(fd);
++ if (con == NULL)
++ return;
++
++ len = htonl(sizeof(len) + sizeof(CancelPacket));
++ pool_write(con, &len, sizeof(len));
++
++ if (pool_write_and_flush(con, sp, sizeof(CancelPacket)) < 0)
++ show_error("%s:Could not send cancel request packet",func);
++ pool_close(con);
++}
++
++static POOL_CONNECTION_POOL *connect_backend(PGR_StartupPacket *sp,
POOL_CONNECTION *frontend)
++{
++ char * func ="connect_backend()";
++ POOL_CONNECTION_POOL *backend;
++
++ /* connect to the backend */
++ backend = pool_create_cp();
++ if (backend == NULL)
++ {
++ pool_send_error_message(frontend, sp->major, "XX000",
"connection cache is full", "",
++ "increace
max_pool", __FILE__, __LINE__);
++ pool_close(frontend);
++ return NULL;
++ }
++
++ /* mark this is a backend connection */
++ backend->slots[0]->con->isbackend = 1;
++ /*
++ * save startup packet info
++ */
++ backend->slots[0]->sp = sp;
++
++ if (pool_config_replication_enabled)
++ {
++ backend->slots[1]->con->isbackend = 1;
++ backend->slots[1]->con->issecondary_backend = 1;
++ /*
++ * save startup packet info
++ */
++ backend->slots[1]->sp = sp;
++ }
++
++ /* send startup packet */
++ if (send_startup_packet(backend->slots[0]) < 0)
++ {
++ show_error("%s:do_child: fails to send startup packet to the
backend",func);
++ pool_close(frontend);
++ return NULL;
++ }
++
++ /* send startup packet */
++ if (pool_config_replication_enabled)
++ {
++ if (send_startup_packet(backend->slots[1]) < 0)
++ {
++ show_error("%s:do_child: fails to send startup packet
to the secondary backend",func);
++ pool_close(frontend);
++ return NULL;
++ }
++ }
++
++ /*
++ * do authentication stuff
++ */
++ if (pool_do_auth(frontend, backend))
++ {
++ pool_close(frontend);
++ pool_discard_cp(sp->user, sp->database, sp->major);
++ return NULL;
++ }
++ return backend;
++}
++
++static int send_params(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL
*backend)
++{
++ char * func = "send_params()";
++ int index;
++ char *name, *value;
++ int len, sendlen;
++
++ index = 0;
++ while (pool_get_param(&MASTER(backend)->params, index++, &name,
&value) == 0)
++ {
++ pool_write(frontend, "S", 1);
++ len = sizeof(sendlen) + strlen(name) + 1 + strlen(value) + 1;
++ sendlen = htonl(len);
++ pool_write(frontend, &sendlen, sizeof(sendlen));
++ pool_write(frontend, name, strlen(name) + 1);
++ pool_write(frontend, value, strlen(value) + 1);
++ }
++
++ if (pool_flush(frontend))
++ {
++ show_error("%s:pool_send_params: pool_flush() failed",func);
++ return -1;
++ }
++ return 0;
++}
++
++/*
++ * ending function of child process
++ */
++static void
++child_end(int sig)
++{
++ signal(sig,SIG_IGN);
++
++ pool_finish();
++ exit(0);
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/cluster_table.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/cluster_table.c
+--- postgresql-8.1.2/src/pgcluster/pglb/cluster_table.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/cluster_table.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,338 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * cluster_tbl.c
++ *
++ * NOTE:
++ * This file is composed of the functions to use a cluster table.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++/*
++ * 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 <stdio.h>
++#include <stdarg.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <sys/time.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <sys/sem.h>
++#include <sys/msg.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <sys/select.h>
++#include <arpa/inet.h>
++#include <sys/file.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#include "replicate_com.h"
++#include "pglb.h"
++
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++int PGRis_cluster_alive(void) ;
++ClusterTbl * PGRscan_cluster(void);
++void PGRset_key_of_cluster(ClusterTbl * ptr, RecoveryPacket * packet);
++ClusterTbl * PGRadd_cluster_tbl (ClusterTbl * conf_data);
++ClusterTbl * PGRset_status_on_cluster_tbl (int status, ClusterTbl * ptr);
++ClusterTbl * PGRsearch_cluster_tbl(ClusterTbl * conf_data);
++
++static int set_cluster_tbl(ClusterTbl * ptr , ClusterTbl * conf_data);
++static ClusterTbl * search_free_cluster_tbl(void );
++static void write_cluster_status_file(ClusterTbl * ptr);
++
++int PGRis_cluster_alive(void)
++{
++ ClusterTbl * ptr = NULL;
++ int use=0;
++ ptr = Cluster_Tbl;
++
++ PGRsem_lock(ClusterSemid,MAX_DB_SERVER);
++ while (ptr->useFlag != TBL_END)
++ {
++ if (ptr->useFlag == TBL_USE)
++ {
++ use++;
++ }
++ ptr++;
++ }
++ PGRsem_unlock(ClusterSemid,MAX_DB_SERVER);
++ return use==0 ? STATUS_ERROR : STATUS_OK;
++}
++
++ClusterTbl *
++PGRscan_cluster(void)
++{
++ char * func = "PGRscan_cluster";
++ ClusterTbl * ptr = NULL;
++ ClusterTbl * rtn = NULL;
++ int min_use_rate = 100;
++ int use_rate = 0;
++ int cnt = 0;
++
++
++ ptr = Cluster_Tbl;
++ if (ptr == NULL)
++ {
++ show_error("%s:Cluster Table is not initialize",func);
++ return (ClusterTbl *)NULL;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:%d ClusterDB can be used",func,ClusterNum);
++#endif
++ PGRsem_lock(ClusterSemid,MAX_DB_SERVER);
++ while ((cnt <= ClusterNum) && (ptr->useFlag != TBL_END))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:%s [%d],useFlag->%d max->%d use_num->%d\n",
++ func,
ptr->hostName,ptr->port,ptr->useFlag,ptr->max_connect,ptr->use_num);
++#endif
++ cnt ++;
++ if (ptr->useFlag != TBL_USE)
++ {
++ ptr ++;
++ continue;
++ }
++ if (ptr->max_connect <= ptr->use_num)
++ {
++ ptr ++;
++ continue;
++ }
++ if (ptr->use_num > 0)
++ {
++ use_rate = ptr->use_num * 100 / ptr->max_connect ;
++ }
++ else
++ {
++ use_rate = 0;
++ rtn = ptr;
++ break;
++ }
++ if (min_use_rate > use_rate)
++ {
++ min_use_rate = use_rate;
++ rtn = ptr;
++ }
++ ptr ++;
++ }
++ if (rtn != NULL)
++ {
++ rtn->use_num ++;
++ }
++ PGRsem_unlock(ClusterSemid,MAX_DB_SERVER);
++ return rtn;
++}
++
++void
++PGRset_key_of_cluster(ClusterTbl * ptr, RecoveryPacket * packet)
++{
++ int max_connect = 0;
++ int port = 0;
++
++ memset(ptr,0,sizeof(ClusterTbl));
++ memcpy(ptr->hostName,packet->hostName,sizeof(ptr->hostName));
++ max_connect = ntohs(packet->max_connect);
++ if (max_connect >= 0)
++ {
++ ptr->max_connect = max_connect;
++ }
++ else
++ {
++ ptr->max_connect = DEFAULT_CONNECT_NUM;
++ }
++ port = ntohs(packet->port);
++ if ( port >= 0)
++ {
++ ptr->port = port;
++ }
++ else
++ {
++ ptr->port = DEFAULT_PORT;
++ }
++}
++
++ClusterTbl *
++PGRadd_cluster_tbl (ClusterTbl * conf_data)
++{
++ char * func = "PGRadd_cluster_tbl()";
++ ClusterTbl * ptr;
++
++ ptr = PGRsearch_cluster_tbl(conf_data);
++ if ((ptr != NULL) && (ptr->useFlag == TBL_USE ))
++ {
++ ptr->max_connect = conf_data->max_connect;
++ ptr->use_num = 0;
++ ptr->rate = 0;
++ return ptr;
++ }
++ ptr = search_free_cluster_tbl();
++ if (ptr == (ClusterTbl *) NULL)
++ {
++ show_error("%s:no more free space in cluster table",func);
++ return (ClusterTbl *)NULL;
++ }
++ if (ClusterNum < Max_DB_Server)
++ {
++ set_cluster_tbl( ptr, conf_data);
++ return ptr;
++ }
++ return (ClusterTbl *)NULL;
++}
++
++ClusterTbl *
++PGRset_status_on_cluster_tbl (int status, ClusterTbl * ptr)
++{
++#ifdef PRINT_DEBUG
++ char * func = "PGRset_status_on_cluster_tbl()";
++#endif
++
++ if (ptr != (ClusterTbl*)NULL)
++ {
++ if (ptr->useFlag != status)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:host:%s port:%d max:%d use:%d
status%d",
++ func,
ptr->hostName,ptr->port,ptr->max_connect,ptr->useFlag,status);
++#endif
++ ptr->useFlag = status;
++ write_cluster_status_file(ptr);
++ if (status == TBL_USE)
++ {
++ if (ClusterNum < Max_DB_Server)
++ ClusterNum ++ ;
++ }
++ else if (status != TBL_STOP)
++ {
++ if (ClusterNum > 0)
++ ClusterNum -- ;
++ }
++ }
++ }
++ return ptr;
++}
++
++static void
++write_cluster_status_file(ClusterTbl * ptr)
++{
++ switch( ptr->useFlag)
++ {
++ case TBL_FREE:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s free",
++ ptr->port,
++ ptr->hostName);
++ break;
++ case TBL_INIT:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s
initialize",
++ ptr->port,
++ ptr->hostName);
++ break;
++ case TBL_USE:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s start
use",
++ ptr->port,
++ ptr->hostName);
++ break;
++ case TBL_ERROR:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s error",
++ ptr->port,
++ ptr->hostName);
++ break;
++ case TBL_END:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s end",
++ ptr->port,
++ ptr->hostName);
++ break;
++ }
++}
++
++ClusterTbl *
++PGRsearch_cluster_tbl(ClusterTbl * conf_data)
++{
++ ClusterTbl *ptr;
++ int cnt = 0;
++ int rec_num = 0;
++
++ ptr = Cluster_Tbl;
++ while ((cnt <= ClusterNum) && (rec_num < Max_DB_Server))
++ {
++ if (ptr->port > 0)
++ {
++ if ((!strcmp(ptr->hostName,conf_data->hostName)) &&
++ (ptr->port == conf_data->port))
++ {
++ return ptr;
++ }
++ if (ptr->useFlag == TBL_USE)
++ {
++ cnt ++;
++ }
++ }
++ ptr ++;
++ rec_num ++;
++ }
++ return (ClusterTbl *)NULL;
++}
++
++static int
++set_cluster_tbl(ClusterTbl * ptr , ClusterTbl * conf_data)
++{
++ int rec_no;
++
++ rec_no = ptr->rec_no;
++ memcpy(ptr->hostName,conf_data->hostName,sizeof(ptr->hostName));
++ ptr->max_connect = conf_data->max_connect;
++ ptr->port = conf_data->port;
++ ptr->use_num = conf_data->use_num;
++ ptr->rate = conf_data->rate;
++ PGRset_status_on_cluster_tbl (TBL_USE, ptr);
++
++ return STATUS_OK;
++}
++
++static ClusterTbl *
++search_free_cluster_tbl(void )
++{
++ ClusterTbl *ptr;
++ int cnt = 0;
++
++ ptr = Cluster_Tbl;
++ while ((cnt <= ClusterNum ) && (cnt < Max_DB_Server))
++ {
++ if (ptr->useFlag != TBL_USE)
++ {
++ return ptr;
++ }
++ cnt ++;
++ ptr ++;
++ }
++ return (ClusterTbl *)NULL;
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/load_balance.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/load_balance.c
+--- postgresql-8.1.2/src/pgcluster/pglb/load_balance.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/load_balance.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,252 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * load_balance.c
++ *
++ * NOTE:
++ * This file is composed of the functions of load balance modules
++ * with connection pooling or not
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++/*
++ * 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 "postgres.h"
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <sys/time.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/sem.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <sys/select.h>
++#include <arpa/inet.h>
++#include <sys/file.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#include "replicate_com.h"
++#include "pglb.h"
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++int PGRload_balance(void);
++int PGRload_balance_with_pool(void);
++char PGRis_connection_full(ClusterTbl * ptr);
++void PGRrelease_connection(ClusterTbl * ptr);
++void PGRchild_wait(int sig);
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRload_balance()
++ * NOTES
++ * load balance module that normal connection is used
++ * ARGS
++ * void
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGRload_balance(void)
++{
++ char * func = "PGRload_balance()";
++ pid_t pid,pgid;
++ int count;
++ int status;
++ ClusterTbl * cluster_p = NULL;
++
++ signal(SIGCHLD, PGRchild_wait);
++ /* get the least locaded cluster server info */
++ cluster_p = PGRscan_cluster();
++ count = 0;
++ while (cluster_p == NULL )
++ {
++ if ( count > PGLB_CONNECT_RETRY_TIME)
++ {
++ show_error("%s:no cluster available",func);
++ return STATUS_ERROR;
++ }
++ cluster_p = PGRscan_cluster();
++ count ++;
++ }
++
++ pgid = getpgid((pid_t)0);
++ pid = fork();
++ if (pid < 0)
++ {
++ show_error("%s:fork() failed. (%s)",func,strerror(errno));
++ exit(1);
++ }
++ if (pid == 0)
++ {
++ setpgid((pid_t)0,pgid);
++ CurrentCluster = cluster_p;
++
++ if (pool_init_cp())
++ {
++ show_error("%s:pool_init_cp failed",func);
++ exit(1);
++ }
++ PGRsem_lock(ClusterSemid,cluster_p->rec_no);
++ if (PGRget_child_status(getpid()) == STATUS_ERROR)
++ {
++ PGRadd_child_tbl(cluster_p, getpid(), TBL_USE);
++ }
++ PGRsem_unlock(ClusterSemid,cluster_p->rec_no);
++ PGRdo_child(NOT_USE_CONNECTION_POOL );
++ PGRrelease_connection(cluster_p);
++ PGRset_status_to_child_tbl(getpid(), TBL_FREE);
++ exit(0);
++ }
++ else if (pid > 0)
++ {
++ PGRsem_lock(ClusterSemid,cluster_p->rec_no);
++ if (PGRget_child_status(pid) == STATUS_ERROR)
++ {
++ PGRadd_child_tbl(cluster_p, pid, TBL_USE);
++ }
++ PGRsem_unlock(ClusterSemid,cluster_p->rec_no);
++ status = PGRget_child_status(pid);
++ while (status == TBL_USE)
++ {
++ status = PGRget_child_status(pid);
++ usleep(20);
++ }
++ return STATUS_OK;
++ }
++ else
++ {
++ return STATUS_ERROR;
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRload_balance_with_pool()
++ * NOTES
++ * load balance module that connection pooling system is used
++ * ARGS
++ * void
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGRload_balance_with_pool(void)
++{
++ char * func = "PGRload_balance_with_pool()";
++ int count;
++ pid_t pid;
++ ClusterTbl * cluster_p = NULL;
++ int status = TBL_USE;
++
++ /* get the least locaded cluster server info */
++ cluster_p = PGRscan_cluster();
++ count = 0;
++ while (cluster_p == NULL )
++ {
++ if ( count > PGLB_CONNECT_RETRY_TIME)
++ {
++ show_error("%s:no cluster available",func);
++ PGRreturn_no_connection_error();
++ return STATUS_ERROR;
++ }
++ cluster_p = PGRscan_cluster();
++ count ++;
++ }
++ pid = PGRscan_child_tbl(cluster_p);
++ if ((pid == 0) || (pid == STATUS_ERROR))
++ {
++ show_error("%s:no child process available",func);
++ return STATUS_ERROR;
++ }
++ kill(pid,SIGUSR1);
++
++ status = PGRget_child_status(pid);
++ while (status == TBL_USE)
++ {
++ status = PGRget_child_status(pid);
++ usleep(20);
++ }
++
++ return STATUS_OK;
++
++}
++
++char
++PGRis_connection_full(ClusterTbl * ptr)
++{
++ char rtn = 1;
++
++ if (ptr == NULL)
++ {
++ return rtn;
++ }
++ PGRsem_lock(ClusterSemid,ptr->rec_no);
++ if (ptr->max_connect > ptr->use_num)
++ {
++ rtn = 0;
++ }
++ PGRsem_unlock(ClusterSemid,ptr->rec_no);
++ return rtn;
++}
++
++void
++PGRrelease_connection(ClusterTbl * ptr)
++{
++ if (ptr == NULL)
++ {
++ return;
++ }
++ PGRsem_lock(ClusterSemid,MAX_DB_SERVER);
++ if (ptr->use_num > 0)
++ {
++ ptr->use_num --;
++ }
++ PGRsem_unlock(ClusterSemid,MAX_DB_SERVER);
++}
++
++void
++PGRchild_wait(int sig)
++{
++ pid_t pid = 0;
++ int ret = 0;
++
++ do {
++ pid = waitpid(-1,&ret,WNOHANG);
++ if ((pid <= 0) && (WTERMSIG(ret) > 0))
++ {
++ pid = 1;
++ }
++ } while(pid > 0);
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/main.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/main.c
+--- postgresql-8.1.2/src/pgcluster/pglb/main.c 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/main.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,1050 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * main.c
++ *
++ * NOTE:
++ * This file is composed of the main function of pglb.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++/*
++ * 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 "postgres.h"
++#include <stdio.h>
++#include <stdarg.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <sys/sem.h>
++#include <sys/msg.h>
++#include <netdb.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <sys/select.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#include <sys/file.h>
++#include <arpa/inet.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#ifdef HAVE_GETOPT_H
++#include <getopt.h>
++#endif
++
++#include "replicate_com.h"
++#include "pglb.h"
++
++
++
++#define IPC_NMAXSEM (32)
++/*--------------------------------------
++ * GLOBAL VARIABLE DECLARATION
++ *--------------------------------------
++ */
++/* for replicate_com.h */
++ConfDataType * ConfData_Top = (ConfDataType *)NULL;
++ConfDataType * ConfData_End = (ConfDataType *)NULL;
++int SynSemID = -1;
++int MapTableShmid = -1;
++int LifeCheckStartShmid = -1;
++char * LifeCheckStartFlag = NULL;
++int LifeCheckTimeOut = 10;
++FILE * StatusFp = (FILE *)NULL;
++FILE * LogFp = (FILE *)NULL;
++char * PGRStatusFileName = NULL;
++char * PGRLogFileName = NULL;
++int Log_Print = 0;
++int Debug_Print = 0;
++
++int Recv_Port_Number = 0;
++int Recovery_Port_Number = 0;
++uint16_t LifeCheck_Port_Number = 0;
++int Use_Connection_Pool = 0;
++int Max_Pool = 1;
++int Connection_Life_Time = 0;
++int Max_DB_Server = 0;
++int MaxBackends = 0;
++ClusterTbl * Cluster_Tbl = (ClusterTbl *)NULL;
++int ClusterNum = 0;
++int ClusterShmid = 0;
++int ClusterSemid = 0;
++ChildTbl * Child_Tbl = (ChildTbl *)NULL;
++int ChildShmid = 0;
++char * PGR_Data_Path = NULL;
++char * PGR_Write_Path = NULL;
++char * Backend_Socket_Dir = NULL;
++FrontSocket Frontend_FD;
++ClusterTbl * CurrentCluster = NULL;
++
++extern char *optarg;
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++int init_pglb(char * path);
++static void pglb_exit(int signal_args);
++static void load_balance_main(void);
++static void daemonize(void);
++static void write_pid_file(void);
++static void stop_pglb(void);
++static int is_exist_pid_file(void);
++static ClusterTbl * scan_cluster_by_pid(pid_t pid);
++static void usage(void);
++static void close_child(int signal_args);
++
++void PGRrecreate_child(int signal_args);
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * init_pglb()
++ * NOTES
++ * Reading of the setup file
++ * and the initialization of the memory area.
++ * ARGS
++ * char * path: path of the setup file (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++init_pglb(char * path)
++{
++ char * func = "init_pglb()";
++
++ ConfDataType * conf;
++ ClusterTbl cluster_tbl[MAX_DB_SERVER];
++ int size = 0;
++ int rec_no = 0;
++ int i;
++ int max_connect = 0;
++ union semun sem_arg;
++ char fname[256];
++
++ /*
++ * read configuration file
++ */
++ if (path == NULL)
++ {
++ path = ".";
++ }
++ if (PGR_Get_Conf_Data(path,PGLB_CONF_FILE) != STATUS_OK)
++ {
++ show_error("%s:PGR_Get_Conf_Data failed",func);
++ return STATUS_ERROR;
++ }
++
++ /* cluster db status file open */
++ if (PGRStatusFileName == NULL)
++ {
++
snprintf(fname,sizeof(fname),"%s/%s",PGR_Write_Path,PGLB_STATUS_FILE);
++ }
++ else
++ {
++ memcpy(fname,PGRStatusFileName,sizeof(fname));
++ }
++ StatusFp = fopen(fname, "a");
++ if (StatusFp == NULL)
++ {
++ show_error("%s:open() %s file failed. (%s)",
++ func,fname, strerror(errno));
++ exit(1);
++ }
++ /* pglb log file open */
++ if (Log_Print)
++ {
++ if (PGRLogFileName == NULL)
++ {
++
snprintf(fname,sizeof(fname),"%s/%s",PGR_Write_Path,PGLB_LOG_FILE);
++ }
++ else
++ {
++ memcpy(fname,PGRLogFileName,sizeof(fname));
++ }
++ LogFp = fopen(fname,"a");
++ if (LogFp == NULL)
++ {
++ show_error("%s:fopen failed:
(%s)",func,strerror(errno));
++ exit(1);
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:%s open ok",func,fname);
++#endif
++ }
++
++ Backend_Socket_Dir = malloc(128);
++ if (Backend_Socket_Dir == NULL)
++ {
++ show_error("%s:malloc() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(Backend_Socket_Dir,0,128);
++ /* set initiarize data */
++ strcpy(Backend_Socket_Dir,"/tmp");
++ Max_Pool = 1;
++ Connection_Life_Time = 0;
++ Use_Connection_Pool = 0;
++
++ conf = ConfData_Top;
++ while (conf != (ConfDataType *)NULL)
++ {
++ /* get cluster db servers name */
++ if (!strcmp(conf->table,CLUSTER_SERVER_TAG))
++ {
++ rec_no = conf->rec_no;
++ if (!strcmp(conf->key,HOST_NAME_TAG))
++ {
++
memcpy(cluster_tbl[rec_no].hostName,conf->value,sizeof(cluster_tbl[rec_no].hostName));
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,PORT_TAG))
++ {
++ cluster_tbl[rec_no].port = atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,MAX_CONNECT_TAG))
++ {
++ cluster_tbl[rec_no].max_connect =
atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ }
++ else
++ {
++ /* get port number for receive querys */
++ if (!strcmp(conf->key,RECV_PORT_TAG))
++ {
++ Recv_Port_Number = atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ /* get port number for recovery session */
++ if (!strcmp(conf->key,RECOVERY_PORT_TAG))
++ {
++ Recovery_Port_Number = atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ /*
++ if (!STRCMP(conf->key,LIFECHECK_PORT_TAG))
++ {
++ LifeCheck_Port_Number = atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!STRCMP(conf->key,STATUS_LOG_FILE_TAG))
++ {
++ PGRStatusFileName = strdup(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!STRCMP(conf->key,ERROR_LOG_FILE_TAG))
++ {
++ PGRLogFileName = strdup(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ */
++ if (!strcmp(conf->key,MAX_CLUSTER_TAG))
++ {
++ Max_DB_Server = atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,USE_CONNECTION_POOL_TAG))
++ {
++ if (!strcmp(conf->value,"yes"))
++ {
++ Use_Connection_Pool = 1;
++ }
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,MAX_POOL_TAG))
++ {
++ Max_Pool = atoi(conf->value);
++ if (Max_Pool < 0)
++ Max_Pool = 1;
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,CONNECTION_LIFE_TIME))
++ {
++ Connection_Life_Time = atoi(conf->value);
++ if (Connection_Life_Time < 0)
++ Connection_Life_Time = 0;
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,BACKEND_SOCKET_DIR_TAG))
++ {
++ strncpy(Backend_Socket_Dir,conf->value,128);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ }
++ conf = (ConfDataType*)conf->next;
++ }
++ if (Max_DB_Server <= 0)
++ {
++ show_error("%s:Max_DB_Server is wrong value. %s/%s file
should be broken",func, path, PGLB_CONF_FILE);
++ exit(1);
++ }
++ /* shared memory allocation for cluster table */
++ size = sizeof(ClusterTbl) * Max_DB_Server;
++
++ ClusterShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL | 0600);
++ if (ClusterShmid < 0)
++ {
++ show_error("%s:ClusterShm shmget() failed. (%s)",
func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ Cluster_Tbl = (ClusterTbl *)shmat(ClusterShmid,0,0);
++ if (Cluster_Tbl == (ClusterTbl *)-1)
++ {
++ show_error("%s:shmat() failed. (%s)", func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ if ((ClusterSemid = semget(IPC_PRIVATE,MAX_DB_SERVER+1,IPC_CREAT |
IPC_EXCL | 0600)) < 0)
++ {
++ show_error("%s:semget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ for ( i = 0 ; i <= MAX_DB_SERVER ; i ++)
++ {
++ semctl(ClusterSemid, i, GETVAL, sem_arg);
++ sem_arg.val = 1;
++ semctl(ClusterSemid, i, SETVAL, sem_arg);
++ }
++ ClusterNum = 0;
++ /* set cluster db server name into cluster db server table */
++ for ( i = 0 ; i < Max_DB_Server ; i ++)
++ {
++ (Cluster_Tbl + i)->rec_no = i;
++ }
++ (Cluster_Tbl + i)->useFlag = TBL_END;
++ max_connect = 0;
++ for ( i = 0 ; i <= rec_no ; i ++)
++ {
++ cluster_tbl[i].use_num = 0;
++ cluster_tbl[i].rate = 0;
++ if (cluster_tbl[i].max_connect < 0)
++ {
++ cluster_tbl[i].max_connect = 0;
++ }
++ if (max_connect < cluster_tbl[i].max_connect)
++ {
++ max_connect = cluster_tbl[i].max_connect;
++ }
++ PGRadd_cluster_tbl(&cluster_tbl[i]);
++ }
++
++ /* shared memory allocation for children table */
++ size = sizeof(ChildTbl) * (Max_DB_Server + 1) * max_connect *
Max_Pool;
++#ifdef PRINT_DEBUG
++ show_debug("%s:Child_Tbl size is[%d]",func,size);
++#endif
++
++ ChildShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL | 0600);
++ if (ChildShmid < 0)
++ {
++ show_error("%s:ChildShm shmget() failed. (%s)",func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ Child_Tbl = (ChildTbl *)shmat(ChildShmid,0,0);
++ if (Child_Tbl == (ChildTbl *)-1)
++ {
++ show_error("%s:shmat() failed. (%s)", func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(Child_Tbl, 0, size);
++ (Child_Tbl + ( Max_DB_Server * max_connect * Max_Pool) -1)->useFlag =
TBL_END;
++
++ PGR_Free_Conf_Data();
++
++ return STATUS_OK;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * pglb_exit()
++ * NOTES
++ * Closing of pglb process
++ * ARGS
++ * int signal_args: signal number (I)
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++pglb_exit(int signal_args)
++{
++ char fname[256];
++ int rtn;
++
++ Child_Tbl->useFlag = TBL_END;
++ signal(SIGCHLD,SIG_IGN);
++ signal(signal_args,SIG_IGN);
++ kill (0,signal_args);
++ while (wait(NULL) > 0 )
++ ;
++
++ if (ClusterShmid > 0)
++ {
++ rtn = shmdt((char *)Cluster_Tbl);
++ shmctl(ClusterShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ ClusterShmid = 0;
++ Cluster_Tbl = NULL;
++ }
++ if (ChildShmid > 0)
++ {
++ rtn = shmdt((char *)Child_Tbl);
++ shmctl(ChildShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ ChildShmid = 0;
++ Child_Tbl = NULL;
++ }
++ if (ClusterSemid > 0)
++ {
++ semctl(ClusterSemid, 0, IPC_RMID);
++ }
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGLB_PID_FILE);
++ unlink(fname);
++
++ if (StatusFp != NULL)
++ {
++ fflush(StatusFp);
++ fclose(StatusFp);
++ }
++ if (LogFp != NULL)
++ {
++ fflush(LogFp);
++ fclose(LogFp);
++ }
++ if (Frontend_FD.unix_fd != 0)
++ {
++ close(Frontend_FD.unix_fd);
++ snprintf(fname, sizeof(fname), "%s/.s.PGSQL.%d",
Backend_Socket_Dir,Recv_Port_Number);
++ unlink(fname);
++ }
++ if (Frontend_FD.inet_fd != 0)
++ {
++ close(Frontend_FD.inet_fd);
++ }
++ /*
++ PGRsyn_quit();
++ */
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGLB_PID_FILE);
++ unlink(fname);
++
++ exit(0);
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * load_balance_main()
++ * NOTES
++ * This is a main module of load balance function
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++load_balance_main(void)
++{
++ char * func = "load_balance_main()";
++ int status;
++ int rtn;
++ int count = 0;
++
++ Frontend_FD.unix_fd =
PGRcreate_unix_domain_socket(Backend_Socket_Dir, Recv_Port_Number);
++ if (Frontend_FD.unix_fd < 0)
++ {
++ show_error("%s:PGRcreate_unix_domain_socket failed",func);
++ pglb_exit(SIGTERM);
++ }
++ Frontend_FD.inet_fd = PGRcreate_recv_socket("", Recv_Port_Number);
++ if (Frontend_FD.inet_fd < 0)
++ {
++ show_error("%s:PGRcreate_recv_socket failed",func);
++ pglb_exit(SIGTERM);
++ }
++ if (Use_Connection_Pool)
++ {
++ signal(SIGCHLD,PGRrecreate_child);
++ rtn = PGRpre_fork_children(Cluster_Tbl);
++ if (rtn != STATUS_OK)
++ {
++ show_error("%s:PGRpre_fork_children failed",func);
++ pglb_exit(SIGTERM);
++ }
++ }
++
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = 60;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(Frontend_FD.unix_fd,&rmask);
++ if(Frontend_FD.inet_fd)
++ FD_SET(Frontend_FD.inet_fd,&rmask);
++ rtn = select(Max(Frontend_FD.unix_fd, Frontend_FD.inet_fd) +
1, &rmask, (fd_set *)NULL, (fd_set *)NULL, &timeout);
++ if( rtn > 0)
++ {
++ if(PGRis_cluster_alive() == STATUS_ERROR) {
++ show_error("%s:all clusters were dead.",func);
PGRreturn_no_connection_error();
++ count=0;
++ }
++ else
++ {
++ if (Use_Connection_Pool)
++ {
++ status = PGRload_balance_with_pool();
++ }
++ else
++ {
++ status = PGRload_balance();
++ }
++ if (status != STATUS_OK)
++ {
++ show_error("%s:load balance process
failed",func);
++ if ( count > PGLB_CONNECT_RETRY_TIME)
++ {
++ show_error("%s:no cluster
available",func);
++ PGRreturn_connection_full_error();
++ count = 0;
++ }
++ count ++;
++ }
++ else
++ {
++ count = 0;
++ }
++ }
++ }
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * daemonize()
++ * NOTES
++ * Daemonize this process
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++daemonize(void)
++{
++ char * func = "daemonize()";
++ int i;
++ pid_t pid;
++
++ pid = fork();
++ if (pid == (pid_t) -1)
++ {
++ show_error("%s:fork() failed. (%s)",func, strerror(errno));
++ exit(1);
++ return; /* not reached */
++ }
++ else if (pid > 0)
++ { /* parent */
++ exit(0);
++ }
++
++#ifdef HAVE_SETSID
++ if (setsid() < 0)
++ {
++ show_error("%s:setsid() failed. (%s)", func,strerror(errno));
++ exit(1);
++ }
++#endif
++
++ i = open("/dev/null", O_RDWR);
++ dup2(i, 0);
++ dup2(i, 1);
++ dup2(i, 2);
++ close(i);
++}
++
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * write_pid_file()
++ * NOTES
++ * The process ID is written in the file.
++ * This process ID is used when finish pglb.
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++write_pid_file(void)
++{
++ char * func = "write_pid_file()";
++ FILE *fd;
++ char fname[256];
++ char pidbuf[128];
++
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGLB_PID_FILE);
++ fd = fopen(fname, "w");
++ if (!fd)
++ {
++ show_error("%s:open() %s file failed. (%s)",
++ func,fname, strerror(errno));
++ exit(1);
++ }
++ snprintf(pidbuf, sizeof(pidbuf), "%d", getpid());
++ fwrite(pidbuf, strlen(pidbuf), 1, fd);
++ if (fclose(fd))
++ {
++ show_error("%s:fwrite() %s file failed. (%s)",
++ func,fname, strerror(errno));
++ exit(1);
++ }
++}
++
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * stop_pglb()
++ * NOTES
++ * Stop the pglb process
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++stop_pglb(void)
++{
++ char * func = "stop_pglb()";
++ FILE *fd;
++ char fname[256];
++ char pidbuf[128];
++ pid_t pid;
++
++ if (PGR_Write_Path == NULL)
++ {
++ PGR_Write_Path = ".";
++ }
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGLB_PID_FILE);
++ fd = fopen(fname, "r");
++ if (!fd)
++ {
++ show_error("%s:open() %s file failed. (%s)",
++ func,fname, strerror(errno));
++ exit(1);
++ }
++ memset(pidbuf,0,sizeof(pidbuf));
++ fread(pidbuf, sizeof(pidbuf), 1, fd);
++ fclose(fd);
++ pid = atoi(pidbuf);
++ if (kill (pid,SIGTERM) == -1)
++ {
++ show_error("%s:could not stop pid: %d
(%s)",func,pid,strerror(errno));
++ exit(1);
++ }
++}
++
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * is_exist_pid_file()
++ * NOTES
++ * Check existence of pid file.
++ * ARGS
++ * void
++ * RETURN
++ * 1: the pid file is exist
++ * 0: the pid file is not exist
++ *--------------------------------------------------------------------
++ */
++static int
++is_exist_pid_file(void)
++{
++ char fname[256];
++ struct stat buf;
++
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGLB_PID_FILE);
++ if (stat(fname,&buf) == 0)
++ {
++ /* pid file is exist */
++ return 1;
++ }
++ else
++ {
++ /* pid file is not exist */
++ return 0;
++ }
++}
++
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRrecreate_child()
++ * NOTES
++ * create the child process again which it hunged up
++ * ARGS
++ * int signal_args: signal number (expecting the SIGCHLD)
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++void
++PGRrecreate_child(int signal_args)
++{
++ pid_t pid = 0;
++ int status;
++ ClusterTbl * cluster_p;
++
++ReWait:
++
++ errno = 0;
++#ifdef HAVE_WAITPID
++ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
++ {
++#else
++ while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
++ {
++#endif
++ cluster_p = scan_cluster_by_pid(pid);
++ pid = PGRcreate_child(cluster_p);
++ }
++ if ((pid < 0) && (errno == EINTR))
++ goto ReWait;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * close_child()
++ * NOTES
++ * Hung up child process
++ * ARGS
++ * int signal_args: signal number (expecting the SIGUSR2)
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++close_child(int signal_args)
++{
++ char * func = "close_child()";
++ ChildTbl * child;
++ ClusterTbl * cluster;
++ int rec_no = -1;
++
++ if (( Cluster_Tbl == NULL) || (Child_Tbl == NULL))
++ {
++ show_error("%s:Cluster_Tbl or Child_Tbl is not
initialize",func);
++ return ;
++ }
++ cluster = Cluster_Tbl;
++ while(cluster->useFlag != TBL_END)
++ {
++ if (cluster->useFlag == TBL_ERROR_NOTICE)
++ {
++ rec_no = cluster->rec_no;
++ PGRset_status_on_cluster_tbl(TBL_ERROR,cluster);
++ break;
++ }
++ cluster++;
++ }
++ if (rec_no < 0)
++ {
++ return;
++ }
++ child = Child_Tbl;
++ while(child->useFlag != TBL_END)
++ {
++ if (child->rec_no == rec_no)
++ {
++ if (kill (child->pid,SIGTERM) == -1)
++ {
++ show_error("%s:could not stop pid: %d
(%s)",func,child->pid,strerror(errno));
++ return;
++ }
++ PGRchild_wait(signal_args);
++ child->useFlag = DATA_FREE;
++ }
++ child++;
++ }
++ signal(SIGUSR2, close_child);
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * scan_cluster_by_pid()
++ * NOTES
++ * get cluster server record from child process id
++ * ARGS
++ * pid_t pid: child process id (I)
++ * RETURN
++ * OK: pointer of cluster table
++ * NG: NULL
++ *--------------------------------------------------------------------
++ */
++static ClusterTbl *
++scan_cluster_by_pid(pid_t pid)
++{
++ char * func = "scan_cluster_by_pid()";
++ ChildTbl * child_p;
++ ClusterTbl * cluster_p;
++ int cnt;
++
++ child_p = Child_Tbl;
++ if (child_p == NULL)
++ {
++ show_error("%s:Child Table is not initialize",func);
++ return NULL;
++ }
++ cluster_p = Cluster_Tbl;
++ if (cluster_p == NULL)
++ {
++ show_error("%s:Cluster Table is not initialize",func);
++ return NULL;
++ }
++
++ while (child_p->useFlag != TBL_END)
++ {
++ if (child_p->pid == pid)
++ {
++ break;
++ }
++ child_p++;
++ }
++ if (child_p->useFlag == TBL_END)
++ {
++ show_error("%s:pid:%d not found in child table",func,pid);
++ return NULL;
++ }
++
++ cnt = 0;
++ while ((cluster_p->useFlag != TBL_END) && (cnt < ClusterNum))
++ {
++ if (cluster_p->rec_no == child_p->rec_no)
++ {
++ return cluster_p;
++ }
++ cluster_p++;
++ cnt ++;
++ }
++ return NULL;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * usage()
++ * NOTES
++ * show usage of pglb
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++usage(void)
++{
++ char * path;
++
++ path = getenv("PGDATA");
++ if (path == NULL)
++ path = ".";
++ fprintf(stderr,"pglb version [%s]\n",PGLB_VERSION);
++ fprintf(stderr,"A load balancer for PostgreSQL\n\n");
++ fprintf(stderr,"usage: pglb [-D path_of_config_file] [-W
path_of_work_files] [-n][-v][-h][stop | restart]\n");
++ fprintf(stderr," config file default path: %s/%s\n",path,
PGLB_CONF_FILE);
++ fprintf(stderr," -l: print error logs in the log file.\n");
++ fprintf(stderr," -n: don't run in daemon mode.\n");
++ fprintf(stderr," -v: debug mode. need '-n' flag\n");
++ fprintf(stderr," -h: print this help\n");
++ fprintf(stderr," stop: stop pglb\n");
++ fprintf(stderr," restart: restart pglb\n");
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * main()
++ * NOTES
++ * main module of pglb
++ * ARGS
++ * int argc: number of parameter
++ * char ** argv: value of parameter
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++int
++main(int argc, char ** argv)
++{
++ int opt = 0;
++ pid_t pid,pgid;
++ char * r_path = NULL;
++ char * w_path = NULL;
++ int detach = 1;
++
++ signal(SIGHUP, pglb_exit);
++ signal(SIGINT, pglb_exit);
++ signal(SIGQUIT, pglb_exit);
++ signal(SIGTERM, pglb_exit);
++ signal(SIGALRM, SIG_IGN); /* ignored */
++ signal(SIGPIPE, SIG_IGN); /* ignored */
++ signal(SIGTTIN, SIG_IGN); /* ignored */
++ signal(SIGTTOU, SIG_IGN); /* ignored */
++ signal(SIGCHLD,PGRchild_wait);
++ signal(SIGUSR1, SIG_IGN); /* ignored */
++ signal(SIGUSR2, close_child); /* close child process */
++ r_path = getenv("PGDATA");
++ if (r_path == NULL)
++ r_path = ".";
++
++ while ((opt = getopt(argc, argv, "D:W:lvnh")) != -1)
++ {
++ switch (opt)
++ {
++ case 'D':
++ if (!optarg)
++ {
++ usage();
++ exit(1);
++ }
++ r_path = optarg;
++ break;
++ case 'W':
++ if (!optarg)
++ {
++ usage();
++ exit(1);
++ }
++ w_path = optarg;
++ break;
++ case 'l':
++ Log_Print = 1;
++ break;
++ case 'v':
++ Debug_Print = 1;
++ break;
++ case 'n':
++ detach = 0;
++ break;
++ case 'h':
++ usage();
++ exit(0);
++ break;
++ default:
++ usage();
++ exit(1);
++ }
++ }
++ PGR_Data_Path = r_path;
++ if (w_path == NULL)
++ {
++ PGR_Write_Path = PGR_Data_Path;
++ }
++ else
++ {
++ PGR_Write_Path = w_path;
++ }
++
++ if (optind == (argc-1) &&
++ ((!strcmp(argv[optind],"stop")) ||
++ (!strcmp(argv[optind],"restart"))))
++ {
++ stop_pglb();
++ if (!strcmp(argv[optind],"stop"))
++ {
++ exit(0);
++ }
++ }
++ else if (optind == argc)
++ {
++ if (is_exist_pid_file())
++ {
++ fprintf(stderr,"pid file %s/%s found. is another pglb
running?", PGR_Write_Path, PGLB_PID_FILE);
++ exit(1);
++ }
++ }
++ else if (optind < argc)
++ {
++ usage();
++ exit(1);
++ }
++
++ if (detach)
++ {
++ daemonize();
++ }
++ write_pid_file();
++
++ if (init_pglb(PGR_Data_Path) != STATUS_OK)
++ {
++ exit(0);
++ }
++
++ pgid = getpgid((pid_t)0);
++ pid = fork();
++ if (pid < 0)
++ {
++ pglb_exit(0);
++ }
++ if (pid == 0)
++ {
++ setpgid((pid_t)0,pgid);
++ PGRrecovery_main();
++ exit(0);
++ }
++ else
++ {
++ load_balance_main();
++ }
++ pglb_exit(0);
++ return STATUS_OK;
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/pglb.conf.sample
pgcluster-1.5.0rc7/src/pgcluster/pglb/pglb.conf.sample
+--- postgresql-8.1.2/src/pgcluster/pglb/pglb.conf.sample 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/pglb.conf.sample 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,46 @@
++#============================================================
++# Load Balance Server configuration file
++#-------------------------------------------------------------
++# file: pglb.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts are db cluster server
++# o which port use connect to db cluster server
++# o how many connections are allowed on each DB server
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Max_Connection : maximun number of connection to postmaster
++#-------------------------------------------------------------
++#<Cluster_Server_Info>
++# <Host_Name> master.postgres.jp </Host_Name>
++# <Port> 5432 </Port>
++# <Max_Connect> 0 </Max_Connect>
++#</Cluster_Server_Info>
++#<Cluster_Server_Info>
++# <Host_Name> post2.postgres.jp </Host_Name>
++# <Port> 5432 </Port>
++# <Max_Connect> 32 </Max_Connect>
++#</Cluster_Server_Info>
++#<Cluster_Server_Info>
++# <Host_Name> post3.postgres.jp </Host_Name>
++# <Port> 5432 </Port>
++# <Max_Connect> 32 </Max_Connect>
++#</Cluster_Server_Info>
++#-------------------------------------------------------------
++# set Load Balance server information
++# o Backend_Socket_Dir : Unix domain socket path for the backend
++# o Receive_Port : connection from client
++# o Recovery_Port : connection for recovery process
++# o Max_Cluster_Num : maximun number of cluster DB servers
++# o Use_Connection_Pooling : use connection pool [yes/no]
++# o Max_Pool_Each_Server : number of pool connections/DB server
++#-------------------------------------------------------------
++<Backend_Socket_Dir> /tmp </Backend_Socket_Dir>
++<Receive_Port> 5432 </Receive_Port>
++<Recovery_Port> 6001 </Recovery_Port>
++<Max_Cluster_Num> 128 </Max_Cluster_Num>
++<Use_Connection_Pooling> no </Use_Connection_Pooling>
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/pglb.h
pgcluster-1.5.0rc7/src/pgcluster/pglb/pglb.h
+--- postgresql-8.1.2/src/pgcluster/pglb/pglb.h 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/pglb.h 2006-02-09
17:46:26.000000000 +0100
+@@ -0,0 +1,458 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * pglb.h
++ *
++ * Portions Copyright (c) 2003-2006 Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#ifndef PGLB_H
++#define PGLB_H
++
++#define PGLB_VERSION "1.5.0rc7"
++
++#include "../libpgc/libpgc.h"
++
++/*
++ * from pool.h
++ */
++
++/*
++ * define this if you do not want to issue RESET ALL at each new
++ * connection. Also you need to define this for 7.1 or prior
++ * PostgreSQL since they do not support RESET ALL
++ */
++#undef NO_RESET_ALL
++
++/* undef this if you have problems with non blocking accept() */
++#define NONE_BLOCK
++
++#define POOLMAXPATHLEN 8192
++
++/* configuration file name */
++#define POOL_CONF_FILE_NAME "pgpool.conf"
++
++/* pid file directory */
++#define DEFAULT_LOGDIR "/tmp"
++
++/* Unix domain socket directory */
++#define DEFAULT_SOCKET_DIR "/tmp"
++
++/* pid file name */
++#define PID_FILE_NAME "pgpool.pid"
++
++/* strict mode comment in SQL */
++#define STRICT_MODE_STR "/*STRICT*/"
++#define STRICT_MODE(s) (strncasecmp((s), STRICT_MODE_STR,
strlen(STRICT_MODE_STR)) == 0)
++
++typedef enum {
++ POOL_CONTINUE = 0,
++ POOL_IDLE,
++ POOL_END,
++ POOL_ERROR,
++ POOL_FATAL
++} POOL_STATUS;
++
++/* protocol major version numbers */
++#define PROTO_MAJOR_V2 2
++#define PROTO_MAJOR_V3 3
++
++/*
++ * startup packet definitions (v2) stolen from PostgreSQL
++ */
++#define SM_DATABASE 64
++#define SM_USER 32
++#define SM_OPTIONS 64
++#define SM_UNUSED 64
++#define SM_TTY 64
++
++typedef struct PGR_StartupPacket_v2
++{
++ int protoVersion; /* Protocol version */
++ char database[SM_DATABASE]; /* Database name */
++ char user[SM_USER]; /* User name */
++ char options[SM_OPTIONS]; /* Optional additional args */
++ char unused[SM_UNUSED]; /* Unused */
++ char tty[SM_TTY]; /* Tty for debug output */
++} PGR_StartupPacket_v2;
++
++/* startup packet info */
++typedef struct
++{
++ char *startup_packet; /* raw startup packet without packet
length (malloced area) */
++ int len; /* raw startup packet
length */
++ int major; /* protocol major version */
++ int minor; /* protocol minor version */
++ char *database; /* database name in startup_packet (malloced area) */
++ char *user; /* user name in startup_packet (malloced area) */
++} PGR_StartupPacket;
++
++typedef struct CancelPacket
++{
++ int protoVersion; /* Protocol version */
++ int pid; /* bcckend process id */
++ int key; /* cancel key */
++} CancelPacket;
++
++/*
++ * configuration paramters
++ */
++typedef struct {
++ int inetdomain; /* should we make an INET domain socket too?
*/
++ int port; /* port # to bind */
++ char *socket_dir; /* pgpool socket directory */
++ char *backend_host_name; /* backend host name */
++ int backend_port; /* backend port # */
++ char *secondary_backend_host_name; /* secondary backend host
name */
++ int secondary_backend_port; /* secondary backend port # */
++ int num_init_children; /* # of children initially pre-forked
*/
++ int child_life_time; /* if idle for this seconds, child
exits */
++ int connection_life_time; /* if idle for this seconds,
connection closes */
++ int max_pool; /* max # of connection pool per child */
++ char *logdir; /* logging directory */
++ char *backend_socket_dir; /* Unix domain socket directory for the
PostgreSQL server */
++ int replication_mode; /* replication mode */
++ int replication_strict; /* if non 0, wait for completion of the
++ query sent to master to avoid deadlock */
++ /*
++ * if secondary does not respond in this milli seconds, abort this
session.
++ * this is not compatible with replication_strict = 1. 0 means no
timeout.
++ */
++ int replication_timeout;
++
++ int load_balance_mode; /* load balance mode */
++
++ /* followings do not exist in the configuration file */
++ char *current_backend_host_name; /* current backend host name
*/
++ int current_backend_port; /* current backend port # */
++ int replication_enabled; /* replication mode enabled */
++
++ int replication_stop_on_mismatch; /* if there's a data
mismatch between master and secondary
++
* start degenration to stop replication mode
++
*/
++} POOL_CONFIG;
++
++#define MAX_PASSWORD_SIZE (1024)
++
++typedef struct {
++ int num; /* number of entries */
++ char **names; /* parameter names */
++ char **values; /* values */
++} ParamStatus;
++
++/*
++ * stream connection structure
++ */
++typedef struct {
++ int fd; /* fd for connection */
++ FILE *write_fd; /* stream write connection */
++
++ char *hp; /* pending data buffer head address */
++ int po; /* pending data offset */
++ int bufsz; /* pending data buffer size */
++ int len; /* pending data length */
++
++ char *sbuf; /* buffer for pool_read_string */
++ int sbufsz; /* its size in bytes */
++
++ char *buf2; /* buffer for pool_read2 */
++ int bufsz2; /* its size in bytes */
++
++ int isbackend; /* this connection is for backend if non 0 */
++ int issecondary_backend; /* this connection is for
secondary backend if non 0 */
++
++ char tstate; /* transaction state (V3 only) */
++
++ /*
++ * following are used to remember when re-use the authenticated
connection
++ */
++ int auth_kind; /* 3: clear text password, 4: crypt password,
5: md5 password */
++ int pwd_size; /* password (sent back from frontend) size in
host order */
++ char password[MAX_PASSWORD_SIZE]; /* password (sent
back from frontend) */
++ char salt[4]; /* password salt */
++
++ /*
++ * following are used to remember current session paramter status.
++ * re-used connection will need them (V3 only)
++ */
++ ParamStatus params;
++
++ int no_forward; /* if non 0, do not write to frontend */
++
++} POOL_CONNECTION;
++
++/*
++ * connection pool structure
++ */
++typedef struct {
++ PGR_StartupPacket *sp; /* startup packet info */
++ int pid; /* backend pid */
++ int key; /* cancel key */
++ POOL_CONNECTION *con;
++ time_t closetime; /* absolute time in second when the
connection closed
++ * if 0, that means the
connection is under use.
++ */
++} POOL_CONNECTION_POOL_SLOT;
++
++#define MAX_CONNECTION_SLOTS 2
++
++typedef struct {
++ int num; /* number of slots */
++ POOL_CONNECTION_POOL_SLOT *slots[MAX_CONNECTION_SLOTS];
++} POOL_CONNECTION_POOL;
++
++#define MASTER_CONNECTION(p) ((p)->slots[0])
++#define SECONDARY_CONNECTION(p) ((p)->slots[1])
++#define MASTER(p) MASTER_CONNECTION(p)->con
++#define SECONDARY(p) SECONDARY_CONNECTION(p)->con
++#define MAJOR(p) MASTER_CONNECTION(p)->sp->major
++#define TSTATE(p) MASTER(p)->tstate
++
++#define Max(x, y) ((x) > (y) ? (x) : (y))
++#define Min(x, y) ((x) < (y) ? (x) : (y))
++
++/*
++ * pglb
++ */
++
++typedef struct {
++ int useFlag;
++ int sock;
++}SocketTbl;
++
++typedef struct {
++ int useFlag;
++ char hostName[HOSTNAME_MAX_LENGTH];
++ unsigned short port;
++ short max_connect;
++ int use_num;
++ int rate;
++ int rec_no;
++}ClusterTbl;
++
++typedef struct {
++ long mtype;
++ char mdata[1];
++}MsgData;
++
++typedef struct {
++ int useFlag;
++ int rec_no;
++ pid_t pid;
++}ChildTbl;
++
++#define UNIX_DOMAIN_FD (0)
++#define INET_DOMAIN_FD (1)
++typedef struct {
++ int unix_fd;
++ int inet_fd;
++}FrontSocket;
++
++#define pool_config_inetdomain (0)
++#define pool_config_replication_mode (0)
++#define pool_config_replication_strict (0)
++#define pool_config_replication_timeout (0)
++#define pool_config_replication_enabled (0)
++#define pool_config_load_balance_mode (0)
++#define pool_config_replication_stop_on_mismatch (0)
++#define pool_config_port (Recv_Port_Number)
++#define pool_config_socket_dir (Backend_Socket_Dir)
++#define pool_config_backend_host_name (CurrentCluster->hostName)
++#define pool_config_backend_port (CurrentCluster->port)
++#define pool_config_secondary_backend_host_name
(CurrentCluster->hostName)
++#define pool_config_secondary_backend_port (CurrentCluster->port)
++#define pool_config_num_init_children (CurrentCluster->max_connect)
++#define pool_config_child_life_time (Connection_Life_Time)
++#define pool_config_connection_life_time (Connection_Life_Time)
++#define pool_config_max_pool (Max_Pool)
++#define pool_config_logdir "./"
++#define pool_config_backend_socket_dir (Backend_Socket_Dir)
++#define pool_config_current_backend_host_name (CurrentCluster->hostName)
++#define pool_config_current_backend_port (CurrentCluster->port)
++#define REPLICATION (0)
++#define IN_LOAD_BALANCE (0)
++
++/*
++ * for pglb
++ */
++#define MAX_DB_SERVER (32)
++#define PGLB_MAX_SOCKET_QUEUE (10000)
++#define CLUSTER_TBL_SHM_KEY (1010)
++#define PGLB_CONNECT_RETRY_TIME (3)
++#define DEFAULT_CONNECT_NUM (32)
++#define DEFAULT_PORT (5432)
++#define BUF_SIZE (16384)
++#define TBL_FREE (0)
++#define TBL_INIT (1)
++#define TBL_USE (2)
++#define TBL_STOP (3)
++#define TBL_ACCEPT (10)
++#define TBL_ERROR_NOTICE (98)
++#define TBL_ERROR (99)
++#define TBL_END (-1)
++#define STATUS_OK (0)
++#define STATUS_ERROR (-1)
++#ifdef RECOVERY_PREPARE_REQ
++#define ADD_DB RECOVERY_PREPARE_REQ
++#else
++#define ADD_DB (1)
++#endif
++#ifdef RECOVERY_PGDATA_ANS
++#define STOP_DB RECOVERY_PGDATA_ANS
++#else
++#define STOP_DB (3)
++#endif
++#ifdef RECOVERY_FINISH
++#define START_DB RECOVERY_FINISH
++#else
++#define START_DB (9)
++#endif
++#define DELETE_DB (99)
++#define QUERY_TERMINATE (0x00)
++#define RESPONSE_TERMINATE (0x5a)
++#define PGLB_CONF_FILE "pglb.conf"
++#define PGLB_PID_FILE "pglb.pid"
++#define PGLB_STATUS_FILE "pglb.sts"
++#define PGLB_LOG_FILE "pglb.log"
++#define CLUSTER_SERVER_TAG "Cluster_Server_Info"
++#define MAX_CONNECT_TAG "Max_Connect"
++#define RECOVERY_PORT_TAG "Recovery_Port"
++#define RECV_PORT_TAG "Receive_Port"
++#define MAX_CLUSTER_TAG "Max_Cluster_Num"
++#define USE_CONNECTION_POOL_TAG "Use_Connection_Pooling"
++#define MAX_POOL_TAG "Max_Pool_Each_Server"
++#define BACKEND_SOCKET_DIR_TAG "Backend_Socket_Dir"
++#define CONNECTION_LIFE_TIME "Connection_Life_Time"
++#define NOT_USE_CONNECTION_POOL (0)
++#define USE_CONNECTION_POOL (1)
++
++extern int Recv_Port_Number;
++extern int Recovery_Port_Number;
++extern uint16_t LifeCheck_Port_Number;
++extern int Use_Connection_Pool;
++extern int Max_Pool;
++extern int Connection_Life_Time;
++extern int Msg_Id;
++extern ClusterTbl * Cluster_Tbl;
++extern int Max_DB_Server;
++extern int MaxBackends;
++extern char * Backend_Socket_Dir;
++extern int ClusterShmid;
++extern int ClusterSemid;
++extern int ChildShmid;
++extern int ClusterNum;
++extern ChildTbl * Child_Tbl;
++extern char * PGR_Data_Path;
++extern char * PGR_Write_Path;
++extern char * Backend_Socket_Dir;
++extern FrontSocket Frontend_FD;
++extern FILE * StatusFp;
++
++/* for child.c */
++extern POOL_CONNECTION * Frontend;
++extern ClusterTbl * CurrentCluster;
++
++extern char * Function;
++
++extern POOL_CONNECTION_POOL *pool_connection_pool; /* connection pool */
++
++/* extern of main.c */
++extern void PGRrecreate_child(int signal_args);
++
++/* extern of child.c */
++extern int PGRpre_fork_children(ClusterTbl * ptr);
++extern int PGRpre_fork_child(ClusterTbl * ptr);
++extern int PGRdo_child( int use_pool);
++extern int PGRcreate_child(ClusterTbl * cluster_p);
++extern pid_t PGRscan_child_tbl(ClusterTbl * cluster_p);
++extern void notice_backend_error(void);
++extern void do_pooling_child(int sig);
++extern int PGRset_status_to_child_tbl(pid_t pid, int status);
++extern int PGRadd_child_tbl(ClusterTbl * cluster_p, pid_t pid, int status);
++extern int PGRget_child_status(pid_t pid);
++extern void PGRreturn_connection_full_error(void);
++extern void PGRreturn_no_connection_error(void);
++extern void PGRquit_children_on_cluster(int rec_no);
++
++/* extern of cluster_table.c */
++extern int PGRis_cluster_alive(void) ;
++extern ClusterTbl * PGRscan_cluster(void);
++extern void PGRset_key_of_cluster(ClusterTbl * ptr, RecoveryPacket *
packet);
++extern ClusterTbl * PGRadd_cluster_tbl (ClusterTbl * conf_data);
++extern ClusterTbl * PGRset_status_on_cluster_tbl (int status, ClusterTbl *
ptr);
++extern ClusterTbl * PGRsearch_cluster_tbl(ClusterTbl * conf_data);
++
++/* extern of load_balance.c */
++extern int PGRload_balance(void);
++extern int PGRload_balance_with_pool(void);
++extern char PGRis_connection_full(ClusterTbl * ptr);
++extern void PGRuse_connection(ClusterTbl * ptr);
++extern void PGRrelease_connection(ClusterTbl * ptr);
++extern void PGRchild_wait(int sig);
++
++/* extern of recovery.c */
++extern void PGRrecovery_main(void);
++
++/* extern of socket.c */
++extern int PGRcreate_unix_domain_socket(char * sock_dir, unsigned short
port);
++extern int PGRcreate_recv_socket(char * hostName , unsigned short
portNumber);
++extern int PGRcreate_acception(int fd, char * hostName , unsigned short
portNumber);
++extern void PGRclose_sock(int * sock);
++extern int PGRread_byte(int sock,char * buf,int len, int flag);
++extern int PGRcreate_cluster_socket( int * sock, ClusterTbl * ptr );
++
++/* extern of pool_auth.c */
++extern int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL
*cp);
++extern int pool_do_reauth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL
*cp);
++extern int pool_read_message_length(POOL_CONNECTION_POOL *cp);
++extern signed char pool_read_kind(POOL_CONNECTION_POOL *cp);
++
++/* extern of pool_connection_pool.c */
++extern int pool_init_cp(void);
++extern POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int
protoMajor);
++extern void pool_discard_cp(char *user, char *database, int protoMajor);
++extern POOL_CONNECTION_POOL *pool_create_cp(void);
++extern void pool_connection_pool_timer(POOL_CONNECTION_POOL *backend);
++extern void pool_backend_timer_handler(int sig);
++extern int connect_inet_domain_socket(int secondary_backend);
++extern int connect_unix_domain_socket(int secondary_backend);
++extern char PGRis_same_host(char * host1, char * host2);
++extern void pool_finish(void);
++
++/* extern of pool_process_query.c */
++extern POOL_STATUS pool_process_query(POOL_CONNECTION *frontend,
POOL_CONNECTION_POOL *backend, int connection_reuse);
++extern POOL_STATUS ErrorResponse(POOL_CONNECTION *frontend,
POOL_CONNECTION_POOL *backend);
++extern void pool_enable_timeout();
++extern void pool_disable_timeout();
++extern int pool_check_fd(POOL_CONNECTION *cp, int notimeout);
++extern void pool_send_frontend_exits(POOL_CONNECTION_POOL *backend);
++extern POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION
*frontend, POOL_CONNECTION_POOL *backend);
++extern POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION
*frontend, POOL_CONNECTION_POOL *backend);
++extern POOL_STATUS ParameterStatus(POOL_CONNECTION *frontend,
POOL_CONNECTION_POOL *backend);
++extern void pool_send_error_message(POOL_CONNECTION *frontend, int
protoMajor, char *code, char *message, char *detail, char *hint, char *file,
int line);
++
++/* extern of pool_params.c */
++extern int pool_init_params(ParamStatus *params);
++extern void pool_discard_params(ParamStatus *params);
++extern char *pool_find_name(ParamStatus *params, char *name, int *pos);
++extern int pool_get_param(ParamStatus *params, int index, char **name, char
**value);
++extern int pool_add_param(ParamStatus *params, char *name, char *value);
++extern void pool_param_debug_print(ParamStatus *params);
++
++/* extern of pool_stream.c */
++extern POOL_CONNECTION *pool_open(int fd);
++extern void pool_close(POOL_CONNECTION *cp);
++extern int pool_read(POOL_CONNECTION *cp, void *buf, int len);
++extern char *pool_read2(POOL_CONNECTION *cp, int len);
++extern int pool_write(POOL_CONNECTION *cp, void *buf, int len);
++extern int pool_flush(POOL_CONNECTION *cp);
++extern int pool_write_and_flush(POOL_CONNECTION *cp, void *buf, int len);
++extern char *pool_read_string(POOL_CONNECTION *cp, int *len, int line);
++
++/*
++ * external prototype in show.c
++ */
++extern void show_error(const char * fmt,...);
++extern void show_debug(const char * fmt,...);
++extern void PGRwrite_log_file(FILE * fp, const char * fmt,...);
++
++#endif /* PGLB_H */
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/pool_auth.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_auth.c
+--- postgresql-8.1.2/src/pgcluster/pglb/pool_auth.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_auth.c 2006-02-02
22:31:51.000000000 +0100
+@@ -0,0 +1,959 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * pool_auth.c
++ *
++ * NOTE:
++ * authenticaton 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 <sys/types.h>
++#include <netinet/in.h>
++#include <sys/param.h>
++#include <arpa/inet.h>
++#include <errno.h>
++#include <string.h>
++#include <sys/time.h>
++#include <stdio.h>
++#include "replicate_com.h"
++#include "pglb.h"
++
++int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp);
++int pool_do_reauth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp);
++int pool_read_message_length(POOL_CONNECTION_POOL *cp);
++signed char pool_read_kind(POOL_CONNECTION_POOL *cp);
++
++static POOL_STATUS pool_send_auth_ok(POOL_CONNECTION *frontend, int pid,
int key, int protoMajor);
++static int do_clear_text_password(POOL_CONNECTION *backend, POOL_CONNECTION
*frontend, int reauth, int protoMajor);
++static int do_crypt(POOL_CONNECTION *backend, POOL_CONNECTION *frontend,
int reauth, int protoMajor);
++static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int
reauth, int protoMajor);
++
++/*
++* do authentication against backend. if success return 0 otherwise non 0.
++*/
++int pool_do_auth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
++{
++ char * func = "pool_do_auth()";
++ int status;
++ signed char kind;
++ int pid, pid1;
++ int key, key1;
++ int protoMajor;
++ int length;
++
++ protoMajor = MAJOR(cp);
++
++ kind = pool_read_kind(cp);
++ if (kind < 0)
++ {
++ return -1;
++ }
++
++ /* error response? */
++ if (kind == 'E')
++ {
++ /* we assume error response at this stage is likely version
++ * protocol mismatch (v3 frontend vs. v2 backend). So we throw
++ * a V2 protocol error response in the hope that v3 frontend
++ * will negotiate again using v2 protocol.
++ */
++ show_error("%s:pool_do_auth: maybe protocol version mismatch
(current version %d)",func, protoMajor);
++ ErrorResponse(frontend, cp);
++ return -1;
++ }
++ else if (kind != 'R')
++ {
++ show_error("%s:pool_do_auth: expect \"R\" got %c",func, kind);
++ return -1;
++ }
++
++ /*
++ * message length (v3 only) */
++ if (protoMajor == PROTO_MAJOR_V3 && pool_read_message_length(cp) < 0)
++ {
++ return -1;
++ }
++
++ /*
++ * read authentication request kind.
++ *
++ * 0: authentication ok
++ * 1: kerberos v4
++ * 2: kerberos v5
++ * 3: clear text password
++ * 4: crypt password
++ * 5: md5 password
++ * 6: scm credential
++ *
++ * in replication mode, we only supports kind = 0, 3. this is
because to "salt"
++ * cannot be replicated among master and secondary.
++ * in non replication mode, we supports kind = 0, 3, 4, 5
++ */
++
++ status = pool_read(MASTER(cp), &pid, sizeof(pid));
++ if (status < 0)
++ {
++ show_error("%s:pool_do_auth: read authentication kind
failed",func);
++ return -1;
++ }
++
++ if (REPLICATION)
++ {
++ status = pool_read(SECONDARY(cp), &pid1, sizeof(pid1));
++
++ if (status < 0)
++ {
++ show_error("%s:pool_do_auth: read authentication kind
from secondary failed",func);
++ return -1;
++ }
++ }
++
++ pid = ntohl(pid);
++
++ /* trust? */
++ if (pid == 0)
++ {
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ int msglen;
++
++ pool_write(frontend, "R", 1);
++ msglen = htonl(8);
++ pool_write(frontend, &msglen, sizeof(msglen));
++ msglen = htonl(0);
++ if (pool_write_and_flush(frontend, &msglen,
sizeof(msglen)) < 0)
++ {
++ return -1;
++ }
++ }
++ MASTER(cp)->auth_kind = 0;
++ }
++
++ /* clear text password authentication? */
++ else if (pid == 3)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:trying clear text password
authentication",func);
++#endif
++
++ pid = do_clear_text_password(MASTER(cp), frontend, 0,
protoMajor);
++
++ if (pid >= 0 && REPLICATION)
++ {
++ pid = do_clear_text_password(SECONDARY(cp), frontend,
0, protoMajor);
++ }
++ }
++
++ /* crypt authentication? */
++ else if (pid == 4)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:trying crypt authentication",func);
++#endif
++
++ pid = do_crypt(MASTER(cp), frontend, 0, protoMajor);
++
++ if (pid >= 0 && REPLICATION)
++ {
++ pid = do_crypt(SECONDARY(cp), frontend, 0,
protoMajor);
++ }
++ }
++
++ /* md5 authentication? */
++ else if (pid == 5)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:trying md5 authentication",func);
++#endif
++
++ pid = do_md5(MASTER(cp), frontend, 0, protoMajor);
++
++ if (pid >= 0 && REPLICATION)
++ {
++ pid = do_md5(SECONDARY(cp), frontend, 0, protoMajor);
++ }
++ }
++
++ if (pid != 0)
++ {
++ show_error("%s:pool_do_auth: backend does not return
authenticaton ok",func);
++ return -1;
++ }
++
++ /*
++ * authentication ok. now read pid and secret key from the
++ * backend
++ */
++ kind = pool_read_kind(cp);
++ if (kind < 0)
++ {
++ return -1;
++ }
++
++ /* error response? */
++ if (kind == 'E')
++ {
++ if (protoMajor == PROTO_MAJOR_V2)
++ ErrorResponse(frontend, cp);
++ else
++ SimpleForwardToFrontend(kind, frontend, cp);
++ return -1;
++ }
++ else if (kind != 'K')
++ {
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ /* process parameter status */
++ while (kind == 'S')
++ {
++ if (ParameterStatus(frontend, cp) !=
POOL_CONTINUE)
++ return -1;
++
++ pool_flush(frontend);
++
++ kind = pool_read_kind(cp);
++ if (kind < 0)
++ {
++ show_error("%s:pool_do_auth: failed
to read kind while processing ParamterStatus",func);
++ return -1;
++ }
++ }
++ }
++ else
++ {
++ show_error("%s:pool_do_auth: expect \"K\" got
%c",func, kind);
++ return -1;
++ }
++ }
++
++ /*
++ * message length (V3 only)
++ */
++ if (protoMajor == PROTO_MAJOR_V3 && (length =
pool_read_message_length(cp)) != 12)
++ {
++ show_error("%s:pool_do_auth: invalid messages length(%d) for
BackendKeyData",func, length);
++ return -1;
++ }
++
++ /*
++ * OK, read pid and secret key
++ */
++
++ /* pid */
++ pool_read(MASTER(cp), &pid, sizeof(pid));
++ MASTER_CONNECTION(cp)->pid = pid;
++
++ /* key */
++ pool_read(MASTER(cp), &key, sizeof(key));
++ MASTER_CONNECTION(cp)->key = key;
++
++ if (REPLICATION)
++ {
++ pool_read(SECONDARY(cp), &pid1, sizeof(pid1));
++ SECONDARY_CONNECTION(cp)->pid = pid;
++
++ /* key */
++ pool_read(SECONDARY(cp), &key1, sizeof(key1));
++ SECONDARY_CONNECTION(cp)->key = key;
++ }
++
++ return (pool_send_auth_ok(frontend, pid, key, protoMajor));
++}
++
++/*
++* do re-authentication for reused connection. if success return 0 otherwise
non 0.
++*/
++int pool_do_reauth(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *cp)
++{
++ char * func = "pool_do_reauth()";
++ int status;
++ int protoMajor;
++
++ protoMajor = MAJOR(cp);
++
++ switch(MASTER(cp)->auth_kind)
++ {
++ case 0:
++ /* trust */
++ status = 0;
++ break;
++
++ case 3:
++ /* clear text password */
++ status = do_clear_text_password(MASTER(cp), frontend,
1, protoMajor);
++ break;
++
++ case 4:
++ /* crypt password */
++ status = do_crypt(MASTER(cp), frontend, 1,
protoMajor);
++ break;
++
++ case 5:
++ /* md5 password */
++ status = do_md5(MASTER(cp), frontend, 1, protoMajor);
++ break;
++
++ default:
++ show_error("%s: unknown authentication request code
%d",
++ func,MASTER(cp)->auth_kind);
++ return -1;
++ }
++
++ if (status == 0)
++ {
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ int msglen;
++
++ pool_write(frontend, "R", 1);
++ msglen = htonl(8);
++ pool_write(frontend, &msglen, sizeof(msglen));
++ msglen = htonl(0);
++ if (pool_write_and_flush(frontend, &msglen,
sizeof(msglen)) < 0)
++ {
++ return -1;
++ }
++ }
++ }
++ else
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: authentication failed",func);
++#endif
++ return -1;
++ }
++
++ return (pool_send_auth_ok(frontend, MASTER_CONNECTION(cp)->pid,
MASTER_CONNECTION(cp)->key, protoMajor) != POOL_CONTINUE);
++}
++
++/*
++* send authentication ok to frontend. if success return 0 otherwise non 0.
++*/
++static POOL_STATUS pool_send_auth_ok(POOL_CONNECTION *frontend, int pid,
int key, int protoMajor)
++{
++ char kind;
++ int len;
++
++ if (protoMajor == PROTO_MAJOR_V2)
++ {
++ /* return "Authentication OK" to the frontend */
++ kind = 'R';
++ pool_write(frontend, &kind, 1);
++ len = htonl(0);
++ if (pool_write_and_flush(frontend, &len, sizeof(len)) < 0)
++ {
++ return -1;
++ }
++ }
++
++ /* send backend key data */
++ kind = 'K';
++ pool_write(frontend, &kind, 1);
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ len = htonl(12);
++ pool_write(frontend, &len, sizeof(len));
++ }
++ pool_write(frontend, &pid, sizeof(pid));
++ if (pool_write_and_flush(frontend, &key, sizeof(key)) < 0)
++ {
++ return -1;
++ }
++
++ return 0;
++}
++
++/*
++ * perform clear text password authetication
++ */
++static int do_clear_text_password(POOL_CONNECTION *backend, POOL_CONNECTION
*frontend, int reauth, int protoMajor)
++{
++ char * func = "do_clear_text_password()";
++ static int size;
++ static char password[MAX_PASSWORD_SIZE];
++ char response;
++ int kind;
++ int len;
++
++ /* master? */
++ if (!backend->issecondary_backend)
++ {
++ pool_write(frontend, "R", 1); /* authenticaton */
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ len = htonl(8);
++ pool_write(frontend, &len, sizeof(len));
++ }
++ kind = htonl(3); /* clear text password
authentication */
++ pool_write_and_flush(frontend, &kind, sizeof(kind)); /*
indicating clear text password authentication */
++
++ /* read password packet */
++ if (protoMajor == PROTO_MAJOR_V2)
++ {
++ if (pool_read(frontend, &size, sizeof(size)))
++ {
++ show_error("%s: failed to read password
packet size",func);
++ return -1;
++ }
++ }
++ else
++ {
++ char k;
++
++ if (pool_read(frontend, &k, sizeof(k)))
++ {
++ show_error("%s: failed to read password
packet \"p\"",func);
++ return -1;
++ }
++ if (k != 'p')
++ {
++ show_error("%s:packet does not start with
\"p\"",func);
++ return -1;
++ }
++ if (pool_read(frontend, &size, sizeof(size)))
++ {
++ show_error("%s: failed to read password
packet size",func);
++ return -1;
++ }
++ }
++
++ if ((ntohl(size) - 4) > sizeof(password))
++ {
++ show_error("%s: password is too long (size:
%d)",func, ntohl(size) - 4);
++ return -1;
++ }
++
++ if (pool_read(frontend, password, ntohl(size) - 4))
++ {
++ show_error("%s: failed to read password (size:
%d)",func, ntohl(size) - 4);
++ return -1;
++ }
++ }
++
++ /* connection reusing? */
++ if (reauth)
++ {
++ if ((ntohl(size) - 4) != backend->pwd_size)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s; password size does not match in
re-authetication",func);
++#endif
++ return -1;
++ }
++
++ if (memcmp(password, backend->password, backend->pwd_size) !=
0)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s; password does not match in
re-authetication",func);
++#endif
++ return -1;
++ }
++
++ return 0;
++ }
++
++ /* send password packet to backend */
++ if (protoMajor == PROTO_MAJOR_V3)
++ pool_write(backend, "p", 1);
++ pool_write(backend, &size, sizeof(size));
++ pool_write_and_flush(backend, password, ntohl(size) -4);
++ if (pool_read(backend, &response, sizeof(response)))
++ {
++ show_error("%s: failed to read authentication response",func);
++ return -1;
++ }
++
++ if (response != 'R')
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: backend does not return R while processing
clear text password authentication",func);
++#endif
++ return -1;
++ }
++
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ if (pool_read(backend, &len, sizeof(len)))
++ {
++ show_error("%s: failed to read authentication packet
size",func);
++ return -1;
++ }
++
++ if (ntohl(len) != 8)
++ {
++ show_error("%s: incorrect authentication packet size
(%d)",func, ntohl(len));
++ return -1;
++ }
++ }
++
++ /* expect to read "Authentication OK" response. kind should be 0... */
++ if (pool_read(backend, &kind, sizeof(kind)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: failed to read Authentication OK
response",func);
++#endif
++ return -1;
++ }
++
++ /* if authenticated, save info */
++ if (!reauth && kind == 0)
++ {
++ if (!backend->issecondary_backend && protoMajor ==
PROTO_MAJOR_V3)
++ {
++ int msglen;
++
++ pool_write(frontend, "R", 1);
++ msglen = htonl(8);
++ pool_write(frontend, &msglen, sizeof(msglen));
++ msglen = htonl(0);
++ if (pool_write_and_flush(frontend, &msglen,
sizeof(msglen)) < 0)
++ {
++ return -1;
++ }
++ }
++
++ backend->auth_kind = 3;
++ backend->pwd_size = ntohl(size) - 4;
++ memcpy(backend->password, password, backend->pwd_size);
++ }
++ return kind;
++}
++
++/*
++ * perform crypt authetication
++ */
++static int do_crypt(POOL_CONNECTION *backend, POOL_CONNECTION *frontend,
int reauth, int protoMajor)
++{
++ char * func = "do_crypt()";
++ char salt[2];
++ static int size;
++ static char password[MAX_PASSWORD_SIZE];
++ char response;
++ int kind;
++ int len;
++
++ if (!reauth)
++ {
++ /* read salt */
++ if (pool_read(backend, salt, sizeof(salt)))
++ {
++ show_error("%s: failed to read salt",func);
++ return -1;
++ }
++ }
++ else
++ {
++ memcpy(salt, backend->salt, sizeof(salt));
++ }
++
++ /* master? */
++ if (!backend->issecondary_backend)
++ {
++ pool_write(frontend, "R", 1); /* authenticaton */
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ len = htonl(10);
++ pool_write(frontend, &len, sizeof(len));
++ }
++ kind = htonl(4); /* crypt authentication */
++ pool_write(frontend, &kind, sizeof(kind)); /* indicating
crypt authentication */
++ pool_write_and_flush(frontend, salt, sizeof(salt));
/* salt */
++
++ /* read password packet */
++ if (protoMajor == PROTO_MAJOR_V2)
++ {
++ if (pool_read(frontend, &size, sizeof(size)))
++ {
++ show_error("%s: failed to read password
packet size",func);
++ return -1;
++ }
++ }
++ else
++ {
++ char k;
++
++ if (pool_read(frontend, &k, sizeof(k)))
++ {
++ show_error("%s: failed to read password
packet",func);
++ return -1;
++ }
++ if (k != 'p')
++ {
++ show_error("%s: password packet does not
start with \"p\"",func);
++ return -1;
++ }
++ if (pool_read(frontend, &size, sizeof(size)))
++ {
++ show_error("%s: failed to read password
packet size",func);
++ return -1;
++ }
++ }
++
++ if ((ntohl(size) - 4) > sizeof(password))
++ {
++ show_error("%s: password is too long(size: %d)",
func,ntohl(size) - 4);
++ return -1;
++ }
++
++ if (pool_read(frontend, password, ntohl(size) - 4))
++ {
++ show_error("%s: failed to read password (size: %d)",
func,ntohl(size) - 4);
++ return -1;
++ }
++ }
++
++ /* connection reusing? */
++ if (reauth)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:size: %d saved_size: %d",func, (ntohl(size) -
4), backend->pwd_size);
++#endif
++ if ((ntohl(size) - 4) != backend->pwd_size)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: password size does not match in
re-authetication",func);
++#endif
++ return -1;
++ }
++
++ if (memcmp(password, backend->password, backend->pwd_size) !=
0)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: password does not match in
re-authetication",func);
++#endif
++ return -1;
++ }
++
++ return 0;
++ }
++
++ /* send password packet to backend */
++ if (protoMajor == PROTO_MAJOR_V3)
++ pool_write(backend, "p", 1);
++ pool_write(backend, &size, sizeof(size));
++ pool_write_and_flush(backend, password, ntohl(size) -4);
++ if (pool_read(backend, &response, sizeof(response)))
++ {
++ show_error("%s: failed to read authentication response",func);
++ return -1;
++ }
++
++ if (response != 'R')
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: backend does not return R while processing
crypt authentication(%02x) secondary: %d",func, response,
backend->issecondary_backend);
++#endif
++ return -1;
++ }
++
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ if (pool_read(backend, &len, sizeof(len)))
++ {
++ show_error("%s: failed to read authentication packet
size",func);
++ return -1;
++ }
++
++ if (ntohl(len) != 8)
++ {
++ show_error("%s: incorrect authentication packet size
(%d)",func, ntohl(len));
++ return -1;
++ }
++ }
++
++ /* expect to read "Authentication OK" response. kind should be 0... */
++ if (pool_read(backend, &kind, sizeof(kind)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: failed to read Authentication OK
response",func);
++#endif
++ return -1;
++ }
++
++ /* if authenticated, save info */
++ if (!reauth && kind == 0)
++ {
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ int msglen;
++
++ pool_write(frontend, "R", 1);
++ msglen = htonl(8);
++ pool_write(frontend, &msglen, sizeof(msglen));
++ msglen = htonl(0);
++ if (pool_write_and_flush(frontend, &msglen,
sizeof(msglen)) < 0)
++ {
++ return -1;
++ }
++ }
++ backend->auth_kind = 4;
++ backend->pwd_size = ntohl(size) - 4;
++ memcpy(backend->password, password, backend->pwd_size);
++ memcpy(backend->salt, salt, sizeof(salt));
++ }
++ return kind;
++}
++
++/*
++ * perform MD5 authetication
++ */
++static int do_md5(POOL_CONNECTION *backend, POOL_CONNECTION *frontend, int
reauth, int protoMajor)
++{
++ char * func = "do_md5()";
++ char salt[4];
++ static int size;
++ static char password[MAX_PASSWORD_SIZE];
++ char response;
++ int kind;
++ int len;
++
++ if (!reauth)
++ {
++ /* read salt */
++ if (pool_read(backend, salt, sizeof(salt)))
++ {
++ show_error("%s: failed to read salt",func);
++ return -1;
++ }
++ }
++ else
++ {
++ memcpy(salt, backend->salt, sizeof(salt));
++ }
++
++ /* master? */
++ if (!backend->issecondary_backend)
++ {
++ pool_write(frontend, "R", 1); /* authenticaton */
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ len = htonl(12);
++ pool_write(frontend, &len, sizeof(len));
++ }
++ kind = htonl(5);
++ pool_write(frontend, &kind, sizeof(kind)); /* indicating
MD5 */
++ pool_write_and_flush(frontend, salt, sizeof(salt));
/* salt */
++
++ /* read password packet */
++ if (protoMajor == PROTO_MAJOR_V2)
++ {
++ if (pool_read(frontend, &size, sizeof(size)))
++ {
++ show_error("%s: failed to read password
packet size",func);
++ return -1;
++ }
++ }
++ else
++ {
++ char k;
++
++ if (pool_read(frontend, &k, sizeof(k)))
++ {
++ show_error("%s: failed to read password
packet \"p\"",func);
++ return -1;
++ }
++ if (k != 'p')
++ {
++ show_error("%s: password packet does not
start with \"p\"",func);
++ return -1;
++ }
++ if (pool_read(frontend, &size, sizeof(size)))
++ {
++ show_error("%s: failed to read password
packet size",func);
++ return -1;
++ }
++ }
++
++ if ((ntohl(size) - 4) > sizeof(password))
++ {
++ show_error("%s: password is too long(size: %d)",func,
ntohl(size) - 4);
++ return -1;
++ }
++
++ if (pool_read(frontend, password, ntohl(size) - 4))
++ {
++ show_error("%s: failed to read password (size:
%d)",func, ntohl(size) - 4);
++ return -1;
++ }
++ }
++
++ /* connection reusing? */
++ if (reauth)
++ {
++ if ((ntohl(size) - 4) != backend->pwd_size)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s; password size does not match in
re-authetication",func);
++#endif
++ return -1;
++ }
++
++ if (memcmp(password, backend->password, backend->pwd_size) !=
0)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s; password does not match in
re-authetication",func);
++#endif
++ return -1;
++ }
++
++ return 0;
++ }
++
++ /* send password packet to backend */
++ if (protoMajor == PROTO_MAJOR_V3)
++ pool_write(backend, "p", 1);
++ pool_write(backend, &size, sizeof(size));
++ pool_write_and_flush(backend, password, ntohl(size) -4);
++ if (pool_read(backend, &response, sizeof(response)))
++ {
++ show_error("%s: failed to read authentication response",func);
++ return -1;
++ }
++
++ if (response != 'R')
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: backend does not return R while processing
MD5 authentication %c", func,response);
++#endif
++ return -1;
++ }
++
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ if (pool_read(backend, &len, sizeof(len)))
++ {
++ show_error("%s: failed to read authentication packet
size",func);
++ return -1;
++ }
++
++ if (ntohl(len) != 8)
++ {
++ show_error("%s: incorrect authentication packet size
(%d)",func, ntohl(len));
++ return -1;
++ }
++ }
++
++ /* expect to read "Authentication OK" response. kind should be 0... */
++ if (pool_read(backend, &kind, sizeof(kind)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: failed to read Authentication OK
response",func);
++#endif
++ return -1;
++ }
++
++ /* if authenticated, save info */
++ if (!reauth && kind == 0)
++ {
++ if (protoMajor == PROTO_MAJOR_V3)
++ {
++ int msglen;
++
++ pool_write(frontend, "R", 1);
++ msglen = htonl(8);
++ pool_write(frontend, &msglen, sizeof(msglen));
++ msglen = htonl(0);
++ if (pool_write_and_flush(frontend, &msglen,
sizeof(msglen)) < 0)
++ {
++ return -1;
++ }
++ }
++ backend->auth_kind = 5;
++ backend->pwd_size = ntohl(size) - 4;
++ memcpy(backend->password, password, backend->pwd_size);
++ memcpy(backend->salt, salt, sizeof(salt));
++ }
++ return kind;
++}
++
++/*
++ * read message length (V3 only)
++ */
++int pool_read_message_length(POOL_CONNECTION_POOL *cp)
++{
++ char * func = "pool_read_message_length()";
++ int status;
++ int length, length1;
++
++ status = pool_read(MASTER(cp), &length, sizeof(length));
++ if (status < 0)
++ {
++ show_error("%s: error while reading message length",func);
++ return -1;
++ }
++ length = ntohl(length);
++
++ if (REPLICATION)
++ {
++ status = pool_read(SECONDARY(cp), &length1, sizeof(length1));
++ if (status < 0)
++ {
++ show_error("%s: error while reading message length
from secondary backend",func);
++ return -1;
++ }
++ length1 = ntohl(length1);
++
++ if (length != length1)
++ {
++ show_error("%s: length does not match between
backends master(%d) secondary(%d)",
++ func,length, length1);
++ return -1;
++ }
++ }
++
++ if (length < 0)
++ {
++ show_error("%s:read_message_length: invalid message length
(%d)", func, length);
++ return -1;
++ }
++
++ return length;
++}
++
++signed char pool_read_kind(POOL_CONNECTION_POOL *cp)
++{
++ char * func = "pool_read_kind()";
++ int status;
++ char kind, kind1;
++
++ status = pool_read(MASTER(cp), &kind, sizeof(kind));
++ if (status < 0)
++ {
++ show_error("%s:read_message_kind: error while reading message
kind",func);
++ return -1;
++ }
++
++ if (REPLICATION)
++ {
++ status = pool_read(SECONDARY(cp), &kind1, sizeof(kind1));
++ if (status < 0)
++ {
++ show_error("%s: error while reading message kind from
secondary backend",func);
++ return -1;
++ }
++
++ if (kind != kind1)
++ {
++ show_error("%s: kind does not match between backends
master(%d) secondary(%d)",
++ func, kind, kind1);
++ return -1;
++ }
++ }
++
++ return kind;
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/pool_connection_pool.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_connection_pool.c
+--- postgresql-8.1.2/src/pgcluster/pglb/pool_connection_pool.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_connection_pool.c
2006-02-02 22:32:25.000000000 +0100
+@@ -0,0 +1,535 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * pool_connection_pool.c
++ *
++ * NOTE:
++ * connection pool 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 "postgres.h"
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <sys/time.h>
++#include <sys/un.h>
++#include <arpa/inet.h>
++#include <netdb.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <signal.h>
++#include <string.h>
++#include <unistd.h>
++#include <time.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#include "replicate_com.h"
++#include "pglb.h"
++
++POOL_CONNECTION_POOL *pool_connection_pool; /* connection pool */
++
++int pool_init_cp(void);
++POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int
protoMajor);
++void pool_discard_cp(char *user, char *database, int protoMajor);
++POOL_CONNECTION_POOL *pool_create_cp(void);
++void pool_connection_pool_timer(POOL_CONNECTION_POOL *backend);
++void pool_backend_timer_handler(int sig);
++int connect_inet_domain_socket(int secondary_backend);
++int connect_unix_domain_socket(int secondary_backend);
++char PGRis_same_host(char * host1, char * host2);
++void pool_finish(void);
++
++
++static POOL_CONNECTION_POOL_SLOT *create_cp(POOL_CONNECTION_POOL_SLOT *cp,
int secondary_backend);
++static POOL_CONNECTION_POOL *new_connection(POOL_CONNECTION_POOL *p);
++
++
++
++/*
++* initialize connection pools. this should be called once at the startup.
++*/
++int pool_init_cp(void)
++{
++ char * func = "pool_init_cp()";
++ pool_connection_pool = (POOL_CONNECTION_POOL
*)malloc(sizeof(POOL_CONNECTION_POOL)*Max_Pool);
++ if (pool_connection_pool == NULL)
++ {
++ show_error("%s: malloc() failed[%s]",func,strerror(errno));
++ return -1;
++ }
++ memset(pool_connection_pool, 0,
sizeof(POOL_CONNECTION_POOL)*Max_Pool);
++
++ return 0;
++}
++
++/*
++* find connection by user and database
++*/
++POOL_CONNECTION_POOL *pool_get_cp(char *user, char *database, int
protoMajor)
++{
++ char * func = "pool_get_cp()";
++ int i;
++
++ POOL_CONNECTION_POOL *p = pool_connection_pool;
++
++ if (p == NULL)
++ {
++ show_error("%s: pool_connection_pool is not
initialized",func);
++ return NULL;
++ }
++
++ for (i=0;i<Max_Pool;i++)
++ {
++ if (MASTER_CONNECTION(p) &&
++ MASTER_CONNECTION(p)->sp->major == protoMajor &&
++ MASTER_CONNECTION(p)->sp->user != NULL &&
++ strcmp(MASTER_CONNECTION(p)->sp->user, user) == 0 &&
++ strcmp(MASTER_CONNECTION(p)->sp->database, database)
== 0)
++ {
++ /* mark this connection is under use */
++ MASTER_CONNECTION(p)->closetime = 0;
++ return p;
++ }
++ p++;
++ }
++ return NULL;
++}
++
++/*
++ * disconnect and release a connection to the database
++ */
++void pool_discard_cp(char *user, char *database, int protoMajor)
++{
++ char * func = "pool_discard_cp()";
++ POOL_CONNECTION_POOL *p = pool_get_cp(user, database, protoMajor);
++
++ if (p == NULL)
++ {
++ show_error("%s: cannot get connection pool for user %s
datbase %s", func,user, database);
++ return;
++ }
++
++ free(MASTER_CONNECTION(p)->sp->user);
++ free(MASTER_CONNECTION(p)->sp->database);
++ free(MASTER_CONNECTION(p)->sp->startup_packet);
++ pool_close(MASTER_CONNECTION(p)->con);
++
++ memset(p, 0, sizeof(POOL_CONNECTION_POOL));
++}
++
++
++/*
++* create a connection pool by user and database
++*/
++POOL_CONNECTION_POOL *pool_create_cp(void)
++{
++ char * func = "pool_create_cp()";
++ int i;
++ time_t closetime;
++ POOL_CONNECTION_POOL *oldestp;
++
++ POOL_CONNECTION_POOL *p = pool_connection_pool;
++
++ if (p == NULL)
++ {
++ show_error("%s: pool_connection_pool is not
initialized",func);
++ return NULL;
++ }
++
++ for (i=0; i<Max_Pool; i++)
++ {
++ if (MASTER_CONNECTION(p) == NULL)
++ return new_connection(p);
++ p++;
++ }
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:no empty connection slot was found",func);
++#endif
++
++ /*
++ * no empty connection slot was found. look for the oldest connection
and discard it.
++ */
++ oldestp = p = pool_connection_pool;
++ closetime = MASTER_CONNECTION(p)->closetime;
++ for (i=0; i<Max_Pool; i++)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:user: %s database: %s closetime: %d",
++ func,
++ MASTER_CONNECTION(p)->sp->user,
++ MASTER_CONNECTION(p)->sp->database,
++ MASTER_CONNECTION(p)->closetime);
++#endif
++ if (MASTER_CONNECTION(p)->closetime < closetime)
++ {
++ closetime = MASTER_CONNECTION(p)->closetime;
++ oldestp = p;
++ }
++ p++;
++ }
++
++ p = oldestp;
++ pool_send_frontend_exits(p);
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:discarding old %d th connection. user: %s database:
%s",
++ func,
++ oldestp - pool_connection_pool,
++ MASTER_CONNECTION(p)->sp->user,
++ MASTER_CONNECTION(p)->sp->database);
++#endif
++
++ free(MASTER_CONNECTION(p)->sp->user);
++ free(MASTER_CONNECTION(p)->sp->database);
++ free(MASTER_CONNECTION(p)->sp->startup_packet);
++ pool_close(MASTER_CONNECTION(p)->con);
++
++ memset(p, 0, sizeof(POOL_CONNECTION_POOL));
++
++ return new_connection(p);
++}
++
++/*
++ * set backend connection close timer
++ */
++void pool_connection_pool_timer(POOL_CONNECTION_POOL *backend)
++{
++#ifdef PRINT_DEBUG
++ char * func = "pool_connection_pool_timer()";
++#endif
++ POOL_CONNECTION_POOL *p = pool_connection_pool;
++ int i;
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:pool_connection_pool_timer: called",func);
++#endif
++
++ MASTER_CONNECTION(backend)->closetime = time(NULL); /*
set connection close time */
++
++ if (Connection_Life_Time == 0)
++ return;
++
++ /* look for any other timeout */
++ for (i=0;i<Max_Pool;i++, p++)
++ {
++ if (!MASTER_CONNECTION(p))
++ continue;
++ if (MASTER_CONNECTION(p)->sp->user == NULL)
++ continue;
++
++ if (p != backend && MASTER_CONNECTION(p)->closetime)
++ return;
++ }
++
++ /* no other timer found. set my timer */
++#ifdef PRINT_DEBUG
++ show_debug("%s: set alarm after %d seconds",func,
Connection_Life_Time);
++#endif
++ signal(SIGALRM, pool_backend_timer_handler);
++ alarm(Connection_Life_Time);
++}
++
++/*
++ * backend connection close timer handler
++ */
++void pool_backend_timer_handler(int sig)
++{
++#define TMINTMAX 0x7fffffff
++
++#ifdef PRINT_DEBUG
++ char * func = "pool_backend_timer_handler()";
++#endif
++ POOL_CONNECTION_POOL *p = pool_connection_pool;
++ int i;
++ time_t now;
++ time_t nearest = TMINTMAX;
++
++ now = time(NULL);
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:called at %d", func,now);
++#endif
++
++ for (i=0;i<Max_Pool;i++, p++)
++ {
++ if (!MASTER_CONNECTION(p))
++ continue;
++ if (MASTER_CONNECTION(p)->sp->user == NULL)
++ continue;
++
++ /* timer expire? */
++ if (MASTER_CONNECTION(p)->closetime)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: expire time: %d",
++ func,
++
MASTER_CONNECTION(p)->closetime+Connection_Life_Time);
++#endif
++
++ if (now >=
(MASTER_CONNECTION(p)->closetime+Connection_Life_Time))
++ {
++ /* discard expired connection */
++#ifdef PRINT_DEBUG
++ show_debug("%s: expires user %s database %s",
func, MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database);
++#endif
++
++ pool_send_frontend_exits(p);
++
++ free(MASTER_CONNECTION(p)->sp->user);
++ free(MASTER_CONNECTION(p)->sp->database);
++
free(MASTER_CONNECTION(p)->sp->startup_packet);
++ pool_close(MASTER_CONNECTION(p)->con);
++
++ memset(p, 0, sizeof(POOL_CONNECTION_POOL));
++ }
++ else
++ {
++ /* look for nearest timer */
++ if (MASTER_CONNECTION(p)->closetime < nearest)
++ nearest =
MASTER_CONNECTION(p)->closetime;
++ }
++ }
++ }
++
++ /* any remaining timer */
++ if (nearest != TMINTMAX)
++ {
++ nearest = Connection_Life_Time - (now - nearest);
++ if (nearest <= 0)
++ nearest = 1;
++ signal(SIGALRM, pool_backend_timer_handler);
++ alarm(nearest);
++ }
++}
++
++int connect_inet_domain_socket(int secondary_backend)
++{
++ char * func = "connect_inet_domain_socket()";
++ int fd;
++ int len;
++ int on = 1;
++ struct sockaddr_in addr;
++ struct hostent *hp;
++
++ fd = socket(AF_INET, SOCK_STREAM, 0);
++ if (fd < 0)
++ {
++ show_error("%s: socket() failed: %s",func, strerror(errno));
++ return -1;
++ }
++
++ /* set nodelay */
++ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
++ (char *) &on,
++ sizeof(on)) < 0)
++ {
++ show_error("%s: setsockopt() failed: %s", func,
strerror(errno));
++ close(fd);
++ return -1;
++ }
++
++ memset((char *) &addr, 0, sizeof(addr));
++ ((struct sockaddr *)&addr)->sa_family = AF_INET;
++
++ addr.sin_port = htons(CurrentCluster->port);
++ len = sizeof(struct sockaddr_in);
++
++ hp = gethostbyname(CurrentCluster->hostName);
++
++ if ((hp == NULL) || (hp->h_addrtype != AF_INET))
++ {
++ show_error("%s: gethostbyname() failed: %s host: %s",func,
strerror(errno), CurrentCluster->hostName);
++ close(fd);
++ return -1;
++ }
++ memmove((char *) &(addr.sin_addr),
++ (char *) hp->h_addr,
++ hp->h_length);
++
++ if (connect(fd, (struct sockaddr *)&addr, len) < 0)
++ {
++ show_error("%s: connect() failed: %s",func,strerror(errno));
++ close(fd);
++ return -1;
++ }
++ return fd;
++}
++
++int connect_unix_domain_socket(int secondary_backend)
++{
++ char * func = "connect_unix_domain_socket()";
++ struct sockaddr_un addr;
++ int fd;
++ int len;
++ int port;
++
++ fd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (fd == -1)
++ {
++ show_error("%s: setsockopt() failed: %s",
func,strerror(errno));
++ return -1;
++ }
++
++ port = CurrentCluster->port;
++ memset((char *) &addr, 0, sizeof(addr));
++ ((struct sockaddr *)&addr)->sa_family = AF_UNIX;
++ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.s.PGSQL.%d",
++ Backend_Socket_Dir,
++ CurrentCluster->port);
++#ifdef PRINT_DEBUG
++ show_debug("%s:postmaster Unix domain socket: %s",func,
addr.sun_path);
++#endif
++
++ len = sizeof(struct sockaddr_un);
++
++ if (connect(fd, (struct sockaddr *)&addr, len) < 0)
++ {
++ show_error("%s: connect() failed: %s",func, strerror(errno));
++ close(fd);
++ return -1;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:connected to postmaster Unix domain socket: %s fd:
%d", func,addr.sun_path, fd);
++#endif
++ return fd;
++}
++
++static POOL_CONNECTION_POOL_SLOT *create_cp(POOL_CONNECTION_POOL_SLOT *cp,
int secondary_backend)
++{
++ char * func = "create_cp()";
++ int fd;
++ char hostName[HOSTNAME_MAX_LENGTH];
++
++ if (gethostname(hostName,sizeof(hostName)) < 0)
++ {
++ show_error("%s:gethostname() failed.
(%s)",func,strerror(errno));
++ return NULL;
++ }
++ if (PGRis_same_host(hostName,CurrentCluster->hostName) == 1)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:[%s] [%s] is
same",func,hostName,CurrentCluster->hostName);
++#endif
++ fd = connect_unix_domain_socket(secondary_backend);
++ }
++ else
++ {
++ fd = connect_inet_domain_socket(secondary_backend);
++ }
++
++ if (fd < 0)
++ {
++ /* fatal error, notice to parent and exit */
++ notice_backend_error();
++ exit(1);
++ }
++
++ cp->con = pool_open(fd);
++ cp->closetime = 0;
++ return cp;
++}
++
++static POOL_CONNECTION_POOL *new_connection(POOL_CONNECTION_POOL *p)
++{
++ char * func = "new_connection()";
++ /* create master connection */
++ MASTER_CONNECTION(p) = malloc(sizeof(POOL_CONNECTION_POOL_SLOT));
++ if (MASTER_CONNECTION(p) == NULL)
++ {
++ show_error("%s: malloc() failed [%s]",func,strerror(errno));
++ return NULL;
++ }
++ create_cp(MASTER_CONNECTION(p), 0);
++
++ /* initialize Paramter Status save structure */
++ if (pool_init_params(&MASTER(p)->params))
++ {
++ return NULL;
++ }
++ p->num = 1; /* number of slots */
++
++ return p;
++}
++
++char PGRis_same_host(char * host1, char * host2)
++{
++ unsigned int ip1, ip2;
++
++ if ((host1 == NULL) || (host2 == NULL))
++ {
++ return 0;
++ }
++ ip1 = PGRget_ip_by_name( host1);
++ ip2 = PGRget_ip_by_name( host2);
++ if (ip1 == ip2)
++ {
++ return 1;
++ }
++ return 0;
++}
++
++void pool_finish(void)
++{
++ char * func = "pool_finish()";
++ int i;
++
++ POOL_CONNECTION_POOL *p = pool_connection_pool;
++return;
++ if (p == NULL)
++ {
++ show_error("%s:pool_connection_pool is not initialized",func);
++ return ;
++ }
++
++ for (i=0 ; i<Max_Pool ; i++)
++ {
++ if (p == NULL)
++ break;
++ /*
++ if (MASTER_CONNECTION(p)->sp->user != NULL)
++ {
++ free(MASTER_CONNECTION(p)->sp->user);
++ MASTER_CONNECTION(p)->sp->user = NULL;
++ }
++ if (MASTER_CONNECTION(p)->sp->database != NULL)
++ {
++ free(MASTER_CONNECTION(p)->sp->database);
++ MASTER_CONNECTION(p)->sp->database = NULL;
++ }
++ if (MASTER_CONNECTION(p)->sp->startup_packet != NULL)
++ {
++ free(MASTER_CONNECTION(p)->sp->startup_packet);
++ MASTER_CONNECTION(p)->sp->startup_packet = NULL;
++ }
++ */
++ if (MASTER_CONNECTION(p)->con != NULL)
++ {
++ pool_close(MASTER_CONNECTION(p)->con);
++ MASTER_CONNECTION(p)->con = NULL;
++ }
++ memset(p, 0, sizeof(POOL_CONNECTION_POOL));
++ p++;
++ }
++ free((char *)pool_connection_pool);
++ pool_connection_pool = NULL;
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/pool_params.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_params.c
+--- postgresql-8.1.2/src/pgcluster/pglb/pool_params.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_params.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,184 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * pool_params.c
++ *
++ * NOTE:
++ * connection pool 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 <stdio.h>
++#include <sys/time.h>
++#include <time.h>
++#include <stdlib.h>
++#include <string.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#include "replicate_com.h"
++#include "pglb.h"
++
++#define MAX_PARAM_ITEMS 128
++
++int pool_init_params(ParamStatus *params);
++void pool_discard_params(ParamStatus *params);
++char *pool_find_name(ParamStatus *params, char *name, int *pos);
++int pool_get_param(ParamStatus *params, int index, char **name, char
**value);
++int pool_add_param(ParamStatus *params, char *name, char *value);
++void pool_param_debug_print(ParamStatus *params);
++
++/*
++ * initialize parameter structure
++ */
++int pool_init_params(ParamStatus *params)
++{
++ char * func = "pool_init_params()";
++
++ params->num = 0;
++ params->names = malloc(MAX_PARAM_ITEMS*sizeof(char *));
++ if (params->names == NULL)
++ {
++ show_error("%s: cannot allocate memory",func);
++ return -1;
++ }
++ params->values = malloc(MAX_PARAM_ITEMS*sizeof(char *));
++ if (params->values == NULL)
++ {
++ show_error("%s: cannot allocate memory",func);
++ return -1;
++ }
++ return 0;
++}
++
++/*
++ * discard parameter structure
++ */
++void pool_discard_params(ParamStatus *params)
++{
++ int i;
++
++ for (i=0;i<params->num;i++)
++ {
++ free(params->names[i]);
++ free(params->values[i]);
++ }
++ free(params->names);
++ free(params->values);
++}
++
++/*
++ * find param value by name. if found, its value is returned
++ * also, pos is set
++ * if not found, NULL is returned
++ */
++char *pool_find_name(ParamStatus *params, char *name, int *pos)
++{
++ int i;
++
++ for (i=0;i<params->num;i++)
++ {
++ if (!strcmp(name, params->names[i]))
++ {
++ *pos = i;
++ return params->values[i];
++ }
++ }
++ return NULL;
++}
++
++/*
++ * return name and value by index.
++ */
++int pool_get_param(ParamStatus *params, int index, char **name, char
**value)
++{
++ if (index < 0 || index >= params->num)
++ return -1;
++
++ *name = params->names[index];
++ *value = params->values[index];
++
++ return 0;
++}
++
++/*
++ * add or replace name/value pair
++ */
++int pool_add_param(ParamStatus *params, char *name, char *value)
++{
++ char * func = "pool_add_param()";
++ int pos;
++
++ if (pool_find_name(params, name, &pos))
++ {
++ /* name already exists */
++ if (strlen(params->values[pos]) < strlen(value))
++ {
++ params->values[pos] = realloc(params->values[pos],
strlen(value) + 1);
++ if (params->values[pos] == NULL)
++ {
++ show_error("%s: cannot allocate memory",func);
++ return -1;
++ }
++ }
++ strcpy(params->values[pos], value);
++ }
++ else
++ {
++ int num;
++
++ /* add name/value pair */
++ if (params->num >= MAX_PARAM_ITEMS)
++ {
++ show_error("%s: no more room for num",func);
++ return -1;
++ }
++ num = params->num;
++ params->names[num] = strdup(name);
++ if (params->names[num] == NULL)
++ {
++ show_error("%s: cannot allocate memory",func);
++ return -1;
++ }
++ params->values[num] = strdup(value);
++ if (params->values[num] == NULL)
++ {
++ show_error("%s: cannot allocate memory",func);
++ return -1;
++ }
++ params->num++;
++ }
++ return 0;
++}
++
++void pool_param_debug_print(ParamStatus *params)
++{
++#ifdef PRINT_DEBUG
++ char * func = "pool_param_debug_print()";
++#endif
++ int i;
++
++ for (i=0;i<params->num;i++)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: No.%d: name: %s value: %s",func, i,
params->names[i], params->values[i]);
++#endif
++ }
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/pool_process_query.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_process_query.c
+--- postgresql-8.1.2/src/pgcluster/pglb/pool_process_query.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_process_query.c 2006-02-02
22:32:08.000000000 +0100
+@@ -0,0 +1,2100 @@
++/*--------------------------------------------------------------------
++ * 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 <arpa/inet.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);
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/pool_stream.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_stream.c
+--- postgresql-8.1.2/src/pgcluster/pglb/pool_stream.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/pool_stream.c 2006-02-02
22:30:03.000000000 +0100
+@@ -0,0 +1,585 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * pool_stream.c
++ *
++ * NOTE:
++ * stream I/O modules
++ *
++ * 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 <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <sys/time.h>
++
++#include "postgres_fe.h"
++#include "libpq/pqcomm.h"
++#include "replicate_com.h"
++#include "pglb.h"
++
++#define READBUFSZ 1024
++
++POOL_CONNECTION *pool_open(int fd);
++void pool_close(POOL_CONNECTION *cp);
++int pool_read(POOL_CONNECTION *cp, void *buf, int len);
++char *pool_read2(POOL_CONNECTION *cp, int len);
++int pool_write(POOL_CONNECTION *cp, void *buf, int len);
++int pool_flush(POOL_CONNECTION *cp);
++int pool_write_and_flush(POOL_CONNECTION *cp, void *buf, int len);
++char *pool_read_string(POOL_CONNECTION *cp, int *len, int line);
++
++static int mystrlen(char *str, int upper, int *flag);
++static int mystrlinelen(char *str, int upper, int *flag);
++static int save_pending_data(POOL_CONNECTION *cp, void *data, int len);
++static int consume_pending_data(POOL_CONNECTION *cp, void *data, int len);
++
++
++/*
++* open read/write file descriptors.
++* returns POOL_CONNECTION on success otherwise NULL.
++*/
++POOL_CONNECTION *pool_open(int fd)
++{
++ POOL_CONNECTION *cp;
++
++ cp = (POOL_CONNECTION *)malloc(sizeof(POOL_CONNECTION));
++ if (cp == NULL)
++ {
++ show_error("pool_open: malloc failed: %s", strerror(errno));
++ return NULL;
++ }
++
++ memset(cp, 0, sizeof(*cp));
++
++ cp->write_fd = fdopen(fd, "w");
++ if (cp->write_fd == NULL)
++ {
++ show_error("pool_open: fdopen failed: %s",strerror(errno));
++ free(cp);
++ return NULL;
++ }
++
++ /* initialize pending data buffer */
++ cp->hp = malloc(READBUFSZ);
++ if (cp->hp == NULL)
++ {
++ show_error("pool_open: malloc failed");
++ return NULL;
++ }
++ cp->bufsz = READBUFSZ;
++ cp->po = 0;
++ cp->len = 0;
++ cp->sbuf = NULL;
++ cp->sbufsz = 0;
++ cp->buf2 = NULL;
++ cp->sbufsz = 0;
++
++ cp->fd = fd;
++ return cp;
++}
++
++/*
++* close read/write file descriptors.
++*/
++void pool_close(POOL_CONNECTION *cp)
++{
++ close(cp->fd);
++ fclose(cp->write_fd);
++ free(cp->hp);
++ if (cp->sbuf)
++ free(cp->sbuf);
++ if (cp->buf2)
++ free(cp->buf2);
++ pool_discard_params(&cp->params);
++ free(cp);
++}
++
++/*
++* read len bytes from cp
++* returns 0 on success otherwise -1.
++*/
++int pool_read(POOL_CONNECTION *cp, void *buf, int len)
++{
++ static char readbuf[READBUFSZ];
++
++ int consume_size;
++ int readlen;
++ char * ptr = (char *)buf;
++
++ consume_size = consume_pending_data(cp, ptr, len);
++ len -= consume_size;
++ ptr += consume_size;
++
++ while (len > 0)
++ {
++ if (cp->issecondary_backend)
++ {
++ if (pool_check_fd(cp, 0))
++ {
++ show_error("pool_read: secondary data is not
ready. abort this session");
++ exit(1);
++ }
++ }
++
++ readlen = read(cp->fd, readbuf, READBUFSZ);
++ if (readlen == -1)
++ {
++ show_error("pool_read: read failed (%s)",
strerror(errno));
++
++ if (cp->isbackend)
++ {
++ /* fatal error, notice to parent and exit */
++ notice_backend_error();
++ exit(1);
++ }
++ else
++ {
++ return -1;
++ }
++ }
++ else if (readlen == 0)
++ {
++ show_error("pool_read: EOF encountered");
++
++ if (cp->isbackend)
++ {
++ /* fatal error, notice to parent and exit */
++ notice_backend_error();
++ exit(1);
++ }
++ else
++ {
++ /*
++ * if backend offers authentication method,
frontend could close connection
++ */
++ return -1;
++ }
++ }
++
++ if (len < readlen)
++ {
++ /* overrun. we need to save remaining data to pending
buffer */
++ if (save_pending_data(cp, readbuf+len, readlen-len))
++ return -1;
++ memmove(ptr, readbuf, len);
++ break;
++ }
++
++ memmove(ptr, readbuf, readlen);
++ ptr += readlen;
++ len -= readlen;
++ }
++
++ return 0;
++}
++
++/*
++* read exactly len bytes from cp
++* returns buffer address on success otherwise NULL.
++*/
++char *pool_read2(POOL_CONNECTION *cp, int len)
++{
++ char *buf;
++ int req_size;
++ int alloc_size;
++ int consume_size;
++ int readlen;
++
++ req_size = cp->len + len;
++
++ if (req_size > cp->bufsz2)
++ {
++ alloc_size = ((req_size+1)/READBUFSZ+1)*READBUFSZ;
++ cp->buf2 = realloc(cp->buf2, alloc_size);
++ if (cp->buf2 == NULL)
++ {
++ show_error("pool_read2: failed to realloc");
++ exit(1);
++ }
++ cp->bufsz2 = alloc_size;
++ }
++
++ buf = cp->buf2;
++
++ consume_size = consume_pending_data(cp, buf, len);
++ len -= consume_size;
++ buf += consume_size;
++
++ while (len > 0)
++ {
++ if (cp->issecondary_backend)
++ {
++ if (pool_check_fd(cp, 0))
++ {
++ show_error("pool_read2: secondary data is not
ready. abort this session");
++ exit(1);
++ }
++ }
++
++ readlen = read(cp->fd, buf, len);
++ if (readlen == -1)
++ {
++ show_error("pool_read2: read failed (%s)",
strerror(errno));
++
++ if (cp->isbackend)
++ {
++ /* fatal error, notice to parent and exit */
++ notice_backend_error();
++ exit(1);
++ }
++ else
++ {
++ return NULL;
++ }
++ }
++ else if (readlen == 0)
++ {
++ show_error("pool_read2: EOF encountered");
++
++ if (cp->isbackend)
++ {
++ /* fatal error, notice to parent and exit */
++ notice_backend_error();
++ exit(1);
++ }
++ else
++ {
++ /*
++ * if backend offers authentication method,
frontend could close connection
++ */
++ return NULL;
++ }
++ }
++
++ buf += readlen;
++ len -= readlen;
++ }
++
++ return cp->buf2;
++}
++
++/*
++* write len bytes from cp
++* returns 0 on success otherwise -1.
++*/
++int pool_write(POOL_CONNECTION *cp, void *buf, int len)
++{
++ if (!cp->no_forward)
++ fwrite(buf, len, 1, cp->write_fd);
++
++ return 0;
++}
++
++/*
++* flush write buffer
++*/
++int pool_flush(POOL_CONNECTION *cp)
++{
++ if (fflush(cp->write_fd) != 0)
++ {
++ show_error("pool_flush: fflush failed (%s)", strerror(errno));
++
++ if (cp->isbackend)
++ {
++ notice_backend_error();
++ exit(1);
++ }
++ else
++ {
++ return -1;
++ }
++ }
++ return 0;
++}
++
++/*
++* combo of pool_write and pool_flush
++*/
++int pool_write_and_flush(POOL_CONNECTION *cp, void *buf, int len)
++{
++ if (pool_write(cp, buf, len))
++ return -1;
++ return pool_flush(cp);
++}
++
++/*
++ * read a string until EOF or NULL is encountered.
++ * if line is not 0, read until new line is encountered.
++*/
++char *pool_read_string(POOL_CONNECTION *cp, int *len, int line)
++{
++ int readp;
++ int readsize;
++ int readlen;
++ int strlength;
++ int flag;
++ int consume_size;
++
++#ifdef DEBUG
++ static char pbuf[READBUFSZ];
++#endif
++
++ *len = 0;
++ readp = 0;
++
++ /* initialize read buffer */
++ if (cp->sbufsz == 0)
++ {
++ cp->sbuf = malloc(READBUFSZ);
++ if (cp->sbuf == NULL)
++ {
++ show_error("pool_read_string: malloc failed");
++ return NULL;
++ }
++ cp->sbufsz = READBUFSZ;
++ *cp->sbuf = '\0';
++ }
++
++ /* any pending data? */
++ if (cp->len)
++ {
++ if (line)
++ strlength = mystrlinelen(cp->hp+cp->po, cp->len,
&flag);
++ else
++ strlength = mystrlen(cp->hp+cp->po, cp->len, &flag);
++
++ /* buffer is too small? */
++ if ((strlength + 1) > cp->sbufsz)
++ {
++ cp->sbufsz = ((strlength+1)/READBUFSZ+1)*READBUFSZ;
++ cp->sbuf = realloc(cp->sbuf, cp->sbufsz);
++ if (cp->sbuf == NULL)
++ {
++ show_error("pool_read_string: realloc
failed");
++ return NULL;
++ }
++ }
++
++ /* consume pending and save to read string buffer */
++ consume_size = consume_pending_data(cp, cp->sbuf, strlength);
++
++ *len = strlength;
++
++ /* is the string null terminated? */
++ if (consume_size == strlength && !flag)
++ {
++ /* not null or line terminated.
++ * we need to read more since we have not encountered
NULL or new line yet
++ */
++ readsize = cp->sbufsz - strlength;
++ readp = strlength;
++ }
++ else
++ {
++#ifdef PRINT_DEBUG
++ show_debug("pool_read_string: read all from pending
data. po:%d len:%d",
++ cp->po, cp->len);
++#endif
++ return cp->sbuf;
++ }
++ } else
++ {
++ readsize = cp->sbufsz;
++ }
++
++
++ for (;;)
++ {
++ readlen = read(cp->fd, cp->sbuf+readp, readsize);
++ if (readlen == -1)
++ {
++ show_error("pool_read_string: read() failed.
reason:%s", strerror(errno));
++
++ if (cp->isbackend)
++ {
++ notice_backend_error();
++ exit(1);
++ }
++ else
++ {
++ return NULL;
++ }
++ }
++
++ if (readlen == 0)
++ return NULL;
++
++ /* check overrun */
++ if (line)
++ strlength = mystrlinelen(cp->sbuf+readp, readlen,
&flag);
++ else
++ strlength = mystrlen(cp->sbuf+readp, readlen, &flag);
++
++ if (strlength < readlen)
++ {
++ save_pending_data(cp, cp->sbuf+readp+strlength,
readlen-strlength);
++ *len += strlength;
++#ifdef PRINT_DEBUG
++ show_debug("pool_read_string: total result %d with
pending data po:%d len:%d", *len, cp->po, cp->len);
++#endif
++ return cp->sbuf;
++ }
++
++ *len += readlen;
++
++ /* encountered null or newline? */
++ if (flag)
++ {
++ /* ok we have read all data */
++#ifdef PRINT_DEBUG
++ show_debug("pool_read_string: total result %d ",
*len);
++#endif
++ break;
++ }
++
++ readp += readlen;
++ readsize = READBUFSZ;
++
++ if ((*len+readsize) > cp->sbufsz)
++ {
++ cp->sbufsz += READBUFSZ;
++
++ cp->sbuf = realloc(cp->sbuf, cp->sbufsz);
++ if (cp->sbuf == NULL)
++ {
++ show_error("pool_read_string: realloc
failed");
++ return NULL;
++ }
++ }
++ }
++ return cp->sbuf;
++}
++
++/*
++ * returns the byte length of str, including \0, no more than upper.
++ * if encountered \0, flag is set to non 0.
++ * example:
++ * mystrlen("abc", 2) returns 2
++ * mystrlen("abc", 3) returns 3
++ * mystrlen("abc", 4) returns 4
++ * mystrlen("abc", 5) returns 4
++ */
++static int mystrlen(char *str, int upper, int *flag)
++{
++ int len;
++
++ *flag = 0;
++
++ for (len = 0;len < upper; len++, str++)
++ {
++ if (!*str)
++ {
++ len++;
++ *flag = 1;
++ break;
++ }
++ }
++ return len;
++}
++
++/*
++ * returns the byte length of str terminated by \n or \0 (including \n or
\0), no more than upper.
++ * if encountered \0 or \n, flag is set to non 0.
++ * example:
++ * mystrlinelen("abc", 2) returns 2
++ * mystrlinelen("abc", 3) returns 3
++ * mystrlinelen("abc", 4) returns 4
++ * mystrlinelen("abc", 5) returns 4
++ * mystrlinelen("abcd\nefg", 4) returns 4
++ * mystrlinelen("abcd\nefg", 5) returns 5
++ * mystrlinelen("abcd\nefg", 6) returns 5
++ */
++static int mystrlinelen(char *str, int upper, int *flag)
++{
++ int len;
++
++ *flag = 0;
++
++ for (len = 0;len < upper; len++, str++)
++ {
++ if (!*str || *str == '\n')
++ {
++ len++;
++ *flag = 1;
++ break;
++ }
++ }
++ return len;
++}
++
++/*
++ * save pending data
++ */
++static int save_pending_data(POOL_CONNECTION *cp, void *data, int len)
++{
++ int reqlen;
++ size_t realloc_size;
++ char *p;
++
++ /* to be safe */
++ if (cp->len == 0)
++ cp->po = 0;
++
++ reqlen = cp->po + cp->len + len;
++
++ /* pending buffer is enough? */
++ if (reqlen > cp->bufsz)
++ {
++ /* too small, enlarge it */
++ realloc_size = (reqlen/READBUFSZ+1)*READBUFSZ;
++ p = realloc(cp->hp, realloc_size);
++ if (p == NULL)
++ {
++ show_error("save_pending_data: realloc failed");
++ return -1;
++ }
++
++ cp->bufsz = realloc_size;
++ cp->hp = p;
++ }
++
++ memmove(cp->hp + cp->po + cp->len, data, len);
++ cp->len += len;
++
++ return 0;
++}
++
++/*
++ * consume pending data. returns actually consumed data length.
++ */
++static int consume_pending_data(POOL_CONNECTION *cp, void *data, int len)
++{
++ int consume_size;
++
++ if (cp->len <= 0)
++ return 0;
++
++ consume_size = Min(len, cp->len);
++ memmove(data, cp->hp + cp->po, consume_size);
++ cp->len -= consume_size;
++
++ if (cp->len <= 0)
++ cp->po = 0;
++ else
++ cp->po += consume_size;
++
++ return consume_size;
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/recovery.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/recovery.c
+--- postgresql-8.1.2/src/pgcluster/pglb/recovery.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/recovery.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,234 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * recovery.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at pglb for the recovery.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++/*
++ * 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 <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/msg.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <arpa/inet.h>
++#include <sys/file.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#ifdef HAVE_SYS_SELECT_H
++#include <sys/select.h>
++#endif
++#include "replicate_com.h"
++#include "pglb.h"
++
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++void PGRrecovery_main(void);
++
++static int set_recovery(RecoveryPacket *packet);
++static int receive_recovery(int fd);
++
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRrecovery_main()
++ * NOTES
++ * main module of recovery function
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++void
++PGRrecovery_main(void)
++{
++ char * func = "PGRrecovery_main()";
++ int fd = -1;
++ int rtn;
++
++ fd = PGRcreate_recv_socket("", Recovery_Port_Number);
++ if (fd < 0)
++ {
++ show_error("%s:PGRcreate_recv_socket failed",func);
++ exit(1);
++ }
++
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = 60;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(fd,&rmask);
++ rtn = select(fd+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn && FD_ISSET(fd, &rmask))
++ {
++ receive_recovery(fd);
++ }
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * set_recovery()
++ * NOTES
++ * check a recovery request from replication server
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static int
++set_recovery(RecoveryPacket *packet)
++{
++#ifdef PRINT_DEBUG
++ char * func = "set_recovery()";
++#endif
++ int status = STATUS_OK;
++ ClusterTbl key;
++ ClusterTbl * ptr;
++
++ PGRset_key_of_cluster(&key,packet);
++#ifdef PRINT_DEBUG
++ show_debug("%s:received no:%d",func, ntohs(packet->packet_no));
++#endif
++ switch (ntohs(packet->packet_no))
++ {
++ case RECOVERY_PREPARE_REQ:
++ /* add cluster db */
++#ifdef PRINT_DEBUG
++ show_debug("%s:add_db host:%s port:%d max:%d",
++ func,
packet->hostName,ntohs(packet->port),ntohs(packet->max_connect));
++#endif
++ ptr = PGRsearch_cluster_tbl(&key);
++ if (ptr == NULL)
++ {
++ ptr = PGRadd_cluster_tbl(&key);
++ }
++ if (ptr != NULL)
++ {
++ PGRset_status_on_cluster_tbl(TBL_STOP,ptr);
++ if (Use_Connection_Pool)
++ {
++ signal(SIGCHLD,PGRrecreate_child);
++ status = PGRpre_fork_child(ptr);
++ }
++ }
++ break;
++ case RECOVERY_FINISH:
++ /* start cluster db */
++ ptr = PGRsearch_cluster_tbl(&key);
++ if (ptr != NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:start_db host:%s port:%d max:%d",
++
func,packet->hostName,ntohs(packet->port),ntohs(packet->max_connect));
++#endif
++ PGRset_status_on_cluster_tbl(TBL_USE,ptr);
++ }
++ break;
++ case RECOVERY_PGDATA_ANS:
++ /* stop cluster db */
++ ptr = PGRsearch_cluster_tbl(&key);
++ if (ptr != NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:stop_db host:%s port:%d max:%d",
++ func,
packet->hostName,ntohs(packet->port),ntohs(packet->max_connect));
++#endif
++ PGRset_status_on_cluster_tbl(TBL_STOP,ptr);
++ }
++ break;
++ case RECOVERY_ERROR:
++ /* delete cluster db */
++ ptr = PGRsearch_cluster_tbl(&key);
++ if (ptr != NULL)
++ {
++ PGRset_status_on_cluster_tbl(TBL_FREE,ptr);
++ if (Use_Connection_Pool)
++ {
++ PGRquit_children_on_cluster(ptr->rec_no);
++ }
++ }
++ break;
++ /* cluster db has error */
++ case RECOVERY_ERROR_CONNECTION:
++ /* set error cluster db */
++ ptr = PGRsearch_cluster_tbl(&key);
++ if (ptr != NULL)
++ {
++ PGRset_status_on_cluster_tbl(TBL_ERROR,ptr);
++ if (Use_Connection_Pool)
++ {
++ PGRquit_children_on_cluster(ptr->rec_no);
++ }
++ }
++ break;
++ }
++ return STATUS_OK;
++}
++
++static int
++receive_recovery(int fd)
++{
++ int status = STATUS_ERROR;
++ int r_size = -1;
++ int recv_sock = -1;
++ RecoveryPacket packet;
++
++ recv_sock = PGRcreate_acception(fd,"",Recovery_Port_Number);
++ if (recv_sock >= 0 )
++ {
++ memset(&packet,0, sizeof(RecoveryPacket));
++ r_size = PGRread_byte(recv_sock,(char
*)&packet,sizeof(RecoveryPacket),MSG_WAITALL);
++ if ( r_size == sizeof(RecoveryPacket) )
++ {
++ status = set_recovery(&packet);
++ }
++ }
++ PGRclose_sock(&recv_sock);
++ return status;
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pglb/socket.c
pgcluster-1.5.0rc7/src/pgcluster/pglb/socket.c
+--- postgresql-8.1.2/src/pgcluster/pglb/socket.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pglb/socket.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,395 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * socket.c
++ *
++ * NOTE:
++ * This file is composed of the communication modules
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++/*
++ * 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 "postgres.h"
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <sys/wait.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <sys/ipc.h>
++#include <netdb.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <sys/file.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++
++#ifdef HAVE_SYS_SELECT_H
++#include <sys/select.h>
++#endif
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#include "replicate_com.h"
++#include "pglb.h"
++
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++int PGRcreate_unix_domain_socket(char * sock_dir, unsigned short port);
++int PGRcreate_recv_socket(char * hostName , unsigned short portNumber);
++int PGRcreate_acception(int fd, char * hostName , unsigned short
portNumber);
++void PGRclose_sock(int * sock);
++int PGRread_byte(int sock,char * buf,int len, int flag);
++int PGRcreate_cluster_socket( int * sock, ClusterTbl * ptr );
++
++static int create_send_socket(int * fdP, char * hostName , unsigned short
portNumber);
++
++
++/*
++* create UNIX domain socket
++*/
++int
++PGRcreate_unix_domain_socket(char * sock_dir, unsigned short port)
++{
++ char * func = "PGRcreate_unix_domain_socket()";
++ struct sockaddr_un addr;
++ int fd;
++ int status;
++ int len;
++
++ /* set unix domain socket path */
++ fd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (fd == -1)
++ {
++ show_error("%s:Failed to create UNIX domain socket. reason:
%s",func, strerror(errno));
++ return -1;
++ }
++ memset((char *) &addr, 0, sizeof(addr));
++ ((struct sockaddr *)&addr)->sa_family = AF_UNIX;
++ snprintf(addr.sun_path, sizeof(addr.sun_path),
"%s/.s.PGSQL.%d",sock_dir,port);
++ len = sizeof(struct sockaddr_un);
++ status = bind(fd, (struct sockaddr *)&addr, len);
++ if (status == -1)
++ {
++ show_error("%s: bind() failed. reason: %s", func,
strerror(errno));
++ return -1;
++ }
++
++ if (chmod(addr.sun_path, 0777) == -1)
++ {
++ show_error("%s: chmod() failed. reason: %s", func,
strerror(errno));
++ return -1;
++ }
++
++ status = listen(fd, PGLB_MAX_SOCKET_QUEUE);
++ if (status < 0)
++ {
++ show_error("%s: listen() failed. reason: %s", func,
strerror(errno));
++ return -1;
++ }
++ return fd;
++}
++
++int
++PGRcreate_recv_socket(char * hostName , unsigned short portNumber)
++{
++ char * func = "PGRcreate_recv_socket()";
++ int fd,err;
++ size_t len = 0;
++ struct sockaddr_in addr;
++ int one = 1;
++
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
++ {
++ show_error("%s: socket() failed. (%s)", func,
strerror(errno));
++ return -1;
++ }
++ if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
sizeof(one))) == -1)
++ {
++ PGRclose_sock(&fd);
++ show_error("%s: setsockopt() failed. (%s)",func,
strerror(errno));
++ return -1;
++ }
++ addr.sin_family = AF_INET;
++ if (hostName[0] == '\0')
++ addr.sin_addr.s_addr = htonl(INADDR_ANY);
++ else
++ {
++ struct hostent *hp;
++
++ hp = gethostbyname(hostName);
++ if ((hp == NULL) || (hp->h_addrtype != AF_INET))
++ {
++ PGRclose_sock(&fd);
++ return -1;
++ }
++ memmove((char *) &(addr.sin_addr), (char *) hp->h_addr,
hp->h_length);
++ }
++
++ addr.sin_port = htons(portNumber);
++ len = sizeof(struct sockaddr_in);
++
++ err = bind(fd, (struct sockaddr *) & addr, len);
++ if (err < 0)
++ {
++ PGRclose_sock(&fd);
++ show_error("%s: bind() failed. (%s)",func, strerror(errno));
++ return -1;
++ }
++ err = listen(fd, PGLB_MAX_SOCKET_QUEUE);
++ if (err < 0)
++ {
++ PGRclose_sock(&fd);
++ show_error("%s: listen() failed. (%s)", func,
strerror(errno));
++ return -1;
++ }
++ return fd;
++}
++
++int
++PGRcreate_acception(int fd, char * hostName , unsigned short portNumber)
++{
++ char * func = "PGRcreate_acception()";
++ int sock;
++ struct sockaddr addr;
++ size_t len = 0;
++ int one = 1;
++ int count;
++
++ len = sizeof(struct sockaddr);
++ count = 0;
++ while ((sock = accept(fd,&addr,&len)) < 0)
++ {
++ show_error("%s:accept error",func);
++ PGRclose_sock(&fd);
++ if ( count > PGLB_CONNECT_RETRY_TIME)
++ {
++ return -1;
++ }
++ fd = PGRcreate_recv_socket(hostName , portNumber);
++ count ++;
++ }
++
++ count = 0;
++ while (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &one,
sizeof(one)) < 0)
++ {
++ show_error("%s: setsockopt TCP_NODELAY error (%s)",func,
strerror(errno));
++ if ( count > PGLB_CONNECT_RETRY_TIME)
++ {
++ return -1;
++ }
++ count ++;
++ }
++ count = 0;
++ while (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
sizeof(one)) < 0)
++ {
++ show_error("%s:setsockopt SO_KEEPALIVE error
(%s)",func,strerror(errno));
++ if ( count > PGLB_CONNECT_RETRY_TIME)
++ {
++ return -1;
++ }
++ count ++;
++ }
++
++ return sock;
++}
++
++void
++PGRclose_sock(int * sock)
++{
++ close(*sock);
++ *sock = -1;
++}
++
++int
++PGRread_byte(int sock,char * buf,int len, int flag)
++{
++ char * func = "PGRread_byte()";
++ int r;
++ char * read_ptr;
++ int read_size = 0;
++ int max_buf_size ;
++ int pid;
++
++ pid = getpid();
++ max_buf_size = len;
++ read_ptr = (char*)buf;
++ for (;;)
++ {
++ r = recv(sock,read_ptr + read_size ,max_buf_size - read_size,
flag);
++ if (r < 0)
++ {
++ if (errno == EINTR)
++ {
++ continue;
++ }
++#ifdef EAGAIN
++ if (errno == EAGAIN)
++ {
++ return read_size;
++ }
++#endif
++#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
++ if (errno == EWOULDBLOCK)
++ {
++ show_error("%s:no data
(%s)",func,strerror(errno));
++ return read_size;
++ }
++#endif
++#ifdef ECONNRESET
++ if (errno == ECONNRESET)
++ {
++ PGRclose_sock(&sock);
++ show_error("%s:connection reset (%s)",func,
strerror(errno));
++ return -1;
++ }
++#endif
++ show_error("%s:recv() failed.
(%s)",func,strerror(errno));
++ read_size = -1;
++ break;
++ }
++ if (r > 0)
++ {
++ read_size += r;
++ if (max_buf_size == read_size)
++ {
++ break;
++ }
++ break;
++ }
++ if (read_size)
++ {
++ return read_size;
++ }
++ else
++ {
++ return -1;
++ }
++ }
++
++ return read_size;
++}
++
++int
++PGRcreate_cluster_socket( int * sock, ClusterTbl * ptr )
++{
++ char * func = "PGRcreate_cluster_socket()";
++ int status = STATUS_ERROR;
++
++ /*
++ if (PGRis_connection_full(ptr) == 1)
++ {
++ return STATUS_ERROR;
++ }
++ */
++ if (ptr != (ClusterTbl *) NULL)
++ {
++ status = create_send_socket(sock, ptr->hostName, ptr->port)
;
++ }
++ else
++ {
++ show_error("%s:ClusterTbl is not initialize",func);
++ }
++ return status;
++}
++
++static int
++create_send_socket(int * fdP, char * hostName , unsigned short portNumber)
++{
++ char * func = "create_send_socket()";
++ int sock;
++ size_t len = 0;
++ struct sockaddr_in addr;
++ int fd;
++ int one = 1;
++
++#ifdef PRINT_DEBUG
++ show_debug("%s: host:%s port:%d",func, hostName,portNumber);
++#endif
++
++ memset((char *)&addr,0,sizeof(addr));
++
++ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
++ {
++ * fdP = -1;
++
++ show_error("%s:socket() failed. (%s)",func, strerror(errno));
++ return STATUS_ERROR;
++ }
++ if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
sizeof(one))) == -1)
++ {
++ PGRclose_sock(&fd);
++ * fdP = -1;
++ show_error("%s:setsockopt() failed. (%s)", func,
strerror(errno));
++ return STATUS_ERROR;
++ return STATUS_ERROR;
++ }
++ if ((setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
sizeof(one))) == -1)
++ {
++ PGRclose_sock(&fd);
++ * fdP = -1;
++ show_error("%s:setsockopt() failed. (%s)", func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one,
sizeof(one)) < 0)
++ {
++ PGRclose_sock(&fd);
++ * fdP = -1;
++ show_error("%s:setsockopt() failed. (%s)",func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++
++ addr.sin_family = AF_INET;
++ if (hostName[0] == '\0')
++ addr.sin_addr.s_addr = htonl(INADDR_ANY);
++ else
++ {
++ struct hostent *hp;
++
++ hp = gethostbyname(hostName);
++ if ((hp == NULL) || (hp->h_addrtype != AF_INET))
++ {
++ PGRclose_sock(&fd);
++ * fdP = -1;
++ return STATUS_ERROR;
++ }
++ memmove((char *) &(addr.sin_addr), (char *) hp->h_addr,
hp->h_length);
++ }
++
++ addr.sin_port = htons(portNumber);
++ len = sizeof(struct sockaddr_in);
++
++ if ((sock = connect(fd,(struct sockaddr*)&addr,len)) < 0)
++ {
++ PGRclose_sock(&fd);
++ * fdP = -1;
++ return STATUS_ERROR;
++ }
++
++ * fdP = fd;
++ return STATUS_OK;
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/AUTHORS
pgcluster-1.5.0rc7/src/pgcluster/pgrp/AUTHORS
+--- postgresql-8.1.2/src/pgcluster/pgrp/AUTHORS 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/AUTHORS 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,3 @@
++Authors of pglb
++
++pglb was written by Atsushi Mitani
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/COPYING
pgcluster-1.5.0rc7/src/pgcluster/pgrp/COPYING
+--- postgresql-8.1.2/src/pgcluster/pgrp/COPYING 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/COPYING 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,12 @@
++Copyright (c) 2003 Atsushi Mitani
++
++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.
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/Makefile
pgcluster-1.5.0rc7/src/pgcluster/pgrp/Makefile
+--- postgresql-8.1.2/src/pgcluster/pgrp/Makefile 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/Makefile 2006-01-31
15:14:33.000000000 +0100
+@@ -0,0 +1,39 @@
++#-------------------------------------------------------------------------
++#
++# Makefile for src/pgcluster/pgrp
++#
++#-------------------------------------------------------------------------
++
++subdir = src/pgcluster/pgrp
++top_builddir = ../../..
++include $(top_builddir)/src/Makefile.global
++
++OBJS= pqformat.o cascade.o conf.o main.o recovery.o replicate.o rlog.o
++
++EXTRA_OBJS = $(top_builddir)/src/backend/libpq/replicate_com.o
++EXTRA_OBJS += ../libpgc/SUBSYS.o
++
++CFLAGS += -DPRINT_DEBUG -g
++override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) -DBINDIR=\"$(bindir)\"
++all: pgreplicate
++
++pgreplicate: $(OBJS) $(libpq_builddir)/libpq.a
++ $(CC) $(CFLAGS) $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o
$@
++
++install: all installdirs
++ $(INSTALL_PROGRAM) pgreplicate$(X) $(DESTDIR)$(bindir)/pgreplicate$(X)
++ $(INSTALL_DATA) pgreplicate.conf.sample
$(DESTDIR)$(datadir)/pgreplicate.conf.sample
++
++installdirs:
++ $(mkinstalldirs) $(DESTDIR)$(bindir)
++ $(mkinstalldirs) $(DESTDIR)$(datadir)
++
++uninstall:
++ rm -f $(addprefix $(DESTDIR)$(bindir)/, pgreplicate$(X))
++ rm -f $(DESTDIR)$(datadir)/pgreplicate.conf.sample
++
++clean distclean maintainer-clean:
++ rm -f pgreplicate$(X) $(OBJS)
++
++clean_obj:
++ rm -f $(OBJS)
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/README
pgcluster-1.5.0rc7/src/pgcluster/pgrp/README
+--- postgresql-8.1.2/src/pgcluster/pgrp/README 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/README 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,275 @@
++=============================================================
++1. Installation
++=============================================================
++
++1-1. Install Cluster DB Server, Replication Server & Load Balancer
++----------------------------------------------------------------
++$ cd $source_dir
++$ ./configure
++$ gmake
++$ su
++# gmake install
++# chown -R postgres /usr/local/pgsql
++----------------------------------------------------------------
++
++=============================================================
++2. Initialize DB
++=============================================================
++$ su
++# adduser postgres
++# mkdir /usr/local/pgsql/data
++# chown postgres /usr/local/pgsql/data
++# su - postgres
++$ /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
++
++
++=============================================================
++3. Configuration
++=============================================================
++(EX.System Composition)
++
++ |
++ ((Load Balance Server))
++ ( hostname: lb.hoo.com)
++ ( receive port:5432 )
++ ( recovery port:6001 )
++ |
++----------+-------------+------------+----------
++ | |
++ (( Cluster DB 1 )) (( Cluster DB 2 ))
++ ( hostname:c1.hoo.com) ( hostname:c2.hoo.com)
++ ( receive port: 5432 ) ( receive port:5432 )
++ ( recovery port:7001 ) ( recovery port 7002 )
++ | |
++----------+-------------+------------+----------
++ |
++ ((Replication Server))
++ ( hostname:pgr.hoo.com)
++ ( receive port:8001 )
++ ( recovery port:8101 )
++
++
++3-1. Load Balance Server
++
++The setup file of load balance server is copied from the sample file and
edited.
++(the sample file is installed '/usr/local/pgsql/etc' in default)
++----------------------------------------------------------------
++$cd /usr/local/pgsql/etc
++$cp pglb.conf.sample pglb.conf
++----------------------------------------------------------------
++
++In the case of the above system composition example,
++the setup example of pglb.conf file is as the following
++
++#============================================================
++# Load Balance Server configuration file
++#-------------------------------------------------------------
++# file: pglb.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts are db cluster server
++# o which port use connect to db cluster server
++# o how many connections are allowed on each DB server
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Max_Connection : maximun number of connection to postmaster
++#-------------------------------------------------------------
++<Cluster_Server_Info>
++ <Host_Name> c1.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Max_Connect> 32 </Max_Connect>
++</Cluster_Server_Info>
++<Cluster_Server_Info>
++ <Host_Name> c2.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Max_Connect> 32 </Max_Connect>
++</Cluster_Server_Info>
++#-------------------------------------------------------------
++# set Load Balance server information
++# o Receive_Port : connection from client
++# o Recovery_Port : connection for recovery process
++# o Max_Cluster_Num : maximun number of cluster DB servers
++# o Use_Connection_Pooling : use connection pool [yes/no]
++# o Max_Pool_Each_Server : number of pool connections/DB server
++#-------------------------------------------------------------
++<Receive_Port> 5432 </Receive_Port>
++<Recovery_Port> 6001 </Recovery_Port>
++<Max_Cluster_Num> 128 </Max_Cluster_Num>
++<Use_Connection_Pooling> yes </Use_Connection_Pooling>
++<Max_Pool_Each_Server> 1 </Max_Pool_Each_Server>
++
++3-2. Cluster DB Server
++
++The Cluster DB server need edit two configuration files
++('pg_hba.conf' and 'cluster.conf').
++These files are create under the $PG_DATA directory after 'initdb'.
++
++A. pg_hba.conf
++Permission to connect DB via IP connectoins is need for this system.
++
++B. cluster.conf
++In the case of the above system composition example,
++the setup example of cluster.conf file is as the following
++
++#============================================================
++# Cluster DB Server configuration file
++#-------------------------------------------------------------
++# file: cluster.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts & port are replication server
++# o which port use for replication request to replication server
++# o which command use for recovery function
++#
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Recovery_Port : connection for recovery process
++#-------------------------------------------------------------
++<Replicate_Server_Info>
++ <Host_Name> pgr.hoo.com </Host_Name>
++ <Port> 8001 </Port>
++ <Recovery_Port> 8101 </Recovery_Port>
++</Replicate_Server_Info>
++#-------------------------------------------------------------
++# set Cluster DB Server information
++# o Recovery_Port : connection for recovery
++# o Rsync_Path : path of rsync command
++# o Rsync_Option : file transfer option for rsync
++# o When_Stand_Alone : When all replication servers fell,
++# you can set up two kinds of permittion,
++# "real_only" or "read_write".
++#-------------------------------------------------------------
++<Recovery_Port> 7001 </Recovery_Port>
++<Rsync_Path> /usr/bin/rsync </Rsync_Path>
++<Rsync_Option> ssh -1 </Rsync_Option>
++<Rsync_Compress> yes </Rsync_Compress>
++<When_Stand_Alone> read_only </When_Stand_Alone>
++#-------------------------------------------------------------
++# set partitional replicate control information
++# set DB name and Table name to stop reprication
++# o DB_Name : DB name
++# o Table_Name : table name
++#-------------------------------------------------------------
++#<Not_Replicate_Info>
++# <DB_Name> test_db </DB_Name>
++# <Table_Name> log_table </Table_Name>
++#</Not_Replicate_Info>
++
++3-3. Replication Server
++
++The setup file of replication server is copied from the sample file and
edited.
++(the sample file is installed '/usr/local/pgsql/etc' in default)
++----------------------------------------------------------------
++$cd /usr/local/pgsql/etc
++$cp pgreplicate.conf.sample pgreplicate.conf
++----------------------------------------------------------------
++In the case of the above system composition example,
++the setup example of pgreplicate.conf file is as the following
++
++#============================================================
++# PGReplicate configuration file
++#-------------------------------------------------------------
++# file: pgreplicate.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts & port are cluster server
++# o which port use for replication request from cluster server
++#============================================================
++#-------------------------------------------------------------
++# set cluster DB server information
++# o Host_Name : hostname
++# o Port : connection for postmaster
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<Cluster_Server_Info>
++ <Host_Name> c1.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Recovery_Port> 7001 </Recovery_Port>
++</Cluster_Server_Info>
++<Cluster_Server_Info>
++ <Host_Name> c2.hoo.com </Host_Name>
++ <Port> 5432 </Port>
++ <Recovery_Port> 7001 </Recovery_Port>
++</Cluster_Server_Info>
++#-------------------------------------------------------------
++# set Load Balance server information
++# o Host_Name : hostname
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<LoadBalance_Server_Info>
++ <Host_Name> lb.hoo.com </Host_Name>
++ <Recovery_Port> 6001 </Recovery_Port>
++</LoadBalance_Server_Info>
++#-------------------------------------------------------------
++# set PGReplicate server information
++# o Replicate_Port : connection for reprication
++# o Recovery_Port : connection for recovery
++#-------------------------------------------------------------
++<Replication_Port> 8001 </Replication_Port>
++<Recovery_Port> 8101 </Recovery_Port>
++
++
++=============================================================
++4. Start Up / Stop
++=============================================================
++
++4-1. replication server
++
++A. Start replication server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pgreplicate -D /usr/local/pgsql/etc
++----------------------------------------------------------------
++
++B. Stop replication server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pgreplicate -D /usr/local/pgsql/etc stop
++----------------------------------------------------------------
++
++usage: pgreplicate [-D path_of_config_file] [-W path_of_work_files] [-U
login us
++er][-l][-n][-v][-h][stop]
++ -l: print error logs in the log file.
++ -n: don't run in daemon mode.
++ -v: debug mode. need '-n' flag
++ -h: print this help
++ stop: stop pgreplicate
++(config file default path: ./pgreplicate.conf)
++
++4-2. cluster DB server
++$PG_HOME = /usr/local/pgsql
++$PG_DATA = /usr/local/pgsql/data
++
++A. Start cluster DB server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -o "-i" start
++----------------------------------------------------------------
++
++B. Stop cluster DB server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data stop
++----------------------------------------------------------------
++
++4-3. load balance server
++
++A. Start load balance server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pglb -D /usr/local/pgsql/etc
++----------------------------------------------------------------
++
++B. Stop load balance server
++----------------------------------------------------------------
++$ /usr/local/pgsql/bin/pglb -D /usr/local/pgsql/etc stop
++----------------------------------------------------------------
++
++usage: pglb [-D path_of_config_file] [-W path_of_work_files]
[-n][-v][-h][stop]
++ -l: print error logs in the log file.
++ -n: don't run in daemon mode.
++ -v: debug mode. need '-n' flag
++ -h: print this help
++ stop: stop pglb
++ (config file default path: ./pglb.conf)
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/cascade.c
pgcluster-1.5.0rc7/src/pgcluster/pgrp/cascade.c
+--- postgresql-8.1.2/src/pgcluster/pgrp/cascade.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/cascade.c 2006-01-31
23:41:36.000000000 +0100
+@@ -0,0 +1,948 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * cascade.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at pgreplicate for backup and cascade .
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#ifdef USE_REPLICATION
++
++#include "postgres.h"
++#include "postgres_fe.h"
++
++#include <stdio.h>
++#include <unistd.h>
++#ifdef HAVE_SYS_TYPES_H
++#include <sys/types.h>
++#endif
++#ifdef HAVE_FCNTL_H
++#include <fcntl.h>
++#endif
++#include <errno.h>
++#include <ctype.h>
++#include <time.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <sys/sem.h>
++#include <signal.h>
++#include <sys/socket.h>
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif
++#include <netdb.h>
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++#include <dirent.h>
++#include <arpa/inet.h>
++
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#ifdef MULTIBYTE
++#include "mb/pg_wchar.h"
++#endif
++
++#include "libpq-fe.h"
++#include "libpq-int.h"
++#include "fe-auth.h"
++
++#include "access/xact.h"
++#include "replicate_com.h"
++#include "pgreplicate.h"
++
++void PGRinit_cascade_child(void);
++int PGRstartup_cascade(void);
++int PGRsend_lower_cascade(ReplicateHeader * header, char * query);
++int PGRsend_upper_cascade(ReplicateHeader * header, char * query);
++int PGRwait_answer_cascade(int sock);
++ReplicateServerInfo * PGRget_lower_cascade(void);
++ReplicateServerInfo * PGRget_upper_cascade(void);
++void PGRset_cascade_server_status(ReplicateServerInfo * cascade, int
status);
++ReplicateServerInfo * PGRrecv_cascade_answer(ReplicateServerInfo *
cascade,ReplicateHeader * header);
++int PGRsend_cascade(int sock , ReplicateHeader * header, char * query);
++int PGRcascade_main(int sock, ReplicateHeader * header, char * query);
++int PGRwait_notice_rlog_done(void);
++int PGRsend_notice_rlog_done(int sock);
++int PGRsend_notice_quit(void);
++
++#if 0
++static int count_cascade(int flag);
++#endif
++
++static int fixup_socket_for_cascades(int *sock ,ReplicateServerInfo *
target);
++static ReplicateServerInfo * get_cascade_data(int * cnt, int flag);
++static int add_cascade_data(ReplicateHeader * header, ReplicateServerInfo *
add_data);
++static int update_cascade_data(ReplicateHeader * header,
ReplicateServerInfo * update_data);
++static void write_cascade_status_file(ReplicateServerInfo * cascade);
++static int notice_cascade_data(int sock);
++static int notice_cascade_data_to_cluster_db(void);
++
++/**
++ * socket variables, moved from Cascade_Inf->(lower|upper)->sock.
++ * Cascade->Inf is in shared memory, so sometimes cascades returns EBADF
due to not initialized socket in specified process.
++ * 05/10/05 tanida AT sraoss.co.jp
++ */
++
++int lsock=-1; /* socket for lower-cascade. */
++int usock=-1; /* socket for upper-cascade. */
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++
++#if 0
++static int
++count_cascade(int flag)
++{
++ int cnt = 0;
++ int cascade_cnt = 0;
++ ReplicateServerInfo * cascade = NULL;
++
++ if ((Cascade_Tbl == NULL) || (Cascade_Inf == NULL))
++ {
++ return 0;
++ }
++
++ /* count cascadeing replication server */
++ switch (flag)
++ {
++ case UPPER_CASCADE:
++ case ALL_CASCADE:
++ cascade = Cascade_Tbl;
++ break;
++ case LOWER_CASCADE:
++ cascade = Cascade_Inf->myself;
++ break;
++ }
++
++ if (cascade == NULL)
++ {
++ return 0;
++ }
++ while (cascade->useFlag != DB_TBL_END)
++ {
++ if (cascade->useFlag == DB_TBL_USE)
++ {
++ cascade_cnt ++;
++ }
++ if ((flag == UPPER_CASCADE) &&
++ (cascade == Cascade_Inf->myself))
++ {
++ break;
++ }
++ cnt ++;
++ if (cnt >= MAX_DB_SERVER -1 )
++ {
++ break;
++ }
++ cascade ++;
++ }
++ return cascade_cnt;
++}
++#endif /* if 0 */
++
++void
++PGRinit_cascade_child(void) {
++ fixup_socket_for_cascades(&usock,NULL);
++ fixup_socket_for_cascades(&lsock,NULL);
++}
++
++static ReplicateServerInfo *
++get_cascade_data(int * cnt, int flag)
++{
++ char * func = "get_cascade_data()";
++ int i = 0;
++ int loop_cnt = 0;
++ int size = 0;
++ ReplicateServerInfo * buf = NULL;
++ ReplicateServerInfo * cascade = NULL;
++
++ size = sizeof(ReplicateServerInfo) * MAX_DB_SERVER;
++ buf = (ReplicateServerInfo *)malloc(size);
++ if (buf == (ReplicateServerInfo *)NULL)
++ {
++ show_error("%s:malloc failed: (%s)",func,strerror(errno));
++ *cnt = 0;
++ return NULL;
++ }
++ memset(buf,0,size);
++
++ switch (flag)
++ {
++ case UPPER_CASCADE:
++ case ALL_CASCADE:
++ cascade = Cascade_Tbl;
++ break;
++ case LOWER_CASCADE:
++ cascade = Cascade_Inf->myself;
++ break;
++ default:
++ free(buf);
++ *cnt = 0;
++ return NULL;
++
++ }
++
++ if (cascade == NULL)
++ {
++ free(buf);
++ *cnt = 0;
++ return NULL;
++ }
++ PGRsem_lock(CascadeSemID,1);
++ i = 0;
++ loop_cnt = 0;
++ while (cascade->useFlag != DB_TBL_END)
++ {
++ if (cascade->useFlag == DB_TBL_USE)
++ {
++ (buf + i)->useFlag = htonl(cascade->useFlag);
++ strncpy((buf +
i)->hostName,cascade->hostName,sizeof(cascade->hostName));
++ (buf + i)->portNumber = htons(cascade->portNumber);
++ (buf + i)->recoveryPortNumber =
htons(cascade->recoveryPortNumber);
++ (buf + i)->lifecheckPortNumber =
htons(cascade->lifecheckPortNumber);
++ i++;
++ }
++ if ((flag == UPPER_CASCADE) &&
++ (cascade == Cascade_Inf->myself))
++ {
++ break;
++ }
++ loop_cnt ++;
++ if (loop_cnt >= MAX_DB_SERVER -1 )
++ {
++ break;
++ }
++ if (Cascade_Inf->end == cascade)
++ {
++ break;
++ }
++ cascade ++;
++ }
++ *cnt = i;
++ PGRsem_unlock(CascadeSemID,1);
++
++ return buf;
++}
++
++static int
++update_cascade_data(ReplicateHeader * header, ReplicateServerInfo *
update_data)
++{
++ char * func = "update_cascade_data()";
++ int size = 0;
++ int cnt = 0;
++ ReplicateServerInfo * ptr = NULL;
++ ReplicateServerInfo * cascade = NULL;
++ char hostName[HOSTNAME_MAX_LENGTH];
++
++
++#ifdef PRINT_DEBUG
++ show_debug("executing %s",func);
++#endif
++ if ((header == NULL ) || ( update_data == NULL))
++ {
++ show_error("%s:receive data is wrong",func);
++ return STATUS_ERROR;
++ }
++ if ((Cascade_Tbl == NULL) || (Cascade_Inf == NULL))
++ {
++ show_error("%s:config data read error",func);
++ return STATUS_ERROR;
++ }
++
++
++ size = ntohl(header->query_size);
++ cnt = size / sizeof(ReplicateServerInfo);
++ if (cnt >= MAX_DB_SERVER)
++ {
++ show_error("%s:update cascade data is too large. it's more
than %d", func,MAX_DB_SERVER);
++ return STATUS_ERROR;
++ }
++
++ Cascade_Inf->useFlag = DB_TBL_INIT;
++ fixup_socket_for_cascades(&usock,NULL);
++ fixup_socket_for_cascades(&lsock,NULL);
++
++ Cascade_Inf->upper = NULL;
++ Cascade_Inf->lower = NULL;
++
++ gethostname(hostName,sizeof(hostName));
++ ptr = update_data;
++ cascade = Cascade_Tbl;
++ memset(cascade,0,(sizeof(ReplicateServerInfo)*MAX_DB_SERVER));
++ Cascade_Inf->top = cascade;
++ while (cnt > 0)
++ {
++
++ cascade->useFlag = ntohl(ptr->useFlag);
++
strncpy(cascade->hostName,ptr->hostName,sizeof(cascade->hostName));
++ cascade->portNumber = ntohs(ptr->portNumber);
++ cascade->recoveryPortNumber = ntohs(ptr->recoveryPortNumber);
++ cascade->lifecheckPortNumber =
ntohs(ptr->lifecheckPortNumber);
++
++ if
((!strncmp(cascade->hostName,hostName,sizeof(cascade->hostName))) &&
++ (cascade->portNumber == Port_Number) &&
++ (cascade->recoveryPortNumber == Recovery_Port_Number))
++ {
++ Cascade_Inf->myself = cascade;
++ }
++
++ Cascade_Inf->end = cascade;
++ cascade ++;
++ ptr ++;
++ cnt --;
++ cascade->useFlag = DB_TBL_END;
++ }
++ Cascade_Inf->useFlag = DB_TBL_USE;
++
++ return STATUS_OK;
++}
++
++static int
++add_cascade_data(ReplicateHeader * header, ReplicateServerInfo * add_data)
++{
++ char *func = "add_cascade_data()";
++ int size = 0;
++ int cnt = 0;
++ ReplicateServerInfo * ptr = NULL;
++ ReplicateServerInfo * cascade = NULL;
++ char hostName[HOSTNAME_MAX_LENGTH];
++
++ if ((header == NULL ) || ( add_data == NULL))
++ {
++ show_error("%s:receive data is wrong",func);
++ return STATUS_ERROR;
++ }
++ if ((Cascade_Tbl == NULL) || (Cascade_Inf == NULL))
++ {
++ show_error("%s:config data read error",func);
++ return STATUS_ERROR;
++ }
++ size = ntohl(header->query_size);
++ cnt = size / sizeof(ReplicateServerInfo);
++ if (cnt >= MAX_DB_SERVER)
++ {
++ show_error("%s:addtional cascade data is too large. it's more
than %d", func,MAX_DB_SERVER);
++ return STATUS_ERROR;
++ }
++
++ Cascade_Inf->useFlag = DB_TBL_INIT;
++ fixup_socket_for_cascades(&lsock,NULL);
++ Cascade_Inf->lower = NULL;
++
++ gethostname(hostName,sizeof(hostName));
++ ptr = add_data;
++ cascade = Cascade_Inf->myself;
++ cascade ++;
++ while (cnt > 0)
++ {
++ cascade->useFlag = ntohl(ptr->useFlag);
++
strncpy(cascade->hostName,ptr->hostName,sizeof(cascade->hostName));
++ cascade->portNumber = ntohs(ptr->portNumber);
++ cascade->recoveryPortNumber = ntohs(ptr->recoveryPortNumber);
++ cascade->lifecheckPortNumber =
ntohs(ptr->lifecheckPortNumber);
++ cascade->replicate_id=-1;
++ cascade->response_mode=-1;
++
++ Cascade_Inf->end = cascade;
++
++ if
((!strncmp(cascade->hostName,hostName,sizeof(cascade->hostName))) &&
++ (cascade->portNumber == Port_Number) &&
++ (cascade->recoveryPortNumber == Recovery_Port_Number))
++ {
++ ptr ++;
++ cnt --;
++ continue;
++ }
++ cascade ++;
++ cascade->useFlag = DB_TBL_END;
++ ptr ++;
++ cnt --;
++ }
++ Cascade_Inf->useFlag = DB_TBL_USE;
++ return STATUS_OK;
++}
++
++int
++PGRstartup_cascade(void)
++{
++ char * func = "PGRstartup_cascade()";
++ int cnt = 0;
++ int status = STATUS_OK;
++ ReplicateHeader header;
++ ReplicateServerInfo * cascade = NULL;
++ ReplicateServerInfo * buf = NULL;
++
++ if ((Cascade_Tbl == NULL) || (Cascade_Inf == NULL))
++ {
++ show_error("%s:config data read error",func);
++ return STATUS_ERROR;
++ }
++
++ /* count lower server */
++ cascade = Cascade_Inf->myself;
++ if (cascade == NULL)
++ {
++ show_error("%s:cascade data initialize error",func);
++ return STATUS_ERROR;
++ }
++ buf = get_cascade_data(&cnt,LOWER_CASCADE);
++ if (cnt <= 0)
++ {
++ show_error("%s:cascade data get error",func);
++ return STATUS_ERROR;
++ }
++
++ memset(&header,0,sizeof(ReplicateHeader));
++ header.cmdSys = CMD_SYS_CASCADE;
++ header.cmdSts = CMD_STS_TO_UPPER;
++ header.cmdType = CMD_TYPE_ADD;
++ header.query_size = htonl(sizeof(ReplicateServerInfo) * cnt);
++
++ status = PGRsend_upper_cascade(&header, (char *)buf);
++ if (buf != NULL)
++ {
++ free(buf);
++ }
++ if (status == STATUS_OK)
++ {
++ memset(&header,0,sizeof(ReplicateHeader));
++ buf = PGRrecv_cascade_answer( Cascade_Inf->upper, &header);
++ if (buf == NULL)
++ {
++ status=STATUS_ERROR;
++ }
++ else if((header.cmdSys == CMD_SYS_CASCADE) &&
++ (header.cmdSts == CMD_STS_TO_LOWER) &&
++ (header.cmdType == CMD_TYPE_UPDATE_ALL))
++ {
++ status = update_cascade_data(&header,buf);
++ free(buf);
++ }
++
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:startup packet result is %d",func,status);
++#endif
++ return status;
++}
++
++int
++PGRsend_lower_cascade(ReplicateHeader * header, char * query)
++{
++
++
++ char * func = "PGRsend_lower_cascade()";
++ ReplicateServerInfo *lower = PGRget_lower_cascade();
++
++
++ while(lower!=NULL)
++ {
++ /**
++ * check lower_cascade validaty.
++ *
++ */
++ if(lsock!=-1 &&
++
PGRsend_cascade(lsock,header,query)==STATUS_OK)
++ {
++ return STATUS_OK;
++ }
++ else
++ {
++ /**
++ * current lower cascade is
missing.
++ * fix socket , or go to next
one.
++ *
++ */
++ while( lower!=NULL &&
++
fixup_socket_for_cascades(&lsock,lower)!=STATUS_OK)
++ {
++
show_error("%s:lower cascade maybe down,challenge new one.",func);
++
PGRset_cascade_server_status(lower,DB_TBL_ERROR);
++ lower
=PGRget_lower_cascade();
++ }
++ }
++ Cascade_Inf->lower=lower;
++ }
++
++
++ return STATUS_ERROR;
++}
++
++
++int
++PGRsend_upper_cascade(ReplicateHeader * header, char * query)
++{
++ char * func = "PGRsend_upper_cascade()";
++ ReplicateServerInfo *upper = PGRget_upper_cascade();
++
++
++ while(upper!=NULL)
++ {
++ /**
++ * check upper_cascade validaty.
++ *
++ */
++ if(usock!=-1 &&
++ PGRsend_cascade(usock,header,query)==STATUS_OK)
++ {
++ return STATUS_OK;
++ }
++ else
++ {
++ /**
++ * current upper cascade is missing.
++ * fix socket , or go to next one.
++ *
++ */
++ while( upper!=NULL &&
++
fixup_socket_for_cascades(&usock,upper)!=STATUS_OK)
++ {
++ show_error("%s:upper
cascade maybe down,challenge new one.",func);
++
PGRset_cascade_server_status(upper,DB_TBL_ERROR);
++ upper
=PGRget_upper_cascade();
++ }
++ }
++ Cascade_Inf->upper=upper;
++ }
++
++ return STATUS_ERROR;
++}
++
++ReplicateServerInfo *
++PGRget_lower_cascade(void)
++{
++ char * func = "PGRget_lower_cascade()";
++ ReplicateServerInfo * cascade = NULL;
++
++ if ((Cascade_Tbl == NULL) || (Cascade_Inf == NULL))
++ {
++ show_error("%s:config data read error",func);
++ return NULL;
++ }
++
++ /* count lower server */
++
++ cascade = Cascade_Inf->myself;
++ if (cascade == NULL)
++ {
++ show_error("%s:cascade data initialize error",func);
++ return NULL;
++ }
++ if (cascade->useFlag != DB_TBL_END)
++ {
++ cascade ++;
++ }
++ while (cascade->useFlag != DB_TBL_END)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:lower cascade search[%d]@[%s] use[%d]",
++ func,
++ cascade->portNumber,
++ cascade->hostName,
++ cascade->useFlag);
++#endif
++ if (cascade->useFlag == DB_TBL_USE)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:find lower cascade",func);
++#endif
++ return cascade;
++ }
++ cascade ++;
++ }
++ return NULL;
++}
++
++ReplicateServerInfo *
++PGRget_upper_cascade(void)
++{
++ char * func = "PGRget_upper_cascade()";
++ ReplicateServerInfo * cascade = NULL;
++
++ if ((Cascade_Tbl == NULL) || (Cascade_Inf == NULL))
++ {
++ show_error("%s:config data read error",func);
++ return NULL;
++ }
++
++
++ /* count lower server */
++ cascade = Cascade_Inf->myself;
++ if ((cascade == NULL) || (Cascade_Inf->top == cascade))
++ {
++ return NULL;
++ }
++ cascade --;
++ while (cascade != NULL)
++ {
++ if (cascade->useFlag == DB_TBL_USE)
++ {
++ return cascade;
++ }
++ if (Cascade_Inf->top == cascade)
++ {
++ break;
++ }
++ cascade --;
++ }
++ return NULL;
++}
++
++static void
++write_cascade_status_file(ReplicateServerInfo * cascade)
++{
++ switch( cascade->useFlag)
++ {
++ case DB_TBL_FREE:
++ PGRwrite_log_file(StatusFp,"cascade(%s) port(%d)
free",
++ cascade->hostName,
++ cascade->portNumber);
++ break;
++ case DB_TBL_INIT:
++ PGRwrite_log_file(StatusFp,"cascade(%s) port(%d)
initialize",
++ cascade->hostName,
++ cascade->portNumber);
++ break;
++ case DB_TBL_USE:
++ PGRwrite_log_file(StatusFp,"cascade(%s) port(%d)
start use",
++ cascade->hostName,
++ cascade->portNumber);
++ break;
++ case DB_TBL_ERROR:
++ PGRwrite_log_file(StatusFp,"cascade(%s) port(%d)
error",
++ cascade->hostName,
++ cascade->portNumber);
++ break;
++ case DB_TBL_TOP:
++ PGRwrite_log_file(StatusFp,"cascade(%s) port(%d)
become top",
++ cascade->hostName,
++ cascade->portNumber);
++ break;
++ }
++}
++
++void
++PGRset_cascade_server_status(ReplicateServerInfo * cascade, int status)
++{
++ if (cascade == NULL)
++ {
++ return;
++ }
++ if (cascade->useFlag != status)
++ {
++ cascade->useFlag = status;
++ write_cascade_status_file(cascade);
++ }
++}
++
++ReplicateServerInfo *
++PGRrecv_cascade_answer(ReplicateServerInfo * cascade,ReplicateHeader *
header)
++{
++ ReplicateServerInfo * answer = NULL;
++ int sock;
++
++ if ((cascade == NULL) || (header == NULL))
++ {
++ return NULL;
++ }
++
++ /* FIXME: ReplicateServerInfo->sock must be removed in cascading. */
++ if(cascade == Cascade_Inf->upper )
++ {
++ sock=usock;
++ }
++ else if (cascade == Cascade_Inf->lower )
++ {
++ sock=lsock;
++ }
++ else
++ {
++#ifdef PRINT_DEBUG
++ show_debug("PGRrecv_cascade_answer:receiving packet from sock not
belogs to cascade->upper / lower. maybe missing .");
++#endif
++ sock=cascade->sock;
++ }
++ answer = (ReplicateServerInfo*)PGRread_packet(sock,header);
++ return answer;
++}
++
++int
++PGRsend_cascade(int sock , ReplicateHeader * header, char * query)
++{
++ char * func ="PGRsend_cascade()";
++ int s;
++ char * send_ptr;
++ char * buf;
++ int send_size = 0;
++ int buf_size;
++ int header_size;
++ int rtn;
++ fd_set wmask;
++ struct timeval timeout;
++ int query_size = 0;
++
++ /* check parameter */
++ if ((header == NULL) || (sock == -1))
++ {
++ return STATUS_ERROR;
++ }
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:PGRsend_cascade sock[%d]",func,sock);
++#endif
++ query_size = ntohl(header->query_size);
++ header_size = sizeof(ReplicateHeader);
++ buf_size = header_size + query_size + 4;
++ buf = malloc(buf_size);
++ memset(buf,0,buf_size);
++ buf_size -= 4;
++ memcpy(buf,header,header_size);
++ if (query_size > 0)
++ {
++ memcpy((char *)(buf+header_size),query,query_size+1);
++ }
++ send_ptr = buf;
++
++ for (;;)
++ {
++ timeout.tv_sec = 10;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&wmask);
++ FD_SET(sock,&wmask);
++ rtn = select(sock+1, (fd_set *)NULL, &wmask, (fd_set *)NULL,
&timeout);
++
++ if (rtn < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++
++ show_error("%s:select failed ,errno is %s",func ,
strerror(errno));
++ free(buf);
++ return STATUS_ERROR;
++ }
++
++ if (rtn && FD_ISSET(sock, &wmask))
++ {
++ s = send(sock,send_ptr + send_size,buf_size -
send_size ,0);
++ if (s < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++ else
++ {
++ show_error("%s:send failed:
%d(%s)",func, errno, strerror(errno));
++ free(buf);
++ return STATUS_ERROR;
++ }
++ }
++ else if (s == 0)
++ {
++ show_error("%s:unexpected EOF", func);
++ free(buf);
++ return STATUS_ERROR;
++ }
++ send_size += s;
++ if (send_size == buf_size)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:send[%s]
size[%d]",func,query,send_size);
++#endif
++ free(buf);
++ return STATUS_OK;
++ }
++ }
++ }
++ return STATUS_OK;
++}
++
++int
++PGRwait_answer_cascade(int sock)
++{
++ ReplicateHeader header;
++ char * answer = NULL;
++
++ answer = PGRread_packet(sock,&header);
++ if (answer != NULL)
++ {
++ free(answer);
++ return STATUS_OK;
++ }
++ return STATUS_ERROR;
++}
++/**
++ * fixup_socket_for_cascades checks socket's validaty.
++ * returns STATUS_OK if succeeded , or STATUS_ERROR if some error occured.
++ * if target is null , only close socket.
++ *
++ * originally written by tanida AT sraoss.co.jp
++ */
++static int
++fixup_socket_for_cascades(int *sock, ReplicateServerInfo *target)
++{
++ if (*sock > 0)
++ {
++ close(*sock);
++ *sock=-1;
++ }
++ if(target!=NULL) {
++ return
PGR_Create_Socket_Connect(sock,target->hostName,target->portNumber);
++ }
++ return STATUS_OK;
++}
++
++
++static int
++notice_cascade_data(int sock)
++{
++ char * func = "notice_cascade_data";
++ ReplicateServerInfo *cascade_data = NULL;
++ ReplicateHeader header;
++ int cnt = 0;
++ int size = 0;
++
++ if (sock <= 0)
++ {
++ return STATUS_ERROR;
++ }
++
++ cascade_data = get_cascade_data(&cnt, ALL_CASCADE );
++ if (cnt <= 0)
++ {
++ show_error("%s:cascade data is wrong",func);
++ return STATUS_ERROR;
++ }
++ size = sizeof (ReplicateServerInfo) * cnt ;
++
++ memset(&header,0,sizeof(ReplicateHeader));
++ header.cmdSys = CMD_SYS_CASCADE ;
++ header.cmdSts = CMD_STS_TO_LOWER ;
++ header.cmdType = CMD_TYPE_UPDATE_ALL;
++ header.query_size = htonl(size);
++ PGRsend_cascade(sock, &header, (char *)cascade_data );
++ if (cascade_data != NULL)
++ {
++ free(cascade_data);
++ }
++ return STATUS_OK;
++}
++
++int
++PGRcascade_main(int sock, ReplicateHeader * header, char * query)
++{
++ switch (header->cmdSts)
++ {
++ case CMD_STS_TO_UPPER:
++ if (header->cmdType == CMD_TYPE_ADD)
++ {
++ /* add lower cascade data to myself */
++
add_cascade_data(header,(ReplicateServerInfo*)query);
++ /* send cascade data to upper */
++ /* and receive new cascade data from upper */
++ PGRstartup_cascade();
++ /* return to lower with new cascade data */
++ notice_cascade_data(sock);
++ /* notifies a cascade server's information to
Cluster DBs */
++ notice_cascade_data_to_cluster_db();
++ }
++ break;
++ case CMD_STS_TO_LOWER:
++ /*
++ * use for cascading replication
++ */
++ break;
++ }
++ return STATUS_OK;
++}
++
++static int
++notice_cascade_data_to_cluster_db(void)
++{
++ char userName[USERNAME_MAX_LENGTH];
++ ReplicateServerInfo *s=NULL;
++
++ if (Cascade_Inf->lower == NULL)
++ {
++ Cascade_Inf->lower = PGRget_lower_cascade();
++ }
++ if (Cascade_Inf->lower == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ s=Cascade_Inf->lower;
++ memset(userName,0,sizeof(userName));
++ strncpy(userName ,getenv("LOGNAME"),sizeof(userName)-1);
++
++ PGRnotice_replication_server(s->hostName,
++
s->portNumber,
++
s->recoveryPortNumber,
++
s->lifecheckPortNumber,
++ userName);
++
++ return STATUS_OK;
++}
++
++int
++PGRwait_notice_rlog_done(void)
++{
++ ReplicateHeader header;
++ if (lsock != -1)
++ {
++ PGRread_packet(lsock,&header);
++ return STATUS_OK;
++ }
++ return STATUS_ERROR;
++
++}
++
++
++int
++PGRsend_notice_quit(void )
++{
++ ReplicateHeader header;
++ int size = 0;
++
++ size = strlen("QUIT_SAFELY");
++ memset(&header,0,sizeof(ReplicateHeader));
++ header.cmdSys = CMD_SYS_CALL ;
++ header.cmdSts = CMD_STS_RESPONSE ;
++ header.cmdType = CMD_TYPE_FRONTEND_CLOSED;
++ header.query_size = htonl(size);
++ PGRsend_lower_cascade(&header, "QUIT_SAFELY");
++ PGRwait_notice_rlog_done();
++ return STATUS_OK;
++}
++
++int
++PGRsend_notice_rlog_done(int sock)
++{
++ ReplicateHeader header;
++ int size = 0;
++
++ if (sock <= 0)
++ {
++ return STATUS_ERROR;
++ }
++
++ size = strlen(PGR_QUERY_DONE_NOTICE_CMD);
++ memset(&header,0,sizeof(ReplicateHeader));
++ header.cmdSys = CMD_SYS_CASCADE ;
++ header.cmdSts = CMD_STS_RESPONSE ;
++ header.cmdType = 0;
++ header.query_size = htonl(size);
++ PGRsend_cascade(sock, &header, PGR_QUERY_DONE_NOTICE_CMD);
++ return STATUS_OK;
++
++}
++#endif /* USE_REPLICATION */
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/conf.c
pgcluster-1.5.0rc7/src/pgcluster/pgrp/conf.c
+--- postgresql-8.1.2/src/pgcluster/pgrp/conf.c 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/conf.c 2006-01-31
23:42:41.000000000 +0100
+@@ -0,0 +1,602 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * conf.c
++ * Replication server for PostgreSQL
++ *
++ * NOTE:
++ * Read and set configuration data in this modul.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#include "postgres.h"
++
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <fcntl.h>
++#include <ctype.h>
++#include <sys/stat.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <sys/sem.h>
++#include <netdb.h>
++#include <errno.h>
++#include <sys/file.h>
++
++
++
++#include "libpq-fe.h"
++#include "libpq-int.h"
++#include "fe-auth.h"
++
++#include "replicate_com.h"
++#include "pgreplicate.h"
++
++int PGRget_Conf_Data(char * path);
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRget_Conf_Data()
++ * NOTES
++ * Initialize mamory and tables
++ * ARGS
++ * char * path: path of the setup file (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++int
++PGRget_Conf_Data(char * path)
++{
++ char * func = "PGRget_Conf_Data()";
++ HostTbl host_tbl[MAX_DB_SERVER];
++ ConfDataType * conf = NULL;
++ int cnt = 0;
++ int lb_cnt = 0;
++ int cascade_cnt = 0;
++ int rec_no = 0;
++ int lb_rec_no = 0;
++ int cascade_rec_no = -1;
++ int i = 0;
++ int size = 0;
++ char fname[256];
++ union semun sem_arg;
++
++ /*
++ * open log file
++ */
++ if (path == NULL)
++ {
++ path = ".";
++ }
++
++ LogFp = NULL;
++ if (Log_Print)
++ {
++
snprintf(fname,sizeof(fname),"%s/%s",path,PGREPLICATE_LOG_FILE);
++ LogFp = fopen(fname,"a");
++ if (LogFp == NULL)
++ {
++ show_error("%s:fopen failed:
(%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s open ok\n",fname);
++#endif
++ }
++
++ snprintf(fname,sizeof(fname),"%s/%s",path,PGREPLICATE_STATUS_FILE);
++ StatusFp = fopen(fname,"a");
++ if (StatusFp == NULL)
++ {
++ show_error("%s:fopen failed: (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++
++ snprintf(fname,sizeof(fname),"%s/%s",path,PGREPLICATE_RID_FILE);
++ RidFp = fopen(fname,"r+");
++ if (RidFp == NULL)
++ {
++ RidFp = fopen(fname,"w+");
++ if (RidFp == NULL)
++ {
++ show_error("%s:fopen failed:
(%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ }
++
++ /*
++ * read configuration file
++ */
++ if (PGR_Get_Conf_Data(path,PGREPLICATE_CONF_FILE) != STATUS_OK)
++ {
++ show_error("%s:PGR_Get_Conf_Data failed",func);
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("PGR_Get_Conf_Data ok");
++#endif
++
++ /* allocate response information table */
++ PGR_Response_Inf = (ResponseInf *)malloc(sizeof(ResponseInf));
++ if (PGR_Response_Inf == NULL)
++ {
++ show_error("%s:malloc() failed. reason: %s",
func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ PGR_Response_Inf->response_mode = PGR_NORMAL_MODE;
++ PGR_Response_Inf->current_cluster = 0;
++
++ /*
++ * memory allocate load balance table buffer
++ */
++ LoadBalanceTbl = (RecoveryTbl
*)malloc(sizeof(RecoveryTbl)*MAX_DB_SERVER);
++ if (LoadBalanceTbl == (RecoveryTbl *)NULL)
++ {
++ show_error("%s:malloc failed: (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("LoadBalanceTbl allocate ok");
++#endif
++
++ /*
++ * memory allocate cascade server table buffer
++ */
++ size = sizeof(ReplicateServerInfo) * MAX_DB_SERVER;
++ CascadeTblShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL |
0600);
++ if (CascadeTblShmid < 0)
++ {
++ show_error("%s:shmget() failed. reason: %s",
func,strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:CascadeTbl shmget ok",func);
++#endif
++ Cascade_Tbl = (ReplicateServerInfo *)shmat(CascadeTblShmid,0,0);
++ if (Cascade_Tbl == (ReplicateServerInfo *)-1)
++ {
++ show_error("%s:shmat() failed. reason: %s",
func,strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:CascadeTbl shmat ok",func);
++#endif
++ memset(Cascade_Tbl , 0 , size );
++
++ /*
++ * memory allocate cascade index
++ */
++ size = sizeof(CascadeInf);
++ CascadeInfShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL |
0600);
++ if (CascadeInfShmid < 0)
++ {
++ show_error("%s:shmget() failed. reason: %s",
func,strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:CascadeInf shmget ok",func);
++#endif
++ Cascade_Inf = (CascadeInf *)shmat(CascadeInfShmid,0,0);
++ if (Cascade_Inf == (CascadeInf *)-1)
++ {
++ show_error("%s:shmat() failed. reason: %s",func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:CascadeInf shmat ok",func);
++#endif
++ memset(Cascade_Inf , 0 , size );
++
++ /*
++ * memory allocate replication commit log buffer
++ */
++ size = sizeof(CommitLogInf) * MAX_DB_SERVER * MAX_CONNECTIONS;
++ CommitLogShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL | 0600);
++ if (CommitLogShmid < 0)
++ {
++ show_error("%s:shmget() failed. reason: %s", func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:CommitLog shmget ok",func);
++#endif
++ Commit_Log_Tbl = (CommitLogInf *)shmat(CommitLogShmid,0,0);
++ if (Commit_Log_Tbl == (CommitLogInf *)-1)
++ {
++ show_error("%s:shmat() failed. reason: %s",func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:Commit_Log_Tbl shmat ok",func);
++#endif
++ memset(Commit_Log_Tbl , 0 , size );
++ (Commit_Log_Tbl + (MAX_DB_SERVER * MAX_CONNECTIONS) -1)->inf.useFlag
= DB_TBL_END;
++
++ /* create semapho */
++ if ((SemID = semget(IPC_PRIVATE,2,IPC_CREAT | IPC_EXCL | 0600)) < 0)
++ {
++ show_error("%s:semget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ for ( i = 0 ; i < 2 ; i ++)
++ {
++ semctl(SemID, i, GETVAL, sem_arg);
++ sem_arg.val = 1;
++ semctl(SemID, i, SETVAL, sem_arg);
++ }
++
++ /* create semapho */
++ if ((CascadeSemID = semget(IPC_PRIVATE,2,IPC_CREAT | IPC_EXCL |
0600)) < 0)
++ {
++ show_error("%s:semget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ for ( i = 0 ; i < 2 ; i ++)
++ {
++ semctl(CascadeSemID, i, GETVAL, sem_arg);
++ sem_arg.val = 1;
++ semctl(CascadeSemID, i, SETVAL, sem_arg);
++ }
++
++
++ if ((VacuumSemID = semget(IPC_PRIVATE,2,IPC_CREAT | IPC_EXCL | 0600))
< 0)
++ {
++ show_error("%s:semget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ for ( i = 0 ; i < 2 ; i ++)
++ {
++ semctl(VacuumSemID, i, GETVAL, sem_arg);
++ sem_arg.val = 1;
++ semctl(VacuumSemID, i, SETVAL, sem_arg);
++ }
++ size = sizeof(ReplicationLogInf);
++ Replicateion_Log = malloc(size);
++ if (Replicateion_Log == NULL)
++ {
++ show_error("%s:malloc failed: (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(Replicateion_Log , 0 , size );
++ Replicateion_Log->RLog_Sock_Path = NULL;
++#ifdef PRINT_DEBUG
++ show_debug("%s:RLog Memory Allocation ok",func);
++#endif
++
++
++ /*
++ * set each datas into the tables
++ */
++ conf = ConfData_Top;
++ while (conf != (ConfDataType *)NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("registering
(key,value)=(%s,%s)",conf->key,conf->value);
++#endif
++ /* get cluster db data */
++ if (!strcmp(conf->table,CLUSTER_SERVER_TAG))
++ {
++ rec_no = conf->rec_no;
++ if (cnt < rec_no)
++ {
++ cnt = rec_no;
++ if (cnt >= MAX_DB_SERVER)
++ {
++ continue;
++ }
++ }
++ if (!strcmp(conf->key,HOST_NAME_TAG))
++ {
++ int ip;
++
memcpy(host_tbl[rec_no].hostName,conf->value,sizeof(host_tbl[rec_no].hostName));
++#ifdef PRINT_DEBUG
++ show_debug("registering hostname
%s",host_tbl[rec_no].hostName);
++#endif
++ ip=PGRget_ip_by_name(conf->value);
++
++ sprintf(host_tbl[rec_no].resolvedName,
++ "%d.%d.%d.%d",
++ (ip ) & 0xff ,
++ (ip >> 8) & 0xff ,
++ (ip >> 16) & 0xff ,
++ (ip >> 24) & 0xff );
++#ifdef PRINT_DEBUG
++ show_debug("resolved name is
%s",host_tbl[rec_no].resolvedName);
++#endif
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,PORT_TAG))
++ {
++ host_tbl[rec_no].port = atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,RECOVERY_PORT_TAG))
++ {
++ host_tbl[rec_no].recoveryPort =
atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ }
++ /* get cascade server data */
++ else if (!strcmp(conf->table, REPLICATION_SERVER_INFO_TAG))
++ {
++ cascade_rec_no = conf->rec_no ;
++ if (cascade_cnt < cascade_rec_no)
++ {
++ cascade_cnt = cascade_rec_no;
++ if (cascade_cnt >= MAX_DB_SERVER)
++ {
++ continue;
++ }
++ }
++ if (!strcmp(conf->key,HOST_NAME_TAG))
++ {
++
memcpy((Cascade_Tbl+cascade_rec_no)->hostName,conf->value,sizeof(Cascade_Tbl->hostName));
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,PORT_TAG))
++ {
++ if (atoi(conf->value) > 0)
++ {
++
(Cascade_Tbl+cascade_rec_no)->portNumber = atoi(conf->value);
++ }
++ else
++ {
++
(Cascade_Tbl+cascade_rec_no)->portNumber = DEFAULT_PGRP_PORT;
++ }
++ (Cascade_Tbl+cascade_rec_no)->sock = -1;
++
++ conf = (ConfDataType*)conf->next;
++
PGRset_cascade_server_status(Cascade_Tbl+cascade_rec_no,DB_TBL_USE);
++ if (cascade_rec_no == 0)
++ {
++ Cascade_Inf->top = Cascade_Tbl;
++ }
++ continue;
++ }
++ if (!strcmp(conf->key,RECOVERY_PORT_TAG))
++ {
++ if (atoi(conf->value) > 0)
++ {
++
(Cascade_Tbl+cascade_rec_no)->recoveryPortNumber = atoi(conf->value);
++ }
++ else
++ {
++
(Cascade_Tbl+cascade_rec_no)->recoveryPortNumber = DEFAULT_PGRP_RECOVERY_PORT;
++ }
++ (Cascade_Tbl+cascade_rec_no)->rlog_sock=-1;
++ (Cascade_Tbl+cascade_rec_no +1)->useFlag =
DB_TBL_END;
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ }
++ /* get loadbalancer table data */
++ else if (!strcmp(conf->table,LOAD_BALANCE_SERVER_TAG))
++ {
++ lb_rec_no = conf->rec_no;
++ if (lb_cnt < lb_rec_no)
++ {
++ lb_cnt = lb_rec_no;
++ if (lb_cnt >= MAX_DB_SERVER)
++ {
++ continue;
++ }
++ }
++ if (!strcmp(conf->key,HOST_NAME_TAG))
++ {
++ memcpy((LoadBalanceTbl +
lb_rec_no)->hostName, conf->value,sizeof(LoadBalanceTbl->hostName));
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!strcmp(conf->key,RECOVERY_PORT_TAG))
++ {
++ (LoadBalanceTbl + lb_rec_no)->recoveryPort =
atoi(conf->value);
++ (LoadBalanceTbl + lb_rec_no)->sock = -1;
++ (LoadBalanceTbl + lb_rec_no)->recovery_sock =
-1;
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ }
++ else
++ {
++ if (!strcmp(conf->key,REPLICATE_PORT_TAG))
++ {
++ Port_Number = atoi(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ /* get port number for recovery cluster db server */
++ if (!strcmp(conf->key,RECOVERY_PORT_TAG))
++ {
++ if (atoi(conf->value) > 0)
++ {
++ Recovery_Port_Number =
atoi(conf->value);
++ }
++ else
++ {
++ Recovery_Port_Number
=DEFAULT_PGRP_RECOVERY_PORT;
++ }
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!STRCMP(conf->key,LIFECHECK_PORT_TAG))
++ {
++ if (atoi(conf->value) > 0)
++ {
++ LifeCheck_Port_Number =
atoi(conf->value);
++ }
++ else
++ {
++ LifeCheck_Port_Number =
DEFAULT_PGRP_LIFECHECK_PORT;
++ }
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!STRCMP(conf->key,RLOG_PORT_TAG))
++ {
++ if (atoi(conf->value) > 0)
++ {
++ Replicateion_Log->RLog_Port_Number =
atoi(conf->value);
++ }
++ else
++ {
++ Replicateion_Log->RLog_Port_Number =
DEFAULT_PGRP_RLOG_PORT;
++ }
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ /*
++ if (!STRCMP(conf->key,STATUS_LOG_FILE_TAG))
++ {
++ Com_Info.Log_Info.PGRStatusFileName =
strdup(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ if (!STRCMP(conf->key,ERROR_LOG_FILE_TAG))
++ {
++ Com_Info.Log_Info.PGRLogFileName =
strdup(conf->value);
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ */
++ /* get response mode */
++ if (!strcmp(conf->key,RESPONSE_MODE_TAG))
++ {
++ if
(!strcmp(conf->value,RESPONSE_MODE_RELIABLE))
++ {
++ PGR_Response_Inf->response_mode =
PGR_RELIABLE_MODE;
++ }
++ else if
(!strcmp(conf->value,RESPONSE_MODE_FAST))
++ {
++ PGR_Response_Inf->response_mode =
PGR_FAST_MODE;
++ }
++ else
++ {
++ PGR_Response_Inf->response_mode =
PGR_NORMAL_MODE;
++ }
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ /* get replication log use or not */
++ if (!strcmp(conf->key,USE_REPLICATION_LOG_TAG))
++ {
++ if (!strcmp(conf->value,"yes"))
++ {
++ PGR_Use_Replication_Log = true;
++ }
++ conf = (ConfDataType*)conf->next;
++ continue;
++ }
++ conf = (ConfDataType*)conf->next;
++ }
++ conf = (ConfDataType*)conf->next;
++ }
++
++
++ /* create cluster db server table */
++ Host_Tbl_Begin = (HostTbl *)NULL;
++
++ size = sizeof(HostTbl) * MAX_DB_SERVER;
++ HostTblShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL | 0600);
++ if (HostTblShmid < 0)
++ {
++ show_error("%s:shmget() failed. reason: %s",
func,strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:HostTbl shmget ok",func);
++#endif
++ Host_Tbl_Begin = (HostTbl *)shmat(HostTblShmid,0,0);
++ if (Host_Tbl_Begin == (HostTbl *)-1)
++ {
++ show_error("%s:shmat() failed. reason: %s", func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:HostTbl shmat ok",func);
++#endif
++ memset(Host_Tbl_Begin , 0 , size );
++ Host_Tbl_Begin -> useFlag = DB_TBL_END;
++
++ for ( i = 0 ; i <= cnt ; i ++)
++ {
++ PGRadd_HostTbl(&host_tbl[i],DB_TBL_USE);
++ }
++ /* set load balance table */
++ for ( i = 0 ; i <= lb_cnt ; i ++)
++ {
++ (LoadBalanceTbl + i)->port = -1;
++ (LoadBalanceTbl + i)->sock = -1;
++ }
++ memset((LoadBalanceTbl + i),0,sizeof(RecoveryTbl));
++ PGR_Free_Conf_Data();
++
++ /* allocate result buffer of query */
++ PGR_Result = malloc(PGR_MESSAGE_BUFSIZE);
++ if (PGR_Result == NULL)
++ {
++ show_error("%s:malloc() failed. reason: %s", func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(PGR_Result,0,PGR_MESSAGE_BUFSIZE);
++
++ /* allocate log_data */
++ PGR_Log_Header = malloc(sizeof(ReplicateHeader));
++ if (PGR_Log_Header == NULL)
++ {
++ show_error("%s:malloc() failed. reason: %s", func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(PGR_Log_Header,0,sizeof(ReplicateHeader));
++
++ /* allocate send query id */
++ size = sizeof(unsigned int) * (MAX_DB_SERVER +1);
++ PGR_Send_Query_ID = malloc (size);
++ if (PGR_Send_Query_ID == NULL)
++ {
++ show_error("%s:malloc() failed. reason: %s", func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(PGR_Send_Query_ID, 0, size);
++ for ( i = 0 ; i < MAX_DB_SERVER ; i ++)
++ {
++ StartReplication[i] = true;
++ }
++
++ /* set self data into cascade table */
++
++ cascade_rec_no ++;
++
gethostname((Cascade_Tbl+cascade_rec_no)->hostName,sizeof(Cascade_Tbl->hostName));
++ (Cascade_Tbl+cascade_rec_no)->portNumber = Port_Number;
++ (Cascade_Tbl+cascade_rec_no)->recoveryPortNumber =
Recovery_Port_Number;
++ (Cascade_Tbl+cascade_rec_no)->sock = -1;
++
++ PGRset_cascade_server_status(Cascade_Tbl+cascade_rec_no,DB_TBL_USE);
++ /* terminate */
++ (Cascade_Tbl+(cascade_rec_no+1))->useFlag = DB_TBL_END;
++
++ Cascade_Inf->top = Cascade_Tbl;
++ Cascade_Inf->end = Cascade_Tbl+cascade_rec_no;
++ Cascade_Inf->upper = NULL;
++ Cascade_Inf->lower = NULL;
++ if (cascade_rec_no >= 1)
++ {
++ Cascade_Inf->upper = (Cascade_Tbl+cascade_rec_no - 1);
++ }
++ (Cascade_Tbl+(cascade_rec_no+1))->useFlag = DB_TBL_END;
++
++ Cascade_Inf->myself = (Cascade_Tbl+cascade_rec_no);
++ Cascade_Inf->useFlag = DB_TBL_USE;
++
++ PGR_Response_Inf->response_mode = PGR_NORMAL_MODE;
++
++ return STATUS_OK;
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/main.c
pgcluster-1.5.0rc7/src/pgcluster/pgrp/main.c
+--- postgresql-8.1.2/src/pgcluster/pgrp/main.c 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/main.c 2006-01-31
23:40:32.000000000 +0100
+@@ -0,0 +1,1001 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * main.c
++ * Replication server for PostgreSQL
++ *
++ * NOTE:
++ * This is the main module of the replication server.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#include "postgres.h"
++
++#include <stdio.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/time.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <ctype.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <sys/param.h>
++#include <arpa/inet.h>
++#include <sys/file.h>
++#include <pthread.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++#ifdef HAVE_SYS_SELECT_H
++#include <sys/select.h>
++#endif
++
++#ifdef HAVE_GETOPT_H
++#include <getopt.h>
++#endif
++
++#include "miscadmin.h"
++#include "nodes/nodes.h"
++
++#include "libpq-fe.h"
++#include "libpq/libpq-fs.h"
++#include "libpq-int.h"
++#include "fe-auth.h"
++
++
++#include "access/xact.h"
++#include "replicate_com.h"
++#include "pgreplicate.h"
++
++#ifdef WIN32
++#include "win32.h"
++#endif
++#include <arpa/inet.h>
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#ifdef MULTIBYTE
++#include "mb/pg_wchar.h"
++#endif
++
++/*--------------------------------------
++ * GLOBAL VARIABLE DECLARATION
++ *--------------------------------------
++ */
++/* for replicate_com.h */
++
++ConfDataType * ConfData_Top = (ConfDataType *)NULL;
++ConfDataType * ConfData_End = (ConfDataType *)NULL;
++
++/* replication server data */
++uint16_t Port_Number = 0;
++uint16_t LifeCheck_Port_Number = 0;
++uint16_t Recovery_Port_Number = 0;
++bool PGR_Parse_Session_Started = false;
++
++/* global table data */
++HostTbl *Host_Tbl_Begin = NULL;
++Dllist * Transaction_Tbl_Begin = NULL;
++TransactionTbl * Transaction_Tbl_End = NULL;
++RecoveryTbl * LoadBalanceTbl = NULL;
++RecoveryStatusInf * Recovery_Status_Inf = NULL;
++ReplicateHeader * PGR_Log_Header = NULL;
++ReplicateServerInfo * Cascade_Tbl = NULL;;
++CommitLogInf * Commit_Log_Tbl = NULL;
++QueryLogType * Query_Log_Top = NULL;
++QueryLogType * Query_Log_End = NULL;
++CascadeInf * Cascade_Inf = NULL;
++ReplicationLogInf * Replicateion_Log = NULL;
++/* IPC's id data */
++int RecoveryShmid = 0;
++int ReplicateSerializationShmid=0;
++int RecoveryMsgShmid = 0;
++int *RecoveryMsgid = NULL;
++int HostTblShmid = 0;
++int LockWaitTblShmid = 0;
++int LoadBalanceTblShmid = 0;
++int CascadeTblShmid = 0;
++int CascadeInfShmid = 0;
++int CommitLogShmid = 0;
++int QueryLogMsgid = 0;
++int QueryLogAnsMsgid = 0;
++int PGconnMsgid = 0;
++int MaxBackends = 0;
++char * PGR_Result = NULL;
++int SemID = 0;
++int RecoverySemID= 0;
++int RecovErysemid = 0;
++int VacuumSemID = 0;
++int CascadeSemID= 0;
++char * PGR_Data_Path = NULL;
++char * PGR_Write_Path = NULL;
++int IS_SESSION_AUTHORIZATION = 0;
++ResponseInf * PGR_Response_Inf = NULL;
++bool StartReplication[MAX_DB_SERVER];
++bool PGR_Cascade = false;
++bool PGR_Use_Replication_Log = false;
++bool PGR_AutoCommit = true;
++unsigned int * PGR_Send_Query_ID = NULL;
++unsigned int PGR_Query_ID = 0;
++bool exit_processing = false;
++int pgreplicate_pid = 0;
++
++int ReplicateSock = -1;
++
++RecoveryQueueInf RecoveryQueue;
++char * Backend_Socket_Dir = NULL;
++
++unsigned int * PGR_ReplicateSerializationID = NULL;
++
++int Log_Print = 0;
++int Debug_Print = 0;
++FILE * LogFp = (FILE *)NULL;
++FILE * StatusFp = (FILE *)NULL;
++FILE * RidFp = (FILE *)NULL;
++
++extern char *optarg;
++char * PGRuserName = NULL;
++
++int fork_wait_time = 0;
++int Idle_Flag = IDLE_MODE;
++bool Exit_Request = false;
++
++pthread_mutex_t transaction_table_mutex;
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++static void startup_replication_server(void);
++static int replicate_loop(int fd);
++static void replicate_main(void);
++static void quick_exit(SIGNAL_ARGS);
++static void daemonize(void);
++static void write_pid_file(void);
++static void stop_pgreplicate(void);
++static bool is_exist_pid_file(void);
++static void usage(void);
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * replicate_loop()
++ * NOTES
++ * replication module
++ * ARGS
++ * int fd :
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ *--------------------------------------------------------------------
++ */
++static int
++replicate_loop(int fd)
++{
++ char * func = "replicate_loop()";
++ pid_t pgid = 0;
++ pid_t pid = 0;
++ int sock = -1;
++ int rtn = 0;
++ int cnt = 0;
++ int result;
++ bool exist_sys_log=false;
++ bool exist_replicate=false;
++
++
++ result = PGR_Create_Acception(fd,&sock,"",Port_Number);
++ if (result == STATUS_ERROR)
++ {
++ show_error("%s: accept failed (%s)", func, strerror(errno));
++ if (sock != -1)
++ close(sock);
++ return 1;
++ }
++
++ pgid = getpgid(0);
++ pid = fork();
++ if (pid <0)
++ {
++ show_error("%s:fork failed (%s)",func,strerror(errno));
++ PGRreplicate_exit(0);
++ }
++ if (pid == 0)
++ {
++ int status = LOOP_CONTINUE;
++ bool PGR_Cascade = false;
++ ReplicateHeader header;
++ ReplicateHeader header_save_for_recovering;
++ char * query = NULL;
++
++ if (fork_wait_time > 0) {
++#ifdef PRINT_DEBUG
++ show_debug("wait fork(): pid = %d", getpid());
++#endif
++ sleep(fork_wait_time);
++ }
++
++ close(fd);
++
++ signal(SIGHUP, quick_exit);
++ signal(SIGINT, quick_exit);
++ signal(SIGQUIT, quick_exit);
++ signal(SIGTERM, quick_exit);
++ signal(SIGALRM, quick_exit);
++ signal(SIGPIPE, SIG_IGN);
++ setpgid(0,pgid);
++
++ if (PGRinit_transaction_table() != STATUS_OK)
++ {
++ show_error("transaction table memory allocate
failed");
++ PGR_Close_Sock(&sock);
++ exit(1);
++ }
++
++ pthread_mutex_init(&transaction_table_mutex, NULL);
++
++ /* child loop */
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = PGR_RECV_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ if (query != NULL)
++ {
++ free(query);
++ query = NULL;
++ }
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(sock,&rmask);
++ rtn = select(sock+1, &rmask, (fd_set *)NULL, (fd_set
*)NULL, &timeout);
++ if (rtn < 0)
++ {
++ if (errno == EINTR)
++ continue;
++ }
++
++ if (rtn && FD_ISSET(sock, &rmask))
++ {
++ query = NULL;
++ query = PGRread_packet(sock,&header);

++ if ((query == NULL) || (header.cmdSts == 0))
++ {
++
++ if (exist_sys_log)
++ {
++ show_error("%s:upper cascade
closed? , errno=%d(%s)",func,errno,strerror(errno));
++ memset(&header, 0,
sizeof(ReplicateHeader));
++ header.cmdSys = CMD_SYS_CALL;
++ header.cmdSts =
CMD_STS_QUERY_SUSPEND;
++ header.query_size = htonl(0);
++
PGRsend_rlog_to_local(&header, NULL);
++ exist_sys_log = false;
++ }
++ else
++ {
++ int recovery_status =
PGRget_recovery_status();
++#ifdef PRINT_DEBUG
++ show_debug("%s:session
closed",func);
++#endif
++ if (recovery_status ==
RECOVERY_START)
++ {
++
header_save_for_recovering.cmdSts=CMD_TYPE_OTHER;
++
header_save_for_recovering.cmdType=CMD_TYPE_CONNECTION_CLOSE;
++
header_save_for_recovering.query_size = htonl(21);
++
PGRdo_replicate(sock,&header_save_for_recovering,"PGR_CLOSE_CONNECTION");
++ }
++
++ if(exist_replicate)
++ {
++ int recovery_status =
0;
++
++
PGRsem_lock(RecoverySemID,SEM_NUM_OF_RECOVERY);
++ if
(Recovery_Status_Inf->transaction_count > 0)
++ {
++
Recovery_Status_Inf->transaction_count --;
++ }
++
PGRsem_unlock(RecoverySemID, SEM_NUM_OF_RECOVERY);

++#ifdef PRINT_DEBUG
++
show_debug("transaction_status: %d",Recovery_Status_Inf->transaction_count);
++#endif
++
recovery_status=PGRget_recovery_status();
++
header_save_for_recovering.cmdSts=CMD_TYPE_OTHER;
++
header_save_for_recovering.cmdType=CMD_TYPE_CONNECTION_CLOSE;
++
header_save_for_recovering.query_size = htonl(21);
++
PGRdo_replicate(sock,&header_save_for_recovering,"PGR_CLOSE_CONNECTION");
++ }
++ PGRsend_notice_quit();
++ }
++ break;
++ }
++ cnt = 0;
++ memcpy(&header_save_for_recovering,
++ &header,
++ sizeof(ReplicateHeader));
++ switch (header.cmdSys)
++ {
++ case CMD_SYS_PREREPLICATE:
++
if(Cascade_Inf!=NULL ||
++
Cascade_Inf->upper == NULL)
++ {
++
/* 1 means "I am primary replicate server." */
++
PGRreturn_result(sock,"1", PGR_NOWAIT_ANSWER);
++ }
++ else
++ {
++
/* 1 means "I am not primary replicate server." */
++
PGRreturn_result(sock,"0", PGR_NOWAIT_ANSWER);
++ }
++ break;
++ case CMD_SYS_REPLICATE:
++ status =
PGRdo_replicate(sock,&header,query);
++ if
(header.cmdSts == CMD_STS_TRANSACTION )
++ {
++
switch(header.cmdType)
++
{
++
case CMD_TYPE_BEGIN:
++
exist_replicate=true;
++
memcpy(&header_save_for_recovering,
++
&header,
++
sizeof(ReplicateHeader));
++
break;
++
case CMD_TYPE_COMMIT:
++
case CMD_TYPE_ROLLBACK:
++
exist_replicate=false;
++
break;
++
}
++ }
++ else
++ {
++
if ( header.cmdType == CMD_TYPE_COPY )
++
{
++
exist_replicate=true;
++
memcpy(&header_save_for_recovering,
++
&header,
++
sizeof(ReplicateHeader));
++
}
++
else if (header.cmdType == CMD_TYPE_COPY_DATA_END)
++
{
++
exist_replicate=false;
++
}
++ }
++
++ break;
++ case CMD_SYS_LOG:
++ exist_sys_log
= true;
++
PGRsend_rlog_to_local(&header, query);
++ /* set own
replicate id by rlog */
++
PGRset_replication_id(ntohl(header.replicate_id));
++
PGRsend_notice_rlog_done(sock);
++ break;
++ case CMD_SYS_CASCADE:
++ PGR_Cascade =
true;
++
PGRcascade_main(sock,&header,query);
++ break;
++ case CMD_SYS_CALL:
++ if
(header.cmdSts == CMD_STS_TRANSACTION_ABORT)
++ {
++
if (header.cmdType == CMD_TYPE_FRONTEND_CLOSED)
++
{
++
header_save_for_recovering.cmdSts=CMD_TYPE_OTHER;
++
header_save_for_recovering.cmdType=CMD_TYPE_CONNECTION_CLOSE;
++
header_save_for_recovering.query_size = htonl(21);
++

PGRdo_replicate(sock,&header_save_for_recovering,"PGR_CLOSE_CONNECTION");
++
}
++
else
++
{
++
PGRreconfirm_commit(sock,&header);
++
}
++ }
++ else if
(header.cmdSts == CMD_STS_NOTICE)
++ {
++
++ }
++ else if
(header.cmdSts == CMD_STS_RESPONSE)
++ {
++
if (header.cmdType == CMD_TYPE_FRONTEND_CLOSED)
++
{
++#ifdef PRINT_DEBUG
++
show_debug("replicate_loop ends due to frontend_closed.");
++#endif
++
PGRsend_notice_rlog_done(sock);
++
status = LOOP_END;
++
}
++ }
++ break;
++ default:
++#ifdef PRINT_DEBUG
++
show_debug("WARNING: unknown Header->cmdSys %c",header.cmdSys);
++#endif
++ }
++ }
++ if (status == LOOP_END)
++ {
++ break;
++ }
++ }
++
++ pthread_mutex_destroy(&transaction_table_mutex);
++ PGR_Close_Sock(&sock);
++ if (query != NULL)
++ {
++ free(query);
++ query = NULL;
++ }
++ PGRclear_connections();
++#ifdef PRINT_DEBUG
++ show_debug("%s:replicate loop exit",func);
++#endif
++ exit(0);
++ }
++ else
++ {
++ PGR_Close_Sock(&sock);
++ return 0;
++ }
++}
++
++static void
++startup_replication_server(void)
++{
++ ReplicateHeader header;
++ char hostName[HOSTNAME_MAX_LENGTH];
++ char userName[USERNAME_MAX_LENGTH];
++ char query[256];
++
++ if (PGRuserName == NULL)
++ {
++ PGRuserName = getenv("LOGNAME");
++ if (PGRuserName == NULL)
++ {
++ PGRuserName = getenv("USER");
++ if (PGRuserName == NULL)
++ PGRuserName = "postgres";
++ }
++ }
++ memset(&header,0,sizeof(ReplicateHeader));
++ memset(query,0,sizeof(query));
++ memset(hostName,0,sizeof(hostName));
++ memset(userName,0,sizeof(userName));
++ gethostname(hostName,sizeof(hostName)-1);
++ strncpy(userName ,PGRuserName,sizeof(userName)-1);
++ snprintf(query,sizeof(query)-1,"SELECT %s(%d,'%s',%d,%d)",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_STARTUP_REPLICATION_SERVER_FUNC_NO,
++ hostName,
++ Port_Number,
++ Recovery_Port_Number);
++ header.cmdSts = CMD_STS_NOTICE;
++ header.query_id = htonl(PGRget_next_query_id());
++ header.query_size = htonl(strlen(query));
++ memcpy(header.from_host,hostName,sizeof(header.from_host));
++ memcpy(header.userName,userName,sizeof(header.userName));
++ strcpy(header.dbName,"template1");
++ replicate_packet_send_internal( &header,
query,-1,PGRget_recovery_status(),true);
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * replicate_main()
++ * NOTES
++ * Replication main module
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++replicate_main(void)
++{
++#ifdef PRINT_DEBUG
++ char * func = "replicate_main()";
++#endif
++ int status;
++ int rtn;
++
++ /* cascade start up notice */
++ if (Cascade_Inf->upper != NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("initialize cascade information");
++#endif
++ PGRstartup_cascade();
++ }
++
++ status = PGR_Create_Socket_Bind(&ReplicateSock, "", Port_Number);
++
++ if (status != STATUS_OK)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s %d port bind failed. quit.",func,Port_Number);
++#endif
++ stop_pgreplicate();
++ PGRreplicate_exit(0);
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s %d port bind OK",func,Port_Number);
++#endif
++
++
++ /* replication start up notice */
++ startup_replication_server();
++
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ if (exit_processing == true)
++ PGRreplicate_exit(0);
++
++ timeout.tv_sec = PGR_RECV_TIMEOUT;
++ timeout.tv_usec = 0;
++
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(ReplicateSock,&rmask);
++ rtn = select(ReplicateSock+1, &rmask, (fd_set *)NULL, (fd_set
*)NULL, &timeout);
++ if (rtn < 0)
++ continue;
++
++ if (rtn && FD_ISSET(ReplicateSock, &rmask))
++ {
++ /*
++ * get recovery status.
++ */
++ PGRcheck_recovered_host();
++
++ if (exit_processing == true)
++ break;
++
++ /*
++ * call replication module
++ */
++ replicate_loop(ReplicateSock);
++ }
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * quick_exit()
++ * NOTES
++ * Exit child process
++ * ARGS
++ * SIGNAL_ARGS: receive signal number(I)
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++quick_exit(SIGNAL_ARGS)
++{
++#ifdef PRINT_DEBUG
++ show_debug("quick_exit:signo = %d", postgres_signal_arg);
++#endif
++ exit(0);
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * daemonize()
++ * NOTES
++ * Daemonize this process
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++daemonize(void)
++{
++ char * func = "daemonize()";
++ int i;
++ pid_t pid;
++
++ pid = fork();
++ if (pid == (pid_t) -1)
++ {
++ show_error("%s:fork() failed. reason: %s",func,
strerror(errno));
++ exit(1);
++ return; /* not reached */
++ }
++ else if (pid > 0)
++ { /* parent */
++ exit(0);
++ }
++
++#ifdef HAVE_SETSID
++ if (setsid() < 0)
++ {
++ show_error("%s:setsid() failed. reason:%s",
func,strerror(errno));
++ exit(1);
++ }
++#endif
++
++ i = open("/dev/null", O_RDWR);
++ dup2(i, 0);
++ dup2(i, 1);
++ dup2(i, 2);
++ close(i);
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * write_pid_file()
++ * NOTES
++ * The process ID is written in the file.
++ * This process ID is used when finish pglb.
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++write_pid_file(void)
++{
++ char * func = "write_pid_file()";
++ FILE *fd;
++ char fname[256];
++ char pidbuf[128];
++
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGREPLICATE_PID_FILE);
++ fd = fopen(fname, "w");
++ if (!fd)
++ {
++ show_error("%s:could not open pid file as %s. reason: %s",
++ func, fname, strerror(errno));
++ exit(1);
++ }
++ snprintf(pidbuf, sizeof(pidbuf), "%d", getpid());
++ fwrite(pidbuf, strlen(pidbuf), 1, fd);
++ if (fclose(fd))
++ {
++ show_error("%s:could not write pid file as %s. reason: %s",
++ func,fname, strerror(errno));
++ exit(1);
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * stop_pgreplicate()
++ * NOTES
++ * Stop the pgreplicate process
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++stop_pgreplicate(void)
++{
++ char * func = "stop_pgreplicate()";
++ FILE *fd;
++ char fname[256];
++ char pidbuf[128];
++ pid_t pid;
++
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGREPLICATE_PID_FILE);
++ fd = fopen(fname, "r");
++ if (!fd)
++ {
++ show_error("%s:could not open pid file as %s. reason: %s",
++ func,fname, strerror(errno));
++ exit(1);
++ }
++ memset(pidbuf,0,sizeof(pidbuf));
++ fread(pidbuf, sizeof(pidbuf), 1, fd);
++ fclose(fd);
++ pid = atoi(pidbuf);
++
++ if (kill (pid,SIGTERM) == -1)
++ {
++ show_error("%s:could not stop pid: %d, reason:
%s",func,pid,strerror(errno));
++ exit(1);
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * is_exist_pid_file()
++ * NOTES
++ * Check existence of pid file.
++ * ARGS
++ * void
++ * RETURN
++ * 1: the pid file is exist
++ * 0: the pid file is not exist
++ *--------------------------------------------------------------------
++ */
++static bool
++is_exist_pid_file(void)
++{
++ char fname[256];
++ struct stat buf;
++
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Write_Path,
PGREPLICATE_PID_FILE);
++ if (stat(fname,&buf) == 0)
++ {
++ /* pid file is exist */
++ return true;
++ }
++ else
++ {
++ /* pid file is not exist */
++ return false;
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * child_wait()
++ * NOTES
++ * Waiting for hung up a child
++ * ARGS
++ * int signal_args: signal number (expecting the SIGCHLD)
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++void
++child_wait(SIGNAL_ARGS)
++{
++ pid_t pid = 0;
++
++ do {
++ int ret;
++ pid = waitpid(-1,&ret,WNOHANG);
++ } while(pid > 0);
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * usage()
++ * NOTES
++ * show usage of pglb
++ * ARGS
++ * void
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++static void
++usage(void)
++{
++ char * path;
++
++ path = getenv("PGDATA");
++ if (path == NULL)
++ path = ".";
++ fprintf(stderr,"PGReplicate version [%s]\n",PGREPLICATE_VERSION);
++ fprintf(stderr,"A replication server for cluster DB servers (based on
PostgreSQL)\n\n");
++ fprintf(stderr,"usage: pgreplicate [-D path_of_config_file] [-W
path_of_work_files] [-U login user][-l][-n][-v][-h][stop]\n");
++ fprintf(stderr," config file default path: %s/%s\n",path,
PGREPLICATE_CONF_FILE);
++ fprintf(stderr," -l: print error logs in the log file.\n");
++ fprintf(stderr," -n: don't run in daemon mode.\n");
++ fprintf(stderr," -v: debug mode. need '-n' flag\n");
++ fprintf(stderr," -h: print this help\n");
++ fprintf(stderr," stop: stop pgreplicate\n");
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * main()
++ * NOTES
++ * main module of pgreplicate
++ * ARGS
++ * int argc: number of parameter
++ * char ** argv: value of parameter
++ * RETURN
++ * none
++ *--------------------------------------------------------------------
++ */
++int
++main(int argc, char * argv[])
++{
++ char * func = "main()";
++ int opt = 0;
++ pid_t pgid = 0;
++ pid_t pid = 0;
++ char * r_path = NULL;
++ char * w_path = NULL;
++ bool detach = true;
++ pid_t rlog_pid;
++
++ r_path = getenv("PGDATA");
++ if (r_path == NULL)
++ r_path = ".";
++ while ((opt = getopt(argc, argv, "U:D:W:w:lvnh")) != -1)
++ {
++ switch (opt)
++ {
++ case 'U':
++ if (!optarg)
++ {
++ usage();
++ exit(1);
++ }
++ PGRuserName = strdup(optarg);
++ break;
++ case 'D':
++ if (!optarg)
++ {
++ usage();
++ exit(1);
++ }
++ r_path = optarg;
++ break;
++ case 'W':
++ if (!optarg)
++ {
++ usage();
++ exit(1);
++ }
++ w_path = optarg;
++ break;
++ case 'w':
++ fork_wait_time = atoi(optarg);
++ break;
++ case 'l':
++ Log_Print = 1;
++ break;
++ case 'v':
++ Debug_Print = 1;
++ break;
++ case 'n':
++ detach = false;
++ break;
++ case 'h':
++ usage();
++ exit(0);
++ break;
++ default:
++ usage();
++ exit(1);
++ }
++ }
++ PGR_Data_Path = r_path;
++ if (w_path == NULL)
++ {
++ PGR_Write_Path = PGR_Data_Path;
++ }
++ else
++ {
++ PGR_Write_Path = w_path;
++ }
++
++ if (optind == (argc-1) && !strncasecmp(argv[optind],"stop",4))
++ {
++ stop_pgreplicate();
++ exit(0);
++ }
++ else if (optind == argc)
++ {
++ if (is_exist_pid_file())
++ {
++ fprintf(stderr,"pid file %s/%s found. is another
pgreplicate running?", PGR_Write_Path, PGREPLICATE_PID_FILE);
++ exit(1);
++ }
++ }
++ else if (optind < argc)
++ {
++ usage();
++ exit(1);
++ }
++
++ if (detach)
++ {
++ daemonize();
++ }
++ write_pid_file();
++ pgreplicate_pid = getpid();
++
++ signal(SIGINT, set_exit_processing);
++ signal(SIGQUIT, set_exit_processing);
++ signal(SIGTERM, set_exit_processing);
++ signal(SIGCHLD, child_wait);
++ signal(SIGPIPE, SIG_IGN);
++
++ if (PGRget_Conf_Data(PGR_Data_Path) != STATUS_OK)
++ {
++ show_error("%s:PGRget_Conf_Data error",func);
++ PGRreplicate_exit(0);
++ }
++ if (PGRinit_recovery() != STATUS_OK)
++ {
++ show_error("%s:PGRinit_recovery error",func);
++ PGRreplicate_exit(0);
++ }
++ if (PGRload_replication_id() != STATUS_OK)
++ {
++ show_error("%s:PGRload_replication_id error",func);
++ PGRreplicate_exit(0);
++ }
++
++ pgid = getpgid(0);
++ if ( PGR_Use_Replication_Log == true )
++ {
++#ifdef PRINT_DEBUG
++ show_debug("Use Replication Log. Start PGR_RLog_Main()");
++#endif
++ rlog_pid = PGR_RLog_Main();
++ if (rlog_pid < 0)
++ {
++ show_error("%s:PGR_RLog_Main failed",func);
++ PGRreplicate_exit(0);
++ }
++ }
++
++ pid = fork();
++ if (pid == (pid_t) -1)
++ {
++ show_error("%s:fork() failed. reason: %s",
func,strerror(errno));
++ exit(1);
++ }
++ if (pid == 0)
++ {
++ signal(SIGCHLD, SIG_DFL);
++ signal(SIGHUP, quick_exit);
++ signal(SIGINT, quick_exit);
++ signal(SIGQUIT, quick_exit);
++ signal(SIGTERM, quick_exit);
++ signal(SIGPIPE, SIG_IGN);
++ /*
++ * in child process,
++ * call recovery module
++ */
++ setpgid(0,pgid);
++
++ if (fork_wait_time > 0) {
++#ifdef PRINT_DEBUG
++ show_debug("recovery process: wait fork(): pid = %d",
getpid());
++#endif
++ sleep(fork_wait_time);
++ }
++
++ PGRrecovery_main();
++ }
++ else
++ {
++ /*
++ * call replicate module
++ */
++ Replicateion_Log->r_log_sock =-1;
++
++ if (fork_wait_time > 0) {
++#ifdef PRINT_DEBUG
++ show_debug("replicate process: wait fork(): pid =
%d", getpid());
++#endif
++ sleep(fork_wait_time);
++ }
++
++ replicate_main();
++ }
++
++ PGRreplicate_exit(0);
++ return STATUS_OK;
++}
++
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/pgreplicate.conf.sample
pgcluster-1.5.0rc7/src/pgcluster/pgrp/pgreplicate.conf.sample
+--- postgresql-8.1.2/src/pgcluster/pgrp/pgreplicate.conf.sample
1970-01-01 01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/pgreplicate.conf.sample
2006-02-09 17:55:02.000000000 +0100
+@@ -0,0 +1,84 @@
++#=============================================================
++# PGReplicate configuration file
++#-------------------------------------------------------------
++# file: pgreplicate.conf
++#-------------------------------------------------------------
++# This file controls:
++# o which hosts & port are cluster server
++# o which port use for replication request from cluster server
++#=============================================================
++#
++#-------------------------------------------------------------
++# A setup of Cluster DB(s)
++#
++# o Host_Name : The host name of Cluster DB.
++# -- please write a host name by FQDN.
++# -- do not write IP address.
++# o Port : The connection port with postmaster.
++# o Recovery_Port : The connection port at the time of
++# a recovery sequence .
++#-------------------------------------------------------------
++#<Cluster_Server_Info>
++# <Host_Name> master.postgres.jp </Host_Name>
++# <Port> 5432 </Port>
++# <Recovery_Port> 7001 </Recovery_Port>
++#</Cluster_Server_Info>
++#<Cluster_Server_Info>
++# <Host_Name> cluster2.postgres.jp </Host_Name>
++# <Port> 5432 </Port>
++# <Recovery_Port> 7001 </Recovery_Port>
++#</Cluster_Server_Info>
++#<Cluster_Server_Info>
++# <Host_Name> cluster3.postgres.jp </Host_Name>
++# <Port> 5432 </Port>
++# <Recovery_Port> 7001 </Recovery_Port>
++#</Cluster_Server_Info>
++#
++#-------------------------------------------------------------
++# A setup of Load Balance Server
++#
++# o Host_Name : The host name of a load balance server.
++# -- please write a host name by FQDN.
++# -- do not write IP address.
++# o Recovery_Port : The connection port at the time of
++# a recovery sequence .
++#-------------------------------------------------------------
++#<LoadBalance_Server_Info>
++# <Host_Name> loadbalancer.postgres.jp </Host_Name>
++# <Recovery_Port> 6001 </Recovery_Port>
++#</LoadBalance_Server_Info>
++#
++#------------------------------------------------------------
++# A setup of the upper replication server for cascade connection.
++#
++# o Host_Name : The host name of Cluster DB.
++# -- please write a host name by FQDN.
++# -- do not write IP address.
++# o Port : The connection port with postmaster.
++# o Recovery_Port : The connection port at the time of
++# a recovery sequence .
++#------------------------------------------------------------
++#<Replicate_Server_Info>
++# <Host_Name> upper_replicate.postgres.jp </Host_Name>
++# <Port> 8002 </Port>
++# <Recovery_Port> 8102 </Recovery_Port>
++#</Replicate_Server_Info>
++#
++#-------------------------------------------------------------
++# A setup of a replication server
++#
++# o Replicate_Port : connection for replication
++# o Recovery_Port : connection for recovery
++# o Response_mode : timing which returns a response
++# normal -- return result of DB which received the query
++# reliable -- return result after waiting for response of
++# all Cluster DBs.
++# o Use_Replication_Log : use replication log
++# [yes/no]. default : no
++# o RLOG_Port : connection for replication log
++#-------------------------------------------------------------
++<Replication_Port> 8001 </Replication_Port>
++<Recovery_Port> 8101 </Recovery_Port>
++<Response_Mode> normal </Response_Mode>
++<Use_Replication_Log> no </Use_Replication_Log>
++<RLOG_Port> 8301 </RLOG_Port>
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/pgreplicate.h
pgcluster-1.5.0rc7/src/pgcluster/pgrp/pgreplicate.h
+--- postgresql-8.1.2/src/pgcluster/pgrp/pgreplicate.h 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/pgreplicate.h 2006-02-09
17:46:34.000000000 +0100
+@@ -0,0 +1,414 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * pgreplicate.h
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#ifndef PGREPLICATE_H
++#define PGREPLICATE_H
++
++#define PGREPLICATE_VERSION "1.5.0rc7"
++
++#include "lib/dllist.h"
++#include "lib/stringinfo.h"
++#include "../libpgc/libpgc.h"
++
++/* cascade packet id */
++#define CMD_SYS_CASCADE 'C'
++#define CMD_STS_TO_UPPER 'U'
++#define CMD_STS_TO_LOWER 'L'
++#define CMD_TYPE_ADD 'A'
++#define CMD_TYPE_DELTE 'D'
++#define CMD_TYPE_UPDATE_ALL 'A'
++
++/* log packet id */
++#define CMD_SYS_LOG 'L'
++#define CMD_STS_DELETE_QUERY 'q'
++#define CMD_STS_DELETE_TRANSACTION 't'
++#define CMD_STS_UPDATE_QUERY 'r'
++#define CMD_STS_UPDATE_TRANSACTION 'u'
++
++#define INIT_TRANSACTION_TBL_NUM (12)
++#define MAX_HOST_NAME_LEN (128)
++#define MAX_DB_SERVER (32)
++#define MAX_CONNECTIONS (128)
++#define PGR_MAX_TICKETS (0x7FFFFFFF)
++#define PGR_MAX_QUERY_ID (0x7FFFFFFF)
++#define PGR_CONNECT_RETRY_TIME (3)
++#define PGR_EXEC_RETRY_TIME (5)
++#define DB_TBL_FREE (0)
++#define DB_TBL_INIT (1)
++#define DB_TBL_USE (2)
++#define DB_TBL_ERROR (-1)
++#define DB_TBL_TOP (10)
++#define DB_TBL_END (11)
++#define RECOVERY_HEADER_MTYPE (1)
++#define QUERY_LOG_MTYPE (2)
++#define PGREPLICATE_CONF_FILE "pgreplicate.conf"
++#define PGREPLICATE_LOG_FILE "pgreplicate.log"
++#define PGREPLICATE_STATUS_FILE "pgreplicate.sts"
++#define PGREPLICATE_PID_FILE "pgreplicate.pid"
++#define PGREPLICATE_RID_FILE "pgreplicate.rid"
++#define RECOVERY_QUEUE_FILE ".pgr_recovery."
++#define CLUSTER_SERVER_TAG "Cluster_Server_Info"
++#define LOAD_BALANCE_SERVER_TAG "LoadBalance_Server_Info"
++#define REPLICATE_PORT_TAG "Replication_Port"
++#define RECOVERY_PORT_TAG "Recovery_Port"
++#define LIFECHECK_PORT_TAG "LifeCheck_Port"
++#define RLOG_PORT_TAG "RLOG_Port"
++#define RESPONSE_MODE_TAG "Response_Mode"
++#define RESPONSE_MODE_FAST "fast"
++#define RESPONSE_MODE_NORMAL "normal"
++#define RESPONSE_MODE_RELIABLE "reliable"
++#define USE_REPLICATION_LOG_TAG "Use_Replication_Log"
++#define RESERVED_CONNECTIONS_TAG "Reserved_Connections"
++/* semapho numner of recovery queue */
++#define SEM_NUM_OF_RECOVERY (1)
++#define SEM_NUM_OF_RECOVERY_QUEUE (2)
++/* semapho numner of lock tickets */
++#define SEM_NUM_OF_LOCK (1)
++#define STATUS_LOCK_CONFLICT (2)
++#define STATUS_DEADLOCK_DETECT (3)
++#define STATUS_ABORTED (4)
++#define STATUS_NOT_YET_REPLICATE (5)
++#define STATUS_ALREADY_REPLICATED (6)
++#define STATUS_SKIP_REPLICATE (7)
++#define PGR_NOWAIT_ANSWER (0)
++#define PGR_WAIT_ANSWER (1)
++#define LOOP_CONTINUE (0)
++#define LOOP_END (1)
++#define LOWER_CASCADE (1)
++#define UPPER_CASCADE (2)
++#define ALL_CASCADE (3)
++#define NOTICE_SYSTEM_CALL_TYPE (10)
++#define RECOVERY_QUERY_TYPE (20)
++
++#define PGR_TIME_OUT (60)
++#define PGR_SEND_RETRY_CNT (1000)
++#define PGR_SEND_WAIT_MSEC (50)
++#define PGR_RECV_RETRY_CNT (1000)
++#define PGR_RECV_WAIT_MSEC (50)
++#define PGR_SEM_UNLOCK_WAIT_MSEC (100)
++#define PGR_SEM_LOCK_WAIT_MSEC (500)
++#define PGR_RECV_TIMEOUT (10)
++#define PGR_SEND_TIMEOUT (600)
++#define PGR_LOCK_WAIT_MSEC (50)
++#define PGR_LOCK_TIMEOUT (60)
++#define PGR_RECOVERY_RETRY_CNT (6000)
++#define PGR_RECOVERY_WAIT_MSEC (500)
++#define PGR_CHECK_POINT (300)
++
++#define PGR_RECOVERY_1ST_STAGE (1)
++#define PGR_RECOVERY_2ND_STAGE (2)
++
++#define IDLE_MODE (0)
++#define BUSY_MODE (1)
++
++/*
++ * connection table for transaction query
++ */
++typedef struct {
++ int useFlag;
++ int lock;
++ int transaction_count;
++ unsigned short port;
++ unsigned short pid;
++ unsigned int hostIP;
++ unsigned int srcHostIP;
++ char host[HOSTNAME_MAX_LENGTH];
++ char srcHost[HOSTNAME_MAX_LENGTH];
++ char dbName[DBNAME_MAX_LENGTH];
++ bool in_transaction;
++ PGconn * conn;
++}TransactionTbl;
++
++/*
++ * cluster server table
++ */
++typedef struct {
++ int useFlag;
++ char hostName[HOSTNAME_MAX_LENGTH];
++ char resolvedName[21];
++ int port;
++ int recoveryPort;
++ int hostNum;
++ int transaction_count;
++}HostTbl;
++
++
++typedef struct {
++ FILE * queue_fp;
++ int current_queue_no;
++} RecoveryQueueInf;
++
++
++/*
++ * host table for recovery request
++ */
++typedef struct {
++ char hostName[HOSTNAME_MAX_LENGTH];
++ int port;
++ int recoveryPort;
++ int sock;
++ int recovery_sock;
++} RecoveryTbl;
++
++/*
++ * status table for recovery
++ */
++typedef struct {
++ int useFlag;
++ int transaction_count;
++ int recovery_status;
++ unsigned int replication_id;
++ HostTbl target_host;
++ int read_queue_no;
++ int write_queue_no;
++ int check_point;
++} RecoveryStatusInf;
++
++typedef struct {
++ long mtype;
++ long query_mtype;
++ char mdata[1];
++} RecoveryQueueHeader;
++
++typedef struct {
++ long mtype;
++ unsigned int replicationId;
++ char mdata[1];
++} RecoveryQueueQuery;
++
++typedef struct {
++ unsigned int entry_ticket;
++ unsigned int lock_wait_queue_length;
++ int overflow;
++} LockWaitInf;
++
++typedef struct {
++ int response_mode;
++ int current_cluster;
++} ResponseInf;
++
++typedef struct {
++ ReplicateHeader * header;
++ char * query;
++ char * next;
++ char * last;
++} QueryLogType;
++
++typedef struct {
++ ReplicateServerInfo * top;
++ ReplicateServerInfo * end;
++ ReplicateServerInfo * lower;
++ ReplicateServerInfo * upper;
++ ReplicateServerInfo * myself;
++ int useFlag;
++} CascadeInf;
++
++typedef struct {
++ union
++ {
++ int useFlag;
++ int commit_log_num;
++ } inf;
++ ReplicateHeader header;
++} CommitLogInf;
++
++typedef struct {
++ int useFlag;
++ char * RLog_Sock_Path;
++ uint16_t RLog_Port_Number;
++ int r_log_sock;
++ ReplicateHeader * header;
++ char * query;
++} ReplicationLogInf;
++
++typedef struct {
++ char hostName[HOSTNAME_MAX_LENGTH];
++ uint16_t port;
++ uint16_t pid;
++ uint32_t request_id;
++} QueryLogID;
++
++typedef struct {
++ QueryLogID query_log_id;
++ char * last;
++ char * next;
++} ConfirmQueryList;
++
++typedef struct {
++ ReplicateHeader * header;
++ char * query;
++ int dest;
++ HostTbl * host_ptr;
++ int current_cluster;
++ int transaction_count;
++ TransactionTbl *transaction_tbl;
++} ThreadArgInf;
++
++
++/* replication server data */
++extern uint16_t Port_Number;
++extern uint16_t LifeCheck_Port_Number;
++extern uint16_t Recovery_Port_Number;
++extern int Reserved_Connections;
++extern bool PGR_Parse_Session_Started;
++
++/* global tables */
++extern HostTbl * Host_Tbl_Begin;
++extern Dllist * Transaction_Tbl_Begin;
++extern TransactionTbl * Transaction_Tbl_End;
++extern RecoveryTbl * LoadBalanceTbl;
++extern RecoveryStatusInf * Recovery_Status_Inf;
++extern LockWaitInf * Lock_Wait_Tbl;
++extern ReplicateHeader * PGR_Log_Header;
++extern ReplicateServerInfo * Cascade_Tbl;
++extern CascadeInf * Cascade_Inf;
++extern CommitLogInf * Commit_Log_Tbl;
++extern QueryLogType * Query_Log_Top;
++extern QueryLogType * Query_Log_End;
++extern ReplicationLogInf * Replicateion_Log;
++extern int RecoveryShmid;
++extern int ReplicateSerializationShmid;
++extern int RecoveryMsgShmid;
++extern int *RecoveryMsgid;
++extern int HostTblShmid;
++extern int LockWaitTblShmid;
++extern int CascadeTblShmid;
++extern int CascadeInfShmid;
++extern int CommitLogShmid;
++extern int MaxBackends;
++extern char * PGR_Result;
++extern int SemID;
++extern int RecoverySemID;
++extern int CascadeSemID;
++extern int LockSemID;
++extern int VacuumSemID;
++extern char * PGR_Data_Path;
++extern char * PGR_Write_Path;
++extern FILE * LogFp;
++extern FILE * StatusFp;
++extern FILE * RidFp;
++extern int Log_Print;
++extern int Debug_Print;
++extern char * Function;
++extern int IS_SESSION_AUTHORIZATION;
++extern ResponseInf * PGR_Response_Inf;
++extern bool StartReplication[MAX_DB_SERVER];
++extern bool PGR_Cascade;
++extern bool PGR_Use_Replication_Log;
++extern bool PGR_AutoCommit;
++extern unsigned int * PGR_ReplicateSerializationID;
++extern unsigned int * PGR_Send_Query_ID;
++extern unsigned int PGR_Query_ID;
++extern bool exit_processing;
++extern RecoveryQueueInf RecoveryQueue;
++extern int pgreplicate_pid;
++
++extern int ReplicateSock;
++
++/* smart shutdown */
++extern int Idle_Flag;
++extern bool Exit_Request;
++
++/*
++ * external prototype in main.c
++ */
++extern void child_wait(SIGNAL_ARGS);
++
++/*
++ * external prototype in conf.c
++ */
++extern int PGRget_Conf_Data(char * path);
++
++/*
++ * external prototype in replicate.c
++ */
++extern int PGRset_replication_id(uint32_t rid);
++extern bool PGRis_same_host(char * host1, unsigned short port1 , char *
host2, unsigned short port2);
++extern HostTbl * PGRadd_HostTbl(HostTbl * conf_data, int useFlag);
++extern HostTbl * PGRget_master(void);
++extern void PGRset_recovery_status(int status);
++extern int PGRget_recovery_status(void);
++extern int PGRcheck_recovered_host(void);
++extern int PGRset_recovered_host(HostTbl * target,int useFlag);
++extern int PGRinit_recovery(void);
++extern void set_exit_processing(int signo);
++extern void PGRreplicate_exit(int exit_status);
++extern int PGRsend_replicate_packet_to_server( HostTbl * host_ptr,
ReplicateHeader * header, char *query , char * result,unsigned int
replicationId, bool recovery);
++extern int PGRreplicate_packet_send_each_server( HostTbl * ptr, bool
return_response, ReplicateHeader * header, char * query,int dest);
++extern HostTbl * PGRget_HostTbl(char * hostName,int port);
++extern int PGRset_queue(ReplicateHeader * header,char * query);
++extern int PGRset_host_status(HostTbl * host_ptr,int status);
++extern void PGRclear_connections(void);
++extern void PGRsem_unlock( int semid, short sem_num );
++extern void PGRsem_lock( int semid, short sem_num );
++extern int PGRdo_replicate(int sock,ReplicateHeader *header, char * query);
++extern int PGRreturn_result(int dest, char * result, int wait);
++extern int PGRreplicate_packet_send( ReplicateHeader * header, char *
query,int dest,int recovery_status);
++extern char * PGRread_packet(int sock, ReplicateHeader *header);
++extern void PGRnotice_replication_server(char * hostName, unsigned short
portNumber,unsigned short recoveryPortNumber, unsigned short
lifecheckPortNumber, char * userName);
++extern char * PGRread_query(int sock, ReplicateHeader *header);
++extern int PGRwait_transaction_count_clear(HostTbl *host);
++extern int PGRsync_oid(ReplicateHeader *header);
++extern unsigned int PGRget_next_query_id(void);
++extern int PGRinit_transaction_table(void);
++extern int replicate_packet_send_internal(ReplicateHeader * header, char *
query,int dest,int recovery_status,bool isHeldLock);
++extern int PGRsync_oid(ReplicateHeader *header);
++extern int PGRload_replication_id(void);
++
++/*
++ * external prototype in recovery.c
++ */
++extern int PGRsend_load_balance_packet(RecoveryPacket * packet);
++extern void PGRrecovery_main(void);
++extern FILE * PGRget_recovery_queue_file_for_write(void);
++extern FILE * PGRget_recovery_queue_file_for_read(int next);
++
++/*
++ * external prototype in rlog.c
++ */
++extern int PGRwrite_rlog(ReplicateHeader * header, char * query);
++extern ReplicateHeader * PGRget_requested_query(ReplicateHeader * header);
++extern void PGRreconfirm_commit(int sock, ReplicateHeader * header);
++extern void PGRset_rlog(ReplicateHeader * header, char * query);
++extern void PGRunset_rlog(ReplicateHeader * header, char * query);
++extern int PGRresend_rlog_to_db(void);
++extern void PGRreconfirm_query(int sock, ReplicateHeader * header);
++extern pid_t PGR_RLog_Main(void);
++extern int PGRcreate_send_rlog_socket(void);
++extern int PGRsend_rlog_packet(int sock,ReplicateHeader * header, const
char * query_string);
++extern int PGRrecv_rlog_result(int sock,void * result, int size);
++extern int PGRsend_rlog_to_local(ReplicateHeader * header,char * query);
++extern int PGRget_rlog_header(ReplicateHeader * header);
++
++/*
++ * external prototype in cascade.c
++ */
++extern void PGRinit_cascade_child() ;
++extern int PGRstartup_cascade(void);
++extern int PGRsend_lower_cascade(ReplicateHeader * header, char * query);
++extern int PGRsend_upper_cascade(ReplicateHeader * header, char * query);
++extern int PGRwait_answer_cascade(int sock);
++extern ReplicateServerInfo * PGRget_lower_cascade(void);
++extern ReplicateServerInfo * PGRget_upper_cascade(void);
++extern void PGRset_cascade_server_status(ReplicateServerInfo * cascade, int
status);
++extern ReplicateServerInfo * PGRrecv_cascade_answer(ReplicateServerInfo *
cascade,ReplicateHeader * header);
++extern int PGRsend_cascade(int sock , ReplicateHeader * header, char *
query);
++extern int PGRcascade_main(int sock, ReplicateHeader * header, char *
query);
++extern int PGRwait_notice_rlog_done(void);
++extern int PGRsend_notice_rlog_done(int sock);
++extern int PGRsend_notice_quit();
++
++/*
++ * external prototype in pqformat.c
++ */
++extern const char * pq_getmsgstring(StringInfo msg);
++extern unsigned int pq_getmsgint(StringInfo msg, int b);
++extern void pq_copymsgbytes(StringInfo msg, char *buf, int datalen);
++extern const char * pq_getmsgbytes(StringInfo msg, int datalen);
++extern int pq_getmsgbyte(StringInfo msg);
++
++#endif /* PGREPLICATE_H */
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/pqformat.c
pgcluster-1.5.0rc7/src/pgcluster/pgrp/pqformat.c
+--- postgresql-8.1.2/src/pgcluster/pgrp/pqformat.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/pqformat.c 2006-01-31
17:34:54.000000000 +0100
+@@ -0,0 +1,176 @@
++/*-------------------------------------------------------------------------
++ * pqformat.c
++ * Routines for formatting and parsing frontend/backend messages
++ *
++ * These modules copyed from src/backend/libpq/pgformat.c.
++ * Original modules have some shared modules and macro,
++ * then it is difficult link to replication server directory.
++ * Therefore, these modules were custamized.
++ * (removed shared module and macro)
++ *
++ * Original source code is under the following copyright
++ *
++ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
++ * Portions Copyright (c) 1994, Regents of the University of California
++ *
++ *-------------------------------------------------------------------------
++ */
++/*
++ * INTERFACE ROUTINES
++ * Message parsing after input:
++ * pq_getmsgbyte - get a raw byte from a message buffer
++ * pq_getmsgint - get a binary integer from a message buffer
++ * pq_getmsgbytes - get raw data from a message buffer
++ * pq_copymsgbytes - copy raw data from a message buffer
++ * pq_getmsgstring - get a null-terminated text string (with
conversion)
++ */
++
++/* --------------------------------
++ * pq_getmsgstring - get a null-terminated text string (with
conversion)
++ *
++ * May return a pointer directly into the message buffer, or a
pointer
++ * to a palloc'd conversion result.
++ * --------------------------------
++ */
++
++#include "postgres.h"
++
++#include <errno.h>
++#include <sys/param.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++#ifdef HAVE_ENDIAN_H
++#include <endian.h>
++#endif
++
++#if 0
++#include "libpq/libpq.h"
++#include "libpq/pqformat.h"
++#endif
++#include "mb/pg_wchar.h"
++
++#include "libpq-fe.h"
++#include "libpq-int.h"
++#include "fe-auth.h"
++#include "replicate_com.h"
++#include "pgreplicate.h"
++
++const char *
++pq_getmsgstring(StringInfo msg)
++{
++ char *str;
++ int slen;
++
++ if (msg == NULL)
++ {
++ return NULL;
++ }
++ str = &msg->data[msg->cursor];
++ /*
++ * It's safe to use strlen() here because a StringInfo is guaranteed
to
++ * have a trailing null byte. But check we found a null inside the
++ * message.
++ */
++ slen = strlen(str);
++ if (msg->cursor + slen >= msg->len)
++ {
++ show_error("invalid string in message");
++ return NULL;
++ }
++ msg->cursor += slen + 1;
++
++ return str;
++}
++
++
++/* --------------------------------
++ * pq_getmsgint - get a binary integer from a message buffer
++ *
++ * Values are treated as unsigned.
++ * --------------------------------
++ */
++unsigned int
++pq_getmsgint(StringInfo msg, int b)
++{
++ unsigned int result;
++ unsigned char n8;
++ uint16 n16;
++ uint32 n32;
++
++ switch (b)
++ {
++ case 1:
++ pq_copymsgbytes(msg, (char *) &n8, 1);
++ result = n8;
++ break;
++ case 2:
++ pq_copymsgbytes(msg, (char *) &n16, 2);
++ result = ntohs(n16);
++ break;
++ case 4:
++ pq_copymsgbytes(msg, (char *) &n32, 4);
++ result = ntohl(n32);
++ break;
++ default:
++ show_error("unsupported integer size %d", b);
++ result = 0; /* keep compiler
quiet */
++ break;
++ }
++ return result;
++}
++
++/* --------------------------------
++ * pq_copymsgbytes - copy raw data from a message buffer
++ *
++ * Same as above, except data is copied to caller's buffer.
++ * --------------------------------
++ */
++void
++pq_copymsgbytes(StringInfo msg, char *buf, int datalen)
++{
++ if (datalen < 0 || datalen > (msg->len - msg->cursor))
++ {
++ show_error("insufficient data left in message");
++ return;
++ }
++ memcpy(buf, &msg->data[msg->cursor], datalen);
++ msg->cursor += datalen;
++}
++
++
++/* --------------------------------
++ * pq_getmsgbytes - get raw data from a message buffer
++ *
++ * Returns a pointer directly into the message buffer; note this
++ * may not have any particular alignment.
++ * --------------------------------
++ */
++const char *
++pq_getmsgbytes(StringInfo msg, int datalen)
++{
++ const char *result;
++
++ if (datalen < 0 || datalen > (msg->len - msg->cursor))
++ {
++ show_error("insufficient data left in message");
++ return NULL;
++ }
++ result = &msg->data[msg->cursor];
++ msg->cursor += datalen;
++ return result;
++}
++
++/* --------------------------------
++ * pq_getmsgbyte - get a raw byte from a message buffer
++ * --------------------------------
++ */
++int
++pq_getmsgbyte(StringInfo msg)
++{
++ if (msg->cursor >= msg->len)
++ {
++ show_error("no data left in message");
++ return 0;
++ }
++ return (unsigned char) msg->data[msg->cursor++];
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/recovery.c
pgcluster-1.5.0rc7/src/pgcluster/pgrp/recovery.c
+--- postgresql-8.1.2/src/pgcluster/pgrp/recovery.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/recovery.c 2006-01-31
23:44:12.000000000 +0100
+@@ -0,0 +1,1050 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * recovery.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at pgreplicate for the recovery.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#include "postgres.h"
++
++#include <stdio.h>
++#include <unistd.h>
++#include <signal.h>
++#include <sys/wait.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <sys/msg.h>
++#include <netdb.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <time.h>
++#include <arpa/inet.h>
++#include <sys/param.h>
++#include <sys/file.h>
++
++#ifdef HAVE_SYS_SELECT_H
++#include <sys/select.h>
++#endif
++
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#include "miscadmin.h"
++#include "nodes/nodes.h"
++
++#include "libpq-fe.h"
++#include "libpq/libpq-fs.h"
++#include "libpq-int.h"
++#include "fe-auth.h"
++
++#include "access/xact.h"
++#include "replicate_com.h"
++#include "pgreplicate.h"
++
++
++#ifdef WIN32
++#include "win32.h"
++#else
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++#include <arpa/inet.h>
++#endif
++
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#ifdef MULTIBYTE
++#include "mb/pg_wchar.h"
++#endif
++#include "pgreplicate.h"
++
++
++/*--------------------------------------
++ * GLOBAL VARIABLE DECLARATION
++ *--------------------------------------
++ */
++RecoveryPacket MasterPacketData;
++RecoveryTbl Master;
++RecoveryTbl Target;
++
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++static int read_packet(int sock,RecoveryPacket * packet);
++static int read_packet_from_master( RecoveryTbl * host, RecoveryPacket *
packet );
++static int send_recovery_packet(int sock, RecoveryPacket * packet);
++static int send_packet(RecoveryTbl * host, RecoveryPacket * packet );
++static void start_recovery_prepare(void);
++static void reset_recovery_prepare(void);
++static void start_recovery(void);
++static void finish_recovery(void);
++static bool first_setup_recovery(int * sock, RecoveryPacket * packet);
++static bool second_setup_recovery (RecoveryPacket * packet);
++static void pgrecovery_loop(int fd);
++static int PGRsend_queue(RecoveryTbl * master, RecoveryTbl * target);
++static int send_vacuum(HostTbl *host, char * userName, int stage);
++
++#ifdef PRINT_DEBUG
++static void show_recovery_packet(RecoveryPacket * packet);
++#endif
++
++int PGRsend_load_balance_packet(RecoveryPacket * packet);
++void PGRrecovery_main(void);
++
++/*-----------------------------------------------------------
++ * SYMBOL
++ * read_packet()
++ * NOTES
++ * Read recovery packet data
++ * ARGS
++ * int sock : socket
++ * RecoveryPacket * packet : read packet buffer
++ * RETURN
++ * -1 : error
++ * >0 : read size
++ *-----------------------------------------------------------
++ */
++static int
++read_packet(int sock,RecoveryPacket * packet)
++{
++#ifdef PRINT_DEBUG
++ char * func = "read_packet()";
++#endif
++ int r = 0;
++ char * read_ptr = NULL;
++ int read_size = 0;
++ int packet_size = 0;
++
++ if (packet == NULL)
++ {
++ return -1;
++ }
++ read_ptr = (char*)packet;
++ packet_size = sizeof(RecoveryPacket);
++ for (;;)
++ {
++ r = recv(sock,read_ptr + read_size ,packet_size - read_size,
MSG_WAITALL);
++ if (r < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++ else
++ {
++ show_error("%s:recv failed: %d(%s)",func,
errno, strerror(errno));
++ return -1;
++ }
++ }
++ else if (r > 0)
++ {
++ read_size += r;
++ if (read_size == packet_size)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:receive packet",func);
++ show_recovery_packet(packet);
++#endif
++ return read_size;
++ }
++ }
++ else /* r == 0 */
++ {
++ show_error("%s:unexpected EOF", func);
++ return -1;
++ }
++ }
++ return -1;
++}
++
++static int
++read_packet_from_master( RecoveryTbl * host, RecoveryPacket * packet )
++{
++ int read_size = 0;
++ int rtn;
++ fd_set rmask;
++ struct timeval timeout;
++
++ for(;;)
++ {
++ timeout.tv_sec = PGR_RECV_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(host->recovery_sock,&rmask);
++ rtn = select(host->recovery_sock+1, &rmask, (fd_set *)NULL,
(fd_set *)NULL, &timeout);
++
++ if (rtn == 0) /* timeout */
++ {
++ return -1;
++ }
++
++ if (rtn && FD_ISSET(host->recovery_sock, &rmask))
++ {
++ read_size = read_packet(host->recovery_sock, packet);
++ return read_size;
++ }
++ }
++}
++
++static int
++send_recovery_packet(int sock, RecoveryPacket * packet)
++{
++ char *func = "send_recovery_packet";
++ char * send_ptr;
++ int send_size= 0;
++ int buf_size = 0;
++ int s;
++
++ send_ptr = (char *)packet;
++ buf_size = sizeof(RecoveryPacket);
++
++ for (;;)
++ {
++ s = send(sock, send_ptr + send_size,buf_size - send_size ,0);
++ if (s < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++
++ show_error("%s:send error: %d(%s)", func, errno,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ else if (s == 0)
++ {
++ show_error("%s:unexpected EOF", func);
++ return STATUS_ERROR;
++ }
++
++ send_size += s;
++ if (send_size == buf_size)
++ return STATUS_OK;
++ }
++}
++
++static int
++send_packet(RecoveryTbl * host, RecoveryPacket * packet )
++{
++ char * func = "send_packet()";
++ int count = 0;
++
++ if (host->recovery_sock == -1)
++ {
++ while(PGR_Create_Socket_Connect(&(host->recovery_sock),
host->hostName , host->recoveryPort) != STATUS_OK )
++ {
++ if (count > MAX_RETRY_TIMES )
++ {
++ show_error("%s:host[%s]
port[%d]PGR_Create_Socket_Connect failed",func,host->hostName,
host->recoveryPort);
++ return STATUS_ERROR;
++ }
++ count ++;
++ }
++ }
++ count = 0;
++ while (send_recovery_packet(host->recovery_sock,packet) != STATUS_OK)
++ {
++ close(host->recovery_sock);
++ host->recovery_sock = -1;
++ PGR_Create_Socket_Connect(&(host->recovery_sock),
host->hostName , host->recoveryPort);
++#ifdef PRINT_DEBUG
++ show_debug("%s:PGR_Create_Socket_Connectsock[%d] host[%s]
port[%d]",
++
func,host->recovery_sock,host->hostName,host->recoveryPort);
++#endif
++ if (count > PGR_CONNECT_RETRY_TIME )
++ {
++
++ show_error("%s:send failed and
PGR_Create_Socket_Connect failed",func);
++ return STATUS_ERROR;
++ }
++ count ++;
++ }
++ return STATUS_OK;
++}
++
++static void
++start_recovery_prepare(void)
++{
++ PGRset_recovery_status (RECOVERY_PREPARE_START);
++}
++
++static void
++reset_recovery_prepare(void)
++{
++ PGRset_recovery_status (RECOVERY_INIT);
++}
++
++static void
++start_recovery(void)
++{
++ PGRset_recovery_status (RECOVERY_START);
++}
++
++static void
++finish_recovery(void)
++{
++ PGRset_recovery_status (RECOVERY_INIT);
++}
++
++int
++PGRsend_load_balance_packet(RecoveryPacket * packet)
++{
++ char * func = "PGRsend_load_balance_packet()";
++ RecoveryTbl * lbp;
++ int status;
++
++ lbp = LoadBalanceTbl;
++ if (lbp == (RecoveryTbl *)NULL)
++ {
++ show_error("%s:recovery table is NULL",func);
++ return STATUS_ERROR;
++ }
++ while (lbp->hostName[0] != 0)
++ {
++ if (lbp->recovery_sock != -1)
++ {
++ close(lbp->recovery_sock);
++ lbp->recovery_sock = -1;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:host[%s]
port[%d]",func,lbp->hostName,lbp->recoveryPort);
++#endif
++ status = send_packet(lbp,packet);
++ if (lbp->recovery_sock != -1)
++ {
++ close(lbp->recovery_sock);
++ lbp->recovery_sock = -1;
++ }
++ lbp ++;
++ }
++ return STATUS_OK;
++}
++
++static int
++send_vacuum(HostTbl *host, char * userName, int stage)
++{
++ int rtn = STATUS_OK;
++ ReplicateHeader header;
++ char * query = NULL;
++
++ if (stage == PGR_RECOVERY_1ST_STAGE)
++ {
++ query = strdup("VACUUM");
++ }
++ else
++ {
++ query = strdup("VACUUM FULL");
++ }
++ memset(&header,0,sizeof(header));
++ header.query_size = strlen(query) + 1;
++ strncpy(header.dbName,"template1",sizeof(header.dbName));
++ strncpy(header.userName,userName,sizeof(header.userName));
++ header.cmdSys = CMD_SYS_REPLICATE;
++ header.cmdSts = CMD_STS_QUERY;
++ header.cmdType = CMD_TYPE_VACUUM;
++ header.pid = getpid();
++ header.query_id = getpid();
++ header.isAutoCommit=1;
++ rtn =
PGRsend_replicate_packet_to_server(host,&header,query,PGR_Result,0, true);
++ if (query !=NULL)
++ free(query);
++ return rtn;
++}
++
++static bool
++first_setup_recovery(int * sock, RecoveryPacket * packet)
++{
++ char * func = "first_setup_recovery()";
++ int status;
++ HostTbl * master = (HostTbl *)NULL;
++ bool loop_end = false;
++ HostTbl host_tbl;
++ char * userName = NULL;
++
++ memcpy(Target.hostName,packet->hostName,sizeof(Target.hostName));
++ Target.port = ntohs(packet->port);
++ Target.recoveryPort = ntohs(packet->recoveryPort);
++ Target.sock = *sock;
++ Target.recovery_sock = *sock;
++#ifdef PRINT_DEBUG
++ show_debug("%s:1st setup target %s",func,Target.hostName);
++ show_debug("%s:1st setup port %d",func,Target.port);
++#endif
++ /*
++ * check another recovery process
++ */
++ if (PGRget_recovery_status() != RECOVERY_INIT)
++ {
++ /*
++ * recovery process is already running
++ */
++#ifdef PRINT_DEBUG
++ show_debug("%s:already recovery job runing",func);
++#endif
++ memset(packet,0,sizeof(packet));
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_OCCUPIED) ;
++ status = send_packet(&Target,packet);
++ loop_end = true;
++ return loop_end;
++ }
++ /*
++ * add recovery target to host table
++ */
++#ifdef PRINT_DEBUG
++ show_debug("%s:add recovery target to host table",func);
++#endif
++ memcpy(host_tbl.hostName,packet->hostName,sizeof(host_tbl.hostName));
++ host_tbl.port = ntohs(packet->port);
++ PGRset_recovered_host(&host_tbl,DB_TBL_INIT);
++ PGRadd_HostTbl(&host_tbl,DB_TBL_INIT);
++ /*
++ * send prepare recovery to load balancer
++ */
++ PGRsend_load_balance_packet(packet);
++ userName = strdup(packet->userName);
++
++ /*
++ * set RECOVERY_PGDATA_REQ packet data
++ */
++#ifdef PRINT_DEBUG
++ show_debug("%s:set RECOVERY_PGDATA_REQ packet data",func);
++#endif
++ memset(packet,0,sizeof(RecoveryPacket));
++ PGRset_recovery_packet_no(packet, RECOVERY_PGDATA_REQ );
++
++retry_connect_master:
++ master = PGRget_master();
++ if (master == (HostTbl *)NULL)
++ {
++ /*
++ * connection error , master may be down
++ */
++ show_error("%s:get master info error , master may be
down",func);
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_TARGET_ONLY);
++ status = send_packet(&Target, packet);
++ reset_recovery_prepare();
++ loop_end = true;
++ if (userName != NULL)
++ free(userName);
++ return loop_end;
++ }
++ /* send vauum command to master server */
++ status = send_vacuum(master, userName, PGR_RECOVERY_1ST_STAGE );
++ if (status != STATUS_OK)
++ {
++ PGRset_host_status(master, DB_TBL_ERROR);
++ goto retry_connect_master;
++ }
++
++ memcpy(Master.hostName,master->hostName,sizeof(Master.hostName));
++ Master.sock = -1;
++ Master.recovery_sock = -1;
++ Master.port = master->port;
++ Master.recoveryPort = master->recoveryPort;
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:send packet to master %s recoveryPort %d",func,
Master.hostName, Master.recoveryPort);
++#endif
++ status = send_packet(&Master, packet);
++ if (status != STATUS_OK)
++ {
++ /*
++ * connection error , master may be down
++ */
++ show_error("%s:connection error , master may be down",func);
++ PGRset_host_status(master,DB_TBL_ERROR);
++ goto retry_connect_master ;
++ }
++
++ /*
++ * start prepare of recovery
++ * set recovery status to "prepare start"
++ * start transaction count up
++ */
++ start_recovery_prepare();
++ /*
++ * wait answer from master server
++ */
++#ifdef PRINT_DEBUG
++ show_debug("%s:wait answer from master server",func);
++#endif
++ memset(packet,0,sizeof(RecoveryPacket));
++ read_packet_from_master(&Master, packet);
++#ifdef PRINT_DEBUG
++ show_debug("%s:get answer from
master:no[%d]",func,ntohs(packet->packet_no));
++#endif
++ if (ntohs(packet->packet_no) == RECOVERY_PGDATA_ANS)
++ {
++ /*
++ * send a packet to load balancer that is stopped master's
++ * load balancing until all recovery process is finished
++ */
++ PGRsend_load_balance_packet(packet);
++ memcpy((char
*)&MasterPacketData,packet,sizeof(RecoveryPacket));
++
++ /*
++ * prepare answer from master DB
++ */
++ PGRset_recovery_packet_no(packet, RECOVERY_PREPARE_ANS );
++
memcpy(packet->hostName,Master.hostName,sizeof(packet->hostName));
++ status = send_packet(&Target, packet);
++ if (status != STATUS_OK)
++ {
++ show_error("%s:no[%d] send_packet to target
error",func,ntohs(packet->packet_no));
++ PGRset_recovery_packet_no(packet,
RECOVERY_ERROR_TARGET_ONLY);
++ status = send_packet(&Master,packet);
++ reset_recovery_prepare();
++ loop_end = true;
++ }
++ }
++ if (userName != NULL)
++ free(userName);
++
++
++ return loop_end;
++}
++
++static bool
++second_setup_recovery (RecoveryPacket * packet)
++{
++ char * func = "second_setup_recovery()";
++ HostTbl * master = (HostTbl *)NULL;
++ int status;
++ bool loop_end = false;
++ char * userName = NULL;
++
++ start_recovery();
++
++ /* send vauum command to master server */
++ while ((master = PGRget_master()) != NULL)
++ {
++ userName = strdup(packet->userName);
++ status = send_vacuum(master, userName, PGR_RECOVERY_2ND_STAGE
);
++ if (status != STATUS_OK)
++ {
++ PGRset_host_status(master, DB_TBL_ERROR);
++ if (userName != NULL)
++ {
++ free(userName);
++ userName = NULL;
++ }
++ continue;
++ }
++ break;
++ }
++
++ if (master == NULL)
++ {
++ show_error("%s:vacuum error , master may be down",func);
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_TARGET_ONLY);
++ status = send_packet(&Target,packet);
++ status = send_packet(&Master,packet);
++ reset_recovery_prepare();
++
++ return true;
++ }
++
++ /*
++ * wait until all started transactions are going to finish
++ */
++ status = PGRwait_transaction_count_clear(master);
++ if (status != STATUS_OK && PGRget_recovery_status() == RECOVERY_START)
++ {
++ show_error("%s:transaction is too busy, please try again
after",func);
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_TARGET_ONLY);
++ status = send_packet(&Target,packet);
++ status = send_packet(&Master,packet);
++ reset_recovery_prepare();
++ return true;
++ }
++
++ if (PGRget_recovery_status() != RECOVERY_START) {
++ show_error("%s:queue set failed. stop to recovery",func);
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_CONNECTION);
++ status = send_packet(&Target,packet);
++ status = send_packet(&Master,packet);
++ reset_recovery_prepare();
++ if (userName != NULL)
++ free(userName);
++ return true;
++ }
++
++ /*
++ * then, send fsync request to master DB
++ */
++ PGRset_recovery_packet_no(packet, RECOVERY_FSYNC_REQ );
++ status = send_packet(&Master,packet);
++ if (status != STATUS_OK)
++ {
++ /*
++ * connection error , master may be down
++ */
++ show_error("%s:connection error , master may be down",func);
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_CONNECTION);
++ status = send_packet(&Target,packet);
++ status = send_packet(&Master,packet);
++ reset_recovery_prepare();
++ if (userName != NULL)
++ free(userName);
++ return true;
++ }
++
++ if (PGRget_recovery_status() != RECOVERY_START) {
++ show_error("%s:queue set failed. stop to recovery",func);
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_CONNECTION);
++ status = send_packet(&Target,packet);
++ status = send_packet(&Master,packet);
++ reset_recovery_prepare();
++ if (userName != NULL)
++ free(userName);
++ return true;
++ }
++
++ /*
++ * wait answer from master server
++ */
++ memset(packet,0,sizeof(RecoveryPacket));
++ read_packet_from_master(&Master,packet);
++ if (ntohs(packet->packet_no) == RECOVERY_FSYNC_ANS )
++ {
++ /*
++ * master DB finished fsync
++ */
++ PGRset_recovery_packet_no(packet, RECOVERY_START_ANS );
++
memcpy(packet->hostName,Master.hostName,sizeof(packet->hostName));
++ status = send_packet(&Target,packet);
++ if (status != STATUS_OK)
++ {
++ finish_recovery();
++ }
++ loop_end = true;
++ }
++ else
++ {
++ show_error("%s:failure answer returned",func);
++ PGRset_recovery_packet_no(packet, RECOVERY_ERROR_CONNECTION);
++ status = send_packet(&Target,packet);
++ status = send_packet(&Master,packet);
++ reset_recovery_prepare();
++ loop_end = true;
++ }
++ if (userName != NULL)
++ free(userName);
++ return loop_end;
++}
++/**
++ * send queries from queue.
++ *
++ * return
++ * STATUS_OK - success both
++ * STATUS_ERROR - fail both
++ */
++static int
++PGRsend_queue(RecoveryTbl * master, RecoveryTbl * target)
++{
++ char * func = "PGRsend_queue()";
++ HostTbl * master_ptr = NULL;
++ HostTbl * target_ptr = NULL;
++ RecoveryQueueHeader * msg_header = NULL;
++ RecoveryQueueQuery * msg_query = NULL;
++ ReplicateHeader * header = NULL;
++ char * query = NULL;
++ int size = 0;
++ int rcv_header_size = 0;
++ long mtype = 0;
++ int status = 0;
++ int query_size = 0;
++ int rtn=0;
++ if (master == (RecoveryTbl *)NULL)
++ {
++ show_error("%s:there is no master ",func);
++ return STATUS_ERROR;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:master %s - %d",func,master->hostName,master->port);
++#endif
++ master_ptr = PGRget_HostTbl(master->hostName,master->port);
++ if (master_ptr == (HostTbl *)NULL)
++ {
++ show_error("%s:master table is null",func);
++ return STATUS_ERROR;
++ }
++ if (target != (RecoveryTbl *)NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:target %s -
%d",func,target->hostName,target->port);
++#endif
++ target_ptr = PGRget_HostTbl(target->hostName,target->port);
++ if (target_ptr == (HostTbl *)NULL)
++ {
++ show_error("%s:target table is null",func);
++ return STATUS_ERROR;
++ }
++ }
++
++ size = sizeof(RecoveryQueueHeader) + sizeof(ReplicateHeader);
++ msg_header = (RecoveryQueueHeader *)malloc(size+4);
++ if (msg_header == NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:malloc() failed. reason: %s",func,
strerror(errno));
++#endif
++ return STATUS_ERROR;
++ }
++ memset(msg_header,0,size+4);
++ rcv_header_size = sizeof(long)+sizeof(ReplicateHeader);
++ status = STATUS_OK;
++ while (msgrcv(*RecoveryMsgid , msg_header, rcv_header_size,
RECOVERY_HEADER_MTYPE, IPC_NOWAIT) > 0 )
++ {
++ header = (ReplicateHeader *)(msg_header->mdata);
++ query_size = ntohl(header->query_size);
++ if (query_size < 0)
++ {
++ if (msg_query != NULL)
++ {
++ free(msg_query);
++ msg_query = NULL;
++ }
++ break;
++ }
++ size = sizeof(RecoveryQueueQuery) + query_size;
++ mtype = msg_header->query_mtype;
++ msg_query = (RecoveryQueueQuery *) malloc(size+4);
++ if (msg_query == NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:malloc() failed. reason: %s",func,
strerror(errno));
++#endif
++ free(msg_header);
++ msg_header = NULL;
++ return STATUS_ERROR;
++ }
++ memset(msg_query,0,size+4);
++ if (msgrcv(*RecoveryMsgid , msg_query, sizeof(unsigned
int)+query_size, mtype, IPC_NOWAIT) < 0)
++ {
++ show_error("%s:msgrcv() failed.
(%s)",func,strerror(errno) );
++ status = STATUS_ERROR;
++ if (msg_query != NULL)
++ {
++ free(msg_query);
++ msg_query = NULL;
++ }
++ break;
++ }
++ query = (char *)(msg_query->mdata);
++#ifdef PRINT_DEBUG
++ show_debug("%s:put query from queue :%s",func,query);
++#endif
++
rtn=PGRsend_replicate_packet_to_server(master_ptr,header,query,PGR_Result,msg_query->replicationId,
true);
++ if (rtn == STATUS_ERROR || rtn == STATUS_DEADLOCK_DETECT )
++ {
++ show_error("%s:PGRsend_replicate_packet_to_server to
master error",func);
++ status = STATUS_ERROR ;
++ if (msg_query != NULL)
++ {
++ free(msg_query);
++ msg_query = NULL;
++ }
++ break;
++ }
++ if (target_ptr != NULL)
++ {
++
rtn=PGRsend_replicate_packet_to_server(target_ptr,header,query,PGR_Result,msg_query->replicationId,
true);
++ if (rtn == STATUS_ERROR || rtn ==
STATUS_DEADLOCK_DETECT )
++ {
++
show_error("%s:PGRsend_replicate_packet_to_server to target error",func);
++ status = STATUS_ERROR;
++ if (msg_query != NULL)
++ {
++ free(msg_query);
++ msg_query = NULL;
++ }
++ break;
++ }
++ }
++ if (msg_query != NULL)
++ {
++ free(msg_query);
++ msg_query = NULL;
++ }
++ }
++ if (msg_header != NULL)
++ {
++ free(msg_header);
++ msg_header = NULL;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:send_queue return status %d",func,status);
++#endif
++ return status;
++}
++
++static void
++pgrecovery_loop(int fd)
++{
++ char * func = "pgrecovery_loop()";
++ int count;
++ int sock;
++ int status;
++ bool loop_end = false;
++ RecoveryPacket packet;
++ HostTbl new_host;
++ RecoveryTbl * lbp;
++
++ lbp = LoadBalanceTbl;
++ if (lbp == (RecoveryTbl *)NULL)
++ {
++ show_error("%s:recovery table is NULL",func);
++ return ;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:recovery accept port %d",func, Recovery_Port_Number);
++#endif
++ count = 0;
++ while ((status =
PGR_Create_Acception(fd,&sock,"",Recovery_Port_Number)) != STATUS_OK)
++ {
++ show_error("%s:PGR_Create_Acception failed",func);
++ PGR_Close_Sock(&sock);
++ sock = -1;
++ if ( count > PGR_CONNECT_RETRY_TIME)
++ {
++ return;
++ }
++ count ++;
++ }
++ if(sock==-1) {
++ show_error("can't create recovery socket.exit.");
++ PGRreplicate_exit(1);
++ }
++ for(;;)
++ {
++ int read_size = 0;
++ int rtn;
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = RECOVERY_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(sock,&rmask);
++ /*
++ * read packet from target cluster server
++ */
++ rtn = select(sock+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++
++ if (rtn == 0) /* timeout */
++ {
++ return;
++ }
++
++ if (rtn && FD_ISSET(sock, &rmask))
++ {
++ read_size = read_packet(sock, &packet);
++ }
++ else
++ {
++ continue;
++ }
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:receive packet
no:%d",func,ntohs(packet.packet_no));
++#endif
++
++ switch (ntohs(packet.packet_no))
++ {
++ case RECOVERY_PREPARE_REQ :
++ /*
++ * start prepare of recovery
++ */
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:1st master %s - %d",
++ func,Master.hostName,Master.port);
++ show_debug("%s:1st target %s - %d",
++ func,Target.hostName,Target.port);
++#endif
++
++ loop_end = first_setup_recovery(&sock,
&packet);
++#ifdef PRINT_DEBUG
++ show_debug("%s:first_setup_recovery end:%d
",func,loop_end);
++#endif
++ break;
++ case RECOVERY_START_REQ :
++ /*
++ * now, recovery process will start
++ * stop the transaction count up
++ * start queueing and stop send all
queries for master DB
++ */
++#ifdef PRINT_DEBUG
++ show_debug("%s:2nd master %s - %d",
++ func, Master.hostName,Master.port);
++ show_debug("%s:2nd target %s - %d",
++ func, Target.hostName,Target.port);
++#endif
++ loop_end = second_setup_recovery (&packet);
++#ifdef PRINT_DEBUG
++ show_debug("%s:second_setup_recovery end :%d
",
++ func,loop_end);
++#endif
++ break;
++ case RECOVERY_FINISH :
++ /*
++ * finished rsync DB datas from master to
target
++ */
++ /*
++ * send all queries in queue
++ */
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:last master %s - %d",
++ func, Master.hostName,Master.port);
++ show_debug("%s:last target %s - %d",
++ func, Target.hostName,Target.port);
++#endif
++ status = PGRsend_queue(&Master,&Target);
++ if (status == STATUS_OK)
++ {
++
memcpy(new_host.hostName,Target.hostName,sizeof(new_host.hostName));
++ new_host.port = Target.port;
++ new_host.recoveryPort =
Target.recoveryPort;
++
PGRset_recovered_host(&new_host,DB_TBL_USE);
++ PGRadd_HostTbl(&new_host,DB_TBL_USE);
++ }
++ else
++ {
++ /* connection error , master or
target may be down */
++ show_error("%s:PGRsend_queue
failed",func);
++ PGRset_recovery_packet_no(&packet,
RECOVERY_ERROR_CONNECTION);
++ status = send_packet(&Target,&packet);
++ }
++ /*
++ * stop queueing, and re-initialize recovery
status
++ */
++ finish_recovery();
++ loop_end = true;
++ /*
++ * send finish recovery to load balancer
++ */
++ if (Master.recovery_sock != -1)
++ {
++ close(Master.recovery_sock);
++ Master.recovery_sock = -1;
++ }
++ if (Target.recovery_sock != -1)
++ {
++ close(Target.recovery_sock);
++ Target.recovery_sock = -1;
++ }
++ send_packet(&Master, &packet);
++ MasterPacketData.packet_no = packet.packet_no;
++
PGRsend_load_balance_packet(&MasterPacketData);
++ PGRsend_load_balance_packet(&packet);
++ memset((char
*)&MasterPacketData,0,sizeof(RecoveryPacket));
++ break;
++ case RECOVERY_ERROR_ANS :
++#ifdef PRINT_DEBUG
++ show_debug("%s:recovery error accept. top
queueing and initiarse recovery status",func);
++#endif
++ status = PGRsend_queue(&Master,NULL);
++ memset(&packet,0,sizeof(RecoveryPacket));
++ PGRset_recovery_packet_no(&packet,
RECOVERY_ERROR_ANS);
++ send_packet(&Master, &packet);
++ finish_recovery();
++ loop_end = true;
++ PGRset_recovery_packet_no(&MasterPacketData,
RECOVERY_FINISH );
++
PGRsend_load_balance_packet(&MasterPacketData);
++ memset((char
*)&MasterPacketData,0,sizeof(RecoveryPacket));
++ break;
++ default:
++ show_error("%s:unknown packet. abort to parse");
++ loop_end=true;
++ break;
++ }
++ if (loop_end)
++ {
++ if (Master.sock != -1)
++ {
++ close (Master.sock);
++ }
++ if (Master.recovery_sock != -1)
++ {
++ close (Master.recovery_sock);
++ }
++ PGR_Close_Sock(&sock);
++ return;
++ }
++ }
++}
++
++void
++PGRrecovery_main(void)
++{
++ char * func = "PGRrecovery_main()";
++ int status;
++ int fd = -1;
++ int rtn;
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:PGRrecovery_main bind port
%d",func,Recovery_Port_Number);
++#endif
++ status = PGR_Create_Socket_Bind(&fd, "", Recovery_Port_Number);
++ if (status != STATUS_OK)
++ {
++ show_error("%s:PGR_Create_Socket_Bind failed",func);
++ exit(1);
++ }
++ memset(&MasterPacketData,0,sizeof(RecoveryPacket));
++ memset(&Master,0,sizeof(RecoveryTbl));
++ memset(&Target,0,sizeof(RecoveryTbl));
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = RECOVERY_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(fd,&rmask);
++ rtn = select(fd+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn && FD_ISSET(fd, &rmask))
++ {
++ pgrecovery_loop(fd);
++ }
++ }
++}
++
++#ifdef PRINT_DEBUG
++static void
++show_recovery_packet(RecoveryPacket * packet)
++{
++ show_debug("no = %d",ntohs(packet->packet_no));
++ show_debug("max_connect = %d",ntohs(packet->max_connect));
++ show_debug("port = %d",ntohs(packet->port));
++ show_debug("recoveryPort = %d",ntohs(packet->recoveryPort));
++ if (packet->hostName != NULL)
++ show_debug("hostName = %s",packet->hostName);
++ if (packet->pg_data != NULL)
++ show_debug("pg_data = %s",packet->pg_data);
++}
++#endif
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/replicate.c
pgcluster-1.5.0rc7/src/pgcluster/pgrp/replicate.c
+--- postgresql-8.1.2/src/pgcluster/pgrp/replicate.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/replicate.c 2006-01-31
23:38:17.000000000 +0100
+@@ -0,0 +1,3938 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * replicate.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at pgreplicate for the replication.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#include "postgres.h"
++#include "postgres_fe.h"
++
++#include <pthread.h>
++#include <stdio.h>
++#include <stdarg.h>
++#include <sys/types.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <ctype.h>
++#include <time.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <sys/sem.h>
++#include <sys/msg.h>
++#include <signal.h>
++
++
++#include "libpq-fe.h"
++#include "libpq-int.h"
++#include "fe-auth.h"
++
++#include <sys/socket.h>
++#include <unistd.h>
++#include <netdb.h>
++#include <arpa/inet.h>
++
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
++#ifdef HAVE_SYS_SELECT_H
++#include <sys/select.h>
++#endif
++
++
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++
++#ifdef MULTIBYTE
++#include "mb/pg_wchar.h"
++#endif
++
++#include "access/xact.h"
++#include "lib/dllist.h"
++#include "libpq/pqformat.h"
++#include "replicate_com.h"
++#include "pgreplicate.h"
++
++
++#define IPC_NMAXSEM (32)
++
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++static PGconn * pgr_createConn( char * host, char * port,char * database,
char * userName );
++static TransactionTbl * setTransactionTbl(HostTbl * host_ptr,
ReplicateHeader * header);
++static TransactionTbl * insertTransactionTbl( HostTbl * host_ptr,
TransactionTbl * datap);
++static TransactionTbl * getTransactionTbl( HostTbl * host_ptr,
ReplicateHeader * header);
++static void deleteTransactionTbl(HostTbl * host_ptr,ReplicateHeader *
header);
++
++static HostTbl * deleteHostTbl(HostTbl * ptr);
++static bool is_master_in_recovery(char * host, int port,int
recovery_status);
++static void sem_quit(int semid);
++static int send_cluster_status_to_load_balance(HostTbl * host_ptr,int
status);
++static void set_transaction_status(int status);
++static void check_transaction_status(ReplicateHeader * header,HostTbl
*host, TransactionTbl *transaction);
++static void clearHostTbl(void);
++static bool is_need_sync_time(ReplicateHeader * header);
++static bool is_need_wait_answer(ReplicateHeader * header);
++static void write_host_status_file(HostTbl * host_ptr);
++
++static void delete_template(HostTbl * ptr, ReplicateHeader * header);
++static char * check_copy_command(char * query);
++static int read_answer(int dest);
++static bool is_autocommit_off(char * query);
++static bool is_autocommit_on(char * query);
++static unsigned int get_host_ip_from_tbl(char * host);
++static unsigned int get_srcHost_ip_from_tbl(char * srcHost);
++
++static int next_replication_id(void);
++static void check_replication_id(void);
++static bool is_need_use_rlog(ReplicateHeader * header);
++static bool is_need_queue_jump( ReplicateHeader * header,char * query);
++static int check_delete_transaction (HostTbl * host_ptr, ReplicateHeader *
header);
++
++static bool is_executed_query_in_origin( ReplicateHeader *header );
++static bool is_executed_query( PGconn *conn,ReplicateHeader *header );
++
++static void * thread_send_source(void * arg);
++static void * thread_send_cluster(void * arg);
++
++static int send_replicate_packet_to_server( TransactionTbl *
transaction_tbl, int current_cluster, HostTbl * host_ptr, ReplicateHeader *
header, char *query , char * result,unsigned int replicationId, bool
recovery);
++
++static int send_func(HostTbl * host_ptr,ReplicateHeader * header, char *
func,char * result);
++static uint32_t get_oid(HostTbl * host_ptr,ReplicateHeader * header);
++static int set_oid(HostTbl * host_ptr,ReplicateHeader * header, uint32_t
oid);
++static int replicate_lo( PGconn * conn, ReplicateHeader * header, LOArgs *
query);
++static int notice_abort(HostTbl * host_ptr,ReplicateHeader * header);
++static int send_p_parse (PGconn * conn, StringInfo input_message);
++static int send_p_bind (PGconn * conn, StringInfo input_message);
++static int send_p_describe (PGconn * conn, StringInfo input_message);
++static int send_p_execute (PGconn * conn, StringInfo input_message);
++static int send_p_sync (PGconn * conn, StringInfo input_message);
++static int send_p_close (PGconn * conn, StringInfo input_message);
++static void set_string_info(StringInfo input_message, ReplicateHeader *
header, char * query);
++
++int replicate_packet_send_internal(ReplicateHeader * header, char *
query,int dest,int recovery_status,bool isHeldLock);
++bool PGRis_same_host(char * host1, unsigned short port1 , char * host2,
unsigned short port2);
++HostTbl * PGRadd_HostTbl(HostTbl * conf_data, int useFlag);
++HostTbl * PGRget_master(void);
++void PGRset_recovery_status(int status);
++int PGRget_recovery_status(void);
++int PGRcheck_recovered_host(void);
++int PGRset_recovered_host(HostTbl * target,int useFlag);
++int PGRinit_recovery(void);
++void PGRreplicate_exit(int exit_status);
++int PGRsend_replicate_packet_to_server( HostTbl * host_ptr, ReplicateHeader
* header, char *query , char * result,unsigned int replicationId, bool
recovery);
++HostTbl * PGRget_HostTbl(char * hostName,int port);
++int PGRset_queue(ReplicateHeader * header,char * query);
++int PGRset_host_status(HostTbl * host_ptr,int status);
++void PGRclear_transactions(void);
++void PGRclear_connections();
++int PGRset_replication_id(uint32_t id);
++int PGRdo_replicate(int sock,ReplicateHeader *header, char * query);
++int PGRreturn_result(int dest, char * result,int wait);
++int PGRreplicate_packet_send( ReplicateHeader * header, char * query,int
dest,int recovery_status);
++char * PGRread_packet(int sock, ReplicateHeader *header);
++//void PGRnotice_replication_server(char * hostName, unsigned short
portNumber,unsigned short recoveryPortNumber, unsigned short
lifecheckPortNumber, char * userName);
++char * PGRread_query(int sock, ReplicateHeader *header);
++int PGRwait_transaction_count_clear(HostTbl *host);
++
++unsigned int PGRget_next_query_id(void);
++int PGRinit_transaction_table(void);
++int PGRsync_oid(ReplicateHeader *header);
++int PGRload_replication_id(void);
++
++extern pthread_mutex_t transaction_table_mutex;
++
++static int exit_signo = SIGTERM;
++
++bool
++PGRis_same_host(char * host1, unsigned short port1 , char * host2, unsigned
short port2)
++{
++#ifdef PRINT_DEBUG
++ char * func = "PGRis_same_host()";
++#endif
++ unsigned int ip1, ip2;
++
++ if ((host1[0] == '\0' ) || (host2[0] == '\0') ||
++ ( port1 != port2 ))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:not same host",func);
++#endif
++ return false;
++ }
++ ip1 = PGRget_ip_by_name( host1);
++ ip2 = PGRget_ip_by_name( host2);
++
++ if ((ip1 == ip2) && (port1 == port2))
++ {
++ return true;
++ }
++ return false;
++}
++
++static PGconn *
++pgr_createConn( char * host, char * port,char * database, char * userName )
++{
++ char * func = "pgr_createConn()";
++ int cnt = 0;
++ PGconn * conn = NULL;
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:PQsetdbLogin host[%s] port[%s] db[%s] user[%s]",
++ func, host,port,database,userName);
++#endif
++ conn = PQsetdbLogin(host, port, NULL, NULL, database, userName, NULL);
++ /* check to see that the backend Connection was successfully made */
++ cnt = 0;
++ while (PQstatus(conn) == CONNECTION_BAD)
++ {
++ if (conn != NULL)
++ {
++ PQfinish(conn);
++ conn = NULL;
++ }
++ conn = PQsetdbLogin(host, port, NULL, NULL, database,
userName, NULL);
++ if (cnt > PGR_CONNECT_RETRY_TIME )
++ {
++ if (conn != NULL)
++ {
++ PQfinish(conn);
++ conn = NULL;
++ }
++ return (PGconn *)NULL;
++ }
++
++ if(PQstatus(conn) == CONNECTION_BAD && h_errno==2)
++ {
++ sleep(3);
++ show_debug("gethostbyname() failed. sleep and
retrying...");
++ }
++ else if(!strncasecmp(PQerrorMessage(conn),"FATAL: Sorry, too
many clients already",30) ||
++ !strncasecmp(PQerrorMessage(conn),"FATAL:
Non-superuser connection limit",30) )
++ {
++ sleep(3) ;
++ show_debug("Connection overflow. sleep and retrying...");
++ }
++ else if(!strncasecmp(PQerrorMessage(conn),"FATAL: The
database system is starting up",40) )
++ {
++ show_debug("waiting for starting up...");
++ sleep(3) ;
++ }
++ else
++ {
++ show_debug("%s:Retry. h_errno is %d,reason is
'%s'",func,h_errno,PQerrorMessage(conn));
++ sleep(3) ;
++ cnt ++;
++ }
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:PQsetdbLogin ok!!",func);
++#endif
++ return conn;
++}
++
++static TransactionTbl *
++setTransactionTbl(HostTbl * host_ptr, ReplicateHeader * header)
++{
++ char * func = "setTransactionTbl()";
++ TransactionTbl * ptr = NULL;
++ TransactionTbl work ;
++ char port[8];
++ char * hostName, *dbName, *userName;
++
++ if ((host_ptr == NULL) || (header == NULL))
++ {
++ return (TransactionTbl *)NULL;
++ }
++ dbName = (char *)header->dbName;
++ snprintf(port,sizeof(port),"%d", host_ptr->port);
++ userName = (char *)header->userName;
++ hostName = (char *)host_ptr->resolvedName;
++
++ ptr = getTransactionTbl(host_ptr,header);
++ if (ptr != NULL)
++ {
++ ptr->transaction_count = 0;
++ ptr->conn = pgr_createConn(hostName,port,dbName,userName);
++ if (ptr->conn == NULL)
++ {
++ show_error("%s:Transaction is pooling but
pgr_createConn failed",func);
++ deleteTransactionTbl(host_ptr, header);
++ PGRset_host_status(host_ptr,DB_TBL_ERROR);
++ ptr = NULL;
++ }
++ return ptr;
++ }
++
++ memset(&work,0,sizeof(work));
++ strncpy(work.host, hostName, sizeof(work.host));
++ strncpy(work.srcHost, header->from_host, sizeof(work.srcHost));
++ work.hostIP = PGRget_ip_by_name(hostName);
++ work.port = host_ptr->port;
++ work.srcHostIP = PGRget_ip_by_name(header->from_host);
++ work.pid = ntohs(header->pid);
++ strncpy(work.dbName,header->dbName,sizeof(work.dbName));
++ work.conn = pgr_createConn(hostName,port,dbName,userName);
++ if (work.conn == NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: %s@%s is not ready",func,port,hostName);
++#endif
++ return (TransactionTbl *)NULL;
++ }
++ work.useFlag = DB_TBL_USE ;
++ work.in_transaction = false;
++ work.transaction_count = 0;
++ ptr = insertTransactionTbl(host_ptr,&work);
++ if (ptr == (TransactionTbl *)NULL)
++ {
++ show_error("%s:insertTransactionTbl failed",func);
++ return (TransactionTbl *)NULL;
++ }
++ return ptr;
++}
++
++static TransactionTbl *
++insertTransactionTbl( HostTbl * host_ptr, TransactionTbl * datap)
++{
++ char * func = "insertTransactionTbl()";
++ TransactionTbl * workp = NULL;
++
++ pthread_mutex_lock(&transaction_table_mutex);
++ if ((host_ptr == (HostTbl *)NULL) || (datap == (TransactionTbl*)NULL))
++ {
++ show_error("%s:host table or transaction table is NULL",func);
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return (TransactionTbl *)NULL;
++ }
++ if (Transaction_Tbl_Begin == NULL)
++ {
++ if (PGRinit_transaction_table() != STATUS_OK)
++ {
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return (TransactionTbl *)NULL;
++ }
++ }
++
++ workp = (TransactionTbl *)malloc(sizeof(TransactionTbl));
++ memset(workp,0,sizeof(TransactionTbl));
++ Transaction_Tbl_End = workp;
++ workp->hostIP = datap->hostIP;
++ workp->port = datap->port;
++ workp->pid = datap->pid;
++ workp->srcHostIP = datap->srcHostIP;
++ strncpy(workp->host,datap->host,sizeof(workp->host));
++ strncpy(workp->srcHost,datap->srcHost,sizeof(workp->srcHost));
++ strncpy(workp->dbName,datap->dbName,sizeof(workp->dbName));
++ workp->conn = datap->conn;
++ workp->useFlag = DB_TBL_USE;
++ workp->lock = STATUS_OK;
++ workp->in_transaction =datap->in_transaction;
++ workp->transaction_count =datap->transaction_count;
++ DLAddTail(Transaction_Tbl_Begin, DLNewElem(workp));
++
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return workp;
++}
++
++static TransactionTbl *
++getTransactionTbl( HostTbl * host_ptr, ReplicateHeader * header)
++{
++ Dlelem * ptr = NULL;
++ unsigned int host_ip,srcHost_ip;
++ unsigned short pid = 0;
++
++ if (Transaction_Tbl_Begin == (Dllist *) NULL)
++ {
++ return (TransactionTbl * )NULL;
++ }
++ if ((host_ptr == (HostTbl *)NULL) ||
++ (header == (ReplicateHeader *)NULL))
++ {
++ return (TransactionTbl * )NULL;
++ }
++ host_ip = get_host_ip_from_tbl(host_ptr->resolvedName);
++ if (host_ip == 0)
++ {
++ host_ip = PGRget_ip_by_name(host_ptr->resolvedName);
++ }
++ srcHost_ip = get_srcHost_ip_from_tbl(header->from_host);
++ if (srcHost_ip == 0)
++ {
++ srcHost_ip = PGRget_ip_by_name(header->from_host);
++ }
++ pid = ntohs(header->pid);
++
++ pthread_mutex_lock(&transaction_table_mutex);
++
++ ptr = DLGetHead(Transaction_Tbl_Begin);
++ while (ptr)
++ {
++ TransactionTbl *transaction = DLE_VAL(ptr);
++ if ((transaction->useFlag == DB_TBL_USE) &&
++ (transaction->hostIP == host_ip) &&
++ (transaction->port == host_ptr->port) &&
++ (transaction->srcHostIP == srcHost_ip) &&
++
(!strncmp(transaction->dbName,header->dbName,sizeof(transaction->dbName))) &&
++ (transaction->pid == pid))
++ {
++ pthread_mutex_unlock(&transaction_table_mutex);
++ return transaction;
++ }
++ ptr = DLGetSucc(ptr);
++ }
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return (TransactionTbl * )NULL;
++}
++
++static void
++deleteTransactionTbl(HostTbl * host_ptr,ReplicateHeader * header)
++{
++ char * func = "deleteTransactionTbl()";
++ TransactionTbl *ptr = NULL;
++ Dlelem *elem;
++#ifdef PRINT_DEBUG
++ show_debug("%s:",func);
++#endif
++
++ ptr = getTransactionTbl(host_ptr,header);
++
++ pthread_mutex_lock(&transaction_table_mutex);
++
++ if (ptr != NULL)
++ {
++ /*
++ if ( ptr->transaction_count > 0 )
++ {
++ pthread_mutex_unlock(&transaction_table_mutex);
++ return;
++ }
++ */
++ if (ptr->in_transaction)
++ {
++ host_ptr->transaction_count--;
++ }
++
++ if (ptr->conn != NULL)
++ {
++ PQfinish(ptr->conn);
++ }
++ elem = DLGetHead(Transaction_Tbl_Begin);
++ while (elem)
++ {
++ TransactionTbl *transaction = DLE_VAL(elem);
++ if (transaction == ptr) {
++ DLRemove(elem);
++ DLFreeElem(elem);
++ free(ptr);
++
pthread_mutex_unlock(&transaction_table_mutex);
++ return;
++ }
++ elem = DLGetSucc(elem);
++ }
++ }
++ pthread_mutex_unlock(&transaction_table_mutex);
++}
++
++static HostTbl *
++deleteHostTbl(HostTbl * ptr)
++{
++ if (ptr != (HostTbl*)NULL)
++ {
++ memset(ptr,0,sizeof(HostTbl));
++ }
++ return ++ptr;
++}
++
++HostTbl *
++PGRadd_HostTbl(HostTbl *conf_data, int useFlag)
++{
++ HostTbl * ptr = NULL;
++ int cnt = 0;
++
++ ptr = PGRget_HostTbl(conf_data->hostName, conf_data->port);
++ if (ptr != (HostTbl*)NULL)
++ {
++ PGRset_host_status(ptr,useFlag);
++ return ptr;
++ }
++
++ ptr = Host_Tbl_Begin;
++ cnt = 1;
++ while (ptr->useFlag != DB_TBL_END)
++ {
++ if (ptr->useFlag == DB_TBL_FREE)
++ {
++ break;
++ }
++ ptr ++;
++ cnt ++;
++ }
++ if (cnt >= MAX_DB_SERVER)
++ {
++ return (HostTbl*)NULL;
++ }
++ if (ptr->useFlag == DB_TBL_END)
++ {
++ (ptr + 1) -> useFlag = DB_TBL_END;
++ }
++ memset(ptr,0,sizeof(HostTbl));
++ ptr->hostNum = cnt;
++ memcpy(ptr->hostName,conf_data->hostName,sizeof(ptr->hostName));
++
memcpy(ptr->resolvedName,conf_data->resolvedName,sizeof(ptr->resolvedName));
++ ptr->port = conf_data->port;
++ ptr->recoveryPort = conf_data->recoveryPort;
++ ptr->transaction_count = 0;
++ PGRset_host_status(ptr,useFlag);
++
++ return ptr;
++}
++
++HostTbl *
++PGRget_master(void)
++{
++ HostTbl * host_tbl = NULL;
++
++ host_tbl = Host_Tbl_Begin;
++ while(host_tbl->useFlag != DB_TBL_END)
++ {
++ if (host_tbl->useFlag == DB_TBL_USE)
++ {
++ return host_tbl;
++ }
++ host_tbl ++;
++ }
++ return (HostTbl *)NULL;
++}
++
++void
++PGRset_recovery_status(int status)
++{
++ if (RecoverySemID <= 0)
++ return;
++ PGRsem_lock(RecoverySemID,SEM_NUM_OF_RECOVERY);
++ if (Recovery_Status_Inf != (RecoveryStatusInf *)NULL)
++ {
++ Recovery_Status_Inf->recovery_status = status;
++
++ }
++ PGRsem_unlock(RecoverySemID,SEM_NUM_OF_RECOVERY);
++}
++
++int
++PGRget_recovery_status(void)
++{
++ int status = -1;
++
++ if (RecoverySemID <= 0)
++ return -1;
++ PGRsem_lock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ if (Recovery_Status_Inf != (RecoveryStatusInf *)NULL)
++ {
++ status = Recovery_Status_Inf->recovery_status;
++ }
++ PGRsem_unlock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ return status;
++
++}
++
++static void
++set_transaction_status(int status)
++{
++ if (RecoverySemID <= 0)
++ return ;
++ PGRsem_lock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ if (Recovery_Status_Inf != (RecoveryStatusInf *)NULL)
++ {
++ Recovery_Status_Inf->recovery_status = status;
++ }
++ PGRsem_unlock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++}
++
++#if 0
++static int
++get_transaction_status(void)
++{
++ int status = 0;
++
++ if (RecoverySemID <= 0)
++ return 0;
++ PGRsem_lock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ if (Recovery_Status_Inf != (RecoveryStatusInf *)NULL)
++ {
++ status = Recovery_Status_Inf->recovery_status;
++ PGRsem_unlock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ return status;
++ }
++ PGRsem_unlock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ return 0;
++}
++#endif
++
++int
++PGRwait_transaction_count_clear(HostTbl *host)
++{
++ int cnt = 0;
++ int transaction=-1;
++
++ for(cnt=0 ;
++ cnt < PGR_RECOVERY_RETRY_CNT && transaction!=0;
++ cnt++)
++ {
++ PGRsem_lock(SemID, 1);
++ transaction = host->transaction_count;
++ PGRsem_unlock(SemID, 1);
++ usleep(PGR_RECOVERY_WAIT_MSEC);
++ }
++ show_debug("transaction count is %d",transaction);
++ return transaction==0
++ ? STATUS_OK
++ : STATUS_ERROR;
++}
++
++int
++PGRcheck_recovered_host(void)
++{
++ char * func = "PGRcheck_recovered_host()";
++ HostTbl * ptr = NULL;
++ int rtn = STATUS_OK;
++
++ if (RecoverySemID <= 0)
++ return STATUS_ERROR;
++ PGRsem_lock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ if (Recovery_Status_Inf != (RecoveryStatusInf *)NULL)
++ {
++ if (Recovery_Status_Inf->useFlag != DB_TBL_FREE)
++ {
++ ptr = PGRadd_HostTbl((HostTbl
*)&(Recovery_Status_Inf->target_host),Recovery_Status_Inf->useFlag);
++ if (ptr == (HostTbl *) NULL)
++ {
++ show_error("%s:PGRadd_HostTbl failed",func);
++ rtn = STATUS_ERROR;
++ }
++ Recovery_Status_Inf->useFlag = DB_TBL_FREE;
++ memset((HostTbl
*)&(Recovery_Status_Inf->target_host),0,sizeof(HostTbl));
++
++ }
++ }
++ PGRsem_unlock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ return rtn;
++}
++
++int
++PGRset_recovered_host(HostTbl * target, int useFlag)
++{
++ if (RecoverySemID <= 0)
++ return -1;
++ PGRsem_lock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ if (Recovery_Status_Inf != (RecoveryStatusInf *)NULL)
++ {
++ Recovery_Status_Inf->useFlag = useFlag;
++ if (target != (HostTbl*)NULL)
++ {
++ memcpy((HostTbl
*)&(Recovery_Status_Inf->target_host),target,sizeof(HostTbl));
++ PGRset_host_status(target,useFlag);
++ }
++
++ }
++ PGRsem_unlock(RecoverySemID, SEM_NUM_OF_RECOVERY);
++ return 0;
++}
++
++
++static bool
++is_master_in_recovery(char * host , int port,int recovery_status)
++{
++ HostTbl * master = NULL;
++
++ // status = PGRget_recovery_status();
++ if (recovery_status == RECOVERY_START)
++ {
++ master = PGRget_master();
++ if (master == (HostTbl *)NULL)
++ {
++ return false;
++ }
++ if
((!strncmp(host,master->hostName,sizeof(master->hostName))) &&
++ (port == master->port))
++ {
++ return true;
++ }
++ }
++ return false;
++}
++
++int
++PGRinit_recovery(void)
++{
++ char * func = "PGRinit_recovery()";
++ int size = 0;
++ union semun sem_arg;
++ int i = 0;
++
++ if ((RecoverySemID = semget(IPC_PRIVATE,2,IPC_CREAT | IPC_EXCL |
0600)) < 0)
++ {
++ show_error("%s:semget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ for ( i = 0 ; i < 2 ; i ++)
++ {
++ semctl(RecoverySemID, i, GETVAL, sem_arg);
++ sem_arg.val = 1;
++ semctl(RecoverySemID, i, SETVAL, sem_arg);
++ }
++
++ size = sizeof(RecoveryStatusInf);
++ RecoveryShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL | 0600);
++ if (RecoveryShmid < 0)
++ {
++ show_error("%s:shmget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ Recovery_Status_Inf = (RecoveryStatusInf *)shmat(RecoveryShmid,0,0);
++ if (Recovery_Status_Inf == (RecoveryStatusInf *)-1)
++ {
++ show_error("%s:shmat() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(Recovery_Status_Inf,0,size);
++ Recovery_Status_Inf->check_point = PGR_CHECK_POINT ;
++
++ size = sizeof(unsigned int);
++ ReplicateSerializationShmid = shmget(IPC_PRIVATE,size,IPC_CREAT |
IPC_EXCL | 0600);
++ if (ReplicateSerializationShmid < 0)
++ {
++ show_error("%s:shmget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++
++ PGR_ReplicateSerializationID = (unsigned int
*)shmat(ReplicateSerializationShmid,0,0);
++ if( PGR_ReplicateSerializationID == (unsigned int *)-1) {
++ show_error("%s:shmat() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(PGR_ReplicateSerializationID,0,size);
++ PGRset_recovery_status(RECOVERY_INIT);
++ PGRset_recovered_host((HostTbl *)NULL, DB_TBL_FREE);
++ set_transaction_status(0);
++
++ /*
++ * create message queue
++ */
++ RecoveryMsgShmid = shmget(IPC_PRIVATE,size,IPC_CREAT | IPC_EXCL |
0600);
++ if (RecoveryMsgShmid < 0)
++ {
++ show_error("%s:shmget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++
++ RecoveryMsgid = (int *)shmat(RecoveryMsgShmid,0,0);
++ if( RecoveryMsgid < 0) {
++ show_error("%s:shmat() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ *RecoveryMsgid = msgget (IPC_PRIVATE, 00666 | IPC_CREAT );
++ if (*RecoveryMsgid < 0)
++ {
++ show_error("%s:msgget() failed. (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++
++
++ return STATUS_OK;
++}
++
++static void
++clearHostTbl(void)
++{
++
++ HostTbl * ptr = NULL;
++
++ if (Host_Tbl_Begin == NULL)
++ return;
++ /* normal socket close */
++ ptr = Host_Tbl_Begin;
++ while(ptr && ptr->useFlag != DB_TBL_END)
++ {
++ ptr = deleteHostTbl(ptr);
++ }
++}
++
++void
++set_exit_processing(int signo)
++{
++ exit_signo = signo;
++ exit_processing = true;
++ signal(signo, SIG_IGN);
++}
++
++void
++PGRreplicate_exit(int exit_status)
++{
++ char fname[256];
++ int rtn = 0;
++
++ signal(SIGCHLD, SIG_IGN);
++ kill (0, exit_signo);
++
++ child_wait(0);
++
++ if (RidFp != NULL)
++ {
++ rewind(RidFp);
++ if (Recovery_Status_Inf != NULL)
++ {
++
PGRwrite_log_file(RidFp,"%u",Recovery_Status_Inf->replication_id);
++ }
++ fflush(RidFp);
++ fclose(RidFp);
++ RidFp = NULL;
++ }
++
++ if (ReplicateSock > 0)
++ close(ReplicateSock);
++
++ /* recovery status clear */
++ if (RecoverySemID > 0)
++ Recovery_Status_Inf->recovery_status = RECOVERY_INIT;
++
++ /* normal socket close */
++ clearHostTbl();
++
++ if (Host_Tbl_Begin != (HostTbl *)NULL)
++ {
++ rtn = shmdt((char *)Host_Tbl_Begin);
++ shmctl(HostTblShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ }
++
++ if (Cascade_Tbl != (ReplicateServerInfo *)NULL)
++ {
++ rtn = shmdt((char *)Cascade_Tbl);
++ shmctl(CascadeTblShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ }
++
++ if (Cascade_Inf != (CascadeInf *)NULL)
++ {
++ rtn = shmdt((char *)Cascade_Inf);
++ shmctl(CascadeInfShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ }
++
++ if (Commit_Log_Tbl != (CommitLogInf *)NULL)
++ {
++ rtn = shmdt((char *)Commit_Log_Tbl);
++ shmctl(CommitLogShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ }
++
++ if (Recovery_Status_Inf != (RecoveryStatusInf *)NULL)
++ {
++ rtn = shmdt((char *)Recovery_Status_Inf);
++ shmctl(RecoveryShmid,IPC_RMID,(struct shmid_ds *)NULL);
++ }
++ if (PGR_ReplicateSerializationID!=NULL)
++ {
++ shmdt(PGR_ReplicateSerializationID);
++ shmctl(ReplicateSerializationShmid,IPC_RMID,(struct shmid_ds
*)NULL);
++ }
++
++ if (RecoveryMsgid)
++ {
++ if (*RecoveryMsgid >= 0)
++ msgctl(*RecoveryMsgid,IPC_RMID,(struct msqid_ds
*)NULL);
++
++ shmdt(RecoveryMsgid);
++ shmctl(RecoveryMsgShmid, IPC_RMID, NULL);
++ }
++
++ if (StatusFp != NULL)
++ {
++ fflush(StatusFp);
++ fclose(StatusFp);
++ StatusFp = NULL;
++ }
++ if (LogFp != NULL)
++ {
++ fflush(LogFp);
++ fclose(LogFp);
++ LogFp = NULL;
++ }
++
++ if (PGR_Result != NULL)
++ {
++ free(PGR_Result);
++ PGR_Result = NULL;
++ }
++ if (PGR_Response_Inf != NULL)
++ {
++ free(PGR_Response_Inf);
++ PGR_Response_Inf = NULL;
++ }
++
++ if (LoadBalanceTbl != NULL)
++ {
++ free(LoadBalanceTbl);
++ LoadBalanceTbl = NULL;
++ }
++
++ if (PGR_Log_Header != NULL)
++ {
++ free(PGR_Log_Header);
++ PGR_Log_Header = NULL;
++ }
++
++ if (PGR_Send_Query_ID != NULL)
++ {
++ free(PGR_Send_Query_ID);
++ PGR_Send_Query_ID = NULL;
++ }
++
++ if (CascadeSemID > 0)
++ {
++ sem_quit(CascadeSemID);
++ CascadeSemID = 0;
++ }
++ if (SemID > 0)
++ {
++ sem_quit(SemID);
++ SemID = 0;
++ }
++ if (RecoverySemID > 0)
++ {
++ sem_quit(RecoverySemID);
++ RecoverySemID = 0;
++ }
++ if (VacuumSemID > 0)
++ {
++ sem_quit(VacuumSemID);
++ }
++
++ snprintf(fname, sizeof(fname), "%s/%s", PGR_Data_Path,
PGREPLICATE_PID_FILE);
++ unlink(fname);
++
++ /* close socket between rlog process */
++
++ if (Replicateion_Log->r_log_sock >= 0)
++ {
++ close(Replicateion_Log->r_log_sock);
++ Replicateion_Log->r_log_sock = -1;
++ }
++ if (Replicateion_Log->RLog_Sock_Path != NULL)
++ {
++ unlink(Replicateion_Log->RLog_Sock_Path);
++ free(Replicateion_Log->RLog_Sock_Path);
++ Replicateion_Log->RLog_Sock_Path = NULL;
++ }
++ exit(exit_status);
++}
++
++static int
++send_cluster_status_to_load_balance(HostTbl * host_ptr,int status)
++{
++ RecoveryPacket packet;
++ int rtn = 0;
++
++ memset(&packet,0,sizeof(RecoveryPacket));
++ packet.packet_no = htons(status);
++ strncpy(packet.hostName,host_ptr->hostName,sizeof(packet.hostName));
++ packet.port = htons(host_ptr->port);
++ rtn = PGRsend_load_balance_packet(&packet);
++ return rtn;
++}
++
++int
++PGRset_host_status(HostTbl * host_ptr,int status)
++{
++ if (host_ptr == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ if (host_ptr->useFlag != status)
++ {
++ host_ptr->useFlag = status;
++ if (status == DB_TBL_ERROR )
++ {
++ host_ptr->transaction_count = 0;
++
send_cluster_status_to_load_balance(host_ptr,RECOVERY_ERROR_CONNECTION);
++ }
++ write_host_status_file(host_ptr);
++ }
++ return STATUS_OK;
++}
++
++static void
++write_host_status_file(HostTbl * host_ptr)
++{
++ switch( host_ptr->useFlag)
++ {
++ case DB_TBL_FREE:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s free",
++ host_ptr->port,
++ host_ptr->hostName);
++ break;
++ case DB_TBL_INIT:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s
initialize",
++ host_ptr->port,
++ host_ptr->hostName);
++ break;
++ case DB_TBL_USE:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s start
use",
++ host_ptr->port,
++ host_ptr->hostName);
++ break;
++ case DB_TBL_ERROR:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s error",
++ host_ptr->port,
++ host_ptr->hostName);
++ break;
++ case DB_TBL_END:
++ PGRwrite_log_file(StatusFp,"port(%d) host:%s end",
++ host_ptr->port,
++ host_ptr->hostName);
++ break;
++ }
++}
++
++#if 0
++static int
++check_result( PGresult * res )
++{
++ int status = 0;
++
++ status = PQresultStatus(res);
++ if ((status == PGRES_NONFATAL_ERROR ) ||
++ (status == PGRES_FATAL_ERROR ))
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++#endif
++
++/*--------------------------------------------------
++ * SYMBOL
++ * PGRsend_replicate_packet_to_server()
++ * NOTES
++ * Send query data to the cluster DB and recieve result data.
++ * ARGS
++ * HostTbl * host_ptr: the record of cluster DB table (target)
++ * ReplicateHeader * header: header data
++ * char *query: query data
++ * char * result: returned result data
++ * RETURN
++ * STATUS_OK: OK
++ * STATUS_ERROR: NG
++ * STATUS_LOCK_CONFLICT: Lock conflicted
++ *---------------------------------------------------
++ */
++int
++PGRsend_replicate_packet_to_server( HostTbl * host_ptr, ReplicateHeader *
header, char *query , char * result,unsigned int replicationId, bool recovery)
++{
++ char * func = "PGRsend_replicate_packet_to_server()";
++ TransactionTbl * transaction_tbl = NULL;
++ char *database = NULL;
++ char port[8];
++ char *userName = NULL;
++ char * host = NULL;
++ int rtn = 0;
++ int current_cluster = 0;
++ int query_size = 0;
++
++ if ((query == NULL) || (header == NULL))
++ {
++ show_error("%s: query is broken",func);
++ return STATUS_ERROR;
++ }
++ query_size = ntohl(header->query_size);
++ if (query_size < 0)
++ {
++ show_error("%s: query size is broken",func);
++ return STATUS_ERROR;
++ }
++ if (host_ptr == NULL)
++ {
++ return STATUS_ERROR;
++ }
++
++ if (PGR_Response_Inf != NULL)
++ {
++ current_cluster = PGR_Response_Inf->current_cluster;
++ }
++
++ /*
++ * set up the connection
++ */
++ database = (char *)header->dbName;
++ snprintf(port,sizeof(port),"%d", host_ptr->port);
++ userName = (char *)header->userName;
++ host = host_ptr->resolvedName;
++#ifdef PRINT_DEBUG
++ show_debug("%s:host(%s) : port(%s)",func,host,port);
++#endif
++ /*
++ * get the transaction table data
++ * it has the connection data with each cluster DB
++ */
++ transaction_tbl = getTransactionTbl(host_ptr,header);
++ /*
++ * if the transaction process is new one,
++ * create connection data and add the transaction table
++ */
++ if (transaction_tbl == (TransactionTbl *)NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:set new Transaction ",func);
++#endif
++ StartReplication[current_cluster] = true;
++ transaction_tbl = setTransactionTbl(host_ptr, header);
++ if (transaction_tbl == (TransactionTbl *)NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:setTransactionTbl failed",func);
++#endif
++ if ( header->cmdSts != CMD_STS_NOTICE )
++ {
++ PGRset_host_status(host_ptr,DB_TBL_ERROR);
++ }
++ return STATUS_ERROR;
++ }
++ }
++ else
++ {
++ /*
++ * re-use the connection data
++ */
++ if ((transaction_tbl->conn != (PGconn *)NULL) &&
++ (transaction_tbl->conn->sock > 0))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:re-use Transaction ",func);
++#endif
++ StartReplication[current_cluster] = false;
++ }
++ else
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:re-use failed. create new Transaction
",func);
++#endif
++ if (transaction_tbl->conn != (PGconn *)NULL)
++ {
++ PQfinish(transaction_tbl->conn);
++ transaction_tbl->conn = NULL;
++ }
++ transaction_tbl->conn =
pgr_createConn(host,port,database,userName);
++ StartReplication[current_cluster] = true;
++ }
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:connect db:%s port:%s user:%s host:%s ",
++ func, database,port,userName,host);
++#endif
++ rtn = send_replicate_packet_to_server( transaction_tbl,
current_cluster, host_ptr, header, query ,result ,replicationId, recovery);
++ return rtn;
++}
++
++static int
++send_replicate_packet_to_server( TransactionTbl * transaction_tbl, int
current_cluster, HostTbl * host_ptr, ReplicateHeader * header, char *query ,
char * result,unsigned int replicationId, bool recovery)
++{
++ char * func = "send_replicate_packet_to_server()";
++ PGconn * conn = (PGconn *)NULL;
++ PGresult * res = (PGresult *)NULL;
++ char sync_command[256];
++ bool sync_command_flg = false;
++ char * str = NULL;
++ int rtn = 0;
++ int query_size = 0;
++ int hostNum = 0;
++ StringInfoData input_message;
++
++ if (( transaction_tbl == (TransactionTbl *)NULL) ||
++ ( host_ptr == (HostTbl *) NULL) ||
++ (header == (ReplicateHeader *) NULL) ||
++ (query == NULL) ||
++ ( result == NULL))
++ {
++ show_debug("%s:unexpected NULL variable");
++ return STATUS_ERROR;
++ }
++
++ query_size = ntohl(header->query_size);
++ if (query_size < 0)
++ {
++ show_error("%s: query size is broken",func);
++
++ return STATUS_ERROR;
++ }
++
++ if(header->cmdSts == CMD_STS_OTHER &&
++ header->cmdType == CMD_TYPE_CONNECTION_CLOSE)
++ {
++ check_delete_transaction(host_ptr,header);
++ return STATUS_OK;
++ }
++
++ conn = transaction_tbl->conn;
++ if (conn == NULL)
++ {
++ show_error("%s:[%d@%s] may be
down",func,host_ptr->port,host_ptr->hostName);
++ if ( header->cmdSts != CMD_STS_NOTICE )
++ {
++ PGRset_host_status(host_ptr,DB_TBL_ERROR);
++ }
++ return STATUS_ERROR;
++ }
++ hostNum = host_ptr->hostNum;
++
++#if 0
++ /* check query id */
++ if (*(PGR_Send_Query_ID + hostNum ) == ntohl(header->query_id))
++ {
++ return STATUS_OK;
++ }
++#endif
++
++ /*
++ * When the query is transaction query...
++ */
++ if (is_need_sync_time(header) == true)
++ {
++ if (transaction_tbl->transaction_count >1 )
++ {
++ sync_command_flg = false;
++ }
++ else
++ {
++ sync_command_flg = true;
++ }
++ }
++ /*
++ if ((header->cmdSts == CMD_STS_TRANSACTION ) ||
++ (header->cmdSts == CMD_STS_SET_SESSION_AUTHORIZATION ))
++ {
++ if ((header->cmdSts == CMD_STS_TRANSACTION ) &&
++ ((header->cmdType != CMD_TYPE_BEGIN) ||
++ (transaction_tbl->transaction_count >1 )))
++ {
++ sync_command_flg = false;
++ }
++ }
++ */
++
++ /*
++ * execute query
++ */
++
++ if (header->rlog > 0 )
++ {
++
++ if (is_executed_query( conn, header) == true)
++ {
++ return STATUS_OK;
++ }
++ else
++ {
++ show_debug("%s:check replication log issue ,
id=%d,rlog=%d,query=%s
status=not_replicated",func,ntohl(header->replicate_id),header->rlog,query);
++ }
++ }
++ if (( header->cmdSts != CMD_STS_NOTICE ) &&
++ ( header->cmdSts != CMD_STS_PREPARE ) &&
++ ((sync_command_flg == true) ||
++ (StartReplication[current_cluster] == true)))
++ {
++ snprintf(sync_command,sizeof(sync_command),
++ "SELECT %s(%d,%u,%u,%u,%d,%u) ",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_SET_CURRENT_TIME_FUNC_NO,
++ (unsigned int)ntohl(header->tv.tv_sec),
++ (unsigned int)ntohl(header->tv.tv_usec),
++ (unsigned int)ntohl(PGR_Log_Header->replicate_id),
++ PGR_Response_Inf->response_mode,
++ *PGR_ReplicateSerializationID);
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command(%s)",func,sync_command);
++#endif
++ res = PQexec(conn, sync_command);
++ if (res != NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command returns
%s",func,PQcmdStatus(res));
++#endif
++ PQclear(res);
++ }
++ StartReplication[current_cluster] = false;
++ }
++
++ res = NULL;
++ if ((header->cmdType == CMD_TYPE_COPY_DATA) ||
++ (header->cmdType == CMD_TYPE_COPY_DATA_END))
++ {
++ /* copy data replication */
++ rtn =PQputnbytes(conn, query,query_size);
++ if (header->cmdType == CMD_TYPE_COPY_DATA_END)
++ {
++ rtn = PQendcopy(conn);
++ if (rtn == 1) /* failed */
++ {
++ if (transaction_tbl->conn != NULL)
++ {
++ PQfinish(transaction_tbl->conn);
++ transaction_tbl->conn = (PGconn
*)NULL;
++ StartReplication[current_cluster] =
true;
++ }
++ }
++ }
++ *(PGR_Send_Query_ID + hostNum ) == ntohl(header->query_id);
++ return STATUS_OK;
++ }
++ else if (header->cmdSts == CMD_STS_LARGE_OBJECT)
++ {
++ replicate_lo(conn, header,(LOArgs *)query);
++ return STATUS_OK;
++ }
++ else if (header->cmdSts == CMD_STS_PREPARE)
++ {
++
++ if ( !PGR_Parse_Session_Started)
++ {
++ snprintf(sync_command,sizeof(sync_command),
++ "SELECT %s(%d,%u,%u,%u,%d,%u) ",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_SET_CURRENT_TIME_FUNC_NO,
++ (unsigned int)ntohl(header->tv.tv_sec),
++ (unsigned int)ntohl(header->tv.tv_usec),
++ (unsigned
int)ntohl(PGR_Log_Header->replicate_id),
++ PGR_Response_Inf->response_mode,
++ *PGR_ReplicateSerializationID);
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command [%s]",func,sync_command);
++#endif
++ res = PQexec(conn, sync_command);
++ if (res != NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command returns
%s",func,PQcmdStatus(res));
++#endif
++ PQclear(res);
++ res = NULL;
++ }
++ while ((res = PQgetResult(conn)) != NULL)
++ {
++ if (res->resultStatus == PGRES_COPY_IN)
++ {
++ PQclear(res);
++ return STATUS_ERROR;
++ }
++ else if (res->resultStatus == PGRES_COPY_OUT)
++ {
++ conn->asyncStatus = PGASYNC_BUSY;
++ }
++ else if (conn->status == CONNECTION_BAD)
++ {
++ PQclear(res);
++ return STATUS_ERROR;
++ }
++ PQclear(res);
++ }
++ }
++ set_string_info(&input_message,header,query);
++ switch (header->cmdType)
++ {
++ case CMD_TYPE_P_PARSE :
++ if (send_p_parse(conn, &input_message) !=
STATUS_OK)
++ {
++ pqHandleSendFailure(conn);
++ PGR_Parse_Session_Started = false;
++ return STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_P_BIND :
++ if (send_p_bind(conn, &input_message) !=
STATUS_OK)
++ {
++ pqHandleSendFailure(conn);
++ PGR_Parse_Session_Started = false;
++ return STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_P_DESCRIBE :
++ if (send_p_describe(conn, &input_message) !=
STATUS_OK)
++ {
++ pqHandleSendFailure(conn);
++ PGR_Parse_Session_Started = false;
++ return STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_P_EXECUTE :
++ if (send_p_execute(conn,&input_message) !=
STATUS_OK)
++ {
++ pqHandleSendFailure(conn);
++ PGR_Parse_Session_Started = false;
++ return STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_P_SYNC :
++ if (send_p_sync(conn, &input_message) !=
STATUS_OK)
++ {
++ pqHandleSendFailure(conn);
++ PGR_Parse_Session_Started = false;
++ return STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_P_CLOSE :
++ if (send_p_close(conn, &input_message) !=
STATUS_OK)
++ {
++ pqHandleSendFailure(conn);
++ PGR_Parse_Session_Started = false;
++ return STATUS_ERROR;
++ }
++ break;
++ default :
++ break;
++ }
++ return STATUS_OK;
++ }
++ else
++ {
++ if (transaction_tbl->lock != STATUS_OK)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:[%d]transaction_tbl->lock is
[%d]",func,current_cluster,transaction_tbl->lock );
++#endif
++ transaction_tbl->lock = STATUS_OK;
++ }
++ snprintf(sync_command,sizeof(sync_command),
++ "SELECT %s(%d,%u,%u,%d) ",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_SET_CURRENT_REPLICATION_QUERY_ID_NO,
++ replicationId,
++ 0,
++ PGR_Response_Inf->response_mode);
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command(%s)",func,sync_command);
++#endif
++ res = PQexec(conn, sync_command);
++ if (res != NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command returns
%s",func,PQcmdStatus(res));
++#endif
++ PQclear(res);
++ res = NULL;
++ }
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:execute query(%s)",func,query);
++#endif
++ res = PQexec(conn, query);
++
++ }
++
++ if (res == NULL)
++ {
++ StartReplication[current_cluster] = true;
++ show_debug("%s:PQexec status error",func);
++ return STATUS_ERROR;
++ }
++
++ str = PQcmdStatus(res);
++#ifdef PRINT_DEBUG
++ show_debug("%s:PQexec returns :%s",func,str);
++#endif
++ if ((str == NULL) || (*str == '\0'))
++ {
++ if ((result != NULL) && (res != NULL) && (res->errMsg !=
NULL))
++ {
++
snprintf(result,PGR_MESSAGE_BUFSIZE,"E%s",res->errMsg);
++ }
++ else
++ {
++ strcpy(result,"E");
++ }
++ StartReplication[current_cluster] = true;
++ }
++ else
++ {
++ if
(!strncmp(str,PGR_LOCK_CONFLICT_NOTICE_CMD,strlen(PGR_LOCK_CONFLICT_NOTICE_CMD)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:LOCK CONFLICT from PQexec",func);
++#endif
++ if (res != NULL)
++ PQclear(res);
++
++ transaction_tbl->lock = STATUS_LOCK_CONFLICT;
++ return STATUS_LOCK_CONFLICT;
++ }
++ else if
(!strncmp(str,PGR_DEADLOCK_DETECT_NOTICE_CMD,strlen(PGR_DEADLOCK_DETECT_NOTICE_CMD)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:DEADLOCK DETECTED from PQexec",func);
++#endif
++ if (res != NULL)
++ PQclear(res);
++ transaction_tbl->lock = STATUS_DEADLOCK_DETECT;
++ return STATUS_DEADLOCK_DETECT;
++ }
++ snprintf(result,PGR_MESSAGE_BUFSIZE,"C%s",str);
++ }
++ if (res != NULL)
++ PQclear(res);
++
++ /* set send query id */
++ *(PGR_Send_Query_ID + hostNum ) = ntohl(header->query_id);
++
++ /*
++ * if the query is end transaction process...
++ */
++ check_delete_transaction(host_ptr,header);
++ return STATUS_OK;
++
++}
++
++static int
++check_delete_transaction (HostTbl * host_ptr, ReplicateHeader * header)
++{
++ char *database = NULL;
++
++ if ((host_ptr == NULL) || (header == NULL))
++ {
++ return STATUS_ERROR;
++ }
++ database = (char *)header->dbName;
++ if(header->cmdSts == CMD_STS_OTHER &&
++ header->cmdType == CMD_TYPE_CONNECTION_CLOSE)
++ {
++ notice_abort(host_ptr, header);
++ deleteTransactionTbl(host_ptr,header);
++ }
++
++ delete_template(host_ptr, header);
++ return STATUS_OK;
++}
++
++static void
++check_transaction_status(ReplicateHeader * header,
++ HostTbl *host,
++ TransactionTbl *transaction)
++{
++ static bool exec_copy = false;
++ if (header == (ReplicateHeader *)NULL)
++ {
++ return;
++ }
++ if (header->cmdSts == CMD_STS_TRANSACTION )
++ {
++ if (header->cmdType == CMD_TYPE_BEGIN )
++ {
++ host->transaction_count++;
++ transaction->in_transaction = true;
++ transaction->transaction_count ++;
++ }
++ else if ((header->cmdType == CMD_TYPE_COMMIT) ||
++ (header->cmdType == CMD_TYPE_ROLLBACK))
++ {
++ host->transaction_count--;
++ transaction->transaction_count --;
++ if (transaction->transaction_count == 0)
++ {
++ transaction->in_transaction = false;
++ }
++ }
++ }
++ else
++ {
++ if ( header->cmdType == CMD_TYPE_COPY )
++ {
++ exec_copy = true;
++ host->transaction_count--;
++ }
++ else if (header->cmdType == CMD_TYPE_COPY_DATA_END)
++ {
++ if (exec_copy)
++ host->transaction_count--;
++ exec_copy = false;

++ }
++ }
++
++}
++/*
++ * set query in queue
++ */
++int
++PGRset_queue(ReplicateHeader * header,char * query)
++{
++ char * func = "PGRset_queue()";
++ RecoveryQueueHeader * msg_header = NULL;
++ RecoveryQueueQuery * msg_query = NULL;
++ int size = 0;
++ long mtype = 0;
++ int rtn = 0;
++ int query_size = 0;
++
++ if ((*RecoveryMsgid < 0) || (header == NULL))
++ {
++ return STATUS_ERROR;
++ }
++
++ query_size = ntohl(header->query_size);
++ if (query_size < 0)
++ {
++ return STATUS_ERROR;
++ }
++ /*
++ * set header data
++ */
++ size = sizeof(ReplicateHeader) + sizeof(RecoveryQueueHeader);
++ mtype = *PGR_ReplicateSerializationID;
++ msg_header = (RecoveryQueueHeader *)malloc(size+4);
++ if (msg_header == NULL)
++ {
++ show_error("%s:malloc() failed. reason: %s", func,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(msg_header,0,size+4);
++ msg_header->mtype = RECOVERY_HEADER_MTYPE;
++ msg_header->query_mtype = mtype;
++ memcpy(msg_header->mdata,header,sizeof(ReplicateHeader));
++
++ /*
++ * set query data
++ */
++ size = query_size + sizeof(RecoveryQueueQuery);
++ msg_query = (RecoveryQueueQuery *) malloc(size+4);
++ if (msg_query == NULL)
++ {
++ show_error("%s:malloc() failed. reason: %s", func,
strerror(errno));
++ free(msg_header);
++ msg_header = NULL;
++ return STATUS_ERROR;
++ }
++ memset(msg_query,0,size+4);
++ msg_query->mtype = mtype;
++ msg_query->replicationId=*PGR_ReplicateSerializationID;
++ memcpy(msg_query->mdata,query,query_size);
++
++
++
++ /*
++ * send header data to queue
++ */
++ rtn = msgsnd(*RecoveryMsgid, msg_header,
sizeof(long)+sizeof(ReplicateHeader), IPC_NOWAIT);
++ if (rtn < 0)
++ {
++ show_error("%s:msgsnd failed. reason: %s", func,
strerror(errno));
++ free(msg_header);
++ free(msg_query);
++ msgctl(*RecoveryMsgid, IPC_RMID, NULL);
++ *RecoveryMsgid = msgget (IPC_PRIVATE, 00666 | IPC_CREAT );
++ return STATUS_ERROR;
++ }
++ /*
++ * send query data to queue
++ */
++ rtn = msgsnd(*RecoveryMsgid, msg_query, sizeof(unsigned
int)+query_size, IPC_NOWAIT);
++ if (rtn < 0)
++ {
++ show_error("%s:msgsnd failed. reason: %s", func,
strerror(errno));
++ free(msg_header);
++ free(msg_query);
++ msgctl(*RecoveryMsgid, IPC_RMID, NULL);
++ *RecoveryMsgid = msgget (IPC_PRIVATE, 00666 | IPC_CREAT );
++ return STATUS_ERROR;
++ }
++
++ /*
++ * release memory
++ */
++ free(msg_header);
++ free(msg_query);
++ msg_header = NULL;
++ msg_query = NULL;
++
++ return STATUS_OK;
++}
++
++HostTbl *
++PGRget_HostTbl(char * hostName, int port)
++{
++ HostTbl * ptr = NULL;
++ int len = 0;
++
++ if (Host_Tbl_Begin == NULL)
++ {
++ return NULL;
++ }
++ len = strlen(hostName);
++ ptr = Host_Tbl_Begin;
++ if (len > sizeof(ptr->hostName))
++ {
++ len = sizeof(ptr->hostName);
++ }
++ while(ptr->useFlag != DB_TBL_END)
++ {
++ if ((! memcmp(ptr->hostName,hostName,len)) &&
++ (ptr->port == port))
++ {
++ return ptr;
++ }
++ ptr ++;
++ }
++ return (HostTbl*)NULL;
++}
++
++static void
++sem_quit(int semid)
++{
++ semctl(semid, 0, IPC_RMID);
++}
++
++void
++PGRclear_connections(void)
++{
++ Dlelem *ptr = NULL, *next;
++
++ pthread_mutex_lock(&transaction_table_mutex);
++ ptr = DLGetHead(Transaction_Tbl_Begin);
++ while (ptr)
++ {
++ TransactionTbl *transaction = DLE_VAL(ptr);
++ if (transaction->conn != NULL)
++ {
++ PQfinish(transaction->conn);
++ }
++ next = DLGetSucc(ptr);
++ DLRemove(ptr);
++ DLFreeElem(ptr);
++ ptr = next;
++ }
++ DLFreeList(Transaction_Tbl_Begin);
++ Transaction_Tbl_Begin = NULL;
++ pthread_mutex_unlock(&transaction_table_mutex);
++}
++
++static bool
++is_need_sync_time(ReplicateHeader * header)
++{
++ bool rtn = false;
++
++ if ((header->cmdSts == CMD_STS_QUERY ) &&
++ ((header->cmdType == CMD_TYPE_INSERT) ||
++ (header->cmdType == CMD_TYPE_UPDATE) ||
++ (header->cmdType == CMD_TYPE_DELETE) ||
++ (header->cmdType == CMD_TYPE_SET) ||
++ (header->cmdType == CMD_TYPE_EXECUTE)))
++ {
++ rtn = true;
++ }
++ else
++ {
++ if ((header->cmdType == CMD_TYPE_COPY) ||
++ (header->cmdType == CMD_TYPE_SELECT) ||
++ (header->cmdType == CMD_TYPE_VACUUM) ||
++ (header->cmdType == CMD_TYPE_ANALYZE) ||
++ (header->cmdType == CMD_TYPE_BEGIN))
++ {
++ rtn = true;
++ }
++ if ((header->cmdSts == CMD_STS_TRANSACTION ) &&
++ (header->cmdType != CMD_TYPE_BEGIN))
++ {
++ rtn = false;
++ }
++ }
++ return rtn;
++}
++
++static bool
++is_need_wait_answer(ReplicateHeader * header)
++{
++ bool rtn = false;
++
++ if (header->cmdSts == CMD_STS_PREPARE)
++ {
++ rtn = false;
++ }
++ else if ((header->cmdType == CMD_TYPE_COPY) ||
++ (header->cmdType == CMD_TYPE_COPY_DATA) ||
++ (header->cmdType == CMD_TYPE_COPY_DATA_END))
++ {
++ rtn = false;
++ }
++ else if ((header->cmdSts == CMD_STS_QUERY ) &&
++ ((header->cmdType == CMD_TYPE_INSERT) ||
++ (header->cmdType == CMD_TYPE_UPDATE) ||
++ (header->cmdType == CMD_TYPE_DELETE) ||
++ (header->cmdType == CMD_TYPE_VACUUM) ||
++ (header->cmdType == CMD_TYPE_ANALYZE) ||
++ (header->cmdType == CMD_TYPE_EXECUTE)))
++ {
++ rtn = true;
++ }
++ else if ((header->cmdSts == CMD_STS_TRANSACTION ) ||
++ (header->cmdSts == CMD_STS_SET_SESSION_AUTHORIZATION
) ||
++ (header->cmdSts == CMD_STS_TEMP_TABLE ) ||
++ (header->cmdType == CMD_TYPE_SELECT))
++ {
++ rtn = true;
++ }
++
++ return rtn;
++}
++
++static void
++delete_template(HostTbl * ptr, ReplicateHeader * header)
++{
++ if ((ptr == (HostTbl *)NULL ) ||
++ (header == (ReplicateHeader *)NULL) )
++ {
++ return;
++ }
++
++ if ((! strncmp(header->dbName,"template1",9)) ||
++ (! strncmp(header->dbName,"template0",9)))
++ {
++ if ((header->cmdSts != CMD_STS_TRANSACTION ) &&
++ ( header->cmdSts != CMD_STS_SET_SESSION_AUTHORIZATION
) &&
++ ( header->cmdSts != CMD_STS_TEMP_TABLE ))
++ {
++ deleteTransactionTbl(ptr,header);
++ }
++ }
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * check_copy_command()
++ * NOTES
++ * check the query which it is copy command or not
++ * when the query is 'copy from', set 'stdin' after 'from'
++ * ARGS
++ * char * query: query strings(I)
++ * RETURN
++ * copy command : changed copy command
++ * other command : NULL
++ *--------------------------------------------------------------------
++ */
++static char *
++check_copy_command(char * query)
++{
++ char * p;
++ char * p1, *p2, *wp;
++ char * buf;
++ int size;
++
++ if (query == NULL)
++ return NULL;
++ size = strlen(query) + strlen(" stdin ");
++ p = p1 = query;
++ wp = strstr(p,"FROM");
++ if (wp == NULL)
++ wp = strstr(p,"from");
++
++ if (wp != NULL)
++ {
++ p = wp + strlen("FROM");
++ *p = '\0';
++ p ++;
++ while ((isspace(*p)) && (*p != '\0')) p++;
++ while ((!isspace(*p)) && (*p != '\0')) p++;
++ p2 = p;
++ buf = malloc(size);
++ if (buf == NULL)
++ {
++ return NULL;
++ }
++ snprintf(buf,size,"%s stdin %s",p1,p2);
++ return buf;
++ }
++ return NULL;
++}
++
++static int
++next_replication_id(void)
++{
++ char * func = "next_replication_id()";
++
++ if (Recovery_Status_Inf == (RecoveryStatusInf *)NULL)
++ {
++ show_error("%s: Recovery_Status_Inf is NULL",func);
++ return -1;
++ }
++ Recovery_Status_Inf->replication_id ++;
++ Recovery_Status_Inf->check_point --;
++ return (Recovery_Status_Inf->replication_id);
++}
++
++static void
++check_replication_id(void)
++{
++ char * func = "check_replication_id()";
++
++ if (Recovery_Status_Inf == (RecoveryStatusInf *)NULL)
++ {
++ show_error("%s: Recovery_Status_Inf is NULL",func);
++ return ;
++ }
++ if (Recovery_Status_Inf->check_point < 0)
++ {
++ Recovery_Status_Inf->check_point = PGR_CHECK_POINT ;
++ rewind(RidFp);
++
PGRwrite_log_file(RidFp,"%u",Recovery_Status_Inf->replication_id +
PGR_CHECK_POINT );
++ }
++}
++
++int
++PGRset_replication_id(uint32_t id)
++{
++ char * func = "set_replication_id()";
++
++ Recovery_Status_Inf->replication_id = id;
++ return (Recovery_Status_Inf->replication_id);
++}
++
++int
++PGRdo_replicate(int sock,ReplicateHeader *header, char * query)
++{
++
++ char * func = "PGRdo_replicate()";
++
++ struct timeval tv;
++ int status = STATUS_OK;
++ int recovery_status = 0;
++ char * query_string = NULL;
++
++ if (header->cmdType == CMD_TYPE_COPY)
++ {
++ query_string = check_copy_command(query);
++ if (query_string == NULL)
++ {
++ return LOOP_CONTINUE;
++ }
++ }
++ else
++ {
++ query_string = query;
++ if (header->cmdType == CMD_TYPE_SET)
++ {
++ if (is_autocommit_off(query_string) == true)
++ {
++ PGR_AutoCommit = false;
++ }
++ else if (is_autocommit_on(query_string) == true)
++ {
++ PGR_AutoCommit = true;
++ }
++ }
++ }
++ header->isAutoCommit=PGR_AutoCommit ? 1 : 0;
++ gettimeofday(&tv,NULL);
++ header->tv.tv_sec = htonl(tv.tv_sec);
++ header->tv.tv_usec = htonl(tv.tv_usec);
++
++ /* set query id */
++ header->query_id = htonl(PGRget_next_query_id());
++
++ /* save header for logging */
++ if (is_need_sync_time(header) == true)
++ {
++ if (PGR_Log_Header != NULL)
++ {
++ memcpy(PGR_Log_Header,header,sizeof(ReplicateHeader));
++ if (header->rlog == 0)
++ {
++ PGR_Log_Header->replicate_id =
htonl(next_replication_id());
++ }
++ }
++ }
++ /* check rlog */
++ if (header->rlog == CONNECTION_SUSPENDED_TYPE )
++ {
++ if (PGRget_rlog_header(header) == STATUS_OK)
++ {
++ header->rlog = CONNECTION_SUSPENDED_TYPE;
++
++ }
++ }
++
++ /* check recovery mode */
++
++ recovery_status = PGRget_recovery_status();
++ PGRcheck_recovered_host();
++
++ /* send replication packet */
++ status = PGRreplicate_packet_send(
header,query_string,sock,recovery_status);
++ show_debug("%s:PGRreplicate_packet_send returns %d",func,status);
++
++#if 0
++ if(recovery_status == RECOVERY_START &&
++ PGRset_queue(header,query_string)!= STATUS_OK)
++ {
++ /*
++ *Failed to set queue. abort.
++ */
++ show_error("%s:failed to put query to queue.abort to
recovery",func);
++ PGRset_recovery_status(RECOVERY_INIT);
++ }
++#endif
++
++ if ((header->cmdType == CMD_TYPE_COPY) &&
++ (query_string != NULL))
++ {
++ free(query_string);
++ query_string = NULL;
++ }
++
++ if (status == STATUS_ABORTED )
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:status is STATUS_ABORTED",func);
++#endif
++ return LOOP_END;
++ }
++ if (status == STATUS_DEADLOCK_DETECT)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:status is STATUS_DEADLOCK_DETECT",func);
++#endif
++ return LOOP_END;
++ }
++ return LOOP_CONTINUE;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * PGRreturn_result()
++ * NOTES
++ * Return result of execution
++ * ARGS
++ * int dest: socket of destination server (I)
++ * char *result: result data(I)
++ * int wait: wait flag (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ * NG: STATUS_LOCK_CONFLICT
++ * NG: STATUS_DEADLOCK_DETECT
++ *--------------------------------------------------------------------
++ */
++int
++PGRreturn_result(int dest, char * result, int wait)
++{
++ char * func = "PGRreturn_result()";
++ fd_set wmask;
++ struct timeval timeout;
++ int rtn = 0;
++ char * send_ptr = NULL;
++ int send_size= 0;
++ int buf_size = 0;
++ int s = 0;
++ int status = 0;
++ int flag = 0;
++
++ if (result == NULL)
++ {
++ show_error("%s:result is not initialize",func);
++ return STATUS_ERROR;
++ }
++ if (dest < 0)
++ {
++ return STATUS_ERROR;
++ }
++ send_ptr = result;
++ buf_size = PGR_MESSAGE_BUFSIZE;
++ if (buf_size < 1)
++ buf_size = 1;
++
++ /*
++ * Wait for something to happen.
++ */
++#ifdef MSG_DONTWAIT
++ flag |= MSG_DONTWAIT;
++#endif
++#ifdef MSG_NOSIGNAL
++ flag |= MSG_NOSIGNAL;
++#endif
++
++ for (;;)
++ {
++ timeout.tv_sec = PGR_SEND_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ FD_ZERO(&wmask);
++ FD_SET(dest,&wmask);
++
++ rtn = select(dest+1, (fd_set *)NULL, &wmask, (fd_set *)NULL,
&timeout);
++ if (rtn < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++
++ show_error("%s:select failed ,errno is %s",func ,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ else if (rtn && FD_ISSET(dest, &wmask))
++ {
++ s = send(dest,send_ptr + send_size,buf_size -
send_size ,flag);
++ if (s < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++ else
++ {
++ show_error("%s:send error: %d(%s)",
func, errno, strerror(errno));
++ memset(send_ptr, 0,
PGR_MESSAGE_BUFSIZE);
++ return STATUS_ERROR;
++ }
++ }
++ else if (s > 0)
++ {
++ send_size += s;
++ if (send_size == buf_size)
++ {
++ status = STATUS_OK;
++ if (wait == PGR_WAIT_ANSWER)
++ {
++ status = read_answer(dest);
++ }
++ return status;
++ }
++ }
++ else /* s == 0 */
++ {
++ show_error("%s:unexpected EOF", func);
++ memset(send_ptr, 0, PGR_MESSAGE_BUFSIZE);
++ return STATUS_ERROR;
++ }
++ }
++ }
++ memset(send_ptr, 0, PGR_MESSAGE_BUFSIZE);
++ return STATUS_ERROR;
++}
++
++/*--------------------------------------------------------------------
++ * SYMBOL
++ * read_answer()
++ * NOTES
++ * Receive answer packet
++ * ARGS
++ * int dest: socket of destination server (I)
++ * RETURN
++ * OK: STATUS_OK
++ * NG: STATUS_ERROR
++ * NG: STATUS_LOCK_CONFLICT
++ * NG: STATUS_DEADLOCK_DETECT
++ *--------------------------------------------------------------------
++ */
++static int
++read_answer(int dest)
++{
++ char * func = "read_answer()";
++ fd_set rmask;
++ struct timeval timeout;
++ int rtn;
++ ReplicateHeader header;
++ char * answer = NULL;
++ int status = STATUS_ERROR;
++
++ for(;;)
++ {
++ if (answer != NULL)
++ {
++ free(answer);
++ answer = NULL;
++ }
++ timeout.tv_sec = PGR_RECV_TIMEOUT;
++ timeout.tv_usec = 0;
++ FD_ZERO(&rmask);
++ FD_SET(dest,&rmask);
++ rtn = select(dest+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++
++ show_error("%s:select failed ,errno is %s",func ,
strerror(errno));
++ return STATUS_ERROR;
++ }
++ else if (rtn && FD_ISSET(dest, &rmask))
++ {
++ memset(&header,0,sizeof(ReplicateHeader));
++ answer = PGRread_packet(dest,&header);
++ if (answer == NULL)
++ {
++ status = STATUS_ERROR;
++ break;
++ }
++ if ((header.cmdSts != CMD_STS_RESPONSE) &&
++ (header.cmdSts != CMD_STS_NOTICE))
++ {
++ show_error("%s:[%c]none response packet
received",func,header.cmdSts);
++ free(answer);
++ answer = NULL;
++ status = STATUS_ERROR;
++ break;
++ }
++ if (answer != NULL)
++ {
++ if
(!strncmp(answer,PGR_QUERY_DONE_NOTICE_CMD,strlen(PGR_QUERY_DONE_NOTICE_CMD)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:QUERY DONE",func);
++#endif
++ status = STATUS_OK;
++ }
++ else if
(!strncmp(answer,PGR_QUERY_ABORTED_NOTICE_CMD,strlen(PGR_QUERY_ABORTED_NOTICE_CMD)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:QUERY ABORTED",func);
++#endif
++ status = STATUS_ABORTED;
++ }
++ else if
(!strncmp(answer,PGR_LOCK_CONFLICT_NOTICE_CMD,strlen(PGR_LOCK_CONFLICT_NOTICE_CMD)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:LOCK CONFLICT
!!",func);
++#endif
++ status = STATUS_LOCK_CONFLICT;
++ }
++ else if
(!strncmp(answer,PGR_DEADLOCK_DETECT_NOTICE_CMD,strlen(PGR_DEADLOCK_DETECT_NOTICE_CMD)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:DEADLOCK DETECT
!!",func);
++#endif
++ status = STATUS_DEADLOCK_DETECT;
++ }
++ free(answer);
++ answer = NULL;
++ }
++ return status;
++ }
++ }
++ return status;
++}
++
++/*--------------------------------------------------
++ * SYMBOL
++ * PGRreplicate_packet_send()
++ * NOTES
++ * Send query to each cluster DB servers and return result.
++ * ARGS
++ * ReplicateHeader * header : packet header (I)
++ * char * query : query for replication (I)
++ * int dest : destination socket for return result (I)
++ * RETURN
++ * OK : STATUS_OK
++ * NG : STATUS_ERROR
++ * DEADLOCK : STATUS_DEADLOCK_DETECT
++ *---------------------------------------------------
++ */
++int
++PGRreplicate_packet_send( ReplicateHeader * header, char * query,int
dest,int recovery_status) {
++ return
replicate_packet_send_internal(header,query,dest,recovery_status,false);
++}
++
++
++int
++replicate_packet_send_internal(ReplicateHeader * header, char * query,int
dest,int recovery_status,bool isHeldLock)
++{
++ char * func = "PGRreplicate_packet_send()";
++ HostTbl * host_ptr = (HostTbl*)NULL;
++ HostTbl * source_host_ptr = (HostTbl*)NULL;
++ int status = STATUS_OK;
++ int sem_cnt = 0;
++ int sem_id = 0;
++ char *database = NULL;
++ char port[8];
++ char *userName = NULL;
++ char * host = NULL;
++ char result[PGR_MESSAGE_BUFSIZE];
++
++ pthread_attr_t attr;
++ int rc = 0;
++ int t = 0;
++ int t_cnt = 0;
++ int source_t_cnt = -1;
++ int transaction_count = 0;
++
++
++ pthread_t thread[MAX_DB_SERVER];
++ ThreadArgInf thread_arg[MAX_DB_SERVER];
++
++
++#ifdef PRINT_DEBUG
++ show_debug("cmdSts=%c",header->cmdSts);
++ if(header->cmdType!='\0')
++ show_debug("cmdType=%c",header->cmdType);
++ show_debug("rlog=%d",header->rlog);
++ show_debug("port=%d",ntohs(header->port));
++ show_debug("pid=%d",ntohs(header->pid));
++ show_debug("from_host=%s",header->from_host);
++ show_debug("dbName=%s",header->dbName);
++ show_debug("userName=%s",header->userName);
++ show_debug("recieve sec=%u",ntohl(header->tv.tv_sec));
++ show_debug("recieve usec=%u",ntohl(header->tv.tv_usec));
++ show_debug("query_size=%d",ntohl(header->query_size));
++ show_debug("request_id=%d",ntohl(header->request_id));
++ show_debug("replicate_id=%d",ntohl(header->replicate_id));
++ if (header->cmdSts != CMD_STS_PREPARE)
++ {
++ show_debug("query=%s",query);
++ }
++#endif
++
++ /* check rlog type */
++ if (header->rlog == FROM_R_LOG_TYPE)
++ {
++ if (is_executed_query_in_origin(header) == false)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("this query is not yet done in source
cluster db. so it wait for receive re-replicate request");
++#endif
++ /* wait re-replicate request */
++ return STATUS_SKIP_REPLICATE;
++ }
++ }
++ /*
++ * loop while registrated cluster DB exist
++ */
++ if (Host_Tbl_Begin == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ host_ptr = Host_Tbl_Begin;
++ PGR_Response_Inf->current_cluster = 0;
++ memset(result,0,sizeof(result));
++ sem_cnt = 1;
++
++ if (is_need_queue_jump(header,query) == false)
++ {
++ sem_id = SemID;
++ }
++ else
++ {
++ sem_id = VacuumSemID;
++ }
++ if(!isHeldLock) {
++#ifdef PRINT_DEBUG
++
++ show_debug("sem_lock [%d] req",sem_cnt);
++#endif
++
++ PGRsem_lock(sem_id,sem_cnt);
++#ifdef PRINT_DEBUG
++ show_debug("sem_lock [%d] got it",sem_cnt);
++#endif
++ }
++ ++*PGR_ReplicateSerializationID;
++
++ /* set replication log */
++ if (is_need_use_rlog(header) == true)
++ {
++ PGRset_rlog(header,query);
++ }
++
++ pthread_attr_init(&attr);
++ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
++ PGR_Response_Inf->current_cluster = 0;
++ t_cnt = 0;
++ while(host_ptr->useFlag != DB_TBL_END)
++ {
++ show_debug("%s:checking host %s for creating
threads",func,host_ptr->hostName);
++ /*
++ * check the status of the cluster DB
++ */
++ if (host_ptr->useFlag != DB_TBL_USE)
++ {
++ host_ptr ++;
++ continue;
++ }
++ /*
++ * skip loop during recover and the host name is master DB
++ */
++ if (is_master_in_recovery(host_ptr->hostName,
host_ptr->port,recovery_status) == true)
++ {
++ TransactionTbl *transaction_tbl =
getTransactionTbl(host_ptr,header);
++ if (host_ptr->transaction_count == 0 ||
++ transaction_tbl == NULL ||
++ transaction_tbl->in_transaction == false)
++ {
++ if (PGRset_queue(header,query) != STATUS_OK)
++ {
++ show_error("%s:failed to put query to
queue.abort to recovery",func);
++ PGRset_recovery_status(RECOVERY_INIT);
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s master is using for
recovery",func);
++#endif
++ host_ptr ++;
++ continue;
++ }
++ }
++ /*
++ * compare with the host name and the exceptional host name
++ */
++ thread_arg[t_cnt].header = header;
++ thread_arg[t_cnt].query = query;
++ thread_arg[t_cnt].dest = dest;
++ thread_arg[t_cnt].host_ptr = host_ptr;
++ thread_arg[t_cnt].current_cluster = t_cnt;
++ thread_arg[t_cnt].transaction_tbl = (TransactionTbl *)NULL;
++
++ if
(PGRis_same_host(header->from_host,ntohs(header->port),host_ptr->resolvedName,
host_ptr->port) == true)
++ {
++ show_debug("[%d] is same host",t_cnt);
++ /* replication to source cluster db */
++ source_host_ptr = host_ptr;
++ source_t_cnt = t_cnt;
++
++ if (header->rlog == FROM_R_LOG_TYPE )
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: This simple query was
suspended. Therefore this query is not re-replicated to source cluster
db.",func);
++#endif
++ }
++ t_cnt++;
++ }
++ /* replication to other cluster db */
++ else
++ {
++ show_debug("[%d] is not same host",t_cnt);
++ if ((header->rlog == CONNECTION_SUSPENDED_TYPE ) &&
++ (header->cmdSts == CMD_STS_TRANSACTION) )
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: This transaction query was
suspended. Therefore this query is not replicated to other cluster
dbs.",func);
++#endif
++ }
++ else
++ {
++ /*
++ * get the transaction table data
++ * it has the connection data with each
cluster DB
++ */
++ thread_arg[t_cnt].transaction_tbl =
getTransactionTbl(host_ptr,header);
++ /*
++ * if the transaction process is new one,
++ * create connection data and add the
transaction table
++ */
++ if (thread_arg[t_cnt].transaction_tbl ==
(TransactionTbl *)NULL)
++ {
++ thread_arg[t_cnt].transaction_tbl =
setTransactionTbl(host_ptr, header);
++ if (thread_arg[t_cnt].transaction_tbl
== (TransactionTbl *)NULL)
++ {
++#ifdef PRINT_DEBUG
++
show_debug("%s:setTransactionTbl failed",func);
++#endif
++ if ( header->cmdSts !=
CMD_STS_NOTICE )
++ {
++
PGRset_host_status(host_ptr,DB_TBL_ERROR);
++ }
++ host_ptr ++;
++ continue;
++ }
++ StartReplication[t_cnt] = true;
++ }
++ else
++ {
++ /*
++ * re-use the connection data
++ */
++ if
((thread_arg[t_cnt].transaction_tbl->conn != (PGconn *)NULL) &&
++
(thread_arg[t_cnt].transaction_tbl->conn->sock > 0))
++ {
++ /*
++
memset(thread_arg[t_cnt].transaction_tbl->conn->inBuffer,0,thread_arg[t_cnt].transaction_tbl->conn->inBufSize);
++
memset(thread_arg[t_cnt].transaction_tbl->conn->outBuffer,0,thread_arg[t_cnt].transaction_tbl->conn->outBufSize);
++ */
++ StartReplication[t_cnt] =
false;
++ }
++ else
++ {
++ if
(thread_arg[t_cnt].transaction_tbl->conn != (PGconn *)NULL)
++ {
++
PQfinish(thread_arg[t_cnt].transaction_tbl->conn);
++
thread_arg[t_cnt].transaction_tbl->conn = NULL;
++ }
++
++ database = (char
*)header->dbName;
++
snprintf(port,sizeof(port),"%d", host_ptr->port);
++ userName = (char
*)header->userName;
++ host = host_ptr->hostName;
++
++
thread_arg[t_cnt].transaction_tbl->conn =
pgr_createConn(host,port,database,userName);
++ StartReplication[t_cnt] =
true;
++ }
++ }
++ check_transaction_status(header, host_ptr,
thread_arg[t_cnt].transaction_tbl);
++ transaction_count =
thread_arg[t_cnt].transaction_tbl->transaction_count;
++ rc = pthread_create(&thread[t_cnt], &attr,
thread_send_cluster, (void*)&thread_arg[t_cnt]);
++
++ if (rc)
++ {
++ show_error("pthread_create error");
++ }
++ t_cnt++;
++ }
++ }
++ /*
++ * send replication query to each cluster server
++ */
++
++ host_ptr++;
++ PGR_Response_Inf->current_cluster ++;
++ status = STATUS_OK;
++ }
++ pthread_attr_destroy(&attr);
++ for ( t = 0 ; t < t_cnt; )
++ {
++ if (t == source_t_cnt)
++ {
++ t++;
++ continue;
++ }
++ rc = pthread_join(thread[t], (void **)NULL);
++ if ((rc != 0) && (errno == EINTR))
++ {
++ usleep(100);
++ continue;
++ }
++ pthread_detach(thread[t]);
++ t++;
++ }
++
++ thread_arg[source_t_cnt].transaction_count = transaction_count;
++ /*
++ * send replication query to source cluster server.
++ */
++ if (source_t_cnt >= 0)
++ {
++ thread_send_source( (void*)&thread_arg[source_t_cnt]);
++ }
++ else
++ {
++ show_debug("thread_send_source was not executed");
++ }
++ /* unset replication log */
++ if (is_need_use_rlog(header) == true)
++ {
++ PGRunset_rlog(header,query);
++ }
++
++ check_replication_id();
++ if (header->cmdSts == CMD_STS_PREPARE)
++ {
++ if (header->cmdType != CMD_TYPE_P_SYNC)
++ {
++ if (PGR_Parse_Session_Started == false)
++ {
++ PGR_Parse_Session_Started = true;
++ }
++ }
++ }
++ else
++ {
++ PGR_Parse_Session_Started = false;
++ }
++
++ if(!isHeldLock) {
++#ifdef PRINT_DEBUG
++ show_debug("sem_unlock[%d]",sem_cnt);
++#endif
++ PGRsem_unlock(sem_id,sem_cnt);
++ }
++ return status;
++}
++
++static void *
++thread_send_source(void * arg)
++{
++ char * func = "thread_send_source()";
++ ThreadArgInf * thread_arg = NULL;
++ ReplicateHeader * header = (ReplicateHeader*)NULL;
++ char * query = NULL;
++ int dest = 0;
++ HostTbl * host_ptr = (HostTbl*)NULL;
++ int status = STATUS_OK;
++ int transaction_count = 0;
++ char result[PGR_MESSAGE_BUFSIZE];
++ bool sync_command_flg = false;
++
++ if (arg == NULL)
++ {
++ show_error("%s:arg is NULL",func);
++ status = STATUS_ERROR;
++ pthread_exit((void *) status);
++ }
++ thread_arg = (ThreadArgInf *)arg;
++ header = thread_arg->header;
++ query = thread_arg->query;
++ dest = thread_arg->dest;
++ host_ptr = thread_arg->host_ptr;
++ transaction_count = thread_arg->transaction_count;
++
++ if(header->cmdSts==CMD_STS_OTHER &&
++ header->cmdType==CMD_TYPE_CONNECTION_CLOSE)
++ {
++ return (void *)0;
++ }
++
++ if (header->rlog == FROM_R_LOG_TYPE )
++ {
++ /* It is not necessary to return rlog to source DB. */
++#ifdef PRINT_DEBUG
++ show_debug("%s: It is not necessary to return rlog to source
DB",func);
++#endif
++ status = STATUS_OK;
++ // pthread_exit((void *) status);
++ return (void *)status;
++ }
++
++ /**
++ * NOTE:
++ * We can use PGR_ReplicateSerializationID here , because
++ * all queries from cluster server isn't recovery query.
++ *
++ */
++ if (is_need_sync_time(header) == true)
++ {
++ if (transaction_count >1 )
++ {
++ sync_command_flg = false;
++ }
++ else
++ {
++ sync_command_flg = true;
++ }
++ }
++ if (sync_command_flg == true)
++ {
++ snprintf(result,PGR_MESSAGE_BUFSIZE,
++ "%d,%u,%u,%u,%d,%u",
++ PGR_SET_CURRENT_TIME_FUNC_NO,
++ (unsigned int)ntohl(header->tv.tv_sec),
++ (unsigned int)ntohl(header->tv.tv_usec),
++ (unsigned int)ntohl(PGR_Log_Header->replicate_id),
++ PGR_Response_Inf->response_mode,
++ *PGR_ReplicateSerializationID);
++ }
++ else
++ {
++ snprintf(result,PGR_MESSAGE_BUFSIZE,
++ "%d,%u,%u,%d",
++ PGR_SET_CURRENT_REPLICATION_QUERY_ID_NO,
++ *PGR_ReplicateSerializationID,
++ 0,
++ PGR_Response_Inf->response_mode);
++ }
++ /* execute query in the exceptional host */
++ /* it is not use replication */
++ if (is_need_wait_answer(header) == true)
++ {
++ status = PGRreturn_result(dest,result, PGR_WAIT_ANSWER);
++ }
++ else
++ {
++ status = PGRreturn_result(dest, result, PGR_NOWAIT_ANSWER);
++ }
++
++ if (status == STATUS_ERROR )
++ {
++ show_error("%s: %s[%d] should be down
",func,host_ptr->hostName,host_ptr->port);
++ PGRset_host_status(host_ptr,DB_TBL_ERROR);
++ }
++
++ /* delete server table when query use template db */
++ if (PGR_Response_Inf->response_mode != PGR_RELIABLE_MODE)
++ {
++ delete_template(host_ptr,header);
++ }
++#ifdef PRINT_DEBUG
++ show_debug("end thread_send_source()");
++#endif
++ return (void *)0;
++ // pthread_exit((void *) 0);
++}
++
++static void *
++thread_send_cluster(void * arg)
++{
++ char * func = "thread_send_cluster()";
++ ThreadArgInf * thread_arg = NULL;
++ ReplicateHeader * header = (ReplicateHeader*)NULL;
++ char * query = NULL;
++ int dest = 0;
++ HostTbl * host_ptr = (HostTbl*)NULL;
++ int rtn = 0;
++ int status = STATUS_OK;
++ TransactionTbl * transaction_tbl = (TransactionTbl *)NULL;
++ int current_cluster = 0;
++ char result[PGR_MESSAGE_BUFSIZE];
++
++#ifdef PRINT_DEBUG
++ show_debug("start thread_send_cluster()");
++#endif
++ if (arg == NULL)
++ {
++ show_error("%s:arg is NULL",func);
++ status = STATUS_ERROR;
++ pthread_exit((void *) status);
++ }
++
++ thread_arg = (ThreadArgInf *)arg;
++ header = thread_arg->header;
++ query = thread_arg->query;
++ dest = thread_arg->dest;
++ host_ptr = thread_arg->host_ptr;
++ transaction_tbl = thread_arg->transaction_tbl;
++ current_cluster = thread_arg->current_cluster;
++
++
++ if(header->cmdSts==CMD_STS_OTHER &&
++ header->cmdType==CMD_TYPE_CONNECTION_CLOSE)
++ {
++ check_delete_transaction(host_ptr, header);
++ return (void *)0;
++ }
++
++
++ rtn = send_replicate_packet_to_server( transaction_tbl,
current_cluster, host_ptr, header, query ,
result,*PGR_ReplicateSerializationID, false);
++ show_debug("%s:return value from send_replicate_packet_to_server() is
%d",func,rtn);
++ if (rtn == STATUS_ABORTED)
++ {
++ snprintf(result,PGR_MESSAGE_BUFSIZE,"%d",
PGR_NOTICE_ABORT_FUNC_NO);
++#ifdef PRINT_DEBUG
++ show_debug("%s:pthread abort[%d]",func,current_cluster );
++#endif
++ status = PGRreturn_result(dest, result, PGR_NOWAIT_ANSWER);
++ status = STATUS_ABORTED;
++ pthread_exit((void *) status);
++ }
++ /* delete server table when query use template db */
++ delete_template(host_ptr,header);
++#ifdef PRINT_DEBUG
++ show_debug("%s:pthread_exit[%d]",func,current_cluster );
++#endif
++
++ pthread_exit((void *) 0);
++}
++
++/*--------------------------------------------------
++ * SYMBOL
++ * PGRreplicate_packet_send_each_server()
++ * NOTES
++ * Send query to a cluster DB server and return result.
++ * ARGS
++ * HostTbl * ptr : cluster server info table (I)
++ * bool return_response : flag for return result(I)
++ * ReplicateHeader * header: header data (I)
++ * char * query : query data (I)
++ * int dest : socket of destination server(I)
++ * RETURN
++ * OK : STATUS_OK
++ * NG : STATUS_ERROR
++ *---------------------------------------------------
++ */
++int
++PGRreplicate_packet_send_each_server( HostTbl * ptr, bool return_response,
ReplicateHeader * header, char * query,int dest)
++{
++ char * func = "PGRreplicate_packet_send_each_server()";
++ char * host;
++ int rtn;
++
++ host = ptr->hostName;
++ /*
++ * send query to cluster DB
++ */
++ if (PGR_Result == NULL)
++ {
++ show_error("%s:PGR_Result is not initialize",func);
++ return STATUS_ERROR;
++ }
++
++ rtn = PGRsend_replicate_packet_to_server( ptr,
header,query,PGR_Result, dest, false);
++
++ return rtn;
++}
++
++/*--------------------------------------------------
++ * SYMBOL
++ * PGRread_packet()
++ * NOTES
++ * Read packet data and send the query to each cluster DB.
++ * The packet data has header data and query data.
++ * ARGS
++ * int sock : socket (I)
++ * ReplicateHeader *header : header data (O)
++ * RETURN
++ * OK: pointer of read query
++ * NG: NULL
++ *---------------------------------------------------
++ */
++char *
++PGRread_packet(int sock, ReplicateHeader *header)
++{
++ char * func = "PGRread_packet()";
++ int r =0;
++ int cnt = 0;
++ char * read_ptr = NULL;
++ int read_size = 0;
++ int header_size = 0;
++ char * query = NULL;
++ fd_set rmask;
++ struct timeval timeout;
++ int rtn;
++
++ if (header == NULL)
++ {
++ return NULL;
++ }
++ memset(header,0,sizeof(ReplicateHeader));
++ read_ptr = (char*)header;
++ header_size = sizeof(ReplicateHeader);
++ cnt = 0;
++
++ for (;;){
++ /*
++ * read header data
++ */
++
++ timeout.tv_sec = 1;
++ timeout.tv_usec = 0;
++
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(sock,&rmask);
++ rtn = select(sock+1, &rmask, (fd_set *)NULL,(fd_set *)NULL,
&timeout);
++
++ if (rtn < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++
++ show_error("%s:select failed ,errno is %s",func ,
strerror(errno));
++ return NULL;
++ }
++
++ if (rtn && FD_ISSET(sock, &rmask))
++ {
++ r = recv(sock,read_ptr + read_size ,header_size -
read_size, MSG_WAITALL);
++ /*
++ r = recv(sock,read_ptr + read_size ,header_size -
read_size, 0);
++ */
++ if (r < 0)
++ {
++ show_debug("%s:recv failed:
(%s)",func,strerror(errno));
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++ else
++ {
++ show_error("%s:recv failed:
(%s)",func,strerror(errno));
++ return NULL;
++ }
++ }
++ else if (r > 0)
++ {
++ read_size += r;
++ if ( read_size == header_size)
++ {
++ query = PGRread_query(sock,header);
++ return query;
++ }
++ }
++ else if (r == 0)
++ {
++ return NULL;
++ }
++ }
++ }
++ return NULL;
++}
++
++char *
++PGRread_query(int sock, ReplicateHeader *header)
++{
++ char * func = "PGRread_query()";
++ int r =0;
++ int cnt = 0;
++ char * read_ptr;
++ int read_size = 0;
++ int query_size = 0;
++ char * query = NULL;
++
++ query_size = ntohl(header->query_size);
++ if (query_size < 0)
++ {
++ show_error("%s:receive size less than 0",func);
++ return NULL;
++ }
++ query = malloc(query_size+4);
++ if (query == NULL)
++ {
++ /*
++ * buffer allocation failed
++ */
++ show_error("%s:malloc failed: (%s)",func,strerror(errno));
++ return NULL;
++ }
++ memset(query,0,query_size+4);
++ if (query_size == 0)
++ {
++ return query;
++ }
++ read_size = 0;
++ cnt = 0;
++ read_ptr = (char *)query;
++ for (;;)
++ {
++ /*
++ * read query data
++ */
++
++ /*r = recv(sock,read_ptr + read_size ,query_size - read_size,
MSG_WAITALL); */
++ r = recv(sock,read_ptr + read_size ,query_size - read_size,
0);
++ if (r < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++ else
++ {
++ show_error("%s:recv failed:
(%s)",func,strerror(errno));
++ free(query);
++ query = NULL;
++ return NULL;
++ }
++ }
++ else if (r > 0)
++ {
++ read_size += r;
++ if ( read_size == query_size)
++ {
++ return query;
++ }
++ }
++ else /* r == 0 */
++ {
++ show_error("%s:unexpected EOF", func);
++ free(query);
++ query = NULL;
++ return NULL;
++ }
++ }
++ free(query);
++ query = NULL;
++ return NULL;
++}
++
++static bool
++is_autocommit_off(char * query)
++{
++ int i;
++ char buf[256];
++ char * p = NULL;
++
++ if (query == NULL)
++ return false;
++ memset(buf,0,sizeof(buf));
++ p = query;
++ i = 0;
++ while ( *p != '\0' )
++ {
++ buf[i++] = toupper(*p);
++ p++;
++ if (i >= (sizeof(buf) -2))
++ break;
++ }
++ p = strstr(buf,"AUTOCOMMIT");
++ if ( p == NULL)
++ {
++ return false;
++ }
++ p = strstr(buf,"OFF");
++ if ( p == NULL )
++ {
++ return false;
++ }
++ return true;
++}
++
++static bool
++is_autocommit_on(char * query)
++{
++ int i;
++ char buf[256];
++ char * p = NULL;
++
++ if (query == NULL)
++ return false;
++ memset(buf,0,sizeof(buf));
++ p = query;
++ i = 0;
++ while ( *p != '\0' )
++ {
++ buf[i++] = toupper(*p);
++ p++;
++ if (i >= (sizeof(buf) -2))
++ break;
++ }
++ p = strstr(buf,"AUTOCOMMIT");
++ if ( p == NULL)
++ {
++ return false;
++ }
++ p = strstr(buf,"ON");
++ if ( p == NULL )
++ {
++ return false;
++ }
++ return true;
++}
++
++static unsigned int
++get_host_ip_from_tbl(char * host)
++{
++ Dlelem * ptr = NULL;
++
++ pthread_mutex_lock(&transaction_table_mutex);
++ if (Transaction_Tbl_Begin == NULL)
++ {
++ pthread_mutex_unlock(&transaction_table_mutex);
++ return 0;
++ }
++ ptr = DLGetHead(Transaction_Tbl_Begin);
++ while (ptr)
++ {
++ TransactionTbl *transaction = DLE_VAL(ptr);
++ if
(!strncmp(transaction->host,host,sizeof(transaction->host)))
++ {
++ pthread_mutex_unlock(&transaction_table_mutex);
++ return transaction->hostIP;
++ }
++ ptr = DLGetSucc(ptr);
++ }
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return 0;
++}
++
++static unsigned int
++get_srcHost_ip_from_tbl(char * srcHost)
++{
++ Dlelem * ptr = NULL;
++
++ pthread_mutex_lock(&transaction_table_mutex);
++
++ if (Transaction_Tbl_Begin == NULL)
++ {
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return 0;
++ }
++ ptr = DLGetHead(Transaction_Tbl_Begin);
++ while (ptr)
++ {
++ TransactionTbl *transaction = DLE_VAL(ptr);
++ if
(!strncmp(transaction->srcHost,srcHost,sizeof(transaction->srcHost)))
++ {
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return transaction->srcHostIP;
++ }
++ ptr = DLGetSucc(ptr);
++ }
++ pthread_mutex_unlock(&transaction_table_mutex);
++
++ return 0;
++}
++
++unsigned int
++PGRget_next_query_id(void)
++{
++ if (PGR_Query_ID >= PGR_MAX_QUERY_ID)
++ {
++ PGR_Query_ID = 0;
++ }
++ PGR_Query_ID ++;
++ return PGR_Query_ID;
++}
++
++
++void
++PGRnotice_replication_server(char * hostName, unsigned short
portNumber,unsigned short recoveryPortNumber, unsigned short
lifecheckPortNumber, char * userName)
++{
++ char * func ="PGRnotice_replication_server()";
++ ReplicateHeader header;
++ char query[PGR_MESSAGE_BUFSIZE];
++
++ if (((hostName == NULL) || (*hostName == 0)) ||
++ ((userName == NULL) || (*userName == 0)) ||
++ ((portNumber == 0) || (recoveryPortNumber == 0)))
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: can not connect
server[%s][%s][%d][%d]",func,hostName,userName,portNumber,recoveryPortNumber);
++#endif
++ return;
++ }
++ memset(&header,0,sizeof(ReplicateHeader));
++ memset(query,0,sizeof(query));
++ snprintf(query,sizeof(query)-1,"SELECT %s(%d,'%s',%d,%d,%d)",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_STARTUP_REPLICATION_SERVER_FUNC_NO,
++ hostName,
++ portNumber,
++ recoveryPortNumber,
++ lifecheckPortNumber);
++ header.cmdSys = CMD_SYS_CALL;
++ header.cmdSts = CMD_STS_NOTICE;
++ header.query_size = htonl(strlen(query));
++ header.query_id = htonl(PGRget_next_query_id());
++ strncpy(header.from_host,hostName,sizeof(header.from_host));
++ strncpy(header.userName,userName,sizeof(header.userName));
++ strcpy(header.dbName,"template1");
++ PGRreplicate_packet_send( &header, query, NOTICE_SYSTEM_CALL_TYPE
,RECOVERY_INIT);
++}
++
++static bool
++is_need_use_rlog(ReplicateHeader * header)
++{
++ bool rtn = false;
++ if ((Cascade_Inf->useFlag != DB_TBL_USE) ||
++ (PGR_Use_Replication_Log != true) ||
++ (header->rlog > 0))
++ {
++ rtn=false;
++ }
++ else if ((header->cmdSts == CMD_STS_QUERY ) &&
++ ((header->cmdType == CMD_TYPE_INSERT) ||
++ (header->cmdType == CMD_TYPE_UPDATE) ||
++ (header->cmdType == CMD_TYPE_DELETE) ||
++ (header->cmdType == CMD_TYPE_EXECUTE)))
++ {
++ rtn = true;
++ }
++ else
++ {
++ if ((header->cmdSts == CMD_STS_TRANSACTION ) &&
++ (header->cmdType == CMD_TYPE_COMMIT))
++ {
++ rtn = true;
++ }
++ }
++ return rtn;
++}
++
++
++
++int
++PGRinit_transaction_table(void)
++{
++ if (Transaction_Tbl_Begin != NULL)
++ {
++ DLFreeList(Transaction_Tbl_Begin);
++ }
++
++ Transaction_Tbl_Begin = DLNewList();
++
++ return STATUS_OK;
++}
++
++
++static bool
++is_need_queue_jump( ReplicateHeader * header,char *query)
++{
++ if (header == NULL)
++ {
++ return true;
++ }
++
++ if (header->cmdSts == CMD_STS_QUERY)
++ {
++ if ((header->cmdType == CMD_TYPE_VACUUM ) ||
++ (header->cmdType == CMD_TYPE_ANALYZE ))
++ {
++ if ((strstr(query,"full") == NULL) &&
++ (strstr(query,"FULL") == NULL))
++ {
++ return true;
++ }
++ }
++ }
++ return false;
++}
++
++
++static bool
++is_executed_query_in_origin( ReplicateHeader *header )
++{
++ char *database = NULL;
++ char port[8];
++ char *userName = NULL;
++ char * host = NULL;
++ HostTbl * host_ptr = (HostTbl*)NULL;
++ TransactionTbl * transaction_tbl = (TransactionTbl*)NULL;
++ PGconn * conn = (PGconn *)NULL;
++ bool result = false;
++
++ if (Host_Tbl_Begin == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ host_ptr = Host_Tbl_Begin;
++ while(host_ptr->useFlag != DB_TBL_END)
++ {
++ /*
++ * check the status of the cluster DB
++ */
++ if (host_ptr->useFlag != DB_TBL_USE)
++ {
++ host_ptr ++;
++ continue;
++ }
++ if
(PGRis_same_host(header->from_host,ntohs(header->port),host_ptr->hostName,
host_ptr->port) == true)
++ {
++ break;
++ }
++ host_ptr ++;
++ }
++ if (host_ptr->useFlag == DB_TBL_END)
++ {
++ return false;
++ }
++ /*
++ * set up the connection
++ */
++ transaction_tbl = getTransactionTbl(host_ptr,header);
++ if (transaction_tbl == (TransactionTbl *)NULL)
++ {
++ transaction_tbl = setTransactionTbl(host_ptr, header);
++ if (transaction_tbl == (TransactionTbl *)NULL)
++ {
++ return false;
++ }
++ }
++ else
++ {
++ if ((transaction_tbl->conn == (PGconn *)NULL) ||
++ (transaction_tbl->conn->sock <= 0))
++ {
++ database = (char *)header->dbName;
++ snprintf(port,sizeof(port),"%d", host_ptr->port);
++ userName = (char *)header->userName;
++ host = host_ptr->hostName;
++ transaction_tbl->conn =
pgr_createConn(host,port,database,userName);
++ }
++ }
++ conn = transaction_tbl->conn;
++ if (conn == NULL)
++ {
++ return false;
++ }
++
++ result = is_executed_query( conn, header);
++ deleteTransactionTbl(host_ptr,header);
++ return result;
++}
++
++
++static bool
++is_executed_query( PGconn *conn, ReplicateHeader * header)
++{
++ char * func = "is_executed_query()";
++ static PGresult * res = (PGresult *)NULL;
++ char sync_command[PGR_MESSAGE_BUFSIZE];
++ char * str = NULL;
++
++ snprintf(sync_command,sizeof(sync_command),
++ "SELECT %s(%d,%u,%u,%u,%d) ",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_QUERY_CONFIRM_ANSWER_FUNC_NO,
++ (unsigned int)ntohl(header->tv.tv_sec),
++ (unsigned int)ntohl(header->tv.tv_usec),
++ (unsigned int)ntohl(header->replicate_id),
++ PGR_Response_Inf->response_mode);
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command(%s)",func,sync_command);
++#endif
++ res = PQexec(conn, sync_command);
++ if (res != NULL)
++ {
++ str = PQcmdStatus(res);
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:PQcmdStatus return[%s]",func,str);
++#endif
++ if ((str != NULL) &&
++
(!strncmp(str,PGR_ALREADY_REPLICATED_NOTICE_CMD,strlen(PGR_ALREADY_REPLICATED_NOTICE_CMD))))
++ {
++ PQclear(res);
++ return true;
++ }
++ PQclear(res);
++
++ }
++ return false;
++}
++
++static int
++replicate_lo( PGconn * conn, ReplicateHeader * header, LOArgs * query)
++{
++ int status = STATUS_OK;
++ int mode = 0;
++ Oid lobjId = 0;
++ int fd = 0;
++ char * buf = NULL;
++ char * filename = NULL;
++ size_t len = 0;
++ int offset = 0;
++ int whence = 0;
++
++ if ((conn == (PGconn *)NULL) || (query == (LOArgs *)NULL) || (header
== (ReplicateHeader *)NULL))
++ {
++ return STATUS_ERROR;
++ }
++ switch (header->cmdType)
++ {
++ case CMD_TYPE_LO_IMPORT :
++ filename = query->buf;
++ if (lo_import(conn, filename) > 0 )
++ {
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_LO_CREATE :
++ mode = (int)ntohl(query->arg1);
++ if (lo_creat(conn, mode) > 0)
++ {
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_LO_OPEN :
++ lobjId = (Oid)ntohl(query->arg1);
++ mode = (int)ntohl(query->arg2);
++ if (lo_open(conn, lobjId, mode) > 0)
++ {
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_LO_WRITE :
++ fd = (int)ntohl(query->arg1);
++ len = (int)ntohl(query->arg2);
++ buf = query->buf;
++ if (lo_write(conn, fd, buf, len) == len )
++ {
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_LO_LSEEK :
++ fd = (int)ntohl(query->arg1);
++ offset = (int)ntohl(query->arg2);
++ whence = (int)ntohl(query->arg3);
++ if (lo_lseek(conn, fd, offset, whence) >= 0)
++ {
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_LO_CLOSE :
++ fd = (int)ntohl(query->arg1);
++ if (lo_close(conn, fd) == 0)
++ {
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ break;
++ case CMD_TYPE_LO_UNLINK :
++ lobjId = (Oid)ntohl(query->arg1);
++ if (lo_unlink(conn,lobjId) >= 0)
++ {
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ break;
++ default :
++ break;
++ }
++ return status;
++}
++
++static int
++send_func(HostTbl * host_ptr,ReplicateHeader * header, char * func,char *
result)
++{
++ char * f ="send_func()";
++ char *database = NULL;
++ char port[8];
++ char *userName = NULL;
++ char * host = NULL;
++ char * str = NULL;
++ TransactionTbl * transaction_tbl = (TransactionTbl *)NULL;
++ PGresult * res = (PGresult *)NULL;
++ PGconn * conn = (PGconn *)NULL;
++ int rtn = 0;
++ int current_cluster = 0;
++
++ if ((host_ptr == (HostTbl *)NULL) ||
++ (header == (ReplicateHeader *)NULL) ||
++ (func == NULL) ||
++ (result == NULL))
++ {
++ return STATUS_ERROR;
++ }
++ /*
++ * set up the connection
++ */
++ database = (char *)header->dbName;
++ snprintf(port,sizeof(port),"%d", host_ptr->port);
++ userName = (char *)header->userName;
++ host = host_ptr->hostName;
++ if (PGR_Response_Inf != NULL)
++ {
++ current_cluster = PGR_Response_Inf->current_cluster;
++ }
++
++ /*
++ * get the transaction table data
++ * it has the connection data with each cluster DB
++ */
++ transaction_tbl = getTransactionTbl(host_ptr,header);
++ /*
++ * if the transaction process is new one,
++ * create connection data and add the transaction table
++ */
++ if (transaction_tbl == (TransactionTbl *)NULL)
++ {
++ transaction_tbl = setTransactionTbl(host_ptr, header);
++ if (transaction_tbl == (TransactionTbl *)NULL)
++ {
++ StartReplication[current_cluster] = true;
++ show_error("%s:setTransactionTbl failed",f);
++ if ( header->cmdSts != CMD_STS_NOTICE )
++ {
++ PGRset_host_status(host_ptr,DB_TBL_ERROR);
++ }
++ return STATUS_ERROR;
++ }
++ }
++ else
++ {
++ /*
++ * re-use the connection data
++ */
++ if ((transaction_tbl->conn != (PGconn *)NULL) &&
++ (transaction_tbl->conn->sock > 0))
++ {
++ StartReplication[current_cluster] = false;
++ }
++ else
++ {
++ if (transaction_tbl->conn != (PGconn *)NULL)
++ {
++ PQfinish(transaction_tbl->conn);
++ }
++ transaction_tbl->conn =
pgr_createConn(host,port,database,userName);
++ StartReplication[current_cluster] = true;
++ }
++ }
++ conn = transaction_tbl->conn;
++
++ if (conn == NULL)
++ {
++ show_error("%s:[%d@%s] may be
down",f,host_ptr->port,host_ptr->hostName);
++ if ( header->cmdSts != CMD_STS_NOTICE )
++ {
++ StartReplication[current_cluster] = true;
++ PGRset_host_status(host_ptr,DB_TBL_ERROR);
++ }
++ return STATUS_ERROR;
++ }
++ res = PQexec(conn, func);
++ if (res == NULL)
++ {
++ StartReplication[current_cluster] = true;
++ return STATUS_ERROR;
++ }
++ str = PQcmdStatus(res);
++ if ((str == NULL) || (*str == '\0'))
++ {
++ rtn = STATUS_ERROR;
++ }
++ else
++ {
++ snprintf(result, PGR_MESSAGE_BUFSIZE, "%s",str);
++ rtn = STATUS_OK;
++ }
++ if (res != NULL)
++ PQclear(res);
++ return rtn;
++}
++
++static uint32_t
++get_oid(HostTbl * host_ptr,ReplicateHeader * header)
++{
++ char * func = "get_oid()";
++ char sync_command[PGR_MESSAGE_BUFSIZE];
++ char result[PGR_MESSAGE_BUFSIZE];
++
++ memset(result,0,sizeof(result));
++ snprintf(sync_command,sizeof(sync_command),
++ "SELECT %s(%d)",
++ PGR_SYSTEM_COMMAND_FUNC, PGR_GET_OID_FUNC_NO);
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command(%s)",func,sync_command);
++#endif
++
++ if (send_func(host_ptr, header, sync_command, result) == STATUS_OK)
++ {
++ return (strtoul(result, NULL, 10));
++ }
++ return 0;
++}
++
++static int
++set_oid(HostTbl * host_ptr,ReplicateHeader * header, uint32_t oid)
++{
++ char * func = "set_oid()";
++ char sync_command[PGR_MESSAGE_BUFSIZE];
++ char result[PGR_MESSAGE_BUFSIZE];
++
++ memset(result,0,sizeof(result));
++ snprintf(sync_command,sizeof(sync_command),
++ "SELECT %s(%d,%u)",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_SET_OID_FUNC_NO,
++ oid);
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command(%s)",func,sync_command);
++#endif
++ return ( send_func(host_ptr, header, sync_command, result) );
++}
++
++/*
++ * sync oid during cluster DB's
++ */
++int
++PGRsync_oid(ReplicateHeader *header)
++{
++ HostTbl * host_ptr = (HostTbl*)NULL;
++ uint32_t max_oid = 0;
++ uint32_t oid = 0;
++ int recovery_status = 0;
++
++ /* get current oid of all cluster db's */
++ host_ptr = Host_Tbl_Begin;
++ if (host_ptr == (HostTbl *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ recovery_status = PGRget_recovery_status();
++ while(host_ptr->useFlag != DATA_END)
++ {
++ /*
++ * check the status of the cluster DB
++ */
++ if (host_ptr->useFlag != DATA_USE)
++ {
++ host_ptr ++;
++ continue;
++ }
++ /*
++ * skip loop during recover and the host name is master DB
++ */
++ if (is_master_in_recovery(host_ptr->hostName,
host_ptr->port,recovery_status) == true)
++ {
++ host_ptr ++;
++ continue;
++ }
++ oid = get_oid(host_ptr,header);
++ if (max_oid < oid )
++ {
++ max_oid = oid;
++ }
++ host_ptr ++;
++ }
++ if (max_oid <= 0)
++ return STATUS_ERROR;
++
++ /* set oid in cluster db */
++ host_ptr = Host_Tbl_Begin;
++ while(host_ptr->useFlag != DATA_END)
++ {
++ /*
++ * check the status of the cluster DB
++ */
++ if (host_ptr->useFlag != DATA_USE)
++ {
++ host_ptr ++;
++ continue;
++ }
++ /*
++ * skip loop during recover and the host name is master DB
++ */
++ if (is_master_in_recovery(host_ptr->hostName,
host_ptr->port,recovery_status) == true)
++ {
++ host_ptr ++;
++ continue;
++ }
++ set_oid(host_ptr,header,max_oid);
++ host_ptr ++;
++ }
++
++ return STATUS_OK;
++}
++
++int
++PGRload_replication_id(void)
++{
++ char * func = "PGRload_replication_id()";
++ char buf[256];
++ char *p;
++
++ if (Recovery_Status_Inf == (RecoveryStatusInf *)NULL)
++ {
++ show_error("%s: Recovery_Status_Inf is NULL",func);
++ return STATUS_ERROR;
++ }
++ if (RidFp == (FILE *)NULL)
++ {
++ show_error("%s: replication id file is not open",func);
++ return STATUS_ERROR;
++ }
++ rewind(RidFp);
++ if (fgets(buf,sizeof(buf),RidFp) == NULL)
++ {
++ show_debug(" replication id file is empty");
++ Recovery_Status_Inf->replication_id = 0;
++ }
++ else
++ {
++ p = strrchr(buf,' ');
++ if (p != NULL)
++ {
++ p++;
++ Recovery_Status_Inf->replication_id = (uint32_t)
atol(p);
++ show_debug(" set replication id
[%u]",Recovery_Status_Inf->replication_id);
++ }
++ else
++ {
++ Recovery_Status_Inf->replication_id = 0;
++ show_debug("no replicationid in this file");
++ }
++ }
++ return STATUS_OK;
++}
++
++static int
++notice_abort(HostTbl * host_ptr,ReplicateHeader * header)
++{
++ char * func = "PGR_Notice_Abort()";
++ char sync_command[PGR_MESSAGE_BUFSIZE];
++ char result[PGR_MESSAGE_BUFSIZE];
++
++ memset(result,0,sizeof(result));
++ snprintf(sync_command,sizeof(sync_command),
++ "SELECT %s(%d)",
++ PGR_SYSTEM_COMMAND_FUNC,
++ PGR_NOTICE_ABORT_FUNC_NO);
++#ifdef PRINT_DEBUG
++ show_debug("%s:sync_command(%s)",func,sync_command);
++#endif
++ return ( send_func(host_ptr, header, sync_command, result) );
++}
++
++static int
++send_p_parse (PGconn * conn, StringInfo input_message)
++{
++ const char *stmt_name;
++ const char *query_string;
++ int numParams;
++ Oid paramTypes;
++
++ /* get name,query */
++ stmt_name = pq_getmsgstring(input_message);
++ query_string = pq_getmsgstring(input_message);
++ /* send name,query */
++ if (pqPutMsgStart('P', false, conn) < 0 ||
++ pqPuts(stmt_name, conn) < 0 ||
++ pqPuts(query_string, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ /* get number of parameter */
++ numParams = pq_getmsgint(input_message, 2);
++ /* send number of parameter */
++ if (pqPutInt(numParams, 2, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ if (numParams > 0)
++ {
++ int i;
++ for (i = 0; i < numParams; i++)
++ {
++ paramTypes = pq_getmsgint(input_message, 4);
++ if (pqPutInt(paramTypes, 4, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ }
++ }
++ if (pqPutMsgEnd(conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++send_p_bind (PGconn * conn, StringInfo input_message)
++{
++ const char *portal_name;
++ const char *stmt_name;
++ int numPFormats;
++ int16 pformats;
++ int numParams;
++ int numRFormats;
++ int16 rformats;
++ int i;
++
++ /* Get&Send the fixed part of the message */
++ portal_name = pq_getmsgstring(input_message);
++ stmt_name = pq_getmsgstring(input_message);
++ if (pqPutMsgStart('B', false, conn) < 0 ||
++ pqPuts(portal_name, conn) < 0 ||
++ pqPuts(stmt_name, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++
++ /* Get&Send the parameter format codes */
++ numPFormats = pq_getmsgint(input_message, 2);
++ if (pqPutInt(numPFormats, 2, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ if (numPFormats > 0)
++ {
++ for (i = 0; i < numPFormats; i++)
++ {
++ pformats = pq_getmsgint(input_message, 2);
++ if (pqPutInt(pformats, 2, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ }
++ }
++
++ /* Get&Send the parameter value count */
++ numParams = pq_getmsgint(input_message, 2);
++ if (pqPutInt(numParams, 2, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ if (numParams > 0)
++ {
++ int32 plength;
++ for (i = 0 ; i < numParams ; i ++)
++ {
++ plength = pq_getmsgint(input_message, 4);
++ if (plength != -1)
++ {
++ const char *pvalue =
pq_getmsgbytes(input_message, plength);
++ if (pqPutInt(plength, 4, conn) < 0 ||
++ pqPutnchar(pvalue, plength, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ }
++ else
++ {
++ if (pqPutInt(plength, 4, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ }
++ }
++ }
++
++ /* Get&Send the result format codes */
++ numRFormats = pq_getmsgint(input_message, 2);
++ if (pqPutInt(numRFormats, 2, conn) < 0 )
++ {
++ return STATUS_ERROR;
++ }
++ if (numRFormats > 0)
++ {
++ for (i = 0; i < numRFormats; i++)
++ {
++ rformats = pq_getmsgint(input_message, 2);
++ if (pqPutInt(rformats, 2, conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ }
++ }
++ if (pqPutMsgEnd(conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++send_p_describe (PGconn * conn, StringInfo input_message)
++{
++
++ int describe_type;
++ const char *describe_target;
++
++ describe_type = pq_getmsgbyte(input_message);
++ describe_target = pq_getmsgstring(input_message);
++
++ /* construct the Describe Portal message */
++ if (pqPutMsgStart('D', false, conn) < 0 ||
++ pqPutc(describe_type, conn) < 0 ||
++ pqPuts(describe_target, conn) < 0 ||
++ pqPutMsgEnd(conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++send_p_execute (PGconn * conn, StringInfo input_message)
++{
++ const char *portal_name;
++ int max_rows;
++
++ portal_name = pq_getmsgstring(input_message);
++ max_rows = pq_getmsgint(input_message, 4);
++ /* construct the Execute message */
++ if (pqPutMsgStart('E', false, conn) < 0 ||
++ pqPuts(portal_name, conn) < 0 ||
++ pqPutInt(max_rows, 4, conn) < 0 ||
++ pqPutMsgEnd(conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++
++static int
++send_p_sync (PGconn * conn, StringInfo input_message)
++{
++ PGresult *result;
++ PGresult *lastResult;
++
++ /* construct the Sync message */
++ if (pqPutMsgStart('S', false, conn) < 0 ||
++ pqPutMsgEnd(conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ /* remember we are using extended query protocol */
++ conn->queryclass = PGQUERY_EXTENDED;
++
++ /*
++ * Give the data a push. In nonblock mode, don't complain if we're
unable
++ * to send it all; PQgetResult() will do any additional flushing
needed.
++ */
++ if (pqFlush(conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++
++ /* OK, it's launched! */
++ conn->asyncStatus = PGASYNC_BUSY;
++
++ lastResult = NULL;
++ while ((result = PQgetResult(conn)) != NULL)
++ {
++ if (lastResult)
++ {
++ if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
++ result->resultStatus == PGRES_FATAL_ERROR)
++ {
++ PQclear(result);
++ result = lastResult;
++ }
++ else
++ PQclear(lastResult);
++ }
++ lastResult = result;
++ if (result->resultStatus == PGRES_COPY_IN ||
++ result->resultStatus == PGRES_COPY_OUT ||
++ conn->status == CONNECTION_BAD)
++ break;
++ }
++ if (lastResult != NULL)
++ {
++ PQclear(lastResult);
++ }
++ return STATUS_OK;
++}
++
++static int
++send_p_close (PGconn * conn, StringInfo input_message)
++{
++
++ int close_type;
++ const char *close_target;
++
++ close_type = pq_getmsgbyte(input_message);
++ close_target = pq_getmsgstring(input_message);
++ if (pqPutMsgStart('C', false, conn) < 0 ||
++ pqPutc(close_type, conn) < 0 ||
++ pqPuts(close_target, conn) < 0 ||
++ pqPutMsgEnd(conn) < 0)
++ {
++ return STATUS_ERROR;
++ }
++ return STATUS_OK;
++}
++static void
++set_string_info(StringInfo input_message, ReplicateHeader * header, char *
query)
++{
++ int len;
++ len = ntohl(header->query_size);
++ input_message->data = query;
++ input_message->maxlen = len;
++ input_message->len = len -1;
++ input_message->cursor = 0;
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/pgrp/rlog.c
pgcluster-1.5.0rc7/src/pgcluster/pgrp/rlog.c
+--- postgresql-8.1.2/src/pgcluster/pgrp/rlog.c 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/pgrp/rlog.c 2006-01-31
23:47:23.000000000 +0100
+@@ -0,0 +1,1288 @@
++/*--------------------------------------------------------------------
++ * FILE:
++ * rlog.c
++ *
++ * NOTE:
++ * This file is composed of the functions to call with the source
++ * at pgreplicate for replicate ahead log.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ *--------------------------------------------------------------------
++ */
++#ifdef USE_REPLICATION
++
++#include "postgres.h"
++#include "postgres_fe.h"
++
++#include <stdio.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/un.h>
++#include <unistd.h>
++#ifdef HAVE_FCNTL_H
++#include <fcntl.h>
++#endif
++#include <errno.h>
++#include <ctype.h>
++#include <time.h>
++#include <sys/ipc.h>
++#include <sys/shm.h>
++#include <sys/sem.h>
++#include <sys/msg.h>
++#include <signal.h>
++#include <sys/socket.h>
++#include <netdb.h>
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++#include <dirent.h>
++#include <arpa/inet.h>
++
++#ifdef HAVE_CRYPT_H
++#include <crypt.h>
++#endif
++
++#ifdef MULTIBYTE
++#include "mb/pg_wchar.h"
++#endif
++
++#include "libpq-fe.h"
++#include "libpq-int.h"
++#include "fe-auth.h"
++#include "access/xact.h"
++#include "replicate_com.h"
++#include "pgreplicate.h"
++
++int RLog_Recv_Sock = -1;
++/*--------------------------------------
++ * PROTOTYPE DECLARATION
++ *--------------------------------------
++ */
++int PGRwrite_rlog(ReplicateHeader * header, char * query);
++ReplicateHeader * PGRget_requested_query(ReplicateHeader * header);
++void PGRreconfirm_commit(int sock, ReplicateHeader * header);
++void PGRset_rlog(ReplicateHeader * header, char * query);
++void PGRunset_rlog(ReplicateHeader * header, char * query);
++int PGRresend_rlog_to_db(void);
++pid_t PGR_RLog_Main(void);
++int PGRcreate_send_rlog_socket(void);
++int PGRget_sync_data(ReplicateHeader *header);
++int PGRdelete_sync_data(ReplicateHeader *header);
++int PGRsend_rlog_packet(int sock,ReplicateHeader * header, const char *
query_string);
++int PGRrecv_rlog_result(int sock,void * result, int size);
++int PGRsend_rlog_to_local(ReplicateHeader * header,char * query);
++int PGRget_rlog_header(ReplicateHeader * header);
++
++static int set_query_log(ReplicateHeader * header, char * query);
++static QueryLogType * get_query_log_by_header(ReplicateHeader * header);
++static QueryLogType * get_query_log(ReplicateHeader * header);
++static void delete_query_log(ReplicateHeader * header);
++static int set_commit_log(ReplicateHeader * header);
++static CommitLogInf * get_commit_log(ReplicateHeader * header);
++static void delete_commit_log(ReplicateHeader * header);
++static bool was_committed_transaction(ReplicateHeader * header);
++static int create_recv_rlog_socket(void);
++static int do_rlog(int fd);
++static int recv_message(int sock,char * buf, int len);
++static int send_message(int sock, char * msg, int len);
++static void exit_rlog(int sig);
++static int reconfirm_commit(ReplicateHeader * header);
++
++
++int
++PGRwrite_rlog(ReplicateHeader * header, char * query)
++{
++ char * func = "PGRwrite_rlog()";
++
++ if (header == NULL)
++ {
++ show_error("%s:header is null",func);
++ return STATUS_ERROR;
++ }
++ switch (header->cmdSts)
++ {
++ case CMD_STS_QUERY:
++#ifdef PRINT_DEBUG
++ show_debug("%s:set_query_log",func);
++#endif
++ set_query_log(header,query);
++ break;
++ case CMD_STS_DELETE_QUERY:
++#ifdef PRINT_DEBUG
++ show_debug("%s:delete_query_log",func);
++#endif
++ delete_query_log(header);
++ break;
++ case CMD_STS_TRANSACTION:
++ if (header->cmdType == CMD_TYPE_COMMIT)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:set_commit_log call",func);
++#endif
++ set_commit_log(header);
++ }
++ break;
++ case CMD_STS_DELETE_TRANSACTION:
++ if (header->cmdType == CMD_TYPE_COMMIT)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:delete_commit_log call",func);
++#endif
++ delete_commit_log(header);
++ }
++ break;
++ default:
++ show_error("%s:unknown status %c",func,header->cmdSts);
++ break;
++ }
++ return STATUS_OK;
++}
++
++ReplicateHeader *
++PGRget_requested_query(ReplicateHeader * header)
++{
++ QueryLogType * query_log = NULL;
++
++ if (Query_Log_Top == NULL)
++ {
++ return NULL;
++ }
++ query_log = Query_Log_Top;
++ while(query_log != (QueryLogType *)NULL)
++ {
++ if ((query_log->header->request_id == header->request_id) &&
++ (query_log->header->pid == header->pid) &&
++ (query_log->header->port == header->port) &&
++
(!strncmp(query_log->header->from_host,header->from_host,sizeof(header->from_host))))
++ {
++ return query_log->header;
++ }
++ query_log = (QueryLogType *)(query_log->next);
++ }
++ return (ReplicateHeader *)NULL;
++}
++
++static int
++set_query_log(ReplicateHeader * header, char * query)
++{
++ char * func = "set_query_log()";
++ int size = 0;
++ QueryLogType * query_log = NULL;
++
++ if (Query_Log_Top == NULL)
++ {
++ Query_Log_Top = (QueryLogType *)malloc(sizeof(QueryLogType));
++ if (Query_Log_Top == (QueryLogType *)NULL)
++ {
++ show_error("%s:malloc failed:
(%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ Query_Log_Top->next = NULL;
++ Query_Log_Top->last = NULL;
++ Query_Log_End = Query_Log_Top;
++ Query_Log_End->next = NULL;
++ Query_Log_End->last = NULL;
++ query_log = Query_Log_Top;
++ }
++ else
++ {
++ query_log = (QueryLogType *)malloc(sizeof(QueryLogType));
++ if (query_log == (QueryLogType *)NULL)
++ {
++ show_error("%s:malloc failed:
(%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ Query_Log_End->next = (char *)query_log;
++ query_log->last = (char *)Query_Log_End;
++ query_log->next = NULL;
++ Query_Log_End = query_log;
++ }
++ query_log->header = (ReplicateHeader
*)malloc(sizeof(ReplicateHeader));
++ if (query_log->header == (ReplicateHeader *)NULL)
++ {
++ show_error("%s:malloc failed: (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ size = ntohl(header->query_size);
++
++ query_log->query = (char *)malloc(size+4);
++ if (query_log->query == (char *)NULL)
++ {
++ show_error("%s:malloc failed: (%s)",func,strerror(errno));
++ return STATUS_ERROR;
++ }
++ memset(query_log->query,0,size+4);
++ memcpy(query_log->header,header,sizeof(ReplicateHeader));
++ query_log->header->rlog = FROM_R_LOG_TYPE ;
++ memcpy(query_log->query,query,size);
++
++ return STATUS_OK;
++}
++
++static QueryLogType *
++get_query_log_by_header(ReplicateHeader * header)
++{
++ QueryLogType * query_log = NULL;
++
++ if (Query_Log_Top == NULL)
++ {
++ return (QueryLogType *)NULL;
++ }
++ query_log = Query_Log_Top;
++#ifdef PRINT_DEBUG
++ show_debug("get_query_log_by_header:header is %d,%d,%d,%s",
++ header->request_id,
++ header->pid,
++ header->port,
++ header->from_host);
++#endif
++
++ while(query_log != (QueryLogType *)NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("get_query_log_by_header:comparing to %d,%d,%d,%s",
++ query_log->header->request_id,
++ query_log->header->pid,
++ query_log->header->port,
++ query_log->header->from_host);
++#endif
++ if ((query_log->header->request_id == header->request_id) &&
++ (query_log->header->pid == header->pid) &&
++ (query_log->header->port == header->port) &&
++
(!strncmp(query_log->header->from_host,header->from_host,sizeof(header->from_host))))
++ {
++ return query_log;
++ }
++ query_log = (QueryLogType *)(query_log->next);
++ }
++ return (QueryLogType *)NULL;
++}
++
++static QueryLogType *
++get_query_log(ReplicateHeader * header)
++{
++ QueryLogType * query_log = NULL;
++
++ if (Query_Log_Top == NULL)
++ {
++ return NULL;
++ }
++ query_log = Query_Log_Top;
++ while(query_log != (QueryLogType *)NULL)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("get_qurey_log: comparing in log is %d,header is
%d",query_log->header->replicate_id,header->replicate_id);
++#endif
++ if (query_log->header->replicate_id == header->replicate_id)
++ {
++ return query_log;
++ }
++ query_log = (QueryLogType *)(query_log->next);
++ }
++ return (QueryLogType*)NULL;
++}
++
++static void
++delete_query_log(ReplicateHeader * header)
++{
++ QueryLogType * query_log = NULL;
++ QueryLogType * last = NULL;
++ QueryLogType * next = NULL;
++
++ query_log = get_query_log(header);
++
++ if (query_log == NULL)
++ {
++ return ;
++ }
++ last = (QueryLogType *)query_log->last;
++ next = (QueryLogType *)query_log->next;
++
++ /* change link */
++ if (last != (QueryLogType *)NULL)
++ {
++ last->next = (char *)next;
++ }
++ else
++ {
++ Query_Log_Top = next;
++ }
++ if (next != (QueryLogType *)NULL)
++ {
++ next->last = (char *)last;
++ }
++ else
++ {
++ Query_Log_End = last;
++ }
++
++ /* delete contents */
++ if (query_log->header != NULL)
++ {
++ free(query_log->header);
++ }
++ if (query_log->query != NULL)
++ {
++ free(query_log->query);
++ }
++ free(query_log);
++}
++
++static int
++set_commit_log(ReplicateHeader * header)
++{
++
++ CommitLogInf * commit_log = NULL;
++ ReplicateHeader * c_header;
++
++ if (Commit_Log_Tbl == NULL)
++ {
++ return STATUS_ERROR;
++ }
++ commit_log = Commit_Log_Tbl + 1;
++ while ( commit_log->inf.useFlag != DB_TBL_END )
++ {
++ if (commit_log->inf.useFlag != DB_TBL_USE)
++ {
++ commit_log->inf.useFlag = DB_TBL_USE;
++ c_header = &(commit_log->header);
++ memcpy(c_header,header,sizeof(ReplicateHeader));
++ Commit_Log_Tbl->inf.commit_log_num ++;
++ break;
++ }
++ commit_log ++;
++ }
++ return STATUS_OK;
++}
++
++static CommitLogInf *
++get_commit_log(ReplicateHeader * header)
++{
++ CommitLogInf * commit_log = NULL;
++ ReplicateHeader * c_header;
++ int cnt = 0;
++
++ if (Commit_Log_Tbl == NULL)
++ {
++ return (CommitLogInf *)NULL;
++ }
++ commit_log = Commit_Log_Tbl + 1;
++ while ( commit_log->inf.useFlag != DB_TBL_END )
++ {
++ if (commit_log->inf.useFlag == DB_TBL_USE)
++ {
++ cnt ++;
++ c_header = &(commit_log->header);
++ if (c_header == NULL)
++ {
++ commit_log ++;
++ continue;
++ }
++ if (c_header->replicate_id == header->replicate_id)
++ {
++ return commit_log;
++ }
++ }
++ else
++ {
++ }
++ if (cnt >= Commit_Log_Tbl->inf.commit_log_num)
++ {
++ break;
++ }
++ commit_log ++;
++ }
++ return (CommitLogInf *)NULL;
++}
++
++static void
++delete_commit_log(ReplicateHeader * header)
++{
++ CommitLogInf * commit_log = NULL;
++
++ commit_log = get_commit_log(header);
++ if (commit_log != NULL)
++ {
++ memset(&(commit_log->header),0,sizeof(commit_log->header));
++ commit_log->inf.useFlag = DB_TBL_INIT;
++ Commit_Log_Tbl->inf.commit_log_num --;
++ }
++}
++
++static bool
++was_committed_transaction(ReplicateHeader * header)
++{
++ CommitLogInf * commit_log = NULL;
++
++ commit_log = get_commit_log(header);
++ if (commit_log != NULL)
++ {
++ return true;
++ }
++ return false;
++}
++
++void
++PGRreconfirm_commit(int sock, ReplicateHeader * header)
++{
++ int result = PGR_NOT_YET_COMMIT;
++
++ if (Replicateion_Log == NULL)
++ {
++ return ;
++ }
++
++ if (Replicateion_Log->r_log_sock > 0)
++ {
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++ }
++ Replicateion_Log->r_log_sock = PGRcreate_send_rlog_socket();
++ if (Replicateion_Log->r_log_sock == -1)
++ return;
++
++ header->query_size = 0;
++ PGRsend_rlog_packet(Replicateion_Log->r_log_sock,header,"");
++ PGRrecv_rlog_result(Replicateion_Log->r_log_sock,&result,
sizeof(result));
++
++
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++
++ snprintf(PGR_Result,PGR_MESSAGE_BUFSIZE,"%d,%d",
PGR_TRANSACTION_CONFIRM_ANSWER_FUNC_NO,result);
++
++ PGRreturn_result(sock, PGR_Result,PGR_NOWAIT_ANSWER);
++}
++
++static int
++reconfirm_commit(ReplicateHeader * header)
++{
++ char * func = "reconfirm_commit()";
++ int result = PGR_NOT_YET_COMMIT;
++
++ /* check the transaction was committed */
++ if (was_committed_transaction(header) == true)
++ {
++ result = PGR_ALREADY_COMMITTED;
++#ifdef PRINT_DEBUG
++ show_debug("%s:PGR_ALREADY_COMMITTED",func);
++#endif
++ }
++ else
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:PGR_NOT_YET_COMMIT",func);
++#endif
++ }
++ return result;
++}
++
++void
++PGRset_rlog(ReplicateHeader * header, char * query)
++{
++ char * func = "PGRset_rlog()";
++ int status = STATUS_OK;
++ bool send_flag = false;
++
++ if (PGR_Log_Header == NULL)
++ {
++ return;
++ }
++ switch (header->cmdSts)
++ {
++ case CMD_STS_QUERY:
++ send_flag = true;
++ break;
++ case CMD_STS_TRANSACTION:
++ if (header->cmdType ==
CMD_TYPE_COMMIT)
++ {
++ send_flag =
true;
++
PGR_Log_Header->cmdType = header->cmdType;
++
PGR_Log_Header->query_size = htonl(strlen(query));
++ }
++ break;
++ }
++ if (send_flag != true)
++ {
++ show_error("%s:send_flag is false",func);
++ return;
++ }
++ PGR_Log_Header->cmdSys = CMD_SYS_LOG;
++ if (Cascade_Inf->useFlag == DB_TBL_USE)
++ {
++ /* save log data in remote server */
++#ifdef PRINT_DEBUG
++ show_debug("%s:set rlog %s",func,query);
++#endif
++ status =
PGRsend_lower_cascade(PGR_Log_Header, query);
++ if (status == STATUS_OK) {
++
status=PGRwait_notice_rlog_done();
++ }
++ if (status != STATUS_OK)
++ {
++#ifdef PRINT_DEBUG
++
show_debug("%s:PGRsend_lower_cascade failed",func);
++#endif
++ PGRwrite_rlog(PGR_Log_Header,
query);
++ }
++ }
++ else
++ {
++ /* save log data in local server */
++ PGRwrite_rlog(PGR_Log_Header, query);
++ }
++}
++
++void
++PGRunset_rlog(ReplicateHeader * header, char * query)
++{
++ int status = STATUS_OK;
++ bool send_flag = false;
++
++ if (PGR_Log_Header == NULL)
++ {
++ return;
++ }
++ switch (header->cmdSts)
++ {
++ case CMD_STS_QUERY:
++ send_flag = true;
++ PGR_Log_Header->cmdSts = CMD_STS_DELETE_QUERY;
++ break;
++ case CMD_STS_TRANSACTION:
++ if (PGR_Log_Header->cmdType == CMD_TYPE_COMMIT)
++ {
++ PGR_Log_Header->cmdSts =
CMD_STS_DELETE_TRANSACTION;
++ PGR_Log_Header->query_size =
htonl(strlen(query));
++ send_flag = true;
++ }
++ break;
++ }
++ if (send_flag != true)
++ {
++ return;
++ }
++ PGR_Log_Header->cmdSys = CMD_SYS_LOG;
++ if (Cascade_Inf->useFlag == DB_TBL_USE)
++ {
++ /* save log data in remote server */
++#ifdef PRINT_DEBUG
++ show_debug("unset rlog %s",query);
++#endif
++
++ status = PGRsend_lower_cascade(PGR_Log_Header, query);
++ if (status == STATUS_OK)
++ {
++ status=PGRwait_notice_rlog_done();
++ }
++ if (status != STATUS_OK)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("PGRsend_lower_cascade recv failed");
++#endif
++ PGRwrite_rlog(PGR_Log_Header, query);
++ }
++ }
++ else
++ {
++ /* save log data in local server */
++ PGRwrite_rlog(PGR_Log_Header, query);
++ }
++}
++
++int
++PGRresend_rlog_to_db(void)
++{
++ char *func="PGRresend_rlog_to_db";
++ QueryLogType * query_log = NULL;
++ QueryLogType * next = NULL;
++ int status = STATUS_OK;
++ int dest = 0;
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:enter.",func);
++#endif
++
++ query_log = Query_Log_Top;
++
++ while (query_log != NULL)
++ {
++
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:processing qlog,query=%s",func,query_log->query);
++#endif
++ if (query_log->header->rlog != FROM_R_LOG_TYPE )
++ {
++ query_log = (QueryLogType *)query_log->next;
++ continue;
++ }
++ status =
replicate_packet_send_internal(query_log->header,query_log->query,
dest,RECOVERY_INIT,false);
++#ifdef PRINT_DEBUG
++ show_debug("%s:status=%d",func,status);
++#endif
++ if (status == STATUS_SKIP_REPLICATE )
++ {
++ Query_Log_Top = query_log;
++ query_log = (QueryLogType *)query_log->next;
++ }
++ else
++ {
++ if (query_log->header != NULL)
++ {
++ free(query_log->header );
++ }
++ if (query_log->query != NULL)
++ {
++ free(query_log->query );
++ }
++ next = (QueryLogType *)query_log->next;
++ free(query_log);
++ query_log = next;
++ Query_Log_Top = query_log;
++ }
++ if (query_log != NULL)
++ {
++ Query_Log_End = (QueryLogType *)query_log->next;
++ }
++ else
++ {
++ Query_Log_End = (QueryLogType *)NULL;
++ }
++ }
++
++#ifdef PRINT_DEBUG
++ show_debug("%s:exit.",func);
++#endif
++
++ return STATUS_OK;
++}
++
++pid_t
++PGR_RLog_Main(void)
++{
++ char * func = "PGR_RLog_Main()";
++ int afd = -1;
++ int rtn;
++ struct sockaddr addr;
++ socklen_t addrlen;
++ pid_t pid = 0;
++ pid_t pgid = 0;
++
++ extern int fork_wait_time;
++
++ if (Replicateion_Log == NULL)
++ {
++ show_error("%s:Replicateion_Log is NULL",func);
++ return -1;
++ }
++ pgid = getpgid(0);
++ if ((pid = fork()) != 0 )
++ {
++ return pid;
++ }
++ signal(SIGTERM,exit_rlog);
++ signal(SIGINT,exit_rlog);
++ signal(SIGQUIT,exit_rlog);
++ signal(SIGPIPE,SIG_IGN);
++
++ if (PGRinit_transaction_table() != STATUS_OK)
++ {
++ show_error("RLog process transaction table memory allocate
failed");
++ return -1;
++ }
++
++ setpgid(0,pgid);
++ RLog_Recv_Sock = create_recv_rlog_socket();
++ if(RLog_Recv_Sock == -1)
++ {
++ show_error("rlog socket creation failure.quit all process.");
++ kill(pgreplicate_pid, SIGINT);
++ exit_rlog(0);
++ }
++
++ if (fork_wait_time > 0) {
++#ifdef PRINT_DEBUG
++ show_debug("rlog process: wait fork(): pid = %d", getpid());
++#endif
++ sleep(fork_wait_time);
++ }
++
++ for (;;)
++ {
++ fd_set rmask;
++ struct timeval timeout;
++
++ timeout.tv_sec = PGR_RECV_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ Idle_Flag = IDLE_MODE ;
++ if (Exit_Request)
++ {
++ exit_rlog(0);
++ }
++ /*
++ * Wait for something to happen.
++ */
++ FD_ZERO(&rmask);
++ FD_SET(RLog_Recv_Sock,&rmask);
++ rtn = select(RLog_Recv_Sock+1, &rmask, (fd_set *)NULL,
(fd_set *)NULL, &timeout);
++ if (rtn && FD_ISSET(RLog_Recv_Sock, &rmask))
++ {
++ Idle_Flag = BUSY_MODE ;
++ addrlen = sizeof(addr);
++ afd = accept(RLog_Recv_Sock, &addr, &addrlen);
++ if (afd < 0)
++ {
++ continue;
++ }
++ else
++ {
++ do_rlog(afd);
++ close(afd);
++ }
++ }
++ }
++ exit(0);
++}
++
++static int
++create_recv_rlog_socket(void)
++{
++ char * func = "create_recv_socket()";
++ struct sockaddr_un addr;
++ int fd;
++ int status;
++ int len;
++
++ /* set unix domain socket path */
++ fd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (fd == -1)
++ {
++ show_error("%s:Failed to create UNIX domain socket. reason:
%s",func, strerror(errno));
++ return -1;
++ }
++ memset((char *) &addr, 0, sizeof(addr));
++ ((struct sockaddr *)&addr)->sa_family = AF_UNIX;
++ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.s.PGRLOG.%d",
++ PGR_Write_Path,
++ Replicateion_Log->RLog_Port_Number);
++fprintf(stderr,"addr.sun_path[%s]\n",addr.sun_path);
++ if (Replicateion_Log->RLog_Sock_Path == NULL)
++ {
++ Replicateion_Log->RLog_Sock_Path = strdup(addr.sun_path);
++fprintf(stderr,"Replicateion_Log->RLog_Sock_Path[%s]\n",Replicateion_Log->RLog_Sock_Path);
++ }
++ len = sizeof(struct sockaddr_un);
++ status = bind(fd, (struct sockaddr *)&addr, len);
++ if (status == -1)
++ {
++ show_error("%s: bind() failed. reason: %s", func,
strerror(errno));
++ return -1;
++ }
++
++ if (chmod(addr.sun_path, 0770) == -1)
++ {
++ show_error("%s: chmod() failed. reason: %s", func,
strerror(errno));
++ return -1;
++ }
++
++ status = listen(fd, 1000000);
++ if (status < 0)
++ {
++ show_error("%s: listen() failed. reason: %s", func,
strerror(errno));
++ return -1;
++ }
++ return fd;
++}
++
++int
++PGRcreate_send_rlog_socket(void)
++{
++ char * func = "create_recv_socket()";
++ struct sockaddr_un addr;
++ int fd;
++ int len;
++
++ /* set unix domain socket path */
++ fd = socket(AF_UNIX, SOCK_STREAM, 0);
++ if (fd == -1)
++ {
++ show_error("%s:Failed to create UNIX domain socket. reason:
%s",func, strerror(errno));
++ return -1;
++ }
++ memset((char *) &addr, 0, sizeof(addr));
++ ((struct sockaddr *)&addr)->sa_family = AF_UNIX;
++ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.s.PGRLOG.%d",
++ PGR_Write_Path,
++ Replicateion_Log->RLog_Port_Number);
++ if (Replicateion_Log->RLog_Sock_Path == NULL)
++ {
++ Replicateion_Log->RLog_Sock_Path = strdup(addr.sun_path);
++ }
++ len = sizeof(struct sockaddr_un);
++ if (connect(fd, (struct sockaddr *)&addr, len) < 0)
++ {
++ close(fd);
++ return -1;
++ }
++ return fd;
++}
++
++static int
++do_rlog(int fd)
++{
++ char * func = "do_rlog()";
++ QueryLogType * query_log = NULL;
++ ReplicateHeader header;
++ char * query = NULL;
++ int status = STATUS_OK;
++
++ memset(&header,0,sizeof(header));
++ query = PGRread_packet(fd, &header);
++#ifdef PRINT_DEBUG
++ show_debug("%s:got result:cmdSys='%c'",func,header.cmdSys);
++#endif
++ if (header.cmdSys == 0)
++ {
++ return STATUS_ERROR;
++ }
++ switch (header.cmdSys)
++ {
++ case CMD_SYS_REPLICATE:
++ if (header.cmdSts != CMD_STS_DELETE_QUERY)
++ {
++ query_log = get_query_log_by_header(&header);
++ if (query_log != (QueryLogType*)NULL)
++ {
++
memcpy(&header,query_log->header,sizeof(ReplicateHeader));
++ }
++ send_message(fd,(char
*)&header,sizeof(ReplicateHeader));
++ header.cmdSts = CMD_STS_DELETE_QUERY;
++ PGRwrite_rlog(&header, NULL);
++ }
++ else
++ {
++ status =
PGRwrite_rlog((ReplicateHeader*)&header,(char *)NULL);
++ send_message(fd,(char
*)&status,sizeof(status));
++ }
++ break;
++ case CMD_SYS_LOG:
++ status = PGRwrite_rlog((ReplicateHeader*)&header,
query);
++ send_message(fd,(char *)&status,sizeof(status));
++ break;
++ case CMD_SYS_CALL:
++ if (header.cmdSts == CMD_STS_TRANSACTION_ABORT)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s:
CMD_STS_TRANSACTION_ABORT",func);
++#endif
++ status = reconfirm_commit(&header);
++ }
++ else if (header.cmdSts == CMD_STS_QUERY_SUSPEND)
++ {
++#ifdef PRINT_DEBUG
++ show_debug("%s: CMD_STS_QUERY_SUSPEND",func);
++#endif
++ // status =
PGRresend_rlog_to_db();
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s: SYS_CALL process done",func);
++#endif
++ send_message(fd,(char *)&status,sizeof(status));
++ break;
++ }
++#ifdef PRINT_DEBUG
++ show_debug("%s:process result done:cmdSys='%c'",func,header.cmdSys);
++#endif
++ return STATUS_OK;
++}
++
++int
++PGRget_sync_data(ReplicateHeader *header)
++{
++ ReplicateHeader rlog_header;
++
++ if (header == NULL)
++ {
++ return STATUS_ERROR;
++ }
++
++
++ if (Replicateion_Log->r_log_sock > 0)
++ {
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++ }
++ Replicateion_Log->r_log_sock = PGRcreate_send_rlog_socket();
++ if (Replicateion_Log->r_log_sock == -1)
++ return STATUS_ERROR;
++
++ memset(&rlog_header,0,sizeof(ReplicateHeader));
++ send_message( Replicateion_Log->r_log_sock, (char
*)header,sizeof(ReplicateHeader));
++ recv_message( Replicateion_Log->r_log_sock, (char
*)&rlog_header,sizeof(ReplicateHeader));
++ if (rlog_header.cmdSts != 0)
++ {
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++ memcpy(header,&rlog_header, sizeof(ReplicateHeader));
++ return STATUS_OK;
++ }
++
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++
++ return STATUS_ERROR;
++
++}
++
++int
++PGRdelete_sync_data(ReplicateHeader *header)
++{
++ int status;
++ char cmdSts;
++
++ cmdSts = header->cmdSts;
++ header->cmdSts = CMD_STS_DELETE_QUERY;
++ if (header == NULL)
++ {
++ return STATUS_ERROR;
++ }
++
++ if (Replicateion_Log->r_log_sock > 0)
++ {
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++ }
++ Replicateion_Log->r_log_sock = PGRcreate_send_rlog_socket();
++ if (Replicateion_Log->r_log_sock == -1)
++ return STATUS_ERROR;
++
++ send_message( Replicateion_Log->r_log_sock, (char
*)header,sizeof(ReplicateHeader));
++ recv_message( Replicateion_Log->r_log_sock, (char
*)&status,sizeof(status));
++ header->cmdSts = cmdSts;
++
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++
++ return status;
++
++}
++
++int
++PGRsend_rlog_packet(int sock,ReplicateHeader * header, const char *
query_string)
++{
++ char * buf = NULL;
++ int buf_size = 0;
++ int header_size = 0;
++ int query_size = 0;
++ int rtn = 0;
++
++ /* check parameter */
++ if ((sock <= 0) || (header == NULL))
++ {
++ return STATUS_ERROR;
++ }
++ if (query_string != NULL)
++ {
++ query_size = ntohl(header->query_size);
++ }
++ header_size = sizeof(ReplicateHeader);
++ buf_size = header_size + query_size + 4;
++ buf = (char *)malloc(buf_size);
++ if (buf == (char *)NULL)
++ {
++ return STATUS_ERROR;
++ }
++ memset(buf,0,buf_size);
++ buf_size -= 4;
++ memcpy(buf,header,header_size);
++ if (query_size > 0)
++ {
++ memcpy((char *)(buf+header_size),query_string,query_size+1);
++ }
++ rtn = send_message(sock,buf,buf_size);
++ free(buf);
++ return rtn;
++}
++
++int
++PGRrecv_rlog_result(int sock,void * result, int size)
++{
++ char *func = "PGRrecv_rlog_result";
++ fd_set rmask;
++ struct timeval timeout;
++ int rtn;
++
++ if ((result == (void *)NULL) || (size <= 0))
++ {
++ return -1;
++ }
++
++ /*
++ * Wait for something to happen.
++ */
++ rtn = 1;
++ for (;;)
++ {
++ timeout.tv_sec = PGR_RECV_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ FD_ZERO(&rmask);
++ FD_SET(sock,&rmask);
++ rtn = select(sock+1, &rmask, (fd_set *)NULL, (fd_set *)NULL,
&timeout);
++ if (rtn < 0)
++ {
++ if (errno != EINTR || errno != EAGAIN)
++ {
++ show_error("%s: select() failed
(%s)",func,strerror(errno));
++ return -1;
++ }
++ }
++ else if (rtn && FD_ISSET(sock, &rmask))
++ {
++ return (recv_message(sock, (char*)result, size));
++ }
++ }
++ return -1;
++}
++
++
++static int
++recv_message(int sock,char * buf, int len)
++{
++ char *func = "recv_message";
++ int cnt = 0;
++ int r = 0;
++ char * read_ptr;
++ int read_size = 0;
++ cnt = 0;
++ read_ptr = buf;
++
++ for (;;)
++ {
++ r = recv(sock,read_ptr + read_size ,len - read_size, 0);
++ if (r < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++ else
++ {
++ show_error("%s:recv failed: %d(%s)",func,
errno, strerror(errno));
++ return -1;
++ }
++ }
++ else if (r > 0)
++ {
++ read_size += r;
++ if (read_size == len)
++ return read_size;
++ }
++ else /* r == 0 */
++ {
++ show_error("%s:unexpected EOF", func);
++ return -1;
++ }
++ }
++ return -1;
++}
++
++int
++PGRsend_rlog_to_local(ReplicateHeader * header,char * query)
++{
++ int status = STATUS_OK;
++
++ if (Replicateion_Log == NULL)
++ {
++ return STATUS_ERROR;
++ }
++
++ if (Replicateion_Log->r_log_sock > 0)
++ {
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++ }
++
++ Replicateion_Log->r_log_sock = PGRcreate_send_rlog_socket();
++ if (Replicateion_Log->r_log_sock == -1)
++ return STATUS_ERROR;
++
++#ifdef PRINT_DEBUG
++ show_debug("send_to_local %s",query);
++#endif
++ status =
PGRsend_rlog_packet(Replicateion_Log->r_log_sock,header,query);
++#ifdef PRINT_DEBUG
++ show_debug("send_to_local result is %d,errno=%d(%s)",status,errno
,strerror(errno));
++#endif
++
++ if (status != STATUS_ERROR)
++ {
++ PGRrecv_rlog_result(Replicateion_Log->r_log_sock,&status,
sizeof(status));
++ }
++
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++
++ return status;
++}
++
++int
++PGRget_rlog_header(ReplicateHeader * header)
++{
++ int status = STATUS_OK;
++ ReplicateHeader rlog_header;
++
++ if ((Replicateion_Log == NULL) ||
++ (header == NULL))
++ {
++ return STATUS_ERROR;
++ }
++
++ if (Replicateion_Log->r_log_sock > 0)
++ {
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++ }
++ Replicateion_Log->r_log_sock = PGRcreate_send_rlog_socket();
++ if (Replicateion_Log->r_log_sock == -1)
++ return STATUS_ERROR;
++
++ memcpy(&rlog_header,header,sizeof(ReplicateHeader));
++ rlog_header.cmdSys = CMD_SYS_REPLICATE;
++ rlog_header.query_size = 0;
++ status
=PGRsend_rlog_packet(Replicateion_Log->r_log_sock,&rlog_header,"");
++ if (status != STATUS_ERROR)
++ {
++ status =
PGRrecv_rlog_result(Replicateion_Log->r_log_sock,&rlog_header,
sizeof(ReplicateHeader));
++ if (status > 0)
++ {
++ memcpy(header,&rlog_header,sizeof(ReplicateHeader));
++ status = STATUS_OK;
++ }
++ else
++ {
++ status = STATUS_ERROR;
++ }
++ }
++
++ close(Replicateion_Log->r_log_sock );
++ Replicateion_Log->r_log_sock = -1;
++
++ return status;
++}
++
++static int
++send_message(int sock, char * msg, int len)
++{
++ char * func = "send_message()";
++ fd_set wmask;
++ struct timeval timeout;
++ int rtn = 0;
++ char * send_ptr = NULL;
++ int send_size= 0;
++ int buf_size = 0;
++ int s = 0;
++ int flag = 0;
++
++ if ((msg == NULL) || (len <= 0) || (sock <= 0))
++ {
++ return STATUS_ERROR;
++ }
++ send_ptr = msg;
++ buf_size = len;
++
++ /*
++ * Wait for something to happen.
++ */
++#ifdef MSG_DONTWAIT
++ flag |= MSG_DONTWAIT;
++#endif
++#ifdef MSG_NOSIGNAL
++ flag |= MSG_NOSIGNAL;
++#endif
++
++ for (;;)
++ {
++ timeout.tv_sec = PGR_SEND_TIMEOUT;
++ timeout.tv_usec = 0;
++
++ FD_ZERO(&wmask);
++ FD_SET(sock,&wmask);
++ rtn = select(sock+1, (fd_set *)NULL, &wmask, (fd_set *)NULL,
&timeout);
++
++ if (rtn < 0 )
++ {
++ if (errno == EAGAIN || errno == EINTR)
++ continue;
++
++ show_error("%s:send-select error:
%d(%s)",func,errno,strerror(errno));
++ return STATUS_ERROR;
++ }
++ else if (rtn & FD_ISSET(sock, &wmask))
++ {
++ s = send(sock,send_ptr + send_size,buf_size -
send_size ,flag);
++ if (s < 0)
++ {
++ if (errno == EINTR || errno == EAGAIN)
++ continue;
++ else
++ {
++ show_error("%s:send error:
%d(%s)",func,errno,strerror(errno));
++ memset(send_ptr, 0, len);
++ return STATUS_ERROR;
++ }
++ }
++ else if (s == 0)
++ {
++ show_error("%s:unexpected EOF");
++ memset(send_ptr, 0, len);
++ return STATUS_ERROR;
++ }
++ else /* s > 0 */
++ {
++ send_size += s;
++ if (send_size == buf_size)
++ {
++ return STATUS_OK;
++ }
++ }
++ }
++ }
++ show_error("%s:send-select unknown error: %d(%s)",
++ func,errno,strerror(errno));
++ return STATUS_ERROR;
++}
++
++static void
++exit_rlog(int sig)
++{
++ sigset_t mask;
++
++ Exit_Request = true;
++ if (sig == SIGTERM)
++ {
++ if (Idle_Flag == BUSY_MODE)
++ {
++ return;
++ }
++ }
++
++ sigemptyset(&mask);
++ sigaddset(&mask, SIGINT);
++ sigaddset(&mask, SIGTERM);
++ sigaddset(&mask, SIGQUIT);
++ sigprocmask(SIG_BLOCK, &mask, NULL);
++
++ if (RLog_Recv_Sock >= 0)
++ {
++ close(RLog_Recv_Sock);
++ RLog_Recv_Sock = -1;
++ }
++ if (Replicateion_Log->RLog_Sock_Path != NULL)
++ {
++ unlink(Replicateion_Log->RLog_Sock_Path);
++ free(Replicateion_Log->RLog_Sock_Path);
++ }
++ exit(0);
++}
++#endif /* USE_REPLICATION */
+diff -aruN postgresql-8.1.2/src/pgcluster/tool/Makefile
pgcluster-1.5.0rc7/src/pgcluster/tool/Makefile
+--- postgresql-8.1.2/src/pgcluster/tool/Makefile 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/tool/Makefile 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,32 @@
++#-------------------------------------------------------------------------
++#
++# Makefile for src/pgcluster/pgrp
++#
++#-------------------------------------------------------------------------
++
++subdir = src/pgcluster/tool
++top_builddir = ../../..
++include $(top_builddir)/src/Makefile.global
++
++OBJS= pgcbench.o
++
++# EXTRA_OBJS = $(top_builddir)/src/backend/libpq/replicate_com.o
++
++override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) -DBINDIR=\"$(bindir)\"
++
++all: pgcbench
++
++pgcbench: $(OBJS) $(libpq_builddir)/libpq.a
++ $(CC) $(CFLAGS) $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o
$@
++
++install: all installdirs
++ $(INSTALL_PROGRAM) pgcbench$(X) $(DESTDIR)$(bindir)/pgcbench$(X)
++
++installdirs:
++ $(mkinstalldirs) $(DESTDIR)$(bindir)
++
++uninstall:
++ rm -f $(addprefix $(DESTDIR)$(bindir)/, pgcbench$(X))
++
++clean distclean maintainer-clean:
++ rm -f pgcbench$(X) $(OBJS)
+diff -aruN postgresql-8.1.2/src/pgcluster/tool/README
pgcluster-1.5.0rc7/src/pgcluster/tool/README
+--- postgresql-8.1.2/src/pgcluster/tool/README 1970-01-01 01:00:00.000000000
+0100
++++ pgcluster-1.5.0rc7/src/pgcluster/tool/README 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,127 @@
++-----------------------------------------------------
++About pgcbench
++-----------------------------------------------------
++
++The pgcbench is the program which performs the benchmark test of PGCluster.
++Of course, it can use for benchmark not only PGCluster but PostgreSQL.
++
++The pgcbench displays the number of executed transaction per second (tps)
which performed transaction containing SELECT, UPDATE, and INSERT.
++The table set as the object of transaction contains data of 100,000 lines
by a default.
++
++A display is actually as follows.
++ number of clients: 4
++ number of transactions actually processed: 100
++ run time (sec) = 4.416423
++ tps = 22.642759 (including connections establishing)
++
++
++The pgcbench is calculating tps not based on the actually completed number
of transactions but based on the number that is specified at beginning.
++So, the tps displayed may not be right when the benchmark has been
suspended.
++
++The pgcbench was created based on pgbench which is the benchmark program
for PostgreSQL written by Tatsuo Ishii.
++
++
++-----------------------------------------------------
++Feature of pgcbench
++-----------------------------------------------------
++- Although the pgbench is simulating multi-user connections in the single
process, pgcbench has made multi-user connections in the multi-process by
fork.
++- There are some options which specifies the contents of transaction in
pgcbench.
++
++
++-----------------------------------------------------
++Installation of pgcbench
++-----------------------------------------------------
++1. Install with the PGCluster
++Your installation of PGCluster also installs pgcbench automatically.
++
++2. Install only pgcbench
++If you want install only pgcbench, you do not need to compile all of the
PGCluster.
++pgcbench is compiled as follow.
++
++ $ cd $TOP_PGCusters_Source
++ $ ./configure
++ $ cd src/interface/libpq
++ $ make all
++ $ cd $TOP_PGCusters_Source/src/pgcluster/tool
++ $ make
++
++-----------------------------------------------------
++Usage of pgcbench
++-----------------------------------------------------
++pgcbench [options] [DB name]
++pgcbench -i [options] [DB name] (for initialization of DB)
++
++[options]
++-h: host name
++-p: port number
++-c: number of clients connected simultaneously (default is 1)
++-t: number of transaction which each client executed
++-s: scaling factor of table size. 1 means 100,000 records. ( use with '-i' )
++-u: user name
++-P: password
++-n: it does not execute VACUUM and is not cleared history table.
++-v: execute VACUUM and clear history table
++-I: execute INSERT query only
++-U: execute UPDATE query only
++-S: execute SELECT query only
++-f: command script file name
++-T: transaction is performed in the transaction bloc surrounded by BEGIN
and END.
++-C: each session is reconnected for every transaction
++-l: The execution time of each transaction is recorded. That log file is
created under current directory as "pgcbench_log.xxx".
++-d: debug option
++
++-----------------------------------------------------
++Initialization of the database
++-----------------------------------------------------
++In order to execute a default benchmark test by pgcbench, the data base
should be initialized and made test data.
++You can execute the following commands for initialization of the data base.
++
++ $ pgcbench -i [DB name ]
++
++Thereby, the following tables are created (when a scaling factor is 1) .
++
++ table name | row num
++ -----------+--------
++ branches | 1
++ tellers | 10
++ accounts | 100000
++ history | 0
++
++When a scaling factor is changed into 10, 100, 1000, etc., according to it,
the number of table rows will increase 10 times, 100 times, and 1000 times.
++For example, when the scaling factor is set to 100, the number of table
rows is as follows.
++
++ table name | row num
++ -----------+----------
++ branches | 100
++ tellers | 1000
++ accounts | 10000000
++ history | 0
++
++
++-----------------------------------------------------
++Format of the command script file
++-----------------------------------------------------
++The pgcbench read the command script file that is specified with '-f'
option, and executes the specified transaction.
++The specified command can be described one per one line.
++The null line is disregarded and the row which starts with double hyphen
means a comment line.
++In addition to the SQL command, the meta-command which starts with a
backslash can be described in this file.
++
++The following meta-commands can be used.
++
++\setrandom name min max
++ Random digits are set into variable 'name'.
++ The sphere of random digits is between 'min' and 'max'.
++ For example, when you want to execute the benchmark like TPC-B, you
can describes as follows.
++
++
++ \setrandom aid 1 100000
++ \setrandom bid 1 1
++ \setrandom tid 1 10
++ \setrandom delta 1 1000
++ BEGIN
++ UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid
++ SELECT abalance FROM accounts WHERE aid = :aid
++ UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid
++ UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid
++ INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid,
:aid, :delta, 'now')
++ END
+diff -aruN postgresql-8.1.2/src/pgcluster/tool/README.jp
pgcluster-1.5.0rc7/src/pgcluster/tool/README.jp
+--- postgresql-8.1.2/src/pgcluster/tool/README.jp 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/tool/README.jp 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,296 @@
++$Id: README,v 1.2 2005/08/02 01:24:53 tom-sato Exp $
++
++$B"#(B pgcbench $B$H$O(B
++
++pgcbench $B$O(B PGCluster
$B$N%Y%s%A%^!<%/%F%9%H$r9T$&%W%m%0%i%`$G$9!#$b$A$m(B
++$B$s!"(BPGCluster $B$@$1$G$O$J$/(B PostgreSQL
$B$N%Y%s%A%^!<%/$r<B;\$9$k$3$H$b$G(B
++$B$-$^$9!#(B
++
++pgcbench $B$O(B SELECT$B!"(BUPDATE$B!"(BINSERT
$B$r4^$`%H%i%s%6%/%7%g%s$r<B9T$7!"A4(B
++$BBN$N<B9T;~4V$H<B:]$K40N;$7$?$G$"$m$&%H%i%s%6%/%7%g%s$N?t$+$i(B 1
$BIC4V$K(B
++$B<B9T$G$-$?%H%i%s%6%/%7%g%s?t(B (tps)
$B$rI=<($7$^$9!#=hM}$NBP>]$H$J$k%F!<(B
++$B%V%k$O%G%U%)%k%H$G$O(B 10 $BK|9T$N%G!<%?$r4^$_$^$9!#(B
++
++$B<B:]$KI=<($O0J2<$N$h$&$K$J$j$^$9!#(B
++
++ number of clients: 4
++ number of transactions actually processed: 100
++ run time (sec) = 4.416423
++ tps = 22.642759 (including connections establishing)
++
++$B"((B $BCm0U(B
++
++ pgcbench $B$G$O!"%H%i%s%6%/%7%g%s$,<B:]$K40N;$7$?$+$I$&$+$K4X78$J$/!"(B
++ $B:G=i$K;XDj$5$l$?%H%i%s%6%/%7%g%s$N?t$r$b$H$K(B tps
$B$r5a$a$F$$$k$?$a!"(B
++ $B%Y%s%A%^!<%/$,ESCf$G=*N;$7$F$7$^$C$?>l9g!"I=<($5$l$k(B tps
$B$,?.MQ$G$-(B
++ $B$J$$2DG=@-$,$"$k$3$H$KCm0U$7$F$/$@$5$$!#(B
++
++$B$J$*!"(Bpgcbench $B$O(B pgbench $B$H$$$&(B PostgreSQL
$BMQ$K=q$+$l$?%Y%s%A%^!<%/%F(B
++$B%9%H$r9T$J$&%W%m%0%i%`$r$b$H$K:n@.$5$l$^$7$?!#(B
++
++
++$B"#(B pgbench $B$H$N0c$$(B
++
++$B!&(B $B%^%k%A%f!<%64D6-$N<B8=J}K!(B
++
++ pgbench $B$,(B libpq
$B$NHsF14|=hM}5!G=$r;H$C$?%7%s%0%k%W%m%;%9$K$h$C$F%7(B
++ $B%_%e%l!<%H$7$F$$$k$N$KBP$7$F!"(Bpgcbench $B$G$O(B fork
$B$r;H$C$?%^%k%A%W%m(B
++ $B%;%9$K$h$C$F<B8=$7$F$$$^$9!#$3$l$O!"%7%s%0%k%W%m%;%9$G$O%m%C%/$,H/(B
++ $B@8$9$k$H!"(BPGCluster $B$,;_$^$C$F$7$^$&$3$H$rHr$1$k$?$a$G$9!#(B
++
++$B!&(B $B%*%W%7%g%s$NJQ99(B
++
++ pgcbench $B$K$O!"(BPGCluster
$B$N%Y%s%A%^!<%/%F%9%H$r9T$J$&$N$KJXMx$J!"%H(B
++ $B%i%s%6%/%7%g%s$NFbMF$r;XDj$9$k%*%W%7%g%s$,!"$$$/$D$+DI2C$5$l$F$$$^(B
++ $B$9!%(B
++
++
++$B"#(B pgcbench $B$N%$%s%9%H!<%k(B
++
++1. PGCluster $B$r(B configure$B!"(Bmake $B$7$^$9!#(B
++
++ pgcbench $B$N%$%s%9%H!<%k$@$1$,L\E*$G$"$l$P!"(BPGCluster
$B$N$9$Y$F$r%3%s(B
++ $B%Q%$%k$9$kI,MW$O$"$j$^$;$s!#(BPGCluster
$B%=!<%9$N%H%C%W%G%#%l%/%H%j$G(B
++ configure $B$r$7$?8e!"(Bsrc/interface/libpq $B$G(B make all
$B$r<B9T$9$l$P=`(B
++ $BHw40N;$G$9!#(B
++
++2. $B$3$N%G%#%l%/%H%j(B (src/pgcluster/tool) $B$G(B make
$B$r<B9T$7$^$9!#(B
++
++ $B$=$&$9$k$H!"(Bpgcbench
$B$H$$$&<B9T%W%m%0%i%`$,$G$-$^$9!#$=$N$^$^<B9T$7(B
++ $B$F$b9=$$$^$;$s$7!"(Bmake install $B$r<B9T$7$F(B PGCluster
$B$NI8=`<B9T%W%m(B
++ $B%0%i%`%G%#%l%/%H%j(B ($B%G%U%)%k%H$G$O(B /usr/local/pgsql/bin)
$B$K%$%s%9%H!<(B
++ $B%k$9$k$3$H$b$G$-$^$9!#(B
++
++
++$B"#(B pgcbench $B$N;H$$J}(B
++
++ pgcbench [$B%*%W%7%g%s(B] [$B%G!<%?%Y!<%9L>(B]
++
++$B%G!<%?%Y!<%9L>$r>JN,$9$k$H!"%m%0%$%sL>$HF1$8%G!<%?%Y!<%9L>$r;XDj$7$?$b(B
++$B$N$H8+$J$7$^$9!#$J$*!"(Bpgcbench
$B$G%G%U%)%k%H$N%Y%s%A%^!<%/$r<B;\$9$k$?(B
++$B$a$K$O!"8e=R$N(B -i
$B%*%W%7%g%s$r;HMQ$7$F%G!<%?%Y!<%9$r$"$i$+$8$a=i4|2=$7(B
++$B$F$*$/I,MW$,$"$j$^$9!#(B
++
++pgcbench $B$K$O$$$m$$$m$J%*%W%7%g%s$,$"$j$^$9!#(B
++
++-h $B%[%9%HL>(B
++
++ PostgreSQL$B$N(B $B%G!<%?%Y!<%9%G!<%b%s(B postmaster
$B$NF0:n$7$F$$$k%[%9%HL>(B
++ $B$r;XDj$7$^$9!#>JN,$9$k$H4D6-JQ?t(B PGHOST
$B$K@_Dj$7$?%[%9%HL>$,;XDj$5(B
++ $B$l$^$9!#(BPGHOST $B$b;XDj$5$l$F$$$J$$$H<+%[%9%H$K(B Unix
$B%I%a%$%s%=%1%C%H(B
++ $B$G@\B3$7$^$9!#(B
++
++-p $B%]!<%HHV9f(B
++
++ postmaster $B$N;HMQ$9$k%]!<%HHV9f$r;XDj$7$^$9!#>JN,$9$k$H4D6-JQ?t(B
++ PGPORT $B$K@_Dj$7$?%]!<%HHV9f$,;XDj$5$l$^$9!#(BPGPORT
$B$b;XDj$5$l$F$$$J(B
++ $B$$$H(B 5432 $B$,;XDj$5$l$?$b$N$H8+$J$7$^$9!#(B
++
++-c $B%/%i%$%"%s%H?t(B
++
++ $BF1;~<B9T%/%i%$%"%s%H?t$r;XDj$7$^$9!#>JN,;~$O(B 1
$B$H$J$j$^$9!#(Bpgcbench
++ $B$OF1;~<B9T%/%i%$%"%s%H$4$H$K%U%!%$%k%G%#%9%/%j%W%?$r;HMQ$9$k$N$G!"(B
++ $B;HMQ2DG=%U%!%$%k%G%#%9%/%j%W%??t$r1[$($k%/%i%$%"%s%H?t$O;XDj$G$-$^(B
++ $B$;$s!#;HMQ2DG=%U%!%$%k%G%#%9%/%j%W%??t$O(B limit $B$d(B ulimit
$B%3%^%s%I$G(B
++ $B3NG'$9$k$3$H$,$G$-$^$9!#(B
++
++-t $B%H%i%s%6%/%7%g%s?t(B
++
++ $B%H%i%s%6%/%7%g%s?t$r;XDj$7$^$9!#3F%/%i%$%"%s%H$,<B9T$9$k%H%i%s%6%/(B
++ $B%7%g%s?t$O$3$l$r%/%i%$%"%s%H?t$G3d$C$??t$H$J$j$^$9!#>JN,;~$O(B 10
$B$H(B
++ $B$J$j$^$9!#(B
++
++-s $B%9%1!<%j%s%0%U%!%/%?!<(B
++
++ -i $B%*%W%7%g%s$H$H$b$K;HMQ$7$^$9!#%9%1!<%j%s%0%U%!%/%?!<$O(B 1
$B0J>e$N(B
++ $B@0?t$G$9!#%9%1!<%j%s%0%U%!%/%?!<$rJQ$($k$3$H$K$h$j!"%F%9%H$NBP>]$H(B
++ $B$J$k%F!<%V%k$NBg$-$5$,(B 10 $BK|(B $B!_(B
$B%9%1!<%j%s%0%U%!%/%?!<$K$J$j$^$9!#(B
++ $B%G%U%)%k%H$N%9%1!<%j%s%0%U%!%/%?!<$O(B 1 $B$G$9!#(B
++
++-u $B%m%0%$%sL>(B
++
++ DB $B%f!<%6$N%m%0%$%sL>$r;XDj$7$^$9!#>JN,$9$k$H4D6-JQ?t(B PGUSER
$B$K@_Dj(B
++ $B$7$?%m%0%$%sL>$,;XDj$5$l$^$9!#(B
++
++-P $B%Q%9%o!<%I(B
++
++ $B%Q%9%o!<%I$r;XDj$7$^$9!#$J$*!"$3$N%*%W%7%g%s$r;H$&$H!"%Q%9%o!<%I$r(B
++ ps
$B%3%^%s%I$G8+$i$l$k$J$I!"%;%-%e%j%F%#%[!<%k$K$J$k2DG=@-$,$"$k$N$G!"(B
++ $B%F%9%HMQ$K$N$_$*;H$$2<$5$$!#(B
++
++-n
++
++ $B$3$N%*%W%7%g%s$r;XDj$9$k$H!"%Y%s%A%^!<%/3+;OA0$K(B VACUUM $B$H(B
history
++ $B%F!<%V%k$N%/%j%"$r<B9T$7$^$;$s!#(B
++
++-v
++
++ $B$3$N%*%W%7%g%s$r;XDj$9$k$H!"%Y%s%A%^!<%/3+;OA0$K(B VACUUM $B$H(B
history
++ $B%F!<%V%k$N%/%j%"$r<B9T$7$^$9!#(B-v $B$H(B -n
$B$r>JN,$9$k$H!":G>.8B$N(B
++ VACUUM $B$J$I$r<B9T$7$^$9!#$9$J$o$A!"(Bhistory
$B%F!<%V%k$N%/%j%"$H!"(B
++ branches$B!"(Btellers$B!"(Bhistory $B%F!<%V%k$N(B VACUUM
$B$r<B9T$7$^$9!#$3$l$O!"(B
++ VACUUM $B$N;~4V$r:G>.8B$K$7$J$,$i!"%Q%U%)!<%^%s%9$K1F6A$9$k%4%_A]=|$r(B
++ $B8z2LE*$K<B9T$7$^$9!#DL>o$O(B -v $B$H(B -n
$B$r>JN,$9$k$3$H$r?d>)$7$^$9!#(B
++
++-I
++
++ $BA^F~$N$_$N%H%i%s%6%/%7%g%s$r<B9T$7$^$9!#A^F~%9%T!<%I$rB,Dj$7$?$$$H(B
++ $B$-$K;H$$$^$9!#(B
++
++-U
++
++ $B99?7$N$_$N%H%i%s%6%/%7%g%s$r<B9T$7$^$9!#99?7%9%T!<%I$rB,Dj$7$?$$$H(B
++ $B$-$K;H$$$^$9!#(B
++
++-S
++
++ $B8!:w$N$_$N%H%i%s%6%/%7%g%s$r<B9T$7$^$9!#8!:w%9%T!<%I$rB,Dj$7$?$$$H(B
++ $B$-$K;H$$$^$9!#(B
++
++-f $B%U%!%$%kL>(B
++
++ $B%H%i%s%6%/%7%g%s$NFbMF$,5-=R$5$l$?%U%!%$%kL>$r;XDj$7$^$9!#$3$N%*%W(B
++ $B%7%g%s$r;XDj$9$k$H!"%U%!%$%k$K5-=R$5$l$?FbMF$N%H%i%s%6%/%7%g%s$r<B(B
++ $B9T$7$^$9!#%Y%s%A%^!<%/$G;HMQ$9$k%F!<%V%k$O$"$i$+$8$a=i4|2=$7$F$*$/(B
++ $BI,MW$,$"$j$^$9!#F~NO%U%!%$%k$N%U%)!<%^%C%H$O8e=R$7$^$9!#(B
++
++-T
++
++ BEGIN $B$H(B END
$B$G0O$^$l$?%H%i%s%6%/%7%g%s%V%m%C%/Fb$G%H%i%s%6%/%7%g%s(B
++ $B$r<B9T$7$^$9!#(B
++
++-C
++
++ $B$3$N%*%W%7%g%s$r;XDj$9$k$H!":G=i$K3NN)$7$?%3%M%/%7%g%s$r;H$$2s$9$N(B
++ $B$G$O$J$/!"3F%H%i%s%6%/%7%g%s$4$H$K(B DB
$B$X$N@\B3$r9T$$$^$9!#%3%M%/%7%g(B
++ $B%s$N%*!<%P!<$X%C%I$rB,Dj$9$k$N$KM-8z$G$9!#(B
++
++-l
++
++ $B8D!9$N%H%i%s%6%/%7%g%s$N<B9T;~4V$r5-O?$7$^$9!#5-O?@h$O%+%l%s%H%G%#(B
++ $B%l%/%H%j0J2<$N(B pgbench_log.xxx
$B$H$$$&%U%!%$%k$G$9!#%U%!%$%k$N%U%)!<(B
++ $B%^%C%H$O0J2<$N$h$&$K$J$j$^$9!#;~4V$O%^%$%/%mICC10L$G$9!#(B
++
++ <$B%/%i%$%"%s%H(B ID> <$B%H%i%s%6%/%7%g%sHV9f(B> <$B;~4V(B>
++
++-d
++
++ $B%G%P%C%0%*%W%7%g%s!#MM!9$J>pJs$,I=<($5$l$^$9!#(B
++
++
++$B"#(B $B%G!<%?%Y!<%9$N=i4|2=(B
++
++pgcbench $B$G%G%U%)%k%H$N%Y%s%A%^!<%/%F%9%H$r<B;\$9$k$?$a$K$O!"$"$i$+$8(B
++$B$a%G!<%?%Y!<%9$r=i4|2=$7!"%F%9%H%G!<%?$r:n$kI,MW$,$"$j$^$9!#(B
++
++ pgcbench -i [$B%G!<%?%Y!<%9L>(B]
++
++$B$3$l$K$h$j0J2<$N%F!<%V%k$,:n@.$5$l$^$9(B
($B%9%1!<%j%s%0%U%!%/%?!<$,(B 1 $B$N(B
++$B>l9g(B)$B!#(B
++
++ $B%F!<%V%kL>(B | $B9T?t(B
++ ------------+--------
++ branches | 1
++ tellers | 10
++ accounts | 100000
++ history | 0
++
++$B"((B $BCm0U(B
++
++ $BF1$8L>A0$N%F!<%V%k$,$"$k$H:o=|$5$l$F$7$^$&$N$G$4Cm0U2<$5$$!#(B
++
++$B%9%1!<%j%s%0%U%!%/%?!<$r(B 10$B!"(B100$B!"(B1000
$B$J$I$KJQ99$9$k$H!">e5-9T?t$O$=(B
++$B$l$K1~$8$F(B 10 $BG\!"(B100 $BG\!"(B1000
$BG\$K$J$j$^$9!#Nc$($P!"%9%1!<%j%s%0%U%!(B
++$B%/%?!<$r(B 100 $B$H$9$k$H0J2<$N$h$&$K$J$j$^$9!#(B
++
++ $B%F!<%V%kL>(B | $B9T?t(B
++ ------------+----------
++ branches | 100
++ tellers | 1000
++ accounts | 10000000
++ history | 0
++
++
++$B"#(B $BF~NO%U%!%$%k$N%U%)!<%^%C%H(B
++
++pgcbench $B$G$O!"(B-f
$B%*%W%7%g%s$r;XDj$7$F%H%i%s%6%/%7%g%s$K4^$^$l$k(B SQL
++$B%3%^%s%I$NFbMF$r5-=R$7$?%U%!%$%k$rFI$_9~$`$3$H$,$G$-$^$9!#F~NO%U%!%$%k(B
++$B$K$O(B 1 $B9T$K$D$-(B 1
$B$D$N%3%^%s%I$r5-=R$7$^$9!#6u9T$OL5;k$5$l!"Fs=E%O%$%U(B
++$B%s$G;O$^$k9T$O%3%a%s%H$r0UL#$7$^$9!#(B
++
++$B%3%^%s%I$K$O!"(BSQL
$B%3%^%s%I$K2C$(!"%P%C%/%9%i%C%7%e$G;O$^$k%a%?%3%^%s%I(B
++$B$r5-=R$9$k$3$H$,$G$-$^$9!#%a%?%3%^%s%I$O(B pgcbench
$B<+?H$K$h$C$F<B9T$5$l(B
++$B$^$9!#%a%?%3%^%s%I$N7A<0$O%P%C%/%9%i%C%7%e!"$=$ND>8e$K%3%^%s%I$NF0;l!"(B
++$B$=$N<!$K0z?t$,B3$-$^$9!#F0;l%3%^%s%I$H0z?t!"$^$?$=$l$>$l$N0z?t$O6uGrJ8(B
++$B;z$K$h$C$F6hJL$5$l$^$9!#(B
++
++$B:#$N$H$3$m!"0J2<$N%a%?%3%^%s%I$,Dj5A$5$l$F$$$^$9!#(B
++
++\setrandom name min max
++
++ $B:G>.CM(B min $B$H:GBgCM(B max $B$N4V$NCM$r<h$kMp?t$r!"(Bname
$BJQ?t$K@_Dj$7$^$9!#(B
++
++$BJQ?t$KMp?t$r@_Dj$9$k$K$O!"(B\setrandom
$B%a%?%3%^%s%I$r;HMQ$7$F0J2<$N$h$&(B
++$B$K5-=R$7$^$9!#(B
++
++ \setrandom aid 1 100000
++
++$B$3$l$O!"JQ?t(B aid $B$K(B 1 $B$+$i(B 100000
$B$N4V$NMp?t$r@_Dj$7$^$9!#$^$?!"JQ?t$N(B
++$BCM$r(B SQL
$B%3%^%s%I$KKd$a9~$`$K$O!"0J2<$N$h$&$K$=$NL>A0$NA0$K%3%m%s$rIU(B
++$B$1$^$9!#(B
++
++ SELECT abalance FROM accounts WHERE aid = :aid
++
++$BNc$($P!"(BTPC-B
$B$K;w$?%Y%s%A%^!<%/$r9T$&$K$O!"0J2<$N$h$&$K%H%i%s%6%/%7%g(B
++$B%s$NFbMF$r%U%!%$%k$K5-=R$7!"(B-f
$B%*%W%7%g%s$K$h$C$F$=$N%U%!%$%k$r;XDj$7(B
++$B$F(B pgcbench $B$r<B9T$7$^$9!#(B
++
++ \setrandom aid 1 100000
++ \setrandom bid 1 1
++ \setrandom tid 1 10
++ \setrandom delta 1 1000
++ BEGIN
++ UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid
++ SELECT abalance FROM accounts WHERE aid = :aid
++ UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid
++ UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid
++ INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid,
:aid, :delta, 'now')
++
++$B$J$*!"$3$NNc$G$O!"(B-i $B%*%W%7%g%s$r;H$C$F=i4|2=$7$?%G!<%?%Y!<%9(B
($B%9%1!<(B
++$B%j%s%0%U%!%/%?!<$,(B 1 $B$N>l9g(B)
$B$KBP$7$F%Y%s%A%^!<%/$r9T$&$3$H$r2>Dj$7$F(B
++$B$$$^$9!#(B
++
++
++$B"#(B $B%H%i%s%6%/%7%g%s$NDj5A(B
++
++pgcbench $B$N%G%U%)%k%H$N%Y%s%A%^!<%/$G$O!"0J2<$N(B SQL
$B%3%^%s%I$rA4It40N;(B
++$B$7$F(B 1 $B%H%i%s%6%/%7%g%s$H?t$($F$$$^$9!#(B
++
++1. SELECT abalance FROM accounts WHERE aid = :aid
++
++ :aid $B$O(B 1 $B$+$i%9%1!<%j%s%0%U%!%/%?!<(B $B!_(B 10
$BK|$^$G$NCM$r<h$kMp?t$G$9!#(B
++ $B$3$3$G$O(B 1
$B7o$@$18!:w$5$l$^$9!#0J2<!"Mp?t$NCM$O$=$l$>$l$3$N%H%i%s%6(B
++ $B%/%7%g%s$NCf$G$OF1$8CM$r;H$$$^$9!#(B
++
++2. UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid
++
++ :delta $B$O(B 1 $B$+$i(B 1000 $B$^$G$NCM$r<h$kMp?t$G$9!#(B
++
++3. SELECT abalance FROM accounts WHERE aid = :aid
++
++4. INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid,
:aid, :delta, 'now')
++
++ :tid $B$O(B 1 $B$+$i%9%1!<%j%s%0%U%!%/%?!<(B $B!_(B 10
$B$^$G$NCM$r<h$kMp?t!"(B:bid
++ $B$O(B 1 $B$+$i%9%1!<%j%s%0%U%!%/%?!<$^$G$NCM$r<h$kMp?t$G$9!#(B
++
++5. INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid,
:aid, :delta, 'now')
++
++6. INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid,
:aid, :delta, 'now')
++
++7. SELECT abalance FROM accounts WHERE aid = :aid
++
++-T $B%*%W%7%g%s$r;XDj$7$F%H%i%s%6%/%7%g%s%V%m%C%/Fb$G%H%i%s%6%/%7%g%s$r(B
++$B<B9T$9$k>l9g!"(B1 $B$r(B BEGIN$B$K!"(B7 $B$r(B END
$B$KCV$-49$($?(B SQL $B%3%^%s%I$,<B9T$5(B
++$B$l$^$9!#$^$?!"%H%i%s%6%/%7%g%s$H$7$F<B9T$5$l$k(B SQL
$B%3%^%s%I$O!"(B-I $B%*%W(B
++$B%7%g%s(B ($BA^F~$N$_(B) $B$G$"$l$P(B 4$B!"(B-U ($B99?7$N$_(B)
$B$G$"$l$P(B 2$B!"(B-S ($B8!:w$N$_(B)
++$B$G$"$l$P(B 1 $B$H$J$j$^$9!#(B
+diff -aruN postgresql-8.1.2/src/pgcluster/tool/pgcbench.c
pgcluster-1.5.0rc7/src/pgcluster/tool/pgcbench.c
+--- postgresql-8.1.2/src/pgcluster/tool/pgcbench.c 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/tool/pgcbench.c 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,1626 @@
++/*
++ * pgbench: a simple benchmark program for PGCluster
++ * This program was written based on pgbench by Tatsuo Ishii.
++ *
++ * Portions Copyright (c) 2003-2006, Atsushi Mitani
++ * Portions Copyright (c) 2000-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 "postgres_fe.h"
++
++#include "libpq-fe.h"
++
++#include <errno.h>
++
++
++#ifdef WIN32
++#include "win32.h"
++#else
++#include <sys/time.h>
++#include <unistd.h>
++
++#ifdef HAVE_GETOPT_H
++#include <getopt.h>
++#endif
++
++#ifdef HAVE_SYS_SELECT_H
++#include <sys/select.h>
++#endif
++
++/* for getrlimit */
++#include <sys/resource.h>
++#endif /* ! WIN32 */
++
++#include <sys/types.h>
++#include <sys/wait.h>
++
++#include <ctype.h>
++#include <search.h>
++
++extern char *optarg;
++extern int optind;
++
++#ifdef WIN32
++#undef select
++#endif
++
++
++/********************************************************************
++ * some configurable parameters */
++
++#define MAXCLIENTS 4096 /* max number of clients
allowed */
++
++int nclients = 1; /* default number of
simulated clients */
++int nxacts = 10; /* default number of
transactions per
++ * clients */
++
++/*
++ * scaling factor. for example, tps = 10 will make 1000000 tuples of
++ * accounts table.
++ */
++int tps = 1;
++
++/*
++ * end of configurable parameters
++ *********************************************************************/
++
++#define nbranches 1
++#define ntellers 10
++#define naccounts 100000
++
++#define SELECT_ONLY (1)
++#define INSERT_ONLY (2)
++#define UPDATE_ONLY (3)
++#define WITH_TRANSACTION (4)
++#define TPC_B_LIKE (5)
++#define CUSTOM_QUERY (6)
++
++#define SQL_COMMAND 1
++#define META_COMMAND 2
++
++FILE *LOGFILE = NULL;
++
++bool use_log = false; /* log transaction
latencies to a file */
++
++int remains; /* number of
remaining clients */
++
++int is_connect; /* establish
connection for each
++ *
transaction */
++
++char *pghost = "";
++char *pgport = NULL;
++char *pgoptions = NULL;
++char *pgtty = NULL;
++char *login = NULL;
++char *pwd = NULL;
++char *dbName;
++
++typedef struct
++{
++ char *name;
++ char *value;
++} Variable;
++
++typedef struct
++{
++ PGconn *con; /* connection handle to DB */
++ int id; /* client No.
*/
++ int state; /* state No. */
++ int cnt; /* xacts count */
++ int ecnt; /* error count */
++ int maxAct;
++ int listen; /* 0 indicates that
an async query has
++ * been sent
*/
++ int aid; /* account id for
this transaction */
++ int bid; /* branch id for this
transaction */
++ int tid; /* teller id for this
transaction */
++ int delta;
++ int abalance;
++ void *variables;
++ struct timeval txn_begin; /* used for measuring latencies */
++} CState;
++
++typedef struct
++{
++ int type;
++ int argc;
++ char **argv;
++} Command;
++
++Command **commands = NULL;
++
++static void
++usage(void)
++{
++ fprintf(stderr, "usage: pgcbench [-h hostname][-p port][-c
nclients][-t ntransactions][-s scaling_factor][-I(insert only)][-U(update
only)][-S(select only)][-f filename][-u login][-P
password][-d(debug)][dbname]\n");
++ fprintf(stderr, "(initialize mode): pgcbench -i [-h hostname][-p
port][-s scaling_factor][-u login][-P password][-d(debug)][dbname]\n");
++}
++
++/* random number generator */
++static int
++getrand(int min, int max )
++{
++
++ return (min + (int) (max * 1.0 * rand() / (RAND_MAX + 1.0)));
++}
++
++/* set up a connection to the backend */
++static PGconn *
++doConnect(void)
++{
++ PGconn *con;
++ PGresult *res;
++
++ con = PQsetdbLogin(pghost, pgport, pgoptions, pgtty, dbName,
++ login, pwd);
++ if (con == NULL)
++ {
++ fprintf(stderr, "Connection to database '%s' failed.\n",
dbName);
++ fprintf(stderr, "Memory allocatin problem?\n");
++ return (NULL);
++ }
++
++ if (PQstatus(con) == CONNECTION_BAD)
++ {
++ fprintf(stderr, "Connection to database '%s' failed.\n",
dbName);
++
++ if (PQerrorMessage(con))
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ else
++ fprintf(stderr, "No explanation from the backend\n");
++
++ return (NULL);
++ }
++
++ res = PQexec(con, "SET search_path = public");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++
++ return (con);
++}
++
++/* throw away response from backend */
++static void
++discard_response(CState * state)
++{
++ PGresult *res;
++
++ do
++ {
++ res = PQgetResult(state->con);
++ if (res)
++ PQclear(res);
++ } while (res);
++}
++
++/* check to see if the SQL result was good */
++static int
++check(CState * st, PGresult *res, int good)
++{
++ if (res && PQresultStatus(res) != good)
++ {
++ fprintf(stderr, "aborted in state %d: %s", st->state,
PQerrorMessage(st->con));
++ PQfinish(st->con);
++ st->con = NULL;
++ return (-1);
++ }
++ return (0); /* OK */
++}
++
++static int
++compareVariables(const void *v1, const void *v2)
++{
++ return strcmp(((Variable *)v1)->name, ((Variable *)v2)->name);
++}
++
++static char *
++getVariable(CState * st, char *name)
++{
++ Variable key = { name }, *var;
++
++ var = tfind(&key, &st->variables, compareVariables);
++ if (var != NULL)
++ return (*(Variable **)var)->value;
++ else
++ return NULL;
++}
++
++static int
++putVariable(CState * st, char *name, char *value)
++{
++ Variable key = { name }, *var;
++
++ var = tfind(&key, &st->variables, compareVariables);
++ if (var == NULL)
++ {
++ if ((var = malloc(sizeof(Variable))) == NULL)
++ return false;
++
++ var->name = NULL;
++ var->value = NULL;
++
++ if ((var->name = strdup(name)) == NULL
++ || (var->value = strdup(value)) == NULL
++ || tsearch(var, &st->variables, compareVariables) ==
NULL)
++ {
++ free(var->name);
++ free(var->value);
++ free(var);
++ return false;
++ }
++ }
++ else
++ {
++ free((*(Variable **)var)->value);
++ if (((*(Variable **)var)->value = strdup(value)) == NULL)
++ return false;
++ }
++
++ return true;
++}
++
++static char *
++assignVariables(CState * st, char *sql)
++{
++ int i, j;
++ char *p, *name, *val;
++ void *tmp;
++
++ i = 0;
++ while ((p = strchr(&sql[i], ':')) != NULL)
++ {
++ i = j = p - sql;
++ do{
++ i++;
++ }while (isalnum((unsigned char) sql[i]) != 0 || sql[i] ==
'_');
++ if (i == j + 1)
++ continue;
++
++ name = malloc(i - j);
++ if (name == NULL)
++ return NULL;
++ memcpy(name, &sql[j + 1], i-(j+1));
++ name[i-(j+1)] = '\0';
++ val = getVariable(st, name);
++ free(name);
++ if (val == NULL)
++ continue;
++
++ if (strlen(val) > i - j)
++ {
++ tmp = realloc(sql, strlen(sql) - (i - j) +
strlen(val) + 1);
++ if (tmp == NULL)
++ {
++ free(sql);
++ return NULL;
++ }
++ sql = tmp;
++ }
++
++ if (strlen(val) != i - j)
++ memmove(&sql[j + strlen(val)], &sql[i],
strlen(&sql[i]) + 1);
++
++ strncpy(&sql[j], val, strlen(val));
++
++ if (strlen(val) < i - j)
++ {
++ tmp = realloc(sql, strlen(sql) + 1);
++ if (tmp == NULL)
++ {
++ free(sql);
++ return NULL;
++ }
++ sql = tmp;
++ }
++
++ i = j + strlen(val);
++ }
++
++ return sql;
++}
++
++/* process a transaction */
++static void
++doMix(CState * st, int debug, int ttype)
++{
++ char sql[256];
++ PGresult *res;
++
++ if (st->listen)
++ { /* are we
receiver? */
++ if (debug)
++ fprintf(stderr, "client receiving\n");
++ if (!PQconsumeInput(st->con))
++ { /* there's
something wrong */
++ fprintf(stderr, "Client aborted in state %d. Probably
the backend died while processing.\n", st->state);
++ PQfinish(st->con);
++ st->con = NULL;
++ return;
++ }
++ if (PQisBusy(st->con))
++ return; /* don't have the
whole result yet */
++
++ switch (st->state)
++ {
++ case 0: /* response to
"begin" */
++ res = PQgetResult(st->con);
++ if (ttype == WITH_TRANSACTION)
++ {
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ }
++ else
++ {
++ if (check(st, res, PGRES_TUPLES_OK))
++ return;
++ }
++ PQclear(res);
++ discard_response(st);
++ break;
++ case 1: /* response to
"update accounts..." */
++ res = PQgetResult(st->con);
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ PQclear(res);
++ discard_response(st);
++ break;
++ case 2: /* response to
"select abalance ..." */
++ res = PQgetResult(st->con);
++ if (check(st, res, PGRES_TUPLES_OK))
++ return;
++ PQclear(res);
++ discard_response(st);
++ break;
++ case 3: /* response to
"update tellers ..." */
++ res = PQgetResult(st->con);
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ PQclear(res);
++ discard_response(st);
++ break;
++ case 4: /* response to
"update branches ..." */
++ res = PQgetResult(st->con);
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ PQclear(res);
++ discard_response(st);
++ break;
++ case 5: /* response to
"insert into history ..." */
++ res = PQgetResult(st->con);
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ PQclear(res);
++ discard_response(st);
++ break;
++ case 6: /* response to "end"
*/
++
++ /*
++ * transaction finished: record the time it
took in the
++ * log
++ */
++ if (use_log)
++ {
++ double diff;
++ struct timeval now;
++
++ gettimeofday(&now, NULL);
++ diff = (int) (now.tv_sec -
st->txn_begin.tv_sec) * 1000000.0 +
++ (int) (now.tv_usec -
st->txn_begin.tv_usec);
++
++ fprintf(LOGFILE, "%d %d %.0f\n",
st->id, st->cnt, diff);
++ }
++
++ res = PQgetResult(st->con);
++ if (ttype == WITH_TRANSACTION)
++ {
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ }
++ else
++ {
++ if (check(st, res, PGRES_TUPLES_OK))
++ return;
++ }
++ PQclear(res);
++ discard_response(st);
++
++ if (is_connect)
++ {
++ PQfinish(st->con);
++ st->con = NULL;
++ }
++ if (++st->cnt >= st->maxAct)
++ {
++ remains--; /*
I've done */
++ if (st->con != NULL)
++ {
++ PQfinish(st->con);
++ st->con = NULL;
++ }
++ return;
++ }
++ break;
++ }
++
++ /* increment state counter */
++ st->state++;
++ if (st->state > 6)
++ {
++ st->state = 0;
++ remains--; /* I've done */
++ }
++ }
++
++ if (st->con == NULL)
++ {
++ if ((st->con = doConnect()) == NULL)
++ {
++ fprintf(stderr, "Client aborted in establishing
connection.\n");
++ remains--; /* I've aborted */
++ PQfinish(st->con);
++ st->con = NULL;
++ return;
++ }
++ }
++
++ switch (st->state)
++ {
++ case 0: /* about to start */
++ if (ttype == WITH_TRANSACTION)
++ {
++ strcpy(sql, "begin");
++ }
++ else
++ {
++ st->aid = getrand(1, naccounts * tps);
++ snprintf(sql, 256, "select abalance from
accounts where aid = %d", st->aid);
++ }
++ st->aid = getrand(1, naccounts * tps);
++ st->bid = getrand(1, nbranches * tps);
++ st->tid = getrand(1, ntellers * tps);
++ st->delta = getrand(1, 1000);
++ if (use_log)
++ gettimeofday(&(st->txn_begin), NULL);
++ break;
++ case 1:
++ snprintf(sql, 256, "update accounts set abalance =
abalance + %d where aid = %d\n", st->delta, st->aid);
++ break;
++ case 2:
++ snprintf(sql, 256, "select abalance from accounts
where aid = %d", st->aid);
++ break;
++ case 3:
++ if (ttype == 0)
++ {
++ snprintf(sql, 256, "update tellers set
tbalance = tbalance + %d where tid = %d\n",
++ st->delta, st->tid);
++ break;
++ }
++ case 4:
++ if (ttype == 0)
++ {
++ snprintf(sql, 256, "update branches set
bbalance = bbalance + %d where bid = %d", st->delta, st->bid);
++ break;
++ }
++ case 5:
++ snprintf(sql, 256, "insert into
history(tid,bid,aid,delta,mtime) values(%d,%d,%d,%d,'now')",
++ st->tid, st->bid, st->aid,
st->delta);
++ break;
++ case 6:
++ if (ttype == WITH_TRANSACTION)
++ {
++ strcpy(sql, "end");
++ }
++ else
++ {
++ st->aid = getrand(1, naccounts * tps);
++ snprintf(sql, 256, "select abalance from
accounts where aid = %d", st->aid);
++ }
++ break;
++ }
++
++ if (debug)
++ fprintf(stderr, "client sending %s\n", sql);
++
++ if (PQsendQuery(st->con, sql) == 0)
++ {
++ if (debug)
++ fprintf(stderr, "PQsendQuery(%s)failed\n", sql);
++ st->ecnt++;
++ }
++ else
++ {
++ st->listen++; /* flags that should be
listened */
++ }
++}
++
++/* process a select only transaction */
++static void
++doOne(CState * st, int debug, int ttype )
++{
++ char sql[256];
++ PGresult *res;
++
++ if (st->listen)
++ { /* are we
receiver? */
++ if (debug)
++ fprintf(stderr, "client receiving\n");
++ if (!PQconsumeInput(st->con))
++ { /* there's
something wrong */
++ fprintf(stderr, "Client aborted in state %d. Probably
the backend died while processing.\n", st->state);
++ remains--; /* I've aborted */
++ PQfinish(st->con);
++ st->con = NULL;
++ return;
++ }
++ if (PQisBusy(st->con))
++ return; /* don't have the
whole result yet */
++
++ switch (st->state)
++ {
++ case 0: /* response to
"select abalance ..." */
++ res = PQgetResult(st->con);
++ if (ttype == SELECT_ONLY)
++ {
++ if (check(st, res, PGRES_TUPLES_OK))
++ return;
++ }
++ else
++ {
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ }
++ PQclear(res);
++ discard_response(st);
++
++ if (is_connect)
++ {
++ PQfinish(st->con);
++ st->con = NULL;
++ }
++
++ if (++st->cnt >= st->maxAct)
++ {
++ remains--; /*
I've done */
++ if (st->con != NULL)
++ {
++ PQfinish(st->con);
++ st->con = NULL;
++ }
++ return;
++ }
++ break;
++ }
++
++ /* increment state counter */
++ st->state++;
++ if (st->state > 0)
++ {
++ st->state = 0;
++ remains--; /* I've done */
++ }
++ }
++
++ if (st->con == NULL)
++ {
++ if ((st->con = doConnect()) == NULL)
++ {
++ fprintf(stderr, "Client aborted in establishing
connection.\n");
++ PQfinish(st->con);
++ st->con = NULL;
++ return;
++ }
++ }
++
++ switch (st->state)
++ {
++ case 0:
++ st->aid = getrand(1, naccounts * tps);
++ st->bid = getrand(1, nbranches * tps);
++ st->tid = getrand(1, ntellers * tps);
++ st->delta = getrand(1, 1000);
++ if ( ttype == SELECT_ONLY)
++ {
++ snprintf(sql, 256, "select abalance from
accounts where aid = %d", st->aid);
++ }
++ if ( ttype == UPDATE_ONLY)
++ {
++ snprintf(sql, 256, "update accounts set
abalance = abalance + %d where aid = %d\n", st->delta, st->aid);
++ }
++ if ( ttype == INSERT_ONLY)
++ {
++ snprintf(sql, 256, "insert into
history(tid,bid,aid,delta,mtime) values(%d,%d,%d,%d,'now')",
++ st->tid, st->bid, st->aid,
st->delta);
++ }
++ break;
++ }
++
++ if (debug)
++ fprintf(stderr, "client sending %s\n", sql);
++
++ if (PQsendQuery(st->con, sql) == 0)
++ {
++ if (debug)
++ fprintf(stderr, "PQsendQuery(%s)failed\n", sql);
++ st->ecnt++;
++ }
++ else
++ {
++ st->listen++; /* flags that should be
listened */
++ }
++}
++
++static void
++doCustom(CState * st, int debug, int ttype )
++{
++ PGresult *res;
++
++ if (st->listen)
++ { /* are we
receiver? */
++ if (commands[st->state]->type == SQL_COMMAND)
++ {
++ if (debug)
++ fprintf(stderr, "client receiving\n");
++ if (!PQconsumeInput(st->con))
++ { /*
there's something wrong */
++ fprintf(stderr, "Client aborted in state %d.
Probably the backend died while processing.\n", st->state);
++ PQfinish(st->con);
++ st->con = NULL;
++ return;
++ }
++ if (PQisBusy(st->con))
++ return; /* don't have
the whole result yet */
++ }
++
++ /*
++ * transaction finished: record the time it took in the
++ * log
++ */
++ if (use_log && commands[st->state + 1] == NULL)
++ {
++ double diff;
++ struct timeval now;
++
++ gettimeofday(&now, NULL);
++ diff = (int) (now.tv_sec - st->txn_begin.tv_sec) *
1000000.0 +
++ (int) (now.tv_usec - st->txn_begin.tv_usec);
++
++ fprintf(LOGFILE, "%d %d %.0f\n", st->id, st->cnt,
diff);
++ }
++
++ if (commands[st->state]->type == SQL_COMMAND)
++ {
++ res = PQgetResult(st->con);
++ if (strncasecmp(commands[st->state]->argv[0],
"select", 6) != 0)
++ {
++ if (check(st, res, PGRES_COMMAND_OK))
++ return;
++ }
++ else
++ {
++ if (check(st, res, PGRES_TUPLES_OK))
++ return;
++ }
++ PQclear(res);
++ discard_response(st);
++ }
++
++ if (commands[st->state + 1] == NULL)
++ {
++ if (is_connect)
++ {
++ PQfinish(st->con);
++ st->con = NULL;
++ }
++ if (++st->cnt >= st->maxAct)
++ {
++ remains--; /* I've done
*/
++ if (st->con != NULL)
++ {
++ PQfinish(st->con);
++ st->con = NULL;
++ }
++ return;
++ }
++ }
++
++ /* increment state counter */
++ st->state++;
++ if (commands[st->state] == NULL)
++ {
++ st->state = 0;
++ remains--; /* I've done */
++ }
++ }
++
++ if (st->con == NULL)
++ {
++ if ((st->con = doConnect()) == NULL)
++ {
++ fprintf(stderr, "Client aborted in establishing
connection.\n");
++ remains--; /* I've aborted */
++ PQfinish(st->con);
++ st->con = NULL;
++ return;
++ }
++ }
++
++ if (use_log && st->state == 0)
++ gettimeofday(&(st->txn_begin), NULL);
++
++ if (commands[st->state]->type == SQL_COMMAND)
++ {
++ char *sql;
++
++ if ((sql = strdup(commands[st->state]->argv[0])) == NULL
++ || (sql = assignVariables(st, sql)) == NULL)
++ {
++ fprintf(stderr, "out of memory\n");
++ st->ecnt++;
++ return;
++ }
++
++ if (debug)
++ fprintf(stderr, "client sending %s\n", sql);
++
++ if (PQsendQuery(st->con, sql) == 0)
++ {
++ if (debug)
++ fprintf(stderr, "PQsendQuery(%s)failed\n",
sql);
++ st->ecnt++;
++ }
++ else
++ {
++ st->listen++; /* flags that should
be listened */
++ }
++
++ free(sql);
++ }
++ else if (commands[st->state]->type == META_COMMAND)
++ {
++ int argc = commands[st->state]->argc, i;
++ char **argv = commands[st->state]->argv;
++
++ if (debug)
++ {
++ fprintf(stderr, "client executing \\%s", argv[0]);
++ for (i = 1; i < argc; i++)
++ fprintf(stderr, " %s", argv[i]);
++ fprintf(stderr, "\n");
++ }
++
++ if (strcasecmp(argv[0], "setrandom") == 0)
++ {
++ char *val;
++
++ if ((val = malloc(strlen(argv[3]) + 1)) == NULL)
++ {
++ fprintf(stderr, "%s: out of memory\n",
argv[0]);
++ st->ecnt++;
++ return;
++ }
++
++ sprintf(val, "%d", getrand(atoi(argv[2]),
atoi(argv[3])));
++
++ if (putVariable(st, argv[1], val) == false)
++ {
++ fprintf(stderr, "%s: out of memory\n",
argv[0]);
++ free(val);
++ st->ecnt++;
++ return;
++ }
++
++ free(val);
++ st->listen++;
++ }
++ }
++}
++
++/* discard connections */
++static void
++disconnect_all(CState * state)
++{
++ if (state->con)
++ PQfinish(state->con);
++}
++
++/* create tables and setup data */
++static void
++init(void)
++{
++ PGconn *con;
++ PGresult *res;
++ static char *DDLs[] = {
++ "drop table branches",
++ "create table branches(bid int not null,bbalance int,filler
char(88))",
++ "drop table tellers",
++ "create table tellers(tid int not null,bid int,tbalance
int,filler char(84))",
++ "drop table accounts",
++ "create table accounts(aid int not null,bid int,abalance
int,filler char(84))",
++ "drop table history",
++ "create table history(tid int,bid int,aid int,delta int,mtime
timestamp,filler char(22))"};
++ static char *DDLAFTERs[] = {
++ "alter table branches add primary key (bid)",
++ "alter table tellers add primary key (tid)",
++ "alter table accounts add primary key (aid)"};
++
++
++ char sql[256];
++
++ int i;
++
++ if ((con = doConnect()) == NULL)
++ exit(1);
++
++ for (i = 0; i < (sizeof(DDLs) / sizeof(char *)); i++)
++ {
++ res = PQexec(con, DDLs[i]);
++ if (strncmp(DDLs[i], "drop", 4) && PQresultStatus(res) !=
PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ }
++
++ res = PQexec(con, "begin");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++
++ for (i = 0; i < nbranches * tps; i++)
++ {
++ snprintf(sql, 256, "insert into branches(bid,bbalance)
values(%d,0)", i + 1);
++ res = PQexec(con, sql);
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ }
++
++ for (i = 0; i < ntellers * tps; i++)
++ {
++ snprintf(sql, 256, "insert into tellers(tid,bid,tbalance)
values (%d,%d,0)"
++ ,i + 1, i / ntellers + 1);
++ res = PQexec(con, sql);
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ }
++
++ res = PQexec(con, "end");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++
++ /*
++ * occupy accounts table with some data
++ */
++ fprintf(stderr, "creating tables...\n");
++ for (i = 0; i < naccounts * tps; i++)
++ {
++ int j = i + 1;
++
++ if (j % 10000 == 1)
++ {
++ res = PQexec(con, "copy accounts from stdin");
++ if (PQresultStatus(res) != PGRES_COPY_IN)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ }
++
++ snprintf(sql, 256, "%d\t%d\t%d\t\n", j, i / naccounts + 1, 0);
++ if (PQputline(con, sql))
++ {
++ fprintf(stderr, "PQputline failed\n");
++ exit(1);
++ }
++
++ if (j % 10000 == 0)
++ {
++ /*
++ * every 10000 tuples, we commit the copy command.
this should
++ * avoid generating too much WAL logs
++ */
++ fprintf(stderr, "%d tuples done.\n", j);
++ if (PQputline(con, "\\.\n"))
++ {
++ fprintf(stderr, "very last PQputline
failed\n");
++ exit(1);
++ }
++
++ if (PQendcopy(con))
++ {
++ fprintf(stderr, "PQendcopy failed\n");
++ exit(1);
++ }
++
++#ifdef NOT_USED
++
++ /*
++ * do a checkpoint to purge the old WAL logs
++ */
++ res = PQexec(con, "checkpoint");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++#endif /* NOT_USED */
++ }
++ }
++ fprintf(stderr, "set primary key...\n");
++ for (i = 0; i < (sizeof(DDLAFTERs) / sizeof(char *)); i++)
++ {
++ res = PQexec(con, DDLAFTERs[i]);
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ }
++
++ /* vacuum */
++ fprintf(stderr, "vacuum...");
++ res = PQexec(con, "vacuum analyze");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ fprintf(stderr, "done.\n");
++
++ PQfinish(con);
++}
++
++static int
++process_file(char *filename)
++{
++ const char delim[] = " \f\n\r\t\v";
++
++ FILE *fd;
++ int lineno, i, j;
++ char buf[BUFSIZ], *p, *tok;
++ void *tmp;
++
++ if (strcmp(filename, "-") == 0)
++ fd = stdin;
++ else if ((fd = fopen(filename, "r")) == NULL)
++ {
++ fprintf(stderr, "%s: %s\n", strerror(errno), filename);
++ return false;
++ }
++
++ fprintf(stderr, "processing file...\n");
++
++ lineno = 1;
++ i = 0;
++ while (fgets(buf, sizeof(buf), fd) != NULL)
++ {
++ if ((p = strchr(buf, '\n')) != NULL)
++ *p = '\0';
++ p = buf;
++ while (isspace((unsigned char)*p))
++ p++;
++ if (*p == '\0' || strncmp(p, "--", 2) == 0)
++ {
++ lineno++;
++ continue;
++ }
++
++ if ((tmp = realloc(commands, sizeof(Command *) * (i + 1))) ==
NULL)
++ {
++ i--;
++ goto error;
++ }
++ commands = tmp;
++
++ if ((commands[i] = malloc(sizeof(Command))) == NULL)
++ goto error;
++
++ commands[i]->argv = NULL;
++ commands[i]->argc = 0;
++
++ if (*p == '\\')
++ {
++ commands[i]->type = META_COMMAND;
++
++ j = 0;
++ tok = strtok(++p, delim);
++ while (tok != NULL)
++ {
++ tmp = realloc(commands[i]->argv, sizeof(char
*) * (j + 1));
++ if (tmp == NULL)
++ goto error;
++ commands[i]->argv = tmp;
++
++ if ((commands[i]->argv[j] = strdup(tok)) ==
NULL)
++ goto error;
++
++ commands[i]->argc++;
++
++ j++;
++ tok = strtok(NULL, delim);
++ }
++
++ if (strcasecmp(commands[i]->argv[0], "setrandom") ==
0)
++ {
++ int min, max;
++
++ if (commands[i]->argc < 4)
++ {
++ fprintf(stderr, "%s: %d: \\%s:
missing argument\n", filename, lineno, commands[i]->argv[0]);
++ goto error;
++ }
++
++ for (j = 4; j < commands[i]->argc; j++)
++ fprintf(stderr, "%s: %d: \\%s: extra
argument \"%s\" ignored\n", filename, lineno, commands[i]->argv[0],
commands[i]->argv[j]);
++
++ if ((min = atoi(commands[i]->argv[2])) < 0)
++ {
++ fprintf(stderr, "%s: %d: \\%s:
invalid minimum number %s\n", filename, lineno, commands[i]->argv[0],
commands[i]->argv[2]);
++ goto error;
++ }
++
++ if ((max = atoi(commands[i]->argv[3])) < min
|| max > RAND_MAX)
++ {
++ fprintf(stderr, "%s: %d: \\%s:
invalid maximum number %s\n", filename, lineno, commands[i]->argv[0],
commands[i]->argv[3]);
++ goto error;
++ }
++ }
++ else
++ {
++ fprintf(stderr, "%s: %d: invalid command
\\%s\n", filename, lineno, commands[i]->argv[0]);
++ goto error;
++ }
++ }
++ else
++ {
++ commands[i]->type = SQL_COMMAND;
++
++ if ((commands[i]->argv = malloc(sizeof(char *))) ==
NULL)
++ goto error;
++
++ if ((commands[i]->argv[0] = strdup(p)) == NULL)
++ goto error;
++
++ commands[i]->argc++;
++ }
++
++ i++;
++ lineno++;
++ }
++ fclose(fd);
++
++ if ((tmp = realloc(commands, sizeof(Command *) * (i + 1))) == NULL)
++ goto error;
++ commands = tmp;
++
++ commands[i] = NULL;
++
++ return true;
++
++error:
++ if (errno == ENOMEM)
++ fprintf(stderr, "%s: %d: out of memory\n", filename, lineno);
++
++ fclose(fd);
++
++ if (commands == NULL)
++ return false;
++
++ while (i >= 0)
++ {
++ if (commands[i] != NULL)
++ {
++ for (j = 0; j < commands[i]->argc; j++)
++ free(commands[i]->argv[j]);
++
++ free(commands[i]->argv);
++ free(commands[i]);
++ }
++
++ i--;
++ }
++ free(commands);
++
++ return false;
++}
++
++/* print out results */
++static void
++printResults(
++ int ttype, int normal_xacts,
++ struct timeval * tv1, struct timeval * tv2,
++ struct timeval * tv3)
++{
++ double t1,
++ t2;
++ char *s;
++
++ t1 = (tv3->tv_sec - tv1->tv_sec) * 1000000.0 + (tv3->tv_usec -
tv1->tv_usec);
++ t1 = t1 / 1000000.0 ;
++
++ t2 = (tv3->tv_sec - tv1->tv_sec) * 1000000.0 + (tv3->tv_usec -
tv1->tv_usec);
++ t2 = normal_xacts * 1000000.0 / t2;
++
++#define SELECT_ONLY (1)
++#define INSERT_ONLY (2)
++#define UPDATE_ONLY (3)
++#define WITH_TRANSACTION (4)
++ switch (ttype)
++ {
++ case 0:
++ s = "TPC-B (sort of)";
++ break;
++ case SELECT_ONLY :
++ s = "SELECT only";
++ break;
++ case INSERT_ONLY :
++ s = "INSERT only";
++ break;
++ case UPDATE_ONLY :
++ s = "UPDATE only";
++ break;
++ case CUSTOM_QUERY :
++ s = "Custom query";
++ break;
++ default:
++ s = "Mix query";
++ break;
++ }
++
++
++ printf("transaction type: %s\n", s);
++ printf("scaling factor: %d\n", tps);
++ printf("number of clients: %d\n", nclients);
++ printf("number of transactions actually processed: %d\n",
normal_xacts );
++ printf("run time (sec) = %f \n", t1);
++ printf("tps = %f (including connections establishing)\n", t2);
++}
++
++static int
++doChild(int clientId, int min, int max, int debug, int ttype)
++{
++ CState state; /* status of clients */
++
++ struct timeval tv1; /* start up time */
++ fd_set input_mask;
++ int nsocks = 0; /* return from
select(2) */
++ int sock = 0;
++
++ gettimeofday(&tv1, NULL);
++ srand((unsigned int) tv1.tv_usec + clientId );
++
++ memset((char *)&state,0,sizeof(CState));
++ /* make connections to the database */
++ state.id = clientId;
++ if ((state.con = doConnect()) == NULL)
++ exit(1);
++
++ state.maxAct = max - min + 1;
++ /* send start up queries in async manner */
++ switch (ttype)
++ {
++ case WITH_TRANSACTION :
++ case TPC_B_LIKE :
++ doMix(&state, debug, ttype);
++ break;
++ case CUSTOM_QUERY :
++ doCustom(&state, debug, ttype);
++ break;
++ default :
++ doOne(&state, debug, ttype);
++ break;
++ }
++
++ remains = max;
++ for (;;)
++ {
++ if (remains < min || !state.con)
++ {
++ break;
++ }
++
++ FD_ZERO(&input_mask);
++
++ if (ttype != CUSTOM_QUERY || commands[state.state]->type !=
META_COMMAND)
++ {
++ if (state.con == NULL)
++ {
++ if ((state.con = doConnect()) == NULL)
++ {
++ exit(1);
++ }
++ }
++ sock = PQsocket(state.con);
++
++ if (sock < 0)
++ {
++ fprintf(stderr, "Client %d: PQsocket
failed\n", clientId);
++ disconnect_all(&state);
++ exit(1);
++ }
++ FD_SET(sock, &input_mask);
++
++ if ((nsocks = select(sock + 1, &input_mask, (fd_set
*) NULL,
++ (fd_set *) NULL,
(struct timeval *) NULL)) < 0)
++ {
++ if (errno == EINTR)
++ continue;
++ /* must be something wrong */
++ disconnect_all(&state);
++ fprintf(stderr, "select failed: %s\n",
strerror(errno));
++ exit(1);
++ }
++ else if (nsocks == 0)
++ { /*
timeout */
++ fprintf(stderr, "select timeout\n");
++ fprintf(stderr, "client %d:state %d cnt %d
ecnt %d listen %d\n",
++ clientId, state.state,
state.cnt, state.ecnt, state.listen);
++ exit(0);
++ }
++ }
++
++ /* ok, backend returns reply */
++ if (state.con && (FD_ISSET(PQsocket(state.con), &input_mask)
++ || (ttype == CUSTOM_QUERY
++ &&
commands[state.state]->type == META_COMMAND)))
++ {
++ switch (ttype)
++ {
++ case WITH_TRANSACTION :
++ case TPC_B_LIKE :
++ doMix(&state, debug, ttype);
++ break;
++ case CUSTOM_QUERY :
++ doCustom(&state, debug, ttype);
++ break;
++ default :
++ doOne(&state, debug, ttype);
++ break;
++ }
++ }
++ }
++ disconnect_all(&state);
++ return 1;
++}
++
++static int
++doClient(int debug, int ttype)
++{
++ pid_t pid;
++ int i;
++ int min,max;
++ int base,mo;
++
++ base = nxacts / nclients;
++ mo = nxacts % nclients;
++ min = max = 0;
++ for ( i = 0 ; i < nclients ; i ++)
++ {
++ min = max + 1;
++ max += base;
++ if (mo > 0)
++ {
++ max += 1;
++ mo --;
++ }
++ pid = fork();
++ if (pid == 0)
++ {
++ doChild(i, min, max, debug, ttype);
++ exit(0);
++ }
++ }
++ while ( wait(NULL) > 0)
++ ;
++ return 1;
++}
++
++int
++main(int argc, char **argv)
++{
++ int c;
++ int is_init_mode = 0; /* initialize
mode? */
++ int is_no_vacuum = 0; /* no vacuum
at all before
++
* testing? */
++ int is_full_vacuum = 0; /* do full
vacuum before testing? */
++ int debug = 0; /* debug flag */
++ int ttype = TPC_B_LIKE; /*
transaction type */
++ char *filename = NULL;
++
++ struct timeval tv1; /* start up time */
++ struct timeval tv2; /* after establishing all
connections to
++ * the
backend */
++ struct timeval tv3; /* end time */
++
++#if !(defined(__CYGWIN__) || defined(__MINGW32__))
++ struct rlimit rlim;
++#endif
++
++ PGconn *con;
++ PGresult *res;
++ char *env;
++
++ if ((env = getenv("PGHOST")) != NULL && *env != '\0')
++ pghost = env;
++ if ((env = getenv("PGPORT")) != NULL && *env != '\0')
++ pgport = env;
++ else if ((env = getenv("PGUSER")) != NULL && *env != '\0')
++ login = env;
++
++ while ((c = getopt(argc, argv, "ih:nvp:dc:t:s:u:P:CNSlTUIf:")) != -1)
++ {
++ switch (c)
++ {
++ case 'i':
++ is_init_mode++;
++ break;
++ case 'h':
++ pghost = optarg;
++ break;
++ case 'n':
++ is_no_vacuum++;
++ break;
++ case 'v':
++ is_full_vacuum++;
++ break;
++ case 'p':
++ pgport = optarg;
++ break;
++ case 'd':
++ debug++;
++ break;
++ case 'S':
++ ttype = SELECT_ONLY;
++ break;
++ case 'I':
++ ttype = INSERT_ONLY;
++ break;
++ case 'U':
++ ttype = UPDATE_ONLY;
++ break;
++ case 'T':
++ ttype = WITH_TRANSACTION;
++ break;
++ case 'c':
++ nclients = atoi(optarg);
++ if (nclients <= 0 || nclients > MAXCLIENTS)
++ {
++ fprintf(stderr, "invalid number of
clients: %d\n", nclients);
++ exit(1);
++ }
++#if !(defined(__CYGWIN__) || defined(__MINGW32__))
++#ifdef RLIMIT_NOFILE /* most platform uses RLIMIT_NOFILE */
++ if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
++ {
++#else /* but BSD doesn't
... */
++ if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
++ {
++#endif /* HAVE_RLIMIT_NOFILE */
++ fprintf(stderr, "getrlimit failed.
reason: %s\n", strerror(errno));
++ exit(1);
++ }
++ if (rlim.rlim_cur <= (nclients + 2))
++ {
++ fprintf(stderr, "You need at least %d
open files resource but you are only allowed to use %ld.\n", nclients + 2,
(long) rlim.rlim_cur);
++ fprintf(stderr, "Use limit/ulimt to
increase the limit before using pgbench.\n");
++ exit(1);
++ }
++#endif /* #if !(defined(__CYGWIN__) || defined(__MINGW32__)) */
++ break;
++ case 'C':
++ is_connect = 1;
++ break;
++ case 's':
++ tps = atoi(optarg);
++ if (tps <= 0)
++ {
++ fprintf(stderr, "invalid scaling
factor: %d\n", tps);
++ exit(1);
++ }
++ break;
++ case 't':
++ nxacts = atoi(optarg);
++ if (nxacts <= 0)
++ {
++ fprintf(stderr, "invalid number of
transactions: %d\n", nxacts);
++ exit(1);
++ }
++ break;
++ case 'u':
++ login = optarg;
++ break;
++ case 'P':
++ pwd = optarg;
++ break;
++ case 'l':
++ use_log = true;
++ break;
++ case 'f':
++ ttype = CUSTOM_QUERY;
++ filename = optarg;
++ break;
++ default:
++ usage();
++ exit(1);
++ break;
++ }
++ }
++
++ if (argc > optind)
++ dbName = argv[optind];
++ else
++ {
++ if ((env = getenv("PGDATABASE")) != NULL && *env != '\0')
++ dbName = env;
++ else if (login != NULL && *login != '\0')
++ dbName = login;
++ else
++ dbName = "";
++ }
++
++ if (is_init_mode)
++ {
++ init();
++ exit(0);
++ }
++
++ if (use_log)
++ {
++ char logpath[64];
++
++ snprintf(logpath, 64, "pgbench_log.%d", getpid());
++ LOGFILE = fopen(logpath, "w");
++
++ if (LOGFILE == NULL)
++ {
++ fprintf(stderr, "Couldn't open logfile \"%s\": %s",
logpath, strerror(errno));
++ exit(1);
++ }
++ }
++
++ if (debug)
++ {
++ printf("pghost: %s pgport: %s nclients: %d nxacts: %d dbName:
%s\n",
++ pghost, pgport, nclients, nxacts, dbName);
++ }
++
++ /* opening connection... */
++ con = doConnect();
++ if (con == NULL)
++ exit(1);
++
++ if (PQstatus(con) == CONNECTION_BAD)
++ {
++ fprintf(stderr, "Connection to database '%s' failed.\n",
dbName);
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++
++ if (ttype == CUSTOM_QUERY)
++ {
++ PQfinish(con);
++ if (process_file(filename) == false)
++ exit(1);
++ }
++ else
++ {
++ /*
++ * get the scaling factor that should be same as count(*) from
++ * branches...
++ */
++ res = PQexec(con, "select count(*) from branches");
++ if (PQresultStatus(res) != PGRES_TUPLES_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ tps = atoi(PQgetvalue(res, 0, 0));
++ if (tps < 0)
++ {
++ fprintf(stderr, "count(*) from branches invalid
(%d)\n", tps);
++ exit(1);
++ }
++ PQclear(res);
++
++ if (!is_no_vacuum)
++ {
++ fprintf(stderr, "starting vacuum...");
++ res = PQexec(con, "vacuum branches");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++
++ res = PQexec(con, "vacuum tellers");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++
++ res = PQexec(con, "delete from history");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ res = PQexec(con, "vacuum history");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s", PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++
++ fprintf(stderr, "end.\n");
++
++ if (is_full_vacuum)
++ {
++ fprintf(stderr, "starting full vacuum...");
++ res = PQexec(con, "vacuum analyze accounts");
++ if (PQresultStatus(res) != PGRES_COMMAND_OK)
++ {
++ fprintf(stderr, "%s",
PQerrorMessage(con));
++ exit(1);
++ }
++ PQclear(res);
++ fprintf(stderr, "end.\n");
++ }
++ }
++ PQfinish(con);
++ }
++
++ /* set random seed */
++ gettimeofday(&tv1, NULL);
++ srand((unsigned int) tv1.tv_usec);
++ /* get start up time */
++ gettimeofday(&tv1, NULL);
++ /* time after connections set up */
++ gettimeofday(&tv2, NULL);
++
++ doClient(debug, ttype);
++
++ /* get end time */
++ gettimeofday(&tv3, NULL);
++ printResults(ttype, nxacts, &tv1, &tv2, &tv3);
++ if (LOGFILE)
++ fclose(LOGFILE);
++ return 1;
++}
+diff -aruN postgresql-8.1.2/src/pgcluster/tool/pgcbench.sh
pgcluster-1.5.0rc7/src/pgcluster/tool/pgcbench.sh
+--- postgresql-8.1.2/src/pgcluster/tool/pgcbench.sh 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/tool/pgcbench.sh 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,30 @@
++#! /bin/bash
++
++set -e
++
++while getopts ih:nvp:dc:t:s:u:P:CNSlTUIf: opt; do
++ case $opt in
++ f)
++ filename=$OPTARG
++ ;;
++ *)
++ opts=(${opts[@]} -$opt $OPTARG)
++ ;;
++ esac
++done
++shift $(($OPTIND - 1))
++dbname=$1
++
++tps=$(psql -At -c "SELECT count(*) FROM branches" $dbname)
++
++vacuumdb -t branches $dbname
++vacuumdb -t tellers $dbname
++psql -c "DELETE FROM history" $dbname
++vacuumdb -t history $dbname
++
++if [ -z $filename ]; then
++ pgcbench ${opts[@]} $@
++else
++ perl -pe "BEGIN { \$tps = $tps } s/\`([^\`]+)\`/eval \$1/eg" $filename \
++ | pgcbench ${opts[@]} -f - $@
++fi
+diff -aruN postgresql-8.1.2/src/pgcluster/tool/tpc-b_like.sql
pgcluster-1.5.0rc7/src/pgcluster/tool/tpc-b_like.sql
+--- postgresql-8.1.2/src/pgcluster/tool/tpc-b_like.sql 1970-01-01
01:00:00.000000000 +0100
++++ pgcluster-1.5.0rc7/src/pgcluster/tool/tpc-b_like.sql 2006-01-10
17:39:44.000000000 +0100
+@@ -0,0 +1,11 @@
++\setrandom aid 1 `100000 * $tps`
++\setrandom bid 1 `1 * $tps`
++\setrandom tid 1 `10 * $tps`
++\setrandom delta 1 1000
++BEGIN
++UPDATE accounts SET abalance = abalance + :delta WHERE aid = :aid
++SELECT abalance FROM accounts WHERE aid = :aid
++UPDATE tellers SET tbalance = tbalance + :delta WHERE tid = :tid
++UPDATE branches SET bbalance = bbalance + :delta WHERE bid = :bid
++INSERT INTO history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid,
:delta, current_timestamp)
++END




Archive powered by MHonArc 2.6.24.

Top of Page