summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael P2011-06-29 23:40:18 +0000
committerMichael P2011-06-29 23:40:18 +0000
commit9cf414760f30f6fb377f47ced0d0315460bf85a7 (patch)
tree8a7e5369c07c1c721d83b18752037f96b773b8f0
parentbc0decce1cb03d4f645f38bb61c7f73b16445fcb (diff)
Support for GTM-Standby
GTM is the unique SPOF of Postgres-XC cluster. This commit adds support for the creation of a Standby node related to GTM to which it is possible to perform a failover in case of a GTM failure. GTM standby supports only serializable transactions and is not able to keep consistent transaction IDs on both GTM and standby when running transactions in parallel. This commits adds 3 options for GTM at startup: - s, to indicate if GTM instance is a standby or not - i, to indicate host/IP of GTM to connect to - q, to indicate port number of GTM to connect to An example of standby startup command: gtm -D $STANDBY_DATA -l $LOG_DATA/standbylog -p $STANDBY_PORT -i $GTM_HOST -q $GTM_PORT -s -n 2 & Two additional keywords have been added in gtm_ctl: - promote, to promote a GTM-Standby to be a GTM - status, to look if a GTM instance is active or standby Ex: gtm_ctl status -S gtm -D $STANDBY_DATA gtm_ctl promote -S gtm -D $STANDBY_DATA Former implementation by Satoshi Nagayasu and Koichi Suzuki. Cleanup and some code refactoring by me.
-rw-r--r--src/backend/utils/mmgr/mcxt.c14
-rw-r--r--src/gtm/client/fe-connect.c9
-rw-r--r--src/gtm/client/fe-misc.c6
-rw-r--r--src/gtm/client/fe-protocol.c208
-rw-r--r--src/gtm/client/gtm_client.c742
-rw-r--r--src/gtm/common/Makefile4
-rw-r--r--src/gtm/common/gtm_list.c426
-rw-r--r--src/gtm/common/gtm_serialize.c974
-rw-r--r--src/gtm/common/gtm_serialize_debug.c94
-rw-r--r--src/gtm/common/gtm_utils.c39
-rw-r--r--src/gtm/common/mcxt.c9
-rw-r--r--src/gtm/common/stringinfo.c35
-rw-r--r--src/gtm/gtm_ctl/gtm_ctl.c192
-rw-r--r--src/gtm/libpq/pqcomm.c9
-rw-r--r--src/gtm/main/Makefile2
-rw-r--r--src/gtm/main/gtm_seq.c357
-rw-r--r--src/gtm/main/gtm_snap.c42
-rw-r--r--src/gtm/main/gtm_standby.c511
-rw-r--r--src/gtm/main/gtm_thread.c11
-rw-r--r--src/gtm/main/gtm_txn.c425
-rw-r--r--src/gtm/main/main.c256
-rw-r--r--src/gtm/path/path.c11
-rw-r--r--src/gtm/proxy/Makefile3
-rw-r--r--src/gtm/proxy/proxy_main.c111
-rw-r--r--src/gtm/proxy/proxy_utils.c51
-rw-r--r--src/gtm/recovery/Makefile2
-rw-r--r--src/gtm/recovery/register.c363
-rw-r--r--src/gtm/recovery/replication.c121
-rw-r--r--src/gtm/recovery/standby_utils.c86
-rw-r--r--src/gtm/test/Makefile53
-rw-r--r--src/gtm/test/clean.sh29
-rw-r--r--src/gtm/test/promote.sh21
-rw-r--r--src/gtm/test/regress.sh18
-rw-r--r--src/gtm/test/start.sh12
-rw-r--r--src/gtm/test/start_a.sh24
-rw-r--r--src/gtm/test/start_s.sh25
-rw-r--r--src/gtm/test/stop.sh31
-rw-r--r--src/gtm/test/test_common.c80
-rw-r--r--src/gtm/test/test_connect.c234
-rw-r--r--src/gtm/test/test_node.c142
-rw-r--r--src/gtm/test/test_node5.c134
-rw-r--r--src/gtm/test/test_repli.c156
-rw-r--r--src/gtm/test/test_repli2.c113
-rw-r--r--src/gtm/test/test_scenario.c86
-rw-r--r--src/gtm/test/test_seq.c424
-rw-r--r--src/gtm/test/test_seq4.c294
-rw-r--r--src/gtm/test/test_seq5.c118
-rw-r--r--src/gtm/test/test_serialize.c318
-rw-r--r--src/gtm/test/test_standby.c138
-rw-r--r--src/gtm/test/test_startup.c103
-rw-r--r--src/gtm/test/test_txn.c218
-rw-r--r--src/gtm/test/test_txn4.c87
-rw-r--r--src/gtm/test/test_txn5.c259
-rw-r--r--src/gtm/test2/Makefile53
-rw-r--r--src/gtm/test2/regress.log415
-rw-r--r--src/gtm/test2/regress2.sh13
-rw-r--r--src/gtm/test2/run.sh9
-rw-r--r--src/gtm/test2/status3
-rw-r--r--src/gtm/test2/status_a.sh12
-rw-r--r--src/gtm/test2/status_s.sh12
-rw-r--r--src/gtm/test2/test.c15
-rw-r--r--src/gtm/test2/test_connect2.c59
-rw-r--r--src/gtm/test2/test_standby.c138
-rw-r--r--src/gtm/test2/test_startup.c103
-rw-r--r--src/gtm/test2/test_txn.c218
-rw-r--r--src/gtm/test2/test_txn2.c238
-rw-r--r--src/gtm/test2/test_txn3.c68
-rw-r--r--src/gtm/test2/test_txn4.c87
-rw-r--r--src/gtm/test2/test_txn5.c259
-rw-r--r--src/include/gen_alloc.h38
-rw-r--r--src/include/gtm/assert.h2
-rw-r--r--src/include/gtm/elog.h2
-rw-r--r--src/include/gtm/gtm.h6
-rw-r--r--src/include/gtm/gtm_c.h3
-rw-r--r--src/include/gtm/gtm_client.h65
-rw-r--r--src/include/gtm/gtm_conn.h5
-rw-r--r--src/include/gtm/gtm_ip.h2
-rw-r--r--src/include/gtm/gtm_list.h244
-rw-r--r--src/include/gtm/gtm_msg.h12
-rw-r--r--src/include/gtm/gtm_proxy.h12
-rw-r--r--src/include/gtm/gtm_seq.h13
-rw-r--r--src/include/gtm/gtm_serialize.h48
-rw-r--r--src/include/gtm/gtm_serialize_debug.h24
-rw-r--r--src/include/gtm/gtm_standby.h45
-rw-r--r--src/include/gtm/gtm_txn.h7
-rw-r--r--src/include/gtm/gtm_utils.h28
-rw-r--r--src/include/gtm/libpq-be.h12
-rw-r--r--src/include/gtm/libpq-int.h5
-rw-r--r--src/include/gtm/palloc.h8
-rw-r--r--src/include/gtm/pqcomm.h8
-rw-r--r--src/include/gtm/proxy_utils.h23
-rw-r--r--src/include/gtm/register.h8
-rw-r--r--src/include/gtm/replication.h25
-rw-r--r--src/include/gtm/standby_utils.h27
-rw-r--r--src/include/gtm/stringinfo.h13
95 files changed, 9948 insertions, 669 deletions
diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c
index 6d31e4e075..7e6346b2be 100644
--- a/src/backend/utils/mmgr/mcxt.c
+++ b/src/backend/utils/mmgr/mcxt.c
@@ -713,3 +713,17 @@ pgport_pfree(void *pointer)
}
#endif
+
+#ifdef PGXC
+#include "gen_alloc.h"
+
+void *current_memcontext(void);
+
+void *current_memcontext()
+{
+ return((void *)CurrentMemoryContext);
+}
+
+Gen_Alloc genAlloc_class = {MemoryContextAlloc, MemoryContextAllocZero, repalloc, pfree, current_memcontext};
+
+#endif
diff --git a/src/gtm/client/fe-connect.c b/src/gtm/client/fe-connect.c
index 7cd3440e4e..bf035decde 100644
--- a/src/gtm/client/fe-connect.c
+++ b/src/gtm/client/fe-connect.c
@@ -82,6 +82,11 @@ PQconnectGTM(const char *conninfo)
if (conn && conn->status != CONNECTION_BAD)
(void) connectGTMComplete(conn);
+ else if (conn != NULL)
+ {
+ freeGTM_Conn(conn);
+ conn = NULL;
+ }
return conn;
}
@@ -607,7 +612,7 @@ keep_going: /* We will come back to here until there is
*/
if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
- (char *) &optval, &optlen) == -1)
+ (char *) &optval, (socklen_t *)&optlen) == -1)
{
appendGTMPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not get socket error status: \n"));
@@ -644,7 +649,7 @@ keep_going: /* We will come back to here until there is
conn->laddr.salen = sizeof(conn->laddr.addr);
if (getsockname(conn->sock,
(struct sockaddr *) & conn->laddr.addr,
- &conn->laddr.salen) < 0)
+ (socklen_t *)&conn->laddr.salen) < 0)
{
appendGTMPQExpBuffer(&conn->errorMessage,
"could not get client address from socket:\n");
diff --git a/src/gtm/client/fe-misc.c b/src/gtm/client/fe-misc.c
index 5c9e25704d..930c8ec41e 100644
--- a/src/gtm/client/fe-misc.c
+++ b/src/gtm/client/fe-misc.c
@@ -224,7 +224,7 @@ gtmpqGetInt(int *result, size_t bytes, GTM_Conn *conn)
*result = (int) ntohl(tmp4);
break;
default:
- fprintf(conn->Pfdebug, "Integer size of (%d) bytes not supported", bytes);
+ fprintf(conn->Pfdebug, "Integer size of (%ld) bytes not supported", bytes);
return EOF;
}
@@ -258,7 +258,7 @@ gtmpqPutInt(int value, size_t bytes, GTM_Conn *conn)
return EOF;
break;
default:
- fprintf(conn->Pfdebug, "Integer size of (%d) bytes not supported", bytes);
+ fprintf(conn->Pfdebug, "Integer size of (%ld) bytes not supported", bytes);
return EOF;
}
@@ -620,12 +620,14 @@ retry3:
* amount of data already read in the current message. We consider
* the message "long" once we have acquired 32k ...
*/
+#ifdef NOT_USED
if (conn->inEnd > 32768 &&
(conn->inBufSize - conn->inEnd) >= 8192)
{
someread = 1;
goto retry3;
}
+#endif
return 1;
}
diff --git a/src/gtm/client/fe-protocol.c b/src/gtm/client/fe-protocol.c
index a873b2a26f..d56496fcfc 100644
--- a/src/gtm/client/fe-protocol.c
+++ b/src/gtm/client/fe-protocol.c
@@ -20,7 +20,10 @@
#include "gtm/libpq-fe.h"
#include "gtm/libpq-int.h"
+#include "gtm/gtm_seq.h"
#include "gtm/gtm_client.h"
+#include "gtm/gtm_serialize.h"
+#include "gtm/register.h"
#include <unistd.h>
#include <netinet/in.h>
@@ -126,7 +129,7 @@ pqParseInput(GTM_Conn *conn)
case 'E': /* error return */
if (gtmpqGetError(conn, result))
return NULL;
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
default:
printfGTMPQExpBuffer(&conn->errorMessage,
@@ -211,7 +214,7 @@ gtmpqGetError(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)result->gr_proxy_data,
result->gr_msglen, conn))
{
- result->gr_status = 1;
+ result->gr_status = GTM_RESULT_UNKNOWN;
return 1;
}
@@ -286,13 +289,18 @@ GTMPQgetResult(GTM_Conn *conn)
return res;
}
+/*
+ * return 0 if parsing command is totally completed.
+ * return 1 if it needs to be read continuously.
+ */
static int
gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
{
int xcnt, xsize;
+ int i;
GlobalTransactionId *xip = NULL;
- result->gr_status = 0;
+ result->gr_status = GTM_RESULT_OK;
if (gtmpqGetInt((int *)&result->gr_type, 4, conn))
return 1;
@@ -332,40 +340,46 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)result->gr_proxy_data,
result->gr_msglen, conn))
{
- result->gr_status = 1;
+ result->gr_status = GTM_RESULT_UNKNOWN;
return 1;
}
return result->gr_status;
}
- result->gr_status = 0;
+ result->gr_status = GTM_RESULT_OK;
switch (result->gr_type)
{
+ case NODE_BEGIN_REPLICATION_INIT_RESULT:
+ break;
+
+ case NODE_END_REPLICATION_INIT_RESULT:
+ break;
+
case TXN_BEGIN_RESULT:
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txnhandle,
sizeof (GTM_TransactionHandle), conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
case TXN_BEGIN_GETGXID_RESULT:
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid_tp.gxid,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid_tp.timestamp,
sizeof (GTM_Timestamp), conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
case TXN_BEGIN_GETGXID_AUTOVACUUM_RESULT:
case TXN_PREPARE_RESULT:
case TXN_START_PREPARED_RESULT:
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid,
sizeof (GlobalTransactionId), conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
case TXN_COMMIT_RESULT:
@@ -373,38 +387,47 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
case TXN_ROLLBACK_RESULT:
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_gxid,
sizeof (GlobalTransactionId), conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
case TXN_GET_GXID_RESULT:
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn.txnhandle,
sizeof (GTM_TransactionHandle), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn.gxid,
sizeof (GlobalTransactionId), conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+
+ case TXN_GET_NEXT_GXID_RESULT:
+ if (gtmpqGetInt((int *)&result->gr_resdata.grd_next_gxid,
+ sizeof (int32), conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
break;
case TXN_BEGIN_GETGXID_MULTI_RESULT:
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_multi.txn_count,
sizeof (int), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_multi.start_gxid,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_multi.timestamp,
sizeof (GTM_Timestamp), conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
case TXN_COMMIT_MULTI_RESULT:
@@ -412,13 +435,13 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_rc_multi.txn_count,
sizeof (int), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_rc_multi.status,
sizeof (int) * result->gr_resdata.grd_txn_rc_multi.txn_count, conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
break;
@@ -427,7 +450,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_snap_multi.txnhandle,
sizeof (GTM_TransactionHandle), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
/* Fall through */
@@ -435,7 +458,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_snap_multi.gxid,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
/* Fall through */
@@ -443,42 +466,42 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_snap_multi.txn_count,
sizeof (int), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_snap_multi.status,
sizeof (int) * result->gr_resdata.grd_txn_snap_multi.txn_count, conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_xmin,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_xmax,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_snapshot.sn_recent_global_xmin,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
- if (gtmpqGetInt(&result->gr_snapshot.sn_xcnt,
+ if (gtmpqGetInt((int *)&result->gr_snapshot.sn_xcnt,
sizeof (int32), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
@@ -495,7 +518,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)xip, sizeof (GlobalTransactionId) * xcnt, conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
@@ -508,7 +531,7 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
case SEQUENCE_ALTER_RESULT:
case SEQUENCE_SET_VAL_RESULT:
if (gtmpqReadSeqKey(&result->gr_resdata.grd_seqkey, conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
case SEQUENCE_GET_CURRENT_RESULT:
@@ -516,12 +539,50 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
case SEQUENCE_GET_LAST_RESULT:
if (gtmpqReadSeqKey(&result->gr_resdata.grd_seq.seqkey, conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_seq.seqval,
sizeof (GTM_Sequence), conn))
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+
+ case SEQUENCE_LIST_RESULT:
+ if (gtmpqGetInt(&result->gr_resdata.grd_seq_list.seq_count,
+ sizeof (int32), conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
+ result->gr_resdata.grd_seq_list.seq =
+ (GTM_SeqInfo **)malloc(sizeof(GTM_SeqInfo *) *
+ result->gr_resdata.grd_seq_list.seq_count);
+
+ for (i = 0 ; i < result->gr_resdata.grd_seq_list.seq_count; i++)
+ {
+ int buflen;
+ char *buf;
+
+ /* a length of the next serialized sequence */
+ if (gtmpqGetInt(&buflen, sizeof (int32), conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
+ /* a data body of the serialized sequence */
+ buf = (char *)malloc(buflen);
+ if (gtmpqGetnchar(buf, buflen, conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
+ result->gr_resdata.grd_seq_list.seq[i] = gtm_deserialize_sequence(buf, buflen);
+
+ free(buf);
+ }
break;
case TXN_GET_STATUS_RESULT:
@@ -534,19 +595,19 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_gid_data.gxid,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_txn_get_gid_data.prepared_gxid,
sizeof (GlobalTransactionId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetInt(&result->gr_resdata.grd_txn_get_gid_data.datanodecnt,
sizeof (int32), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (result->gr_resdata.grd_txn_get_gid_data.datanodecnt != 0)
@@ -554,20 +615,20 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if ((result->gr_resdata.grd_txn_get_gid_data.datanodes = (PGXC_NodeId *)
malloc(sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.datanodecnt)) == NULL)
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_get_gid_data.datanodes,
sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.datanodecnt, conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
}
if (gtmpqGetInt(&result->gr_resdata.grd_txn_get_gid_data.coordcnt,
sizeof (int32), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (result->gr_resdata.grd_txn_get_gid_data.coordcnt != 0)
@@ -575,38 +636,101 @@ gtmpqParseSuccess(GTM_Conn *conn, GTM_Result *result)
if ((result->gr_resdata.grd_txn_get_gid_data.coordinators = (PGXC_NodeId *)
malloc(sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.coordcnt)) == NULL)
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)result->gr_resdata.grd_txn_get_gid_data.coordinators,
sizeof(PGXC_NodeId) * result->gr_resdata.grd_txn_get_gid_data.coordcnt, conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
}
break;
+ case TXN_GXID_LIST_RESULT:
+ result->gr_resdata.grd_txn_gid_list.len = 0;
+ result->gr_resdata.grd_txn_gid_list.ptr = NULL;
+
+ if (gtmpqGetInt((int *)&result->gr_resdata.grd_txn_gid_list.len,
+ sizeof(int32), conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
+ result->gr_resdata.grd_txn_gid_list.ptr =
+ (char *)malloc(result->gr_resdata.grd_txn_gid_list.len);
+
+ if (result->gr_resdata.grd_txn_gid_list.ptr==NULL)
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
+ if (gtmpqGetnchar(result->gr_resdata.grd_txn_gid_list.ptr,
+ result->gr_resdata.grd_txn_gid_list.len,
+ conn)) /* serialized GTM_Transactions */
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+ break;
+
case NODE_UNREGISTER_RESULT:
case NODE_REGISTER_RESULT:
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_node.type,
sizeof (GTM_PGXCNodeType), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
if (gtmpqGetnchar((char *)&result->gr_resdata.grd_node.nodenum,
sizeof (GTM_PGXCNodeId), conn))
{
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
}
break;
+ case NODE_LIST_RESULT:
+ {
+ int i;
+
+ if (gtmpqGetInt(&result->gr_resdata.grd_node_list.num_node, sizeof(int32), conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
+ for (i = 0 ; i < result->gr_resdata.grd_node_list.num_node; i++)
+ {
+ int size;
+ char buf[1024];
+ GTM_PGXCNodeInfo *data = (GTM_PGXCNodeInfo *)malloc(sizeof(GTM_PGXCNodeInfo));
+
+ if (gtmpqGetInt(&size, sizeof(int32), conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+
+ if (gtmpqGetnchar((char *)&buf, size, conn))
+ {
+ result->gr_status = GTM_RESULT_ERROR;
+ break;
+ }
+ gtm_deserialize_pgxcnodeinfo(data, buf, size);
+
+ result->gr_resdata.grd_node_list.nodeinfo[i] = data;
+ }
+
+ break;
+ }
default:
printfGTMPQExpBuffer(&conn->errorMessage,
"unexpected result type from server; result typr was \"%d\"\n",
result->gr_type);
- result->gr_status = -1;
+ result->gr_status = GTM_RESULT_ERROR;
break;
}
@@ -619,7 +743,7 @@ gtmpqReadSeqKey(GTM_SequenceKey seqkey, GTM_Conn *conn)
/*
* Read keylength
*/
- if (gtmpqGetInt(&seqkey->gsk_keylen, 4, conn))
+ if (gtmpqGetInt((int *)&seqkey->gsk_keylen, 4, conn))
return EINVAL;
/*
diff --git a/src/gtm/client/gtm_client.c b/src/gtm/client/gtm_client.c
index f5cafd9af2..454ebe240a 100644
--- a/src/gtm/client/gtm_client.c
+++ b/src/gtm/client/gtm_client.c
@@ -18,17 +18,42 @@
#include <time.h>
+#include "gtm/elog.h"
#include "gtm/gtm_c.h"
+#include "gtm/gtm_ip.h"
#include "gtm/libpq-fe.h"
#include "gtm/libpq-int.h"
#include "gtm/gtm_client.h"
#include "gtm/gtm_msg.h"
+#include "gtm/gtm_serialize.h"
+#include "gtm/register.h"
#include "gtm/assert.h"
void GTM_FreeResult(GTM_Result *result, GTM_PGXCNodeType remote_type);
+static GTM_Result *makeEmptyResultIfIsNull(GTM_Result *oldres);
+
+/*
+ * Make an empty result if old one is null.
+ */
+static GTM_Result *
+makeEmptyResultIfIsNull(GTM_Result *oldres)
+{
+ GTM_Result *res = NULL;
+
+ if (oldres == NULL)
+ {
+ res = (GTM_Result *) malloc(sizeof(GTM_Result));
+ memset(res, 0, sizeof(GTM_Result));
+ }
+ else
+ return oldres;
+
+ return res;
+}
+
/*
* Connection Management API
*/
@@ -45,6 +70,311 @@ disconnect_gtm(GTM_Conn *conn)
}
/*
+ * begin_replication_initial_sync() acquires several locks to prepare
+ * for copying internal transaction, xid and sequence information
+ * to the standby node at its startup.
+ *
+ * returns 1 on success, 0 on failure.
+ */
+int
+begin_replication_initial_sync(GTM_Conn *conn)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn) ||
+ gtmpqPutInt(MSG_NODE_BEGIN_REPLICATION_INIT, sizeof (GTM_MessageType), conn))
+ goto send_failed;
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ Assert(res->gr_type == NODE_BEGIN_REPLICATION_INIT_RESULT);
+ else
+ return 0;
+
+ return 1;
+
+receive_failed:
+send_failed:
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return 0;
+}
+
+/*
+ * end_replication_initial_sync() releases several locks
+ * after copying internal transaction, xid and sequence information
+ * to the standby node at its startup.
+ *
+ * returns 1 on success, 0 on failure.
+ */
+int
+end_replication_initial_sync(GTM_Conn *conn)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn) ||
+ gtmpqPutInt(MSG_NODE_END_REPLICATION_INIT, sizeof (GTM_MessageType), conn))
+ goto send_failed;
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ Assert(res->gr_type == NODE_END_REPLICATION_INIT_RESULT);
+
+ return 1;
+
+receive_failed:
+send_failed:
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return 0;
+}
+
+/*
+ * get_node_list()
+ *
+ * returns a number of nodes on success, -1 on failure.
+ */
+size_t
+get_node_list(GTM_Conn *conn, GTM_PGXCNodeInfo *data, size_t maxlen)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+ size_t num_node;
+ int i;
+
+ for (i = 0; i < maxlen; i++)
+ data[i].nodenum = i;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn) ||
+ gtmpqPutInt(MSG_NODE_LIST, sizeof (GTM_MessageType), conn))
+ goto send_failed;
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ /*
+ * Do something here.
+ */
+ num_node = res->gr_resdata.grd_node_list.num_node;
+
+ fprintf(stderr, "get_node_list: num_node=%ld\n", num_node);
+
+ for (i = 0; i < num_node; i++)
+ {
+ memcpy(&data[i], res->gr_resdata.grd_node_list.nodeinfo[i], sizeof(GTM_PGXCNodeInfo));
+
+ fprintf(stderr, "get_node_list: nodetype=%d, nodenum=%d, datafolder=%s\n",
+ data[i].type, data[i].nodenum, data[i].datafolder);
+ }
+
+ if (res->gr_status == GTM_RESULT_OK)
+ Assert(res->gr_type == NODE_LIST_RESULT);
+
+ return num_node;
+
+receive_failed:
+send_failed:
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
+
+/*
+ * get_next_gxid()
+ *
+ * returns the next gxid on success, InvalidGlobalTransactionId on failure.
+ */
+GlobalTransactionId
+get_next_gxid(GTM_Conn *conn)
+{
+ GTM_Result *res = NULL;
+ GlobalTransactionId next_gxid;
+ time_t finish_time;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn) ||
+ gtmpqPutInt(MSG_TXN_GET_NEXT_GXID, sizeof (GTM_MessageType), conn))
+ goto send_failed;
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ fprintf(stderr, "GTMPQgetResult() done.\n");
+ fflush(stderr);
+
+ next_gxid = res->gr_resdata.grd_next_gxid;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ Assert(res->gr_type == TXN_GET_NEXT_GXID_RESULT);
+
+ /* FIXME: should be a number of gxids */
+ return next_gxid;
+
+receive_failed:
+send_failed:
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return InvalidGlobalTransactionId;
+}
+
+/*
+ * get_txn_gxid_list()
+ *
+ * returns a number of gxid on success, -1 on failure.
+ */
+uint32
+get_txn_gxid_list(GTM_Conn *conn, GTM_Transactions *txn)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+ int txn_count;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn) ||
+ gtmpqPutInt(MSG_TXN_GXID_LIST, sizeof (GTM_MessageType), conn))
+ goto send_failed;
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ Assert(res->gr_type == TXN_GXID_LIST_RESULT);
+
+ txn_count = gtm_deserialize_transactions(txn,
+ res->gr_resdata.grd_txn_gid_list.ptr,
+ res->gr_resdata.grd_txn_gid_list.len);
+
+ return txn_count;
+
+receive_failed:
+send_failed:
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
+
+/*
+ * get_sequence_list()
+ *
+ * returns a number of sequences on success, -1 on failure.
+ */
+size_t
+get_sequence_list(GTM_Conn *conn, GTM_SeqInfo **seq_list, size_t seq_max)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+ int i;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn) ||
+ gtmpqPutInt(MSG_SEQUENCE_LIST, sizeof (GTM_MessageType), conn))
+ goto send_failed;
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ Assert(res->gr_type == SEQUENCE_LIST_RESULT);
+
+ for (i = 0; i < res->gr_resdata.grd_seq_list.seq_count; i++)
+ {
+ seq_list[i] = res->gr_resdata.grd_seq_list.seq[i];
+
+ if ( i >= seq_max )
+ break;
+ }
+
+ return i;
+
+receive_failed:
+send_failed:
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
+
+/*
* Transaction Management API
*/
GlobalTransactionId
@@ -77,7 +407,7 @@ begin_transaction(GTM_Conn *conn, GTM_IsolationLevel isolevel, GTM_Timestamp *ti
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
if (timestamp)
*timestamp = res->gr_resdata.grd_gxid_tp.timestamp;
@@ -89,6 +419,8 @@ begin_transaction(GTM_Conn *conn, GTM_IsolationLevel isolevel, GTM_Timestamp *ti
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return InvalidGlobalTransactionId;
}
@@ -126,13 +458,15 @@ begin_transaction_autovacuum(GTM_Conn *conn, GTM_IsolationLevel isolevel)
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
return res->gr_resdata.grd_gxid;
else
return InvalidGlobalTransactionId;
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return InvalidGlobalTransactionId;
}
@@ -165,7 +499,7 @@ commit_transaction(GTM_Conn *conn, GlobalTransactionId gxid)
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_type == TXN_COMMIT_RESULT);
Assert(res->gr_resdata.grd_gxid == gxid);
@@ -175,6 +509,8 @@ commit_transaction(GTM_Conn *conn, GlobalTransactionId gxid)
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -209,7 +545,7 @@ commit_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, GlobalTran
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_type == TXN_COMMIT_PREPARED_RESULT);
Assert(res->gr_resdata.grd_gxid == gxid);
@@ -219,6 +555,8 @@ commit_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, GlobalTran
send_failed:
receive_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -251,7 +589,7 @@ abort_transaction(GTM_Conn *conn, GlobalTransactionId gxid)
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_type == TXN_ROLLBACK_RESULT);
Assert(res->gr_resdata.grd_gxid == gxid);
@@ -261,6 +599,8 @@ abort_transaction(GTM_Conn *conn, GlobalTransactionId gxid)
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -309,7 +649,7 @@ start_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, char *gid,
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_type == TXN_START_PREPARED_RESULT);
Assert(res->gr_resdata.grd_gxid == gxid);
@@ -319,6 +659,8 @@ start_prepared_transaction(GTM_Conn *conn, GlobalTransactionId gxid, char *gid,
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -352,7 +694,7 @@ prepare_transaction(GTM_Conn *conn, GlobalTransactionId gxid)
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_type == TXN_PREPARE_RESULT);
Assert(res->gr_resdata.grd_gxid == gxid);
@@ -362,6 +704,8 @@ prepare_transaction(GTM_Conn *conn, GlobalTransactionId gxid)
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -406,7 +750,7 @@ get_gid_data(GTM_Conn *conn,
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
*gxid = res->gr_resdata.grd_txn_get_gid_data.gxid;
*prepared_gxid = res->gr_resdata.grd_txn_get_gid_data.prepared_gxid;
@@ -422,6 +766,8 @@ get_gid_data(GTM_Conn *conn,
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -458,7 +804,7 @@ get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid, bool canbe_grouped)
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_type == SNAPSHOT_GET_RESULT);
Assert(res->gr_resdata.grd_txn_snap_multi.gxid == gxid);
@@ -470,6 +816,8 @@ get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid, bool canbe_grouped)
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return NULL;
}
@@ -516,6 +864,8 @@ open_sequence(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence increment,
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -561,7 +911,9 @@ alter_sequence(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence increment,
receive_failed:
send_failed:
- return -1;
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
}
int
@@ -598,6 +950,8 @@ close_sequence(GTM_Conn *conn, GTM_SequenceKey key)
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -634,8 +988,10 @@ rename_sequence(GTM_Conn *conn, GTM_SequenceKey key, GTM_SequenceKey newkey)
return res->gr_status;
- receive_failed:
- send_failed:
+receive_failed:
+send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -668,13 +1024,15 @@ get_current(GTM_Conn *conn, GTM_SequenceKey key)
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
return res->gr_resdata.grd_seq.seqval;
else
return InvalidSequenceValue;
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -713,6 +1071,8 @@ set_val(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence nextval, bool iscalled
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -745,13 +1105,15 @@ get_next(GTM_Conn *conn, GTM_SequenceKey key)
if ((res = GTMPQgetResult(conn)) == NULL)
goto receive_failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
return res->gr_resdata.grd_seq.seqval;
else
return InvalidSequenceValue;
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -788,30 +1150,109 @@ reset_sequence(GTM_Conn *conn, GTM_SequenceKey key)
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
/*
+ * rc would be 0 on success, non-zero on gtm_getnameinfo_all() failure.
+ */
+char *
+node_get_local_addr(GTM_Conn *conn, char *buf, size_t buflen, int *rc)
+{
+ char local_host[NI_MAXHOST];
+ char local_port[NI_MAXSERV];
+
+ *rc = 0;
+
+ memset(local_host, 0, sizeof(local_host));
+ memset(local_port, 0, sizeof(local_port));
+ memset(buf, 0, buflen);
+
+ if (conn->remote_type != PGXC_NODE_GTM_PROXY)
+ {
+ if (gtm_getnameinfo_all(&conn->laddr.addr, conn->laddr.salen,
+ local_host, sizeof(local_host),
+ local_port, sizeof(local_port),
+ NI_NUMERICSERV))
+ {
+ *rc = gtm_getnameinfo_all(&conn->laddr.addr, conn->laddr.salen,
+ local_host, sizeof(local_host),
+ local_port, sizeof(local_port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ }
+ }
+
+ if (local_host[0] != '\0')
+ strncpy(buf, local_host, buflen);
+
+ return buf;
+}
+
+/*
* Register a Node on GTM
* Seen from a Node viewpoint, we do not know if we are directly connected to GTM
* or go through a proxy, so register 0 as proxy number.
* This number is modified at proxy level automatically.
+ *
+ * node_register() returns 0 on success, -1 on failure.
*/
-int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodePort port, GTM_PGXCNodeId nodenum,
+int node_register(GTM_Conn *conn,
+ GTM_PGXCNodeType type,
+ GTM_PGXCNodePort port,
+ GTM_PGXCNodeId nodenum,
char *datafolder)
{
+ char host[1024];
+ int rc;
+
+ node_get_local_addr(conn, host, sizeof(host), &rc);
+ if (rc != 0)
+ return -1;
+
+ return node_register_internal(conn, type, host, port, nodenum, datafolder, NODE_CONNECTED);
+}
+
+int node_register_internal(GTM_Conn *conn,
+ GTM_PGXCNodeType type,
+ const char *host,
+ GTM_PGXCNodePort port,
+ GTM_PGXCNodeId nodenum,
+ char *datafolder,
+ GTM_PGXCNodeStatus status)
+{
GTM_Result *res = NULL;
time_t finish_time;
GTM_PGXCNodeId proxynum = 0;
+ /*
+ * We should be very careful about the format of the message.
+ * Host name and its length is needed only when registering
+ * GTM Proxy.
+ * In other case, they must not be included in the message.
+ */
if (gtmpqPutMsgStart('C', true, conn) ||
+ /* Message Type */
gtmpqPutInt(MSG_NODE_REGISTER, sizeof (GTM_MessageType), conn) ||
+ /* Node Type to Register */
gtmpqPutnchar((char *)&type, sizeof(GTM_PGXCNodeType), conn) ||
+ /* Node Number to Register */
gtmpqPutnchar((char *)&nodenum, sizeof(GTM_PGXCNodeId), conn) ||
+ /* Host name length */
+ gtmpqPutInt(strlen(host), sizeof (GTM_StrLen), conn) ||
+ /* Host name (var-len) */
+ gtmpqPutnchar(host, strlen(host), conn) ||
+ /* Port number */
gtmpqPutnchar((char *)&port, sizeof(GTM_PGXCNodePort), conn) ||
+ /* Proxy ID (zero if connected to GTM directly) */
gtmpqPutnchar((char *)&proxynum, sizeof(GTM_PGXCNodeId), conn) ||
+ /* Data Folder length */
gtmpqPutInt(strlen(datafolder), sizeof (GTM_StrLen), conn) ||
- gtmpqPutnchar(datafolder, strlen(datafolder), conn))
+ /* Data Folder (var-len) */
+ gtmpqPutnchar(datafolder, strlen(datafolder), conn) ||
+ /* Node Status */
+ gtmpqPutInt(status, sizeof(GTM_PGXCNodeStatus), conn))
goto send_failed;
/* Finish the message. */
@@ -831,7 +1272,7 @@ int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodePort port,
goto receive_failed;
/* Check on node type and node number */
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_resdata.grd_node.type == type);
Assert(res->gr_resdata.grd_node.nodenum == nodenum);
@@ -841,6 +1282,8 @@ int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodePort port,
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -872,7 +1315,7 @@ int node_unregister(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenu
goto receive_failed;
/* Check on node type and node number */
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_resdata.grd_node.type == type);
Assert(res->gr_resdata.grd_node.nodenum == nodenum);
@@ -882,6 +1325,8 @@ int node_unregister(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenu
receive_failed:
send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
return -1;
}
@@ -893,3 +1338,264 @@ GTM_FreeResult(GTM_Result *result, GTM_PGXCNodeType remote_type)
gtmpqFreeResultData(result, remote_type);
free(result);
}
+
+int
+backend_disconnect(GTM_Conn *conn, bool is_postmaster, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum)
+{
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn) ||
+ gtmpqPutInt(MSG_BACKEND_DISCONNECT, sizeof (GTM_MessageType), conn) ||
+ gtmpqPutc(is_postmaster, conn))
+ goto send_failed;
+
+ /*
+ * Then send node type and node number if backend is a postmaster to
+ * disconnect the correct node.
+ */
+ if (is_postmaster)
+ {
+ if (gtmpqPutnchar((char *)&type,
+ sizeof(GTM_PGXCNodeType), conn) ||
+ gtmpqPutnchar((char *)&nodenum,
+ sizeof(GTM_PGXCNodeId), conn))
+ goto send_failed;
+ }
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ return 1;
+
+send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
+
+int
+begin_transaction_multi(GTM_Conn *conn, int txn_count, GTM_IsolationLevel *txn_isolation_level,
+ bool *txn_read_only, GTMProxy_ConnID *txn_connid,
+ int *txn_count_out, GlobalTransactionId *gxid_out, GTM_Timestamp *ts_out)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+ int i;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */
+ goto send_failed;
+
+ if (gtmpqPutInt(MSG_TXN_BEGIN_GETGXID_MULTI, sizeof (GTM_MessageType), conn) ||
+ gtmpqPutInt(txn_count, sizeof(int), conn))
+ goto send_failed;
+
+ for (i = 0; i < txn_count; i++)
+ {
+ gtmpqPutInt(txn_isolation_level[i], sizeof(int), conn);
+ gtmpqPutc(txn_read_only[i], conn);
+ gtmpqPutInt(txn_connid[i], sizeof(int), conn);
+ }
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ {
+ memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int));
+ memcpy(gxid_out, &res->gr_resdata.grd_txn_get_multi.start_gxid, sizeof(GlobalTransactionId));
+ memcpy(ts_out, &res->gr_resdata.grd_txn_get_multi.timestamp, sizeof(GTM_Timestamp));
+ }
+
+ return res->gr_status;
+
+receive_failed:
+send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
+
+int
+commit_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid,
+ int *txn_count_out, int *status_out)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+ int i;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */
+ goto send_failed;
+
+ if (gtmpqPutInt(MSG_TXN_COMMIT_MULTI, sizeof (GTM_MessageType), conn) ||
+ gtmpqPutInt(txn_count, sizeof(int), conn))
+ goto send_failed;
+
+ for (i = 0; i < txn_count; i++)
+ {
+ if (gtmpqPutc(true, conn) ||
+ gtmpqPutnchar((char *)&gxid[i],
+ sizeof (GlobalTransactionId), conn))
+ goto send_failed;
+ }
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ {
+ memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int));
+ memcpy(status_out, &res->gr_resdata.grd_txn_rc_multi.status, sizeof(int) * (*txn_count_out));
+ }
+
+ return res->gr_status;
+
+receive_failed:
+send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
+
+int
+abort_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid,
+ int *txn_count_out, int *status_out)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+ int i;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */
+ goto send_failed;
+
+ if (gtmpqPutInt(MSG_TXN_ROLLBACK_MULTI, sizeof (GTM_MessageType), conn) ||
+ gtmpqPutInt(txn_count, sizeof(int), conn))
+ goto send_failed;
+
+ for (i = 0; i < txn_count; i++)
+ {
+ if (gtmpqPutc(true, conn) ||
+ gtmpqPutnchar((char *)&gxid[i],
+ sizeof (GlobalTransactionId), conn))
+ goto send_failed;
+ }
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ {
+ memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int));
+ memcpy(status_out, &res->gr_resdata.grd_txn_rc_multi.status, sizeof(int) * (*txn_count_out));
+ }
+
+ return res->gr_status;
+
+receive_failed:
+send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
+
+int
+snapshot_get_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid,
+ int *txn_count_out, int *status_out,
+ GlobalTransactionId *xmin_out, GlobalTransactionId *xmax_out,
+ GlobalTransactionId *recent_global_xmin_out, int32 *xcnt_out)
+{
+ GTM_Result *res = NULL;
+ time_t finish_time;
+ int i;
+
+ /* Start the message. */
+ if (gtmpqPutMsgStart('C', true, conn)) /* FIXME: no proxy header */
+ goto send_failed;
+
+ if (gtmpqPutInt(MSG_SNAPSHOT_GET_MULTI, sizeof (GTM_MessageType), conn) ||
+ gtmpqPutInt(txn_count, sizeof(int), conn))
+ goto send_failed;
+
+ for (i = 0; i < txn_count; i++)
+ {
+ if (gtmpqPutc(true, conn) ||
+ gtmpqPutnchar((char *)&gxid[i],
+ sizeof (GlobalTransactionId), conn))
+ goto send_failed;
+ }
+
+ /* Finish the message. */
+ if (gtmpqPutMsgEnd(conn))
+ goto send_failed;
+
+ /* Flush to ensure backend gets it. */
+ if (gtmpqFlush(conn))
+ goto send_failed;
+
+ finish_time = time(NULL) + CLIENT_GTM_TIMEOUT;
+ if (gtmpqWaitTimed(true, false, conn, finish_time) ||
+ gtmpqReadData(conn) < 0)
+ goto receive_failed;
+
+ if ((res = GTMPQgetResult(conn)) == NULL)
+ goto receive_failed;
+
+ if (res->gr_status == GTM_RESULT_OK)
+ {
+ memcpy(txn_count_out, &res->gr_resdata.grd_txn_get_multi.txn_count, sizeof(int));
+ memcpy(status_out, &res->gr_resdata.grd_txn_rc_multi.status, sizeof(int) * (*txn_count_out));
+ memcpy(xmin_out, &res->gr_snapshot.sn_xmin, sizeof(GlobalTransactionId));
+ memcpy(xmax_out, &res->gr_snapshot.sn_xmax, sizeof(GlobalTransactionId));
+ memcpy(recent_global_xmin_out, &res->gr_snapshot.sn_recent_global_xmin, sizeof(GlobalTransactionId));
+ memcpy(xcnt_out, &res->gr_snapshot.sn_xcnt, sizeof(int32));
+ }
+
+ return res->gr_status;
+
+receive_failed:
+send_failed:
+ conn->result = makeEmptyResultIfIsNull(conn->result);
+ conn->result->gr_status = GTM_RESULT_COMM_ERROR;
+ return -1;
+}
diff --git a/src/gtm/common/Makefile b/src/gtm/common/Makefile
index 5ee1eb8bbf..1b94a9e933 100644
--- a/src/gtm/common/Makefile
+++ b/src/gtm/common/Makefile
@@ -9,8 +9,10 @@ NAME=gtm
SO_MAJOR_VERSION= 1
SO_MINOR_VERSION= 0
+LDFLAGS=-L$(top_build_dir)/common -L$(top_build_dir)/libpq
+LIBS=-lpthread
-OBJS=aset.o mcxt.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o
+OBJS=aset.o mcxt.o gtm_utils.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o gtm_serialize.o gtm_serialize_debug.o
all:all-lib
diff --git a/src/gtm/common/gtm_list.c b/src/gtm/common/gtm_list.c
index 234483cf55..46245f1951 100644
--- a/src/gtm/common/gtm_list.c
+++ b/src/gtm/common/gtm_list.c
@@ -20,17 +20,17 @@
#include "gtm/memutils.h"
#include "gtm/assert.h"
-#define equal(a, b) ((a) == (b))
+#define gtm_equal(a, b) ((a) == (b))
#ifdef USE_ASSERT_CHECKING
/*
- * Check that the specified List is valid (so far as we can tell).
+ * Check that the specified gtm_List is valid (so far as we can tell).
*/
static void
-check_list_invariants(List *list)
+check_list_invariants(gtm_List *list)
{
- if (list == NIL)
+ if (list == gtm_NIL)
return;
Assert(list->length > 0);
@@ -48,21 +48,21 @@ check_list_invariants(List *list)
#endif /* USE_ASSERT_CHECKING */
/*
- * Return a freshly allocated List. Since empty non-NIL lists are
+ * Return a freshly allocated gtm_List. Since empty non-gtm_NIL lists are
* invalid, new_list() also allocates the head cell of the new list:
* the caller should be sure to fill in that cell's data.
*/
-static List *
+static gtm_List *
new_list()
{
- List *new_list;
- ListCell *new_head;
+ gtm_List *new_list;
+ gtm_ListCell *new_head;
- new_head = (ListCell *) palloc(sizeof(*new_head));
+ new_head = (gtm_ListCell *) palloc(sizeof(*new_head));
new_head->next = NULL;
/* new_head->data is left undefined! */
- new_list = (List *) palloc(sizeof(*new_list));
+ new_list = (gtm_List *) palloc(sizeof(*new_list));
new_list->length = 1;
new_list->head = new_head;
new_list->tail = new_head;
@@ -72,17 +72,17 @@ new_list()
/*
* Allocate a new cell and make it the head of the specified
- * list. Assumes the list it is passed is non-NIL.
+ * list. Assumes the list it is passed is non-gtm_NIL.
*
* The data in the new head cell is undefined; the caller should be
* sure to fill it in
*/
static void
-new_head_cell(List *list)
+new_head_cell(gtm_List *list)
{
- ListCell *new_head;
+ gtm_ListCell *new_head;
- new_head = (ListCell *) palloc(sizeof(*new_head));
+ new_head = (gtm_ListCell *) palloc(sizeof(*new_head));
new_head->next = list->head;
list->head = new_head;
@@ -91,17 +91,17 @@ new_head_cell(List *list)
/*
* Allocate a new cell and make it the tail of the specified
- * list. Assumes the list it is passed is non-NIL.
+ * list. Assumes the list it is passed is non-gtm_NIL.
*
* The data in the new tail cell is undefined; the caller should be
* sure to fill it in
*/
static void
-new_tail_cell(List *list)
+new_tail_cell(gtm_List *list)
{
- ListCell *new_tail;
+ gtm_ListCell *new_tail;
- new_tail = (ListCell *) palloc(sizeof(*new_tail));
+ new_tail = (gtm_ListCell *) palloc(sizeof(*new_tail));
new_tail->next = NULL;
list->tail->next = new_tail;
@@ -116,15 +116,15 @@ new_tail_cell(List *list)
* value, rather than continuing to use the pointer passed as the
* first argument.
*/
-List *
-lappend(List *list, void *datum)
+gtm_List *
+gtm_lappend(gtm_List *list, void *datum)
{
- if (list == NIL)
+ if (list == gtm_NIL)
list = new_list();
else
new_tail_cell(list);
- lfirst(list->tail) = datum;
+ gtm_lfirst(list->tail) = datum;
check_list_invariants(list);
return list;
}
@@ -132,15 +132,15 @@ lappend(List *list, void *datum)
/*
* Add a new cell to the list, in the position after 'prev_cell'. The
* data in the cell is left undefined, and must be filled in by the
- * caller. 'list' is assumed to be non-NIL, and 'prev_cell' is assumed
- * to be non-NULL and a member of 'list'.
+ * caller. 'list' is assumed to be non-gtm_NIL, and 'prev_cell' is assumed
+ * to be non-NULL and a gtm_member of 'list'.
*/
-static ListCell *
-add_new_cell(List *list, ListCell *prev_cell)
+static gtm_ListCell *
+add_new_cell(gtm_List *list, gtm_ListCell *prev_cell)
{
- ListCell *new_cell;
+ gtm_ListCell *new_cell;
- new_cell = (ListCell *) palloc(sizeof(*new_cell));
+ new_cell = (gtm_ListCell *) palloc(sizeof(*new_cell));
/* new_cell->data is left undefined! */
new_cell->next = prev_cell->next;
prev_cell->next = new_cell;
@@ -154,18 +154,18 @@ add_new_cell(List *list, ListCell *prev_cell)
}
/*
- * Add a new cell to the specified list (which must be non-NIL);
+ * Add a new cell to the specified list (which must be non-gtm_NIL);
* it will be placed after the list cell 'prev' (which must be
- * non-NULL and a member of 'list'). The data placed in the new cell
+ * non-NULL and a gtm_member of 'list'). The data placed in the new cell
* is 'datum'. The newly-constructed cell is returned.
*/
-ListCell *
-lappend_cell(List *list, ListCell *prev, void *datum)
+gtm_ListCell *
+gtm_lappend_cell(gtm_List *list, gtm_ListCell *prev, void *datum)
{
- ListCell *new_cell;
+ gtm_ListCell *new_cell;
new_cell = add_new_cell(list, prev);
- lfirst(new_cell) = datum;
+ gtm_lfirst(new_cell) = datum;
check_list_invariants(list);
return new_cell;
}
@@ -177,15 +177,15 @@ lappend_cell(List *list, ListCell *prev, void *datum)
* value, rather than continuing to use the pointer passed as the
* second argument.
*/
-List *
-lcons(void *datum, List *list)
+gtm_List *
+gtm_lcons(void *datum, gtm_List *list)
{
- if (list == NIL)
+ if (list == gtm_NIL)
list = new_list();
else
new_head_cell(list);
- lfirst(list->head) = datum;
+ gtm_lfirst(list->head) = datum;
check_list_invariants(list);
return list;
}
@@ -198,18 +198,18 @@ lcons(void *datum, List *list)
*
* The nodes in list2 are merely appended to the end of list1 in-place
* (i.e. they aren't copied; the two lists will share some of the same
- * storage). Therefore, invoking list_free() on list2 will also
+ * storage). Therefore, invoking gtm_list_free() on list2 will also
* invalidate a portion of list1.
*/
-List *
-list_concat(List *list1, List *list2)
+gtm_List *
+gtm_list_concat(gtm_List *list1, gtm_List *list2)
{
- if (list1 == NIL)
+ if (list1 == gtm_NIL)
return list2;
- if (list2 == NIL)
+ if (list2 == gtm_NIL)
return list1;
if (list1 == list2)
- elog(ERROR, "cannot list_concat() a list to itself");
+ elog(ERROR, "cannot gtm_list_concat() a list to itself");
list1->length += list2->length;
@@ -227,23 +227,23 @@ list_concat(List *list1, List *list2)
* list -- it may or may not be the same as the pointer that was
* passed.
*
- * Note that any cells removed by list_truncate() are NOT pfree'd.
+ * Note that any cells removed by gtm_list_truncate() are NOT pfree'd.
*/
-List *
-list_truncate(List *list, int new_size)
+gtm_List *
+gtm_list_truncate(gtm_List *list, int new_size)
{
- ListCell *cell;
+ gtm_ListCell *cell;
int n;
if (new_size <= 0)
- return NIL; /* truncate to zero length */
+ return gtm_NIL; /* truncate to zero length */
/* If asked to effectively extend the list, do nothing */
- if (new_size >= list_length(list))
+ if (new_size >= gtm_list_length(list))
return list;
n = 1;
- foreach(cell, list)
+ gtm_foreach(cell, list)
{
if (n == new_size)
{
@@ -265,12 +265,12 @@ list_truncate(List *list, int new_size)
* Locate the n'th cell (counting from 0) of the list. It is an assertion
* failure if there is no such cell.
*/
-static ListCell *
-list_nth_cell(List *list, int n)
+static gtm_ListCell *
+list_nth_cell(gtm_List *list, int n)
{
- ListCell *match;
+ gtm_ListCell *match;
- Assert(list != NIL);
+ Assert(list != gtm_NIL);
Assert(n >= 0);
Assert(n < list->length);
check_list_invariants(list);
@@ -287,29 +287,29 @@ list_nth_cell(List *list, int n)
/*
* Return the data value contained in the n'th element of the
- * specified list. (List elements begin at 0.)
+ * specified list. (gtm_List elements begin at 0.)
*/
void *
-list_nth(List *list, int n)
+gtm_list_nth(gtm_List *list, int n)
{
- return lfirst(list_nth_cell(list, n));
+ return gtm_lfirst(list_nth_cell(list, n));
}
/*
- * Return true iff 'datum' is a member of the list. Equality is
- * determined via equal(), so callers should ensure that they pass a
+ * Return true if 'datum' is a gtm_member of the list. Equality is
+ * determined via gtm_equal(), so callers should ensure that they pass a
* Node as 'datum'.
*/
bool
-list_member(List *list, void *datum)
+gtm_list_member(gtm_List *list, void *datum)
{
- ListCell *cell;
+ gtm_ListCell *cell;
check_list_invariants(list);
- foreach(cell, list)
+ gtm_foreach(cell, list)
{
- if (equal(lfirst(cell), datum))
+ if (gtm_equal(gtm_lfirst(cell), datum))
return true;
}
@@ -317,19 +317,19 @@ list_member(List *list, void *datum)
}
/*
- * Return true iff 'datum' is a member of the list. Equality is
+ * Return true if 'datum' is a gtm_member of the list. Equality is
* determined by using simple pointer comparison.
*/
bool
-list_member_ptr(List *list, void *datum)
+gtm_list_member_ptr(gtm_List *list, void *datum)
{
- ListCell *cell;
+ gtm_ListCell *cell;
check_list_invariants(list);
- foreach(cell, list)
+ gtm_foreach(cell, list)
{
- if (lfirst(cell) == datum)
+ if (gtm_lfirst(cell) == datum)
return true;
}
@@ -340,23 +340,23 @@ list_member_ptr(List *list, void *datum)
* Delete 'cell' from 'list'; 'prev' is the previous element to 'cell'
* in 'list', if any (i.e. prev == NULL iff list->head == cell)
*
- * The cell is pfree'd, as is the List header if this was the last member.
+ * The cell is pfree'd, as is the gtm_List header if this was the last gtm_member.
*/
-List *
-list_delete_cell(List *list, ListCell *cell, ListCell *prev)
+gtm_List *
+gtm_list_delete_cell(gtm_List *list, gtm_ListCell *cell, gtm_ListCell *prev)
{
check_list_invariants(list);
- Assert(prev != NULL ? lnext(prev) == cell : list_head(list) == cell);
+ Assert(prev != NULL ? gtm_lnext(prev) == cell : gtm_list_head(list) == cell);
/*
* If we're about to delete the last node from the list, free the whole
- * list instead and return NIL, which is the only valid representation of
+ * list instead and return gtm_NIL, which is the only valid representation of
* a zero-length list.
*/
if (list->length == 1)
{
- list_free(list);
- return NIL;
+ gtm_list_free(list);
+ return gtm_NIL;
}
/*
@@ -379,21 +379,21 @@ list_delete_cell(List *list, ListCell *cell, ListCell *prev)
/*
* Delete the first cell in list that matches datum, if any.
- * Equality is determined via equal().
+ * Equality is determined via gtm_equal().
*/
-List *
-list_delete(List *list, void *datum)
+gtm_List *
+gtm_list_delete(gtm_List *list, void *datum)
{
- ListCell *cell;
- ListCell *prev;
+ gtm_ListCell *cell;
+ gtm_ListCell *prev;
check_list_invariants(list);
prev = NULL;
- foreach(cell, list)
+ gtm_foreach(cell, list)
{
- if (equal(lfirst(cell), datum))
- return list_delete_cell(list, cell, prev);
+ if (gtm_equal(gtm_lfirst(cell), datum))
+ return gtm_list_delete_cell(list, cell, prev);
prev = cell;
}
@@ -403,19 +403,19 @@ list_delete(List *list, void *datum)
}
/* As above, but use simple pointer equality */
-List *
-list_delete_ptr(List *list, void *datum)
+gtm_List *
+gtm_list_delete_ptr(gtm_List *list, void *datum)
{
- ListCell *cell;
- ListCell *prev;
+ gtm_ListCell *cell;
+ gtm_ListCell *prev;
check_list_invariants(list);
prev = NULL;
- foreach(cell, list)
+ gtm_foreach(cell, list)
{
- if (lfirst(cell) == datum)
- return list_delete_cell(list, cell, prev);
+ if (gtm_lfirst(cell) == datum)
+ return gtm_list_delete_cell(list, cell, prev);
prev = cell;
}
@@ -428,53 +428,53 @@ list_delete_ptr(List *list, void *datum)
/*
* Delete the first element of the list.
*
- * This is useful to replace the Lisp-y code "list = lnext(list);" in cases
+ * This is useful to replace the Lisp-y code "list = gtm_lnext(list);" in cases
* where the intent is to alter the list rather than just traverse it.
- * Beware that the removed cell is freed, whereas the lnext() coding leaves
+ * Beware that the removed cell is freed, whereas the gtm_lnext() coding leaves
* the original list head intact if there's another pointer to it.
*/
-List *
-list_delete_first(List *list)
+gtm_List *
+gtm_list_delete_first(gtm_List *list)
{
check_list_invariants(list);
- if (list == NIL)
- return NIL; /* would an error be better? */
+ if (list == gtm_NIL)
+ return gtm_NIL; /* would an error be better? */
- return list_delete_cell(list, list_head(list), NULL);
+ return gtm_list_delete_cell(list, gtm_list_head(list), NULL);
}
/*
* Generate the union of two lists. This is calculated by copying
- * list1 via list_copy(), then adding to it all the members of list2
+ * list1 via gtm_list_copy(), then adding to it all the members of list2
* that aren't already in list1.
*
* Whether an element is already a member of the list is determined
- * via equal().
+ * via gtm_equal().
*
* The returned list is newly-allocated, although the content of the
* cells is the same (i.e. any pointed-to objects are not copied).
*
* NB: this function will NOT remove any duplicates that are present
* in list1 (so it only performs a "union" if list1 is known unique to
- * start with). Also, if you are about to write "x = list_union(x, y)"
- * you probably want to use list_concat_unique() instead to avoid wasting
+ * start with). Also, if you are about to write "x = gtm_list_union(x, y)"
+ * you probably want to use gtm_list_concat_unique() instead to avoid wasting
* the list cells of the old x list.
*
* This function could probably be implemented a lot faster if it is a
* performance bottleneck.
*/
-List *
-list_union(List *list1, List *list2)
+gtm_List *
+gtm_list_union(gtm_List *list1, gtm_List *list2)
{
- List *result;
- ListCell *cell;
+ gtm_List *result;
+ gtm_ListCell *cell;
- result = list_copy(list1);
- foreach(cell, list2)
+ result = gtm_list_copy(list1);
+ gtm_foreach(cell, list2)
{
- if (!list_member(result, lfirst(cell)))
- result = lappend(result, lfirst(cell));
+ if (!gtm_list_member(result, gtm_lfirst(cell)))
+ result = gtm_lappend(result, gtm_lfirst(cell));
}
check_list_invariants(result);
@@ -482,21 +482,21 @@ list_union(List *list1, List *list2)
}
/*
- * This variant of list_union() determines duplicates via simple
+ * This variant of gtm_list_union() determines duplicates via simple
* pointer comparison.
*/
-List *
-list_union_ptr(List *list1, List *list2)
+gtm_List *
+gtm_list_union_ptr(gtm_List *list1, gtm_List *list2)
{
- List *result;
- ListCell *cell;
+ gtm_List *result;
+ gtm_ListCell *cell;
- result = list_copy(list1);
- foreach(cell, list2)
+ result = gtm_list_copy(list1);
+ gtm_foreach(cell, list2)
{
- if (!list_member_ptr(result, lfirst(cell)))
- result = lappend(result, lfirst(cell));
+ if (!gtm_list_member_ptr(result, gtm_lfirst(cell)))
+ result = gtm_lappend(result, gtm_lfirst(cell));
}
check_list_invariants(result);
@@ -513,23 +513,23 @@ list_union_ptr(List *list1, List *list2)
* "intersection" if list1 is known unique beforehand.
*
* This variant works on lists of pointers, and determines list
- * membership via equal(). Note that the list1 member will be pointed
+ * membership via gtm_equal(). Note that the list1 gtm_member will be pointed
* to in the result.
*/
-List *
-list_intersection(List *list1, List *list2)
+gtm_List *
+gtm_list_intersection(gtm_List *list1, gtm_List *list2)
{
- List *result;
- ListCell *cell;
+ gtm_List *result;
+ gtm_ListCell *cell;
- if (list1 == NIL || list2 == NIL)
- return NIL;
+ if (list1 == gtm_NIL || list2 == gtm_NIL)
+ return gtm_NIL;
- result = NIL;
- foreach(cell, list1)
+ result = gtm_NIL;
+ gtm_foreach(cell, list1)
{
- if (list_member(list2, lfirst(cell)))
- result = lappend(result, lfirst(cell));
+ if (gtm_list_member(list2, gtm_lfirst(cell)))
+ result = gtm_lappend(result, gtm_lfirst(cell));
}
check_list_invariants(result);
@@ -543,21 +543,21 @@ list_intersection(List *list1, List *list2)
* input lists.
*
* This variant works on lists of pointers, and determines list
- * membership via equal()
+ * membership via gtm_equal()
*/
-List *
-list_difference(List *list1, List *list2)
+gtm_List *
+gtm_list_difference(gtm_List *list1, gtm_List *list2)
{
- ListCell *cell;
- List *result = NIL;
+ gtm_ListCell *cell;
+ gtm_List *result = gtm_NIL;
- if (list2 == NIL)
- return list_copy(list1);
+ if (list2 == gtm_NIL)
+ return gtm_list_copy(list1);
- foreach(cell, list1)
+ gtm_foreach(cell, list1)
{
- if (!list_member(list2, lfirst(cell)))
- result = lappend(result, lfirst(cell));
+ if (!gtm_list_member(list2, gtm_lfirst(cell)))
+ result = gtm_lappend(result, gtm_lfirst(cell));
}
check_list_invariants(result);
@@ -565,22 +565,22 @@ list_difference(List *list1, List *list2)
}
/*
- * This variant of list_difference() determines list membership via
+ * This variant of gtm_list_difference() determines list membership via
* simple pointer equality.
*/
-List *
-list_difference_ptr(List *list1, List *list2)
+gtm_List *
+gtm_list_difference_ptr(gtm_List *list1, gtm_List *list2)
{
- ListCell *cell;
- List *result = NIL;
+ gtm_ListCell *cell;
+ gtm_List *result = gtm_NIL;
- if (list2 == NIL)
- return list_copy(list1);
+ if (list2 == gtm_NIL)
+ return gtm_list_copy(list1);
- foreach(cell, list1)
+ gtm_foreach(cell, list1)
{
- if (!list_member_ptr(list2, lfirst(cell)))
- result = lappend(result, lfirst(cell));
+ if (!gtm_list_member_ptr(list2, gtm_lfirst(cell)))
+ result = gtm_lappend(result, gtm_lfirst(cell));
}
check_list_invariants(result);
@@ -591,49 +591,49 @@ list_difference_ptr(List *list1, List *list2)
* Append datum to list, but only if it isn't already in the list.
*
* Whether an element is already a member of the list is determined
- * via equal().
+ * via gtm_equal().
*/
-List *
-list_append_unique(List *list, void *datum)
+gtm_List *
+gtm_list_append_unique(gtm_List *list, void *datum)
{
- if (list_member(list, datum))
+ if (gtm_list_member(list, datum))
return list;
else
- return lappend(list, datum);
+ return gtm_lappend(list, datum);
}
/*
- * This variant of list_append_unique() determines list membership via
+ * This variant of gtm_list_append_unique() determines list membership via
* simple pointer equality.
*/
-List *
-list_append_unique_ptr(List *list, void *datum)
+gtm_List *
+gtm_list_append_unique_ptr(gtm_List *list, void *datum)
{
- if (list_member_ptr(list, datum))
+ if (gtm_list_member_ptr(list, datum))
return list;
else
- return lappend(list, datum);
+ return gtm_lappend(list, datum);
}
/*
* Append to list1 each member of list2 that isn't already in list1.
*
* Whether an element is already a member of the list is determined
- * via equal().
+ * via gtm_equal().
*
- * This is almost the same functionality as list_union(), but list1 is
+ * This is almost the same functionality as gtm_list_union(), but list1 is
* modified in-place rather than being copied. Note also that list2's cells
- * are not inserted in list1, so the analogy to list_concat() isn't perfect.
+ * are not inserted in list1, so the analogy to gtm_list_concat() isn't perfect.
*/
-List *
-list_concat_unique(List *list1, List *list2)
+gtm_List *
+gtm_list_concat_unique(gtm_List *list1, gtm_List *list2)
{
- ListCell *cell;
+ gtm_ListCell *cell;
- foreach(cell, list2)
+ gtm_foreach(cell, list2)
{
- if (!list_member(list1, lfirst(cell)))
- list1 = lappend(list1, lfirst(cell));
+ if (!gtm_list_member(list1, gtm_lfirst(cell)))
+ list1 = gtm_lappend(list1, gtm_lfirst(cell));
}
check_list_invariants(list1);
@@ -641,18 +641,18 @@ list_concat_unique(List *list1, List *list2)
}
/*
- * This variant of list_concat_unique() determines list membership via
+ * This variant of gtm_list_concat_unique() determines list membership via
* simple pointer equality.
*/
-List *
-list_concat_unique_ptr(List *list1, List *list2)
+gtm_List *
+gtm_list_concat_unique_ptr(gtm_List *list1, gtm_List *list2)
{
- ListCell *cell;
+ gtm_ListCell *cell;
- foreach(cell, list2)
+ gtm_foreach(cell, list2)
{
- if (!list_member_ptr(list1, lfirst(cell)))
- list1 = lappend(list1, lfirst(cell));
+ if (!gtm_list_member_ptr(list1, gtm_lfirst(cell)))
+ list1 = gtm_lappend(list1, gtm_lfirst(cell));
}
check_list_invariants(list1);
@@ -663,20 +663,20 @@ list_concat_unique_ptr(List *list1, List *list2)
* Free all storage in a list, and optionally the pointed-to elements
*/
static void
-list_free_private(List *list, bool deep)
+list_free_private(gtm_List *list, bool deep)
{
- ListCell *cell;
+ gtm_ListCell *cell;
check_list_invariants(list);
- cell = list_head(list);
+ cell = gtm_list_head(list);
while (cell != NULL)
{
- ListCell *tmp = cell;
+ gtm_ListCell *tmp = cell;
- cell = lnext(cell);
+ cell = gtm_lnext(cell);
if (deep)
- pfree(lfirst(tmp));
+ pfree(gtm_lfirst(tmp));
pfree(tmp);
}
@@ -690,10 +690,10 @@ list_free_private(List *list, bool deep)
* free'd.
*
* On return, the argument to this function has been freed, so the
- * caller would be wise to set it to NIL for safety's sake.
+ * caller would be wise to set it to gtm_NIL for safety's sake.
*/
void
-list_free(List *list)
+gtm_list_free(gtm_List *list)
{
list_free_private(list, false);
}
@@ -704,10 +704,10 @@ list_free(List *list)
* list must contain a pointer to a palloc()'d region of memory!)
*
* On return, the argument to this function has been freed, so the
- * caller would be wise to set it to NIL for safety's sake.
+ * caller would be wise to set it to gtm_NIL for safety's sake.
*/
void
-list_free_deep(List *list)
+gtm_list_free_deep(gtm_List *list)
{
/*
* A "deep" free operation only makes sense on a list of pointers.
@@ -718,15 +718,15 @@ list_free_deep(List *list)
/*
* Return a shallow copy of the specified list.
*/
-List *
-list_copy(List *oldlist)
+gtm_List *
+gtm_list_copy(gtm_List *oldlist)
{
- List *newlist;
- ListCell *newlist_prev;
- ListCell *oldlist_cur;
+ gtm_List *newlist;
+ gtm_ListCell *newlist_prev;
+ gtm_ListCell *oldlist_cur;
- if (oldlist == NIL)
- return NIL;
+ if (oldlist == gtm_NIL)
+ return gtm_NIL;
newlist = new_list();
newlist->length = oldlist->length;
@@ -741,9 +741,9 @@ list_copy(List *oldlist)
oldlist_cur = oldlist->head->next;
while (oldlist_cur)
{
- ListCell *newlist_cur;
+ gtm_ListCell *newlist_cur;
- newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur));
+ newlist_cur = (gtm_ListCell *) palloc(sizeof(*newlist_cur));
newlist_cur->data = oldlist_cur->data;
newlist_prev->next = newlist_cur;
@@ -761,18 +761,18 @@ list_copy(List *oldlist)
/*
* Return a shallow copy of the specified list, without the first N elements.
*/
-List *
-list_copy_tail(List *oldlist, int nskip)
+gtm_List *
+gtm_list_copy_tail(gtm_List *oldlist, int nskip)
{
- List *newlist;
- ListCell *newlist_prev;
- ListCell *oldlist_cur;
+ gtm_List *newlist;
+ gtm_ListCell *newlist_prev;
+ gtm_ListCell *oldlist_cur;
if (nskip < 0)
nskip = 0; /* would it be better to elog? */
- if (oldlist == NIL || nskip >= oldlist->length)
- return NIL;
+ if (oldlist == gtm_NIL || nskip >= oldlist->length)
+ return gtm_NIL;
newlist = new_list();
newlist->length = oldlist->length - nskip;
@@ -794,9 +794,9 @@ list_copy_tail(List *oldlist, int nskip)
oldlist_cur = oldlist_cur->next;
while (oldlist_cur)
{
- ListCell *newlist_cur;
+ gtm_ListCell *newlist_cur;
- newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur));
+ newlist_cur = (gtm_ListCell *) palloc(sizeof(*newlist_cur));
newlist_cur->data = oldlist_cur->data;
newlist_prev->next = newlist_cur;
@@ -819,20 +819,20 @@ list_copy_tail(List *oldlist, int nskip)
*/
#ifndef __GNUC__
-ListCell *
-list_head(List *l)
+gtm_ListCell *
+gtm_list_head(gtm_List *l)
{
return l ? l->head : NULL;
}
-ListCell *
-list_tail(List *l)
+gtm_ListCell *
+gtm_list_tail(gtm_List *l)
{
return l ? l->tail : NULL;
}
int
-list_length(List *l)
+gtm_list_length(gtm_List *l)
{
return l ? l->length : 0;
}
@@ -851,13 +851,13 @@ list_length(List *l)
* Given a list, return its length. This is merely defined for the
* sake of backward compatibility: we can't afford to define a macro
* called "length", so it must be a function. New code should use the
- * list_length() macro in order to avoid the overhead of a function
+ * gtm_list_length() macro in order to avoid the overhead of a function
* call.
*/
-int length(List *list);
+int gtm_length(gtm_List *list);
int
-length(List *list)
+gtm_length(gtm_List *list)
{
- return list_length(list);
+ return gtm_list_length(list);
}
diff --git a/src/gtm/common/gtm_serialize.c b/src/gtm/common/gtm_serialize.c
new file mode 100644
index 0000000000..e2b8624f13
--- /dev/null
+++ b/src/gtm/common/gtm_serialize.c
@@ -0,0 +1,974 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_serialize.c
+ * Serialization management of GTM data
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ *
+ * IDENTIFICATION
+ * src/gtm/common/gtm_serialize.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "gtm/gtm_c.h"
+#include "gtm/elog.h"
+#include "gtm/gtm.h"
+#include "gtm/gtm_txn.h"
+#include "gtm/gtm_seq.h"
+#include "gtm/assert.h"
+#include "gtm/register.h"
+#include "gtm/stringinfo.h"
+#include "gtm/libpq.h"
+#include "gtm/pqformat.h"
+#include "gtm/gtm_msg.h"
+
+#include "gen_alloc.h"
+
+#include "gtm/gtm_serialize.h"
+
+/*
+ * gtm_get_snapshotdata_size
+ * Get a serialized size of GTM_SnapshotData structure
+ * Corrected snapshort serialize data calculation.
+ * May 3rd, 2011, K.Suzuki
+ *
+ * Serialize of snapshot_data
+ *
+ * sn_xmin ---> sn_xmax ---> sn_recent_global_xmin
+ * ---> sn_xcnt ---> GXID * sn_xcnt
+ * |<--- sn_xip -->|
+ */
+size_t
+gtm_get_snapshotdata_size(GTM_SnapshotData *data)
+{
+ size_t len = 0;
+ uint32 snapshot_elements;
+
+ snapshot_elements = data->sn_xcnt;
+ len += sizeof(GlobalTransactionId);
+ len += sizeof(GlobalTransactionId);
+ len += sizeof(GlobalTransactionId);
+ len += sizeof(uint32);
+ len += sizeof(GlobalTransactionId) * snapshot_elements;
+
+ return len;
+}
+
+/*
+ * gtm_serialize_snapshotdata
+ * Serialize a GTM_SnapshotData structure
+ */
+size_t
+gtm_serialize_snapshotdata(GTM_SnapshotData *data, char *buf, size_t buflen)
+{
+ int len = 0;
+
+ memset(buf, 0, buflen);
+
+ /* size check */
+ if (gtm_get_snapshotdata_size(data) > buflen)
+ return 0;
+
+ /* GTM_SnapshotData.sn_xmin */
+ memcpy(buf + len, &(data->sn_xmin), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_SnapshotData.sn_xmax */
+ memcpy(buf + len, &(data->sn_xmax), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_SnapshotData.sn_recent_global_xmin */
+ memcpy(buf + len, &(data->sn_recent_global_xmin), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_SnapshotData.sn_xcnt */
+ memcpy(buf + len, &(data->sn_xcnt), sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_SnapshotData.sn_xip */
+#if 0
+ /*
+ * This block of code seems to be wrong. data->sn_xip is an array of GlobalTransacionIDs
+ * and the number of elements are indicated by sn_xcnt.
+ */
+ memcpy(buf + len, &(data->sn_xip), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+#else
+ if(data->sn_xcnt > 0)
+ {
+ memcpy(buf + len, data->sn_xip, sizeof(GlobalTransactionId) * data->sn_xcnt);
+ len += sizeof(GlobalTransactionId) * data->sn_xcnt;
+ }
+#endif
+
+ return len;
+}
+
+/* -----------------------------------------------------
+ * Deserialize a GTM_SnapshotData structure
+ * -----------------------------------------------------
+ */
+size_t
+gtm_deserialize_snapshotdata(GTM_SnapshotData *data, const char *buf, size_t buflen)
+{
+ size_t len = 0;
+
+ /* GTM_SnapshotData.sn_xmin */
+ memcpy(&(data->sn_xmin), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_SnapshotData.sn_xmax */
+ memcpy(&(data->sn_xmax), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_SnapshotData.sn_recent_global_xmin */
+ memcpy(&(data->sn_recent_global_xmin), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_SnapshotData.sn_xcnt */
+ memcpy(&(data->sn_xcnt), buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_SnapshotData.sn_xip */
+ if (data->sn_xcnt > 0)
+ {
+ /*
+ * Please note that this function runs with TopMemoryContext. So we must
+ * free this area manually later.
+ */
+ data->sn_xip = genAlloc(sizeof(GlobalTransactionId) * data->sn_xcnt);
+ memcpy(data->sn_xip, buf + len, sizeof(GlobalTransactionId) * data->sn_xcnt);
+ len += sizeof(GlobalTransactionId) * data->sn_xcnt;
+ }
+ else
+ {
+ data->sn_xip = NULL;
+ }
+
+ return len;
+}
+
+
+/*
+ * gtm_get_transactioninfo_size
+ * Get a serialized size of GTM_TransactionInfo structure
+ *
+ * Original gti_gid serialization was just "null-terminated string".
+ * This should be prefixed with the length of the string.
+ */
+size_t
+gtm_get_transactioninfo_size(GTM_TransactionInfo *data)
+{
+ size_t len = 0;
+
+ if (data == NULL)
+ return len;
+
+ len += sizeof(GTM_TransactionHandle); /* gti_handle */
+ len += sizeof(GTM_ThreadID); /* gti_thread_id */
+ len += sizeof(bool); /* gti_in_use */
+ len += sizeof(GlobalTransactionId);/* gti_gxid */
+ len += sizeof(GTM_TransactionStates); /* gti_state */
+ len += sizeof(PGXC_NodeId);/* gti_coordid */
+ len += sizeof(GlobalTransactionId);/* gti_xmin */
+ len += sizeof(GTM_IsolationLevel); /* gti_isolevel */
+ len += sizeof(bool); /* gti_readonly */
+ len += sizeof(GTMProxy_ConnID);/* gti_backend_id */
+ len += sizeof(uint32); /* gti_datanodecount */
+ len += sizeof(PGXC_NodeId) * data->gti_datanodecount;
+ /* gti_datanodes */
+ len += sizeof(uint32); /* gti_coordcount */
+ len += sizeof(PGXC_NodeId) * data->gti_coordcount;
+ /* gti_coordinators */
+ len += sizeof(uint32);
+ if (data->gti_gid != NULL)
+ len += strlen(data->gti_gid); /* gti_gid */
+
+ len += gtm_get_snapshotdata_size(&(data->gti_current_snapshot));
+ /* gti_current_snapshot */
+ len += sizeof(bool); /* gti_snapshot_set */
+ /* NOTE: nothing to be done for gti_lock */
+ len += sizeof(bool); /* gti_vacuum */
+
+ return len;
+}
+
+
+/* -----------------------------------------------------
+ * Serialize a GTM_TransactionInfo structure
+ * -----------------------------------------------------
+ */
+size_t
+gtm_serialize_transactioninfo(GTM_TransactionInfo *data, char *buf, size_t buflen)
+{
+ int len = 0;
+ char *buf2;
+ int i;
+
+ /* size check */
+ if (gtm_get_transactioninfo_size(data) > buflen)
+ return 0;
+
+ memset(buf, 0, buflen);
+
+ /* GTM_TransactionInfo.gti_handle */
+ memcpy(buf + len, &(data->gti_handle), sizeof(GTM_TransactionHandle));
+ len += sizeof(GTM_TransactionHandle);
+
+ /* GTM_TransactionInfo.gti_thread_id */
+ memcpy(buf + len, &(data->gti_thread_id), sizeof(GTM_ThreadID));
+ len += sizeof(GTM_ThreadID);
+
+ /* GTM_TransactionInfo.gti_in_use */
+ memcpy(buf + len, &(data->gti_in_use), sizeof(bool));
+ len += sizeof(bool);
+
+ /* GTM_TransactionInfo.gti_gxid */
+ memcpy(buf + len, &(data->gti_gxid), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_TransactionInfo.gti_state */
+ memcpy(buf + len, &(data->gti_state), sizeof(GTM_TransactionStates));
+ len += sizeof(GTM_TransactionStates);
+
+ /* GTM_TransactionInfo.gti_coordid */
+ memcpy(buf + len, &(data->gti_coordid), sizeof(PGXC_NodeId));
+ len += sizeof(PGXC_NodeId);
+
+ /* GTM_TransactionInfo.gti_xmin */
+ memcpy(buf + len, &(data->gti_xmin), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_TransactionInfo.gti_isolevel */
+ memcpy(buf + len, &(data->gti_isolevel), sizeof(GTM_IsolationLevel));
+ len += sizeof(GTM_IsolationLevel);
+
+ /* GTM_TransactionInfo.gti_readonly */
+ memcpy(buf + len, &(data->gti_readonly), sizeof(bool));
+ len += sizeof(bool);
+
+ /* GTM_TransactionInfo.gti_backend_id */
+ memcpy(buf + len, &(data->gti_backend_id), sizeof(GTMProxy_ConnID));
+ len += sizeof(GTMProxy_ConnID);
+
+ /* GTM_TransactionInfo.gti_datanodecount */
+ memcpy(buf + len, &(data->gti_datanodecount), sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_TransactionInfo.gti_datanodes */
+ for (i = 0; i < data->gti_datanodecount; i++)
+ {
+ memcpy(buf + len, &(data->gti_datanodes[i]), sizeof(PGXC_NodeId));
+ len += sizeof(PGXC_NodeId);
+ }
+
+ /* GTM_TransactionInfo.gti_coordcount */
+ memcpy(buf + len, &(data->gti_coordcount), sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_TransactionInfo.gti_coordinators */
+ for (i = 0; i < data->gti_coordcount; i++)
+ {
+ memcpy(buf + len, &(data->gti_coordinators[i]), sizeof(PGXC_NodeId));
+ len += sizeof(PGXC_NodeId);
+ }
+
+ /* GTM_TransactionInfo.gti_gid */
+ if (data->gti_gid != NULL)
+ {
+ uint32 gidlen;
+
+ gidlen = (uint32)strlen(data->gti_gid);
+ memcpy(buf + len, &gidlen, sizeof(uint32));
+ len += sizeof(uint32);
+ memcpy(buf + len, data->gti_gid, gidlen);
+ len += gidlen;
+ }
+ else
+ {
+ uint32 gidlen = 0;
+
+ memcpy(buf + len, &gidlen, sizeof(uint32));
+ len += sizeof(uint32);
+ }
+
+ /* GTM_TransactionInfo.gti_current_snapshot */
+ buf2 = malloc(gtm_get_snapshotdata_size(&(data->gti_current_snapshot)));
+ i = gtm_serialize_snapshotdata(&(data->gti_current_snapshot),
+ buf2,
+ gtm_get_snapshotdata_size(&(data->gti_current_snapshot)));
+ memcpy(buf + len, buf2, i);
+ free(buf2);
+ len += i;
+
+ /* GTM_TransactionInfo.gti_snapshot_set */
+ memcpy(buf + len, &(data->gti_snapshot_set), sizeof(bool));
+ len += sizeof(bool);
+
+ /* GTM_TransactionInfo.gti_lock would not be serialized. */
+
+ /* GTM_TransactionInfo.gti_vacuum */
+ memcpy(buf + len, &(data->gti_vacuum), sizeof(bool));
+ len += sizeof(bool);
+
+ return len;
+}
+
+/* -----------------------------------------------------
+ * Deserialize a GTM_TransactionInfo structure
+ * -----------------------------------------------------
+ */
+size_t
+gtm_deserialize_transactioninfo(GTM_TransactionInfo *data, const char *buf, size_t maxlen)
+{
+ int len = 0;
+ int i;
+
+ memset(data, 0, sizeof(GTM_TransactionInfo));
+
+ /* GTM_TransactionInfo.gti_handle */
+ memcpy(&(data->gti_handle), buf + len, sizeof(GTM_TransactionHandle));
+ len += sizeof(GTM_TransactionHandle);
+
+ /* GTM_TransactionInfo.gti_thread_id */
+ memcpy(&(data->gti_thread_id), buf + len, sizeof(GTM_ThreadID));
+ len += sizeof(GTM_ThreadID);
+
+ /* GTM_TransactionInfo.gti_in_use */
+ memcpy(&(data->gti_in_use), buf + len, sizeof(bool));
+ len += sizeof(bool);
+
+ /* GTM_TransactionInfo.gti_gxid */
+ memcpy(&(data->gti_gxid), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_TransactionInfo.gti_state */
+ memcpy(&(data->gti_state), buf + len, sizeof(GTM_TransactionStates));
+ len += sizeof(GTM_TransactionStates);
+
+ /* GTM_TransactionInfo.gti_coordid */
+ memcpy(&(data->gti_coordid), buf + len, sizeof(PGXC_NodeId));
+ len += sizeof(PGXC_NodeId);
+
+ /* GTM_TransactionInfo.gti_xmin */
+ memcpy(&(data->gti_xmin), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_TransactionInfo.gti_isolevel */
+ memcpy(&(data->gti_isolevel), buf + len, sizeof(GTM_IsolationLevel));
+ len += sizeof(GTM_IsolationLevel);
+
+ /* GTM_TransactionInfo.gti_readonly */
+ memcpy(&(data->gti_readonly), buf + len, sizeof(bool));
+ len += sizeof(bool);
+
+ /* GTM_TransactionInfo.gti_backend_id */
+ memcpy(&(data->gti_backend_id), buf + len, sizeof(GTMProxy_ConnID));
+ len += sizeof(GTMProxy_ConnID);
+
+ /* GTM_TransactionInfo.gti_datanodecount */
+ memcpy(&(data->gti_datanodecount), buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_TransactionInfo.gti_datanodes */
+ if (data->gti_datanodes > 0)
+ data->gti_datanodes = (PGXC_NodeId *)genAlloc(sizeof(PGXC_NodeId) * data->gti_datanodecount);
+ else
+ data->gti_datanodes = NULL;
+
+ for (i = 0; i < data->gti_datanodecount; i++)
+ {
+ memcpy(&(data->gti_datanodes[i]), buf + len, sizeof(PGXC_NodeId));
+ len += sizeof(PGXC_NodeId);
+ }
+
+ /* GTM_TransactionInfo.gti_coordcount */
+ memcpy(&(data->gti_coordcount), buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_TransactionInfo.gti_coordinators */
+ if (data->gti_coordinators > 0)
+ data->gti_coordinators = (PGXC_NodeId *)genAlloc(sizeof(PGXC_NodeId) * data->gti_coordcount);
+ else
+ data->gti_coordinators = NULL;
+
+ for (i = 0; i < data->gti_coordcount; i++)
+ {
+ PGXC_NodeId *cur = data->gti_coordinators;
+
+ memcpy(cur, buf + len, sizeof(PGXC_NodeId));
+
+ len += sizeof(PGXC_NodeId);
+ cur++;
+ }
+
+ /* GTM_TransactionInfo.gti_gid */
+ {
+ uint32 gti_len;
+
+ memcpy(&gti_len, buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+ if (gti_len > 0)
+ {
+ data->gti_gid = (char *)genAlloc(gti_len+1);
+ memcpy(data->gti_gid, buf + len, gti_len);
+ data->gti_gid[gti_len] = 0; /* null-terminated */
+ len += gti_len;
+ }
+ else
+ {
+ data->gti_gid = NULL;
+ }
+ }
+
+ /* GTM_TransactionInfo.gti_current_snapshot */
+ i = gtm_deserialize_snapshotdata(&(data->gti_current_snapshot),
+ buf + len,
+ sizeof(GTM_SnapshotData));
+ len += i;
+
+ /* GTM_TransactionInfo.gti_snapshot_set */
+ memcpy(&(data->gti_snapshot_set), buf + len, sizeof(bool));
+ len += sizeof(bool);
+
+ /* GTM_TransactionInfo.gti_lock would not be serialized. */
+
+ /* GTM_TransactionInfo.gti_vacuum */
+ memcpy(&(data->gti_vacuum), buf + len, sizeof(bool));
+ len += sizeof(bool);
+
+ return len;
+}
+
+
+size_t
+gtm_get_transactions_size(GTM_Transactions *data)
+{
+ size_t len = 0;
+ int i;
+
+ len += sizeof(uint32);/* gt_txn_count */
+ len += sizeof(GTM_States);/* gt_gtm_state */
+
+ /* NOTE: nothing to be done for gt_XidGenLock */
+
+ len += sizeof(GlobalTransactionId); /* gt_nextXid */
+ len += sizeof(GlobalTransactionId); /* gt_oldestXid */
+ len += sizeof(GlobalTransactionId); /* gt_xidVacLimit */
+ len += sizeof(GlobalTransactionId); /* gt_xidWarnLimit */
+ len += sizeof(GlobalTransactionId); /* gt_xidStopLimit */
+ len += sizeof(GlobalTransactionId); /* gt_xidWrapLimit */
+
+ len += sizeof(GlobalTransactionId); /* gt_latestCompletedXid */
+ len += sizeof(GlobalTransactionId); /* gt_recent_global_xmin */
+
+ len += sizeof(int32); /* gt_lastslot */
+
+ len += sizeof(int32); /* txn_count */
+
+ for (i = 0; i < GTM_MAX_GLOBAL_TRANSACTIONS; i++)
+ {
+ len += sizeof(size_t); /* length */
+ len += gtm_get_transactioninfo_size(&data->gt_transactions_array[i]);
+ }
+
+ /* NOTE: nothing to be done for gt_open_transactions */
+ /* NOTE: nothing to be done for gt_TransArrayLock */
+
+ return len;
+}
+
+/*
+ * Return a number of serialized transactions.
+ */
+size_t
+gtm_serialize_transactions(GTM_Transactions *data, char *buf, size_t buflen)
+{
+ int len = 0;
+ int i;
+ uint32 txn_count;
+
+ /* size check */
+ if (gtm_get_transactions_size(data) > buflen)
+ return 0;
+
+ memset(buf, 0, buflen);
+
+ /* GTM_Transactions.gt_txn_count */
+ memcpy(buf + len, &(data->gt_txn_count), sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_Transactions.gt_gtm_state */
+ memcpy(buf + len, &(data->gt_gtm_state), sizeof(GTM_States));
+ len += sizeof(GTM_States);
+
+ /* NOTE: nothing to be done for gt_XidGenLock */
+
+ /* GTM_Transactions.gt_nextXid */
+ memcpy(buf + len, &(data->gt_nextXid), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_oldestXid */
+ memcpy(buf + len, &(data->gt_oldestXid), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidVacLimit */
+ memcpy(buf + len, &(data->gt_xidVacLimit), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidWarnLimit */
+ memcpy(buf + len, &(data->gt_xidWarnLimit), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidStopLimit */
+ memcpy(buf + len, &(data->gt_xidStopLimit), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidWrapLimit */
+ memcpy(buf + len, &(data->gt_xidWrapLimit), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_latestCompletedXid */
+ memcpy(buf + len, &(data->gt_latestCompletedXid), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_recent_global_xmin */
+ memcpy(buf + len, &(data->gt_recent_global_xmin), sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_lastslot */
+ memcpy(buf + len, &(data->gt_lastslot), sizeof(int32));
+ len += sizeof(int32);
+
+ /* Count up for valid transactions. */
+ txn_count = 0;
+
+ for (i = 0; i < GTM_MAX_GLOBAL_TRANSACTIONS; i++)
+ {
+ /* Select a used slot with the transaction array. */
+ if (data->gt_transactions_array[i].gti_in_use == TRUE)
+ txn_count++;
+ }
+
+ memcpy(buf + len, &txn_count, sizeof(int32));
+ len += sizeof(int32);
+
+ /*
+ * GTM_Transactions.gt_transactions_array
+ */
+ for (i = 0; i < GTM_MAX_GLOBAL_TRANSACTIONS; i++)
+ {
+ char *buf2;
+ size_t buflen2, len2;
+
+ /*
+ * Not to include invalid global transactions.
+ */
+ if (data->gt_transactions_array[i].gti_gxid == InvalidGlobalTransactionId)
+ continue;
+
+ buflen2 = gtm_get_transactioninfo_size(&data->gt_transactions_array[i]);
+
+ /* store a length of following data. */
+ memcpy(buf + len, &buflen2, sizeof(size_t));
+ len += sizeof(size_t);
+
+ buf2 = (char *)malloc(buflen2);
+
+ len2 = gtm_serialize_transactioninfo(&data->gt_transactions_array[i],
+ buf2,
+ buflen2);
+
+ /* store a serialized GTM_TransactionInfo structure. */
+ memcpy(buf + len, buf2, len2);
+ len += len2;
+
+ free(buf2);
+ }
+
+ /* NOTE: nothing to be done for gt_TransArrayLock */
+ return len;
+}
+
+
+/*
+ * Return a number of deserialized transactions.
+ */
+size_t
+gtm_deserialize_transactions(GTM_Transactions *data, const char *buf, size_t maxlen)
+{
+ int len = 0;
+ int i;
+ uint32 txn_count;
+
+ /* GTM_Transactions.gt_txn_count */
+ memcpy(&(data->gt_txn_count), buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+
+ /* GTM_Transactions.gt_gtm_state */
+ memcpy(&(data->gt_gtm_state), buf + len, sizeof(GTM_States));
+ len += sizeof(GTM_States);
+
+ /* NOTE: nothing to be done for gt_XidGenLock */
+
+ /* GTM_Transactions.gt_nextXid */
+ memcpy(&(data->gt_nextXid), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_oldestXid */
+ memcpy(&(data->gt_oldestXid), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidVacLimit */
+ memcpy(&(data->gt_xidVacLimit), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidWarnLimit */
+ memcpy(&(data->gt_xidWarnLimit), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidStopLimit */
+ memcpy(&(data->gt_xidStopLimit), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_xidWrapLimit */
+ memcpy(&(data->gt_xidWrapLimit), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_latestCompletedXid */
+ memcpy(&(data->gt_latestCompletedXid), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_recent_global_xmin */
+ memcpy(&(data->gt_recent_global_xmin), buf + len, sizeof(GlobalTransactionId));
+ len += sizeof(GlobalTransactionId);
+
+ /* GTM_Transactions.gt_lastslot */
+ memcpy(&(data->gt_lastslot), buf + len, sizeof(int32));
+ len += sizeof(int32);
+
+ /* A number of valid transactions */
+ memcpy(&txn_count, buf + len, sizeof(int32));
+ len += sizeof(int32);
+
+ /* GTM_Transactions.gt_transactions_array */
+ for (i = 0; i < txn_count; i++)
+ {
+ size_t buflen2, len2;
+
+ /* read a length of following data. */
+ memcpy(&buflen2, buf + len, sizeof(size_t));
+ len += sizeof(size_t);
+
+ /* reada serialized GTM_TransactionInfo structure. */
+ len2 = gtm_deserialize_transactioninfo(&(data->gt_transactions_array[i]),
+ buf + len,
+ buflen2);
+
+ len += len2;
+ }
+
+ /* NOTE: nothing to be done for gt_TransArrayLock */
+
+ return txn_count;
+}
+
+
+/*
+ * Return size of PGXC node information
+ */
+size_t
+gtm_get_pgxcnodeinfo_size(GTM_PGXCNodeInfo *data)
+{
+ size_t len = 0;
+
+ len += sizeof(GTM_PGXCNodeType); /* type */
+ len += sizeof(GTM_PGXCNodeId); /* nodenum */
+ len += sizeof(GTM_PGXCNodeId); /* proxynum */
+ len += sizeof(GTM_PGXCNodePort); /* port */
+
+ len += sizeof(uint32); /* ipaddress length */
+ if (data->ipaddress != NULL) /* ipaddress */
+ len += strlen(data->ipaddress);
+
+ len += sizeof(uint32); /* datafolder length */
+ if (data->datafolder != NULL) /* datafolder */
+ len += strlen(data->datafolder);
+
+ len += sizeof(GTM_PGXCNodeStatus); /* status */
+
+ return len;
+}
+
+/*
+ * Return a serialize number of PGXC node information
+ */
+size_t
+gtm_serialize_pgxcnodeinfo(GTM_PGXCNodeInfo *data, char *buf, size_t buflen)
+{
+ size_t len = 0;
+ uint32 len_wk;
+
+ /* size check */
+ if (gtm_get_pgxcnodeinfo_size(data) > buflen)
+ return 0;
+
+ memset(buf, 0, buflen);
+
+ /* GTM_PGXCNodeInfo.type */
+ memcpy(buf + len, &(data->type), sizeof(GTM_PGXCNodeType));
+ len += sizeof(GTM_PGXCNodeType);
+
+ /* GTM_PGXCNodeInfo.nodenum */
+ memcpy(buf + len, &(data->nodenum), sizeof(GTM_PGXCNodeId));
+ len += sizeof(GTM_PGXCNodeId);
+
+ /* GTM_PGXCNodeInfo.proxynum */
+ memcpy(buf + len, &(data->proxynum), sizeof(GTM_PGXCNodeId));
+ len += sizeof(GTM_PGXCNodeId);
+
+ /* GTM_PGXCNodeInfo.port */
+ memcpy(buf + len, &(data->port), sizeof(GTM_PGXCNodePort));
+ len += sizeof(GTM_PGXCNodePort);
+
+ /* GTM_PGXCNodeInfo.ipaddress */
+ if (data->ipaddress == NULL)
+ len_wk = 0;
+ else
+ len_wk = (uint32)strlen(data->ipaddress);
+
+ memcpy(buf + len, &len_wk, sizeof(uint32));
+ len += sizeof(uint32);
+ if (len_wk > 0)
+ {
+ memcpy(buf + len, data->ipaddress, len_wk);
+ len += len_wk;
+ }
+
+ /* GTM_PGXCNodeInfo.datafolder */
+ if (data->datafolder == NULL)
+ len_wk = 0;
+ else
+ len_wk = (uint32)strlen(data->datafolder);
+
+ memcpy(buf + len, &len_wk, sizeof(uint32));
+ len += sizeof(uint32);
+ if (len_wk > 0)
+ {
+ memcpy(buf + len, data->datafolder, len_wk);
+ len += len_wk;
+ }
+
+ /* GTM_PGXCNodeInfo.status */
+ memcpy(buf + len, &(data->status), sizeof(GTM_PGXCNodeStatus));
+ len += sizeof(GTM_PGXCNodeStatus);
+
+ /* NOTE: nothing to be done for node_lock */
+ return len;
+}
+
+
+/*
+ * Return a deserialize number of PGXC node information
+ */
+size_t
+gtm_deserialize_pgxcnodeinfo(GTM_PGXCNodeInfo *data, const char *buf, size_t buflen)
+{
+ size_t len = 0;
+ uint32 len_wk;
+
+ /* GTM_PGXCNodeInfo.type */
+ memcpy(&(data->type), buf + len, sizeof(GTM_PGXCNodeType));
+ len += sizeof(GTM_PGXCNodeType);
+
+ /* GTM_PGXCNodeInfo.nodenum */
+ memcpy(&(data->nodenum), buf + len, sizeof(GTM_PGXCNodeId));
+ len += sizeof(GTM_PGXCNodeId);
+
+ /* GTM_PGXCNodeInfo.proxynum */
+ memcpy(&(data->proxynum), buf + len, sizeof(GTM_PGXCNodeId));
+ len += sizeof(GTM_PGXCNodeId);
+
+ /* GTM_PGXCNodeInfo.port */
+ memcpy(&(data->port), buf + len, sizeof(GTM_PGXCNodePort));
+ len += sizeof(GTM_PGXCNodePort);
+
+ /* GTM_PGXCNodeInfo.ipaddress */
+ memcpy(&len_wk, buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+ if (len_wk == 0)
+ {
+ data->ipaddress = NULL;
+ }
+ else
+ {
+ data->ipaddress = (char *)genAlloc(len_wk + 1);
+ memcpy(data->ipaddress, buf + len, (size_t)len_wk);
+ data->ipaddress[len_wk] = 0; /* null_terminate */
+ len += len_wk;
+ }
+
+ /* GTM_PGXCNodeInfo.datafolder */
+ memcpy(&len_wk, buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+ if (len_wk == 0)
+ {
+ data->datafolder = NULL;
+ }
+ else
+ {
+ data->datafolder = (char *)genAlloc(len_wk + 1);
+ memcpy(data->datafolder, buf + len, (size_t)len_wk);
+ data->datafolder[len_wk] = 0; /* null_terminate */
+ len += len_wk;
+ }
+
+ /* GTM_PGXCNodeInfo.status */
+ memcpy(&(data->status), buf + len, sizeof(GTM_PGXCNodeStatus));
+ len += sizeof(GTM_PGXCNodeStatus);
+
+ /* NOTE: nothing to be done for node_lock */
+
+ return len;
+}
+
+
+/*
+ * Return size of sequence information
+ */
+size_t
+gtm_get_sequence_size(GTM_SeqInfo *seq)
+{
+ size_t len = 0;
+
+ len += sizeof(uint32);/* gs_key.gsk_keylen */
+ len += seq->gs_key->gsk_keylen; /* gs_key.gsk_key */
+ len += sizeof(GTM_SequenceKeyType); /* gs_key.gsk_type */
+ len += sizeof(GTM_Sequence); /* gs_value */
+ len += sizeof(GTM_Sequence); /* gs_init_value */
+ len += sizeof(GTM_Sequence); /* gs_last_value */
+ len += sizeof(GTM_Sequence); /* gs_increment_by */
+ len += sizeof(GTM_Sequence); /* gs_min_value */
+ len += sizeof(GTM_Sequence); /* gs_max_value */
+ len += sizeof(bool); /* gs_cycle */
+ len += sizeof(bool); /* gs_called */
+ len += sizeof(uint32); /* gs_ref_count */
+ len += sizeof(uint32); /* ge_state */
+
+ return len;
+}
+
+/*
+ * Return number of serialized sequence information
+ */
+size_t
+gtm_serialize_sequence(GTM_SeqInfo *s, char *buf, size_t buflen)
+{
+ size_t len = 0;
+
+ /* size check */
+ if (gtm_get_sequence_size(s) > buflen)
+ return 0;
+
+ memset(buf, 0, buflen);
+
+ memcpy(buf + len, &s->gs_key->gsk_keylen, sizeof(uint32));
+ len += sizeof(uint32);/* gs_key.gsk_keylen */
+
+ memcpy(buf + len, s->gs_key->gsk_key, s->gs_key->gsk_keylen);
+ len += s->gs_key->gsk_keylen; /* gs_key.gsk_key */
+
+ memcpy(buf + len, &s->gs_key->gsk_type, sizeof(GTM_SequenceKeyType));
+ len += sizeof(GTM_SequenceKeyType); /* gs_key.gsk_type */
+
+ memcpy(buf + len, &s->gs_value, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_value */
+
+ memcpy(buf + len, &s->gs_init_value, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_init_value */
+
+ memcpy(buf + len, &s->gs_last_value, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_last_value */
+
+ memcpy(buf + len, &s->gs_increment_by, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_increment_by */
+
+ memcpy(buf + len, &s->gs_min_value, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_min_value */
+
+ memcpy(buf + len, &s->gs_max_value, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_max_value */
+
+ memcpy(buf + len, &s->gs_cycle, sizeof(bool));
+ len += sizeof(bool); /* gs_cycle */
+
+ memcpy(buf + len, &s->gs_called, sizeof(bool));
+ len += sizeof(bool); /* gs_called */
+
+ memcpy(buf + len, &s->gs_ref_count, sizeof(uint32));
+ len += sizeof(uint32); /* gs_ref_count */
+
+ memcpy(buf + len, &s->gs_state, sizeof(uint32));
+ len += sizeof(uint32); /* gs_state */
+
+ return len;
+}
+
+/*
+ * Return number of deserialized sequence information
+ */
+GTM_SeqInfo *
+gtm_deserialize_sequence(const char *buf, size_t buflen)
+{
+ size_t len = 0;
+ GTM_SeqInfo *seq;
+
+ seq = (GTM_SeqInfo *)genAlloc0(sizeof(GTM_SeqInfo));
+ seq->gs_key = (GTM_SequenceKeyData *)genAlloc0(sizeof(GTM_SequenceKeyData));
+
+ memcpy(&seq->gs_key->gsk_keylen, buf + len, sizeof(uint32));
+ len += sizeof(uint32);/* gs_key.gsk_keylen */
+
+ seq->gs_key->gsk_key = (char *)genAlloc0(seq->gs_key->gsk_keylen+1);
+ memcpy(seq->gs_key->gsk_key, buf + len, seq->gs_key->gsk_keylen);
+ len += seq->gs_key->gsk_keylen;/* gs_key.gsk_key */
+
+ memcpy(&seq->gs_key->gsk_type, buf + len, sizeof(GTM_SequenceKeyType));
+ len += sizeof(GTM_SequenceKeyType); /* gs_key.gsk_type */
+
+ memcpy(&seq->gs_value, buf + len, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_value */
+
+ memcpy(&seq->gs_init_value, buf + len, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_init_value */
+
+ memcpy(&seq->gs_last_value, buf + len, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_last_value */
+
+ memcpy(&seq->gs_increment_by, buf + len, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_increment_by */
+
+ memcpy(&seq->gs_min_value, buf + len, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_min_value */
+
+ memcpy(&seq->gs_max_value, buf + len, sizeof(GTM_Sequence));
+ len += sizeof(GTM_Sequence); /* gs_max_value */
+
+ memcpy(&seq->gs_cycle, buf + len, sizeof(bool));
+ len += sizeof(bool); /* gs_cycle */
+
+ memcpy(&seq->gs_called, buf + len, sizeof(bool));
+ len += sizeof(bool); /* gs_called */
+
+ memcpy(&seq->gs_ref_count, buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+
+ memcpy(&seq->gs_state, buf + len, sizeof(uint32));
+ len += sizeof(uint32);
+
+ return seq;
+}
diff --git a/src/gtm/common/gtm_serialize_debug.c b/src/gtm/common/gtm_serialize_debug.c
new file mode 100644
index 0000000000..5af6403132
--- /dev/null
+++ b/src/gtm/common/gtm_serialize_debug.c
@@ -0,0 +1,94 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_serialize_debug.c
+ * Debug functionalities of serialization management
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ *
+ * IDENTIFICATION
+ * src/gtm/common/gtm_serialize_debug.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "gtm/gtm_c.h"
+#include "gtm/elog.h"
+#include "gtm/palloc.h"
+#include "gtm/gtm.h"
+#include "gtm/gtm_txn.h"
+#include "gtm/gtm_seq.h"
+#include "gtm/assert.h"
+#include "gtm/register.h"
+#include "gtm/stringinfo.h"
+#include "gtm/libpq.h"
+#include "gtm/pqformat.h"
+#include "gtm/gtm_msg.h"
+
+#include "gtm/gtm_serialize.h"
+
+void
+dump_transactioninfo_elog(GTM_TransactionInfo *txn)
+{
+ int ii;
+
+ elog(LOG, " ========= GTM_TransactionInfo =========");
+ elog(LOG, "gti_handle: %d", txn->gti_handle);
+ elog(LOG, "gti_thread_id: %ld", txn->gti_thread_id);
+ elog(LOG, "gti_in_use: %d", txn->gti_in_use);
+ elog(LOG, "gti_gxid: %d", txn->gti_gxid);
+ elog(LOG, "gti_state: %d", txn->gti_state);
+ elog(LOG, "gti_coordid: %d", txn->gti_coordid);
+ elog(LOG, "gti_xmin: %d", txn->gti_xmin);
+ elog(LOG, "gti_isolevel: %d", txn->gti_isolevel);
+ elog(LOG, "gti_readonly: %d", txn->gti_readonly);
+ elog(LOG, "gti_backend_id: %d", txn->gti_backend_id);
+ elog(LOG, "gti_datanodecount: %d", txn->gti_datanodecount);
+ elog(LOG, "gti_coordcount: %d", txn->gti_coordcount);
+ elog(LOG, "gti_gid: %s", txn->gti_gid);
+
+ elog(LOG, " sn_xmin: %d", txn->gti_current_snapshot.sn_xmin);
+ elog(LOG, " sn_xmax: %d", txn->gti_current_snapshot.sn_xmax);
+ elog(LOG, " sn_recent_global_xmin: %d", txn->gti_current_snapshot.sn_recent_global_xmin);
+ elog(LOG, " sn_xcnt: %d", txn->gti_current_snapshot.sn_xcnt);
+
+ /* Print all the GXIDs in snapshot */
+ for (ii = 0; ii < txn->gti_current_snapshot.sn_xcnt; ii++)
+ {
+ elog (LOG, " sn_xip[%d]: %d", ii, txn->gti_current_snapshot.sn_xip[ii]);
+ }
+
+ elog(LOG, "gti_snapshot_set: %d", txn->gti_snapshot_set);
+ elog(LOG, "gti_vacuum: %d", txn->gti_vacuum);
+ elog(LOG, " ========================================");
+}
+
+void
+dump_transactions_elog(GTM_Transactions *txn, int num_txn)
+{
+ int i;
+
+ elog(LOG, "============ GTM_Transactions ============");
+ elog(LOG, " gt_txn_count: %d", txn->gt_txn_count);
+ elog(LOG, " gt_XidGenLock: %p", &txn->gt_XidGenLock);
+ elog(LOG, " gt_nextXid: %d", txn->gt_nextXid);
+ elog(LOG, " gt_oldestXid: %d", txn->gt_oldestXid);
+ elog(LOG, " gt_xidVacLimit: %d", txn->gt_xidVacLimit);
+ elog(LOG, " gt_xidWarnLimit: %d", txn->gt_xidWarnLimit);
+ elog(LOG, " gt_xidStopLimit: %d", txn->gt_xidStopLimit);
+ elog(LOG, " gt_xidWrapLimit: %d", txn->gt_xidWrapLimit);
+ elog(LOG, " gt_latestCompletedXid: %d", txn->gt_latestCompletedXid);
+ elog(LOG, " gt_recent_global_xmin: %d", txn->gt_recent_global_xmin);
+ elog(LOG, " gt_lastslot: %d", txn->gt_lastslot);
+
+ for (i = 0; i < num_txn; i++)
+ {
+ if (txn->gt_transactions_array[i].gti_gxid != InvalidGlobalTransactionId)
+ dump_transactioninfo_elog(&txn->gt_transactions_array[i]);
+ }
+
+ elog(LOG, " gt_TransArrayLock: %p", &txn->gt_TransArrayLock);
+ elog(LOG, "==========================================");
+}
diff --git a/src/gtm/common/gtm_utils.c b/src/gtm/common/gtm_utils.c
new file mode 100644
index 0000000000..bce9bcb8a2
--- /dev/null
+++ b/src/gtm/common/gtm_utils.c
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_utils.c
+ * Utililies of GTM
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ *
+ * IDENTIFICATION
+ * src/gtm/common/gtm_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "gtm/gtm_utils.h"
+#include "gtm/elog.h"
+#include "gtm/gtm.h"
+
+/*
+ * gtm_report_failure() is an utility function to report fatal failure
+ * which occureed inside GTM to XCM, especially communication errors.
+ *
+ * failed_conn is null-able when failed to establish a connection
+ * with other node.
+ */
+#if 0
+/*
+ * PGXCTODO: This portion of code needs XCM support
+ * to be able to report GTM failures to XC watcher and
+ * enable a GTM reconnection kick.
+ */
+void
+gtm_report_failure(GTM_Conn *failed_conn)
+{
+ elog(LOG, "Calling report_xcwatch_gtm_failure()...");
+ return;
+}
+#endif
diff --git a/src/gtm/common/mcxt.c b/src/gtm/common/mcxt.c
index 1cb007982d..90c63f08f1 100644
--- a/src/gtm/common/mcxt.c
+++ b/src/gtm/common/mcxt.c
@@ -760,4 +760,13 @@ pgport_pfree(void *pointer)
pfree(pointer);
}
+
#endif
+
+#include "gen_alloc.h"
+void *current_memcontext(void);
+void *current_memcontext(void)
+{
+ return((void *)CurrentMemoryContext);
+}
+Gen_Alloc genAlloc_class = {MemoryContextAlloc, MemoryContextAllocZero, repalloc, pfree, current_memcontext};
diff --git a/src/gtm/common/stringinfo.c b/src/gtm/common/stringinfo.c
index 35e4cd87f1..821ca67640 100644
--- a/src/gtm/common/stringinfo.c
+++ b/src/gtm/common/stringinfo.c
@@ -41,6 +41,41 @@ makeStringInfo(void)
}
/*
+ * dupStringInfo
+ *
+ * Get new StringInfo and copy the original to it.
+ */
+StringInfo
+dupStringInfo(StringInfo orig)
+{
+ StringInfo new;
+
+ new = makeStringInfo();
+ if (!new)
+ return(new);
+
+ if (orig->len > 0)
+ {
+ appendBinaryStringInfo(new, orig->data, orig->len);
+ new->cursor = orig->cursor;
+ }
+ return(new);
+}
+
+/*
+ * copyStringInfo
+ * Deep copy: Data part is copied too. Cursor of the destination is
+ * initialized to zero.
+ */
+void
+copyStringInfo(StringInfo to, StringInfo from)
+{
+ resetStringInfo(to);
+ appendBinaryStringInfo(to, from->data, from->len);
+ return;
+}
+
+/*
* initStringInfo
*
* Initialize a StringInfoData struct (with previously undefined contents)
diff --git a/src/gtm/gtm_ctl/gtm_ctl.c b/src/gtm/gtm_ctl/gtm_ctl.c
index 6a87b88011..18fc3f9240 100644
--- a/src/gtm/gtm_ctl/gtm_ctl.c
+++ b/src/gtm/gtm_ctl/gtm_ctl.c
@@ -42,7 +42,9 @@ typedef enum
NO_COMMAND = 0,
START_COMMAND,
STOP_COMMAND,
+ PROMOTE_COMMAND,
RESTART_COMMAND,
+ STATUS_COMMAND
} CtlCommand;
#define DEFAULT_WAIT 60
@@ -92,7 +94,7 @@ static char gtmopts_file[MAXPGPATH];
static char pid_file[MAXPGPATH];
/*
- * Write errors to stderr (or by equal means when stderr is
+ * Write errors to stderr (or by gtm_equal means when stderr is
* not available).
*/
static void
@@ -299,7 +301,6 @@ start_gtm(void)
}
-
/*
* Find the pgport and try a connection
*/
@@ -540,6 +541,36 @@ do_stop(void)
}
}
+static void
+do_promote(void)
+{
+ pgpid_t pid;
+
+ pid = get_pgpid();
+
+ if (pid == 0) /* no pid file */
+ {
+ write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
+ write_stderr(_("Is server running?\n"));
+ exit(1);
+ }
+ else if (pid < 0) /* standalone backend, not gtm */
+ {
+ pid = -pid;
+ write_stderr(_("%s: cannot promote server; "
+ "single-user server is running (PID: %ld)\n"),
+ progname, pid);
+ exit(1);
+ }
+
+ if (kill((pid_t) pid, SIGUSR1) != 0)
+ {
+ write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"), progname, pid,
+ strerror(errno));
+ exit(1);
+ }
+}
+
/*
* restart/reload routines
@@ -621,6 +652,97 @@ do_restart(void)
}
+static void
+do_status(void)
+{
+ pgpid_t pid;
+ char datpath[MAXPGPATH];
+ int mode;
+ FILE *pidf;
+
+ /*
+ * Read a PID file to get GTM server status instead of attaching shared memory.
+ */
+ pidf = fopen(pid_file, "r");
+ if (pidf == NULL)
+ {
+ write_stderr(_("%s: could not open PID file \"%s\": %s\n"),
+ progname, pid_file, strerror(errno));
+ exit(1);
+ }
+
+ if (fscanf(pidf, "%ld", &pid) != 1)
+ {
+ write_stderr(_("%s: invalid data in PID file \"%s\"\n"),
+ progname, pid_file);
+ exit(1);
+ }
+
+ if (fscanf(pidf, "%s", datpath) != 1)
+ {
+ write_stderr(_("%s: invalid data in PID file \"%s\"\n"),
+ progname, pid_file);
+ exit(1);
+ }
+
+ if (fscanf(pidf, "%d", &mode) != 1)
+ {
+ write_stderr(_("%s: invalid data in PID file \"%s\"\n"),
+ progname, pid_file);
+ exit(1);
+ }
+
+ fclose(pidf);
+
+ printf("pid: %ld\n", pid);
+ printf("data: %s\n", datpath);
+ printf("active: %d\n", mode);
+
+#ifdef NOT_USED
+ pid = get_pgpid();
+
+ if (pid == 0) /* no pid file */
+ {
+ write_stderr(_("%s: PID file \"%s\" does not exist\n"),
+ progname, pid_file);
+ write_stderr(_("Is server running?\n"));
+ exit(1);
+ }
+ else if (pid < 0) /* standalone backend, not gtm */
+ {
+ pid = -pid;
+ if (gtm_is_alive((pid_t) pid))
+ {
+ write_stderr(_("%s: cannot get server status; "
+ "single-user server is running (PID: %ld)\n"),
+ progname, pid);
+ write_stderr(_("Please terminate the single-user server and try again.\n"));
+ exit(1);
+ }
+ }
+
+ if (!gtm_is_alive((pid_t) pid))
+ {
+ write_stderr(_("%s: old server process (PID: %ld) seems to be gone\n"),
+ progname, pid);
+ exit(1);
+ }
+
+ /*
+ * status check stuffs.
+ */
+ exitcode = check_gtm();
+ if (exitcode != 0)
+ {
+ write_stderr(_("%s: could not get server status: exit code was %d\n"),
+ progname, exitcode);
+ exit(1);
+ }
+#endif /* NOT_USED */
+}
+
+
+
/*
* utility routines
*/
@@ -660,19 +782,19 @@ static void
do_help(void)
{
printf(_("%s is a utility to start, stop or restart,\n"
- "a GTM server or GTM proxy.\n\n"), progname);
+ "a GTM server, a GTM standby or GTM proxy.\n\n"), progname);
printf(_("Usage:\n"));
- printf(_(" %s start -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname);
- printf(_(" %s stop -S STARTUP_MODE [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname);
- printf(_(" %s restart -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"
+ printf(_(" %s start -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname);
+ printf(_(" %s stop -S STARTUP_MODE [-W] [-t SECS] [-D DATADIR] [-m SHUTDOWN-MODE]\n"), progname);
+ printf(_(" %s promote -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR]\n"), progname);
+ printf(_(" %s restart -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR] [-m SHUTDOWN-MODE]\n"
" [-o \"OPTIONS\"]\n"), progname);
+ printf(_(" %s status -S STARTUP_MODE [-w] [-t SECS] [-D DATADIR]\n"), progname);
printf(_("\nCommon options:\n"));
printf(_(" -D DATADIR location of the database storage area\n"));
printf(_(" -i NODE_ID set gtm_proxy ID registered on GTM\n"));
printf(_(" (option ignored if used with GTM)\n"));
- printf(_(" -S set gtm or gtm_proxy to launch one of them\n"));
- printf(_(" -s, only print errors, no informational messages\n"));
printf(_(" -t SECS seconds to wait when using -w option\n"));
printf(_(" -w wait until operation completes\n"));
printf(_(" -W do not wait until operation completes\n"));
@@ -680,7 +802,7 @@ do_help(void)
printf(_("(The default is to wait for shutdown, but not for start or restart.)\n\n"));
printf(_("\nOptions for start or restart:\n"));
- printf(_(" -S STARTUP-MODE can be \"gtm\" or \"gtm_proxy\"\n"));
+ printf(_(" -S STARTUP-MODE can be \"gtm\", \"gtm_standby\" or \"gtm_proxy\"\n"));
printf(_(" -l FILENAME write (or append) server log to FILENAME\n"));
printf(_(" -o OPTIONS command line options to pass to gtm\n"
" (GTM server executable)\n"));
@@ -816,6 +938,14 @@ main(int argc, char **argv)
break;
case 'S':
gtm_app = xstrdup(optarg);
+ if (strcmp(gtm_app,"gtm_proxy") != 0
+ && strcmp(gtm_app,"gtm_standby") != 0
+ && strcmp(gtm_app,"gtm") != 0)
+ {
+ write_stderr(_("%s: %s launch name set not correct\n"), progname, gtm_app);
+ do_advice();
+ exit(1);
+ }
break;
case 't':
wait_seconds = atoi(optarg);
@@ -849,11 +979,16 @@ main(int argc, char **argv)
ctl_command = START_COMMAND;
else if (strcmp(argv[optind], "stop") == 0)
ctl_command = STOP_COMMAND;
+ else if (strcmp(argv[optind], "promote") == 0)
+ ctl_command = PROMOTE_COMMAND;
else if (strcmp(argv[optind], "restart") == 0)
ctl_command = RESTART_COMMAND;
+ else if (strcmp(argv[optind], "status") == 0)
+ ctl_command = STATUS_COMMAND;
else
{
- write_stderr(_("%s: unrecognized operation mode \"%s\"\n"), progname, argv[optind]);
+ write_stderr(_("%s: unrecognized operation mode \"%s\"\n"),
+ progname, argv[optind]);
do_advice();
exit(1);
}
@@ -931,6 +1066,8 @@ main(int argc, char **argv)
{
case RESTART_COMMAND:
case START_COMMAND:
+ case PROMOTE_COMMAND:
+ case STATUS_COMMAND:
do_wait = false;
break;
case STOP_COMMAND:
@@ -941,6 +1078,26 @@ main(int argc, char **argv)
}
}
+#if 0
+ if (gtm_data)
+ {
+ if (strcmp(gtm_app,"gtm_proxy") == 0)
+ {
+ snprintf(pid_file, MAXPGPATH, "%s/gtm_proxy.pid", gtm_data);
+ snprintf(gtmopts_file, MAXPGPATH, "%s/gtm_proxy.opts", gtm_data);
+ }
+ else if (strcmp(gtm_app,"gtm") == 0)
+ {
+ snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data);
+ snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data);
+ }
+ else if (strcmp(gtm_app,"gtm_standby") == 0)
+ {
+ snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data);
+ snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data);
+ }
+ }
+#else
/* Build strings for pid file and option file */
if (strcmp(gtm_app,"gtm_proxy") == 0)
{
@@ -952,6 +1109,15 @@ main(int argc, char **argv)
snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data);
snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data);
}
+ else if (strcmp(gtm_app,"gtm_standby") == 0)
+ {
+ snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data);
+ snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data);
+ }
+#endif
+
+ if (ctl_command==STATUS_COMMAND)
+ gtm_opts = xstrdup("-c");
switch (ctl_command)
{
@@ -961,9 +1127,15 @@ main(int argc, char **argv)
case STOP_COMMAND:
do_stop();
break;
+ case PROMOTE_COMMAND:
+ do_promote();
+ break;
case RESTART_COMMAND:
do_restart();
break;
+ case STATUS_COMMAND:
+ do_status();
+ break;
default:
break;
}
diff --git a/src/gtm/libpq/pqcomm.c b/src/gtm/libpq/pqcomm.c
index d7c3081864..9647b8488c 100644
--- a/src/gtm/libpq/pqcomm.c
+++ b/src/gtm/libpq/pqcomm.c
@@ -65,6 +65,8 @@
*------------------------
*/
+#include "pg_config.h"
+
#include <signal.h>
#include <fcntl.h>
#include <grp.h>
@@ -73,6 +75,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
+#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_TCP_H
@@ -344,7 +347,7 @@ StreamConnection(int server_fd, Port *port)
port->raddr.salen = sizeof(port->raddr.addr);
if ((port->sock = accept(server_fd,
(struct sockaddr *) & port->raddr.addr,
- &port->raddr.salen)) < 0)
+ (socklen_t *)&port->raddr.salen)) < 0)
{
ereport(LOG,
(EACCES,
@@ -375,7 +378,7 @@ StreamConnection(int server_fd, Port *port)
port->laddr.salen = sizeof(port->laddr.addr);
if (getsockname(port->sock,
(struct sockaddr *) & port->laddr.addr,
- &port->laddr.salen) < 0)
+ (socklen_t *)&port->laddr.salen) < 0)
{
elog(LOG, "getsockname() failed: %m");
return STATUS_ERROR;
@@ -597,6 +600,7 @@ pq_getbytes(Port *myport, char *s, size_t len)
return 0;
}
+#ifdef NOT_USED
/* --------------------------------
* pq_discardbytes - throw away a known number of bytes
*
@@ -626,6 +630,7 @@ pq_discardbytes(Port *myport, size_t len)
}
return 0;
}
+#endif /* NOT_USED */
/* --------------------------------
* pq_getstring - get a null terminated string from connection
diff --git a/src/gtm/main/Makefile b/src/gtm/main/Makefile
index bcdaf89061..d3afcca563 100644
--- a/src/gtm/main/Makefile
+++ b/src/gtm/main/Makefile
@@ -3,7 +3,7 @@
top_build_dir=../..
include $(top_build_dir)/gtm/Makefile.global
-OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o ../common/libgtm.a ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a
+OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o gtm_standby.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a
LDFLAGS=-L$(top_build_dir)/common -L$(top_build_dir)/libpq
LIBS=-lpthread
diff --git a/src/gtm/main/gtm_seq.c b/src/gtm/main/gtm_seq.c
index 35dc023ea0..2c2c7d6423 100644
--- a/src/gtm/main/gtm_seq.c
+++ b/src/gtm/main/gtm_seq.c
@@ -13,19 +13,22 @@
*
*-------------------------------------------------------------------------
*/
-#include "gtm/gtm_c.h"
+#include <unistd.h>
+
+#include "gtm/assert.h"
+#include "gtm/elog.h"
#include "gtm/gtm.h"
+#include "gtm/gtm_client.h"
#include "gtm/gtm_seq.h"
-#include "gtm/assert.h"
-#include "gtm/gtm_list.h"
+#include "gtm/gtm_serialize.h"
+#include "gtm/gtm_standby.h"
#include "gtm/libpq.h"
+#include "gtm/libpq-int.h"
#include "gtm/pqformat.h"
-#include "gtm/gtm_msg.h"
-#include <unistd.h>
typedef struct GTM_SeqInfoHashBucket
{
- List *shb_list;
+ gtm_List *shb_list;
GTM_RWLock shb_lock;
} GTM_SeqInfoHashBucket;
@@ -86,16 +89,16 @@ seq_find_seqinfo(GTM_SequenceKey seqkey)
{
uint32 hash = seq_gethash(seqkey);
GTM_SeqInfoHashBucket *bucket;
- ListCell *elem;
+ gtm_ListCell *elem;
GTM_SeqInfo *curr_seqinfo = NULL;
bucket = &GTMSequences[hash];
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ);
- foreach(elem, bucket->shb_list)
+ gtm_foreach(elem, bucket->shb_list)
{
- curr_seqinfo = (GTM_SeqInfo *) lfirst(elem);
+ curr_seqinfo = (GTM_SeqInfo *) gtm_lfirst(elem);
if (seq_keys_equal(curr_seqinfo->gs_key, seqkey))
break;
curr_seqinfo = NULL;
@@ -152,16 +155,16 @@ seq_add_seqinfo(GTM_SeqInfo *seqinfo)
{
uint32 hash = seq_gethash(seqinfo->gs_key);
GTM_SeqInfoHashBucket *bucket;
- ListCell *elem;
+ gtm_ListCell *elem;
bucket = &GTMSequences[hash];
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_WRITE);
- foreach(elem, bucket->shb_list)
+ gtm_foreach(elem, bucket->shb_list)
{
GTM_SeqInfo *curr_seqinfo = NULL;
- curr_seqinfo = (GTM_SeqInfo *) lfirst(elem);
+ curr_seqinfo = (GTM_SeqInfo *) gtm_lfirst(elem);
if (seq_keys_equal(curr_seqinfo->gs_key, seqinfo->gs_key))
{
@@ -176,7 +179,7 @@ seq_add_seqinfo(GTM_SeqInfo *seqinfo)
/*
* Safe to add the structure to the list
*/
- bucket->shb_list = lappend(bucket->shb_list, seqinfo);
+ bucket->shb_list = gtm_lappend(bucket->shb_list, seqinfo);
GTM_RWLockRelease(&bucket->shb_lock);
return 0;
@@ -206,7 +209,7 @@ seq_remove_seqinfo(GTM_SeqInfo *seqinfo)
return EBUSY;
}
- bucket->shb_list = list_delete(bucket->shb_list, seqinfo);
+ bucket->shb_list = gtm_list_delete(bucket->shb_list, seqinfo);
GTM_RWLockRelease(&seqinfo->gs_lock);
GTM_RWLockRelease(&bucket->shb_lock);
@@ -399,16 +402,16 @@ int GTM_SeqAlter(GTM_SequenceKey seqkey,
/*
* Restore a sequence.
*/
-static int
+int
GTM_SeqRestore(GTM_SequenceKey seqkey,
- GTM_Sequence increment_by,
- GTM_Sequence minval,
- GTM_Sequence maxval,
- GTM_Sequence startval,
- GTM_Sequence curval,
- int32 state,
- bool cycle,
- bool called)
+ GTM_Sequence increment_by,
+ GTM_Sequence minval,
+ GTM_Sequence maxval,
+ GTM_Sequence startval,
+ GTM_Sequence curval,
+ int32 state,
+ bool cycle,
+ bool called)
{
GTM_SeqInfo *seqinfo = NULL;
int errcode = 0;
@@ -515,7 +518,7 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp)
{
int ii = 0;
GTM_SeqInfoHashBucket *bucket;
- ListCell *cell, *prev;
+ gtm_ListCell *cell, *prev;
GTM_SeqInfo *curr_seqinfo = NULL;
int res = 0;
bool deleted;
@@ -527,10 +530,10 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp)
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ);
prev = NULL;
- cell = list_head(bucket->shb_list);
+ cell = gtm_list_head(bucket->shb_list);
while (cell != NULL)
{
- curr_seqinfo = (GTM_SeqInfo *) lfirst(cell);
+ curr_seqinfo = (GTM_SeqInfo *) gtm_lfirst(cell);
deleted = false;
if (seq_key_dbname_equal(nsp, curr_seqinfo->gs_key))
@@ -555,7 +558,7 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp)
{
/* Sequence is not is busy state, it can be deleted safely */
- bucket->shb_list = list_delete_cell(bucket->shb_list, cell, prev);
+ bucket->shb_list = gtm_list_delete_cell(bucket->shb_list, cell, prev);
elog(LOG, "Sequence %s was deleted from GTM",
curr_seqinfo->gs_key->gsk_key);
@@ -566,14 +569,14 @@ seq_drop_with_dbkey(GTM_SequenceKey nsp)
if (deleted)
{
if (prev)
- cell = lnext(prev);
+ cell = gtm_lnext(prev);
else
- cell = list_head(bucket->shb_list);
+ cell = gtm_list_head(bucket->shb_list);
}
else
{
prev = cell;
- cell = lnext(cell);
+ cell = gtm_lnext(cell);
}
}
GTM_RWLockRelease(&bucket->shb_lock);
@@ -636,7 +639,7 @@ GTM_SeqRename(GTM_SequenceKey seqkey, GTM_SequenceKey newseqkey)
/* Release first the structure as it has been taken previously */
seq_release_seqinfo(seqinfo);
- /* Close sequence properly, full name is here */
+ /* Close sequence properly, full name is here */
seqkey->gsk_type = GTM_SEQ_FULL_NAME;
/* Then close properly the old sequence */
GTM_SeqClose(seqkey);
@@ -682,7 +685,7 @@ GTM_SeqSetVal(GTM_SequenceKey seqkey, GTM_Sequence nextval, bool iscalled)
ereport(LOG,
(EINVAL,
errmsg("The sequence with the given key does not exist")));
- return EINVAL;
+ return InvalidSequenceValue;
}
GTM_RWLockAcquire(&seqinfo->gs_lock, GTM_LOCKMODE_WRITE);
@@ -820,7 +823,7 @@ GTM_InitSeqManager(void)
for (ii = 0; ii < SEQ_HASH_TABLE_SIZE; ii++)
{
- GTMSequences[ii].shb_list = NIL;
+ GTMSequences[ii].shb_list = gtm_NIL;
GTM_RWLockInit(&GTMSequences[ii].shb_lock);
}
}
@@ -866,13 +869,15 @@ ProcessSequenceInitCommand(Port *myport, StringInfo message)
*/
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
- if (GTM_SeqOpen(&seqkey, increment, minval, maxval, startval, cycle))
+ if ((errcode = GTM_SeqOpen(&seqkey, increment, minval, maxval, startval, cycle)))
ereport(ERROR,
(errcode,
errmsg("Failed to open a new sequence")));
MemoryContextSwitchTo(oldContext);
+ elog(LOG, "Opening sequence %s", seqkey.gsk_key);
+
pq_getmsgend(message);
/*
@@ -892,6 +897,31 @@ ProcessSequenceInitCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling open_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+ retry:
+ rc = open_sequence(GetMyThreadInfo->thr_conn->standby,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ cycle);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "open_sequence() returns rc %d.", rc);
+ }
+
+ /* FIXME: need to check errors */
}
/*
@@ -937,7 +967,9 @@ ProcessSequenceAlterCommand(Port *myport, StringInfo message)
*/
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
- if (GTM_SeqAlter(&seqkey, increment, minval, maxval, startval, lastval, cycle, is_restart))
+ elog(LOG, "Altering sequence key %s", seqkey.gsk_key);
+
+ if ((errcode = GTM_SeqAlter(&seqkey, increment, minval, maxval, startval, lastval, cycle, is_restart)))
ereport(ERROR,
(errcode,
errmsg("Failed to open a new sequence")));
@@ -960,6 +992,126 @@ ProcessSequenceAlterCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if ( GetMyThreadInfo->thr_conn->standby )
+ {
+ int rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling alter_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+ retry:
+ rc = alter_sequence(GetMyThreadInfo->thr_conn->standby,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ lastval,
+ cycle,
+ is_restart);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "alter_sequence() returns rc %d.", rc);
+ }
+
+ /* FIXME: need to check errors */
+}
+
+
+/*
+ * Process MSG_SEQUENCE_LIST message
+ */
+void
+ProcessSequenceListCommand(Port *myport, StringInfo message)
+{
+ StringInfoData buf;
+ int seq_count = 0;
+ MemoryContext oldContext;
+ GTM_SeqInfo *seq_list[1024]; /* FIXME: make it expandable. */
+ int i;
+
+ if (Recovery_IsStandby())
+ ereport(ERROR,
+ (EPERM,
+ errmsg("Operation not permitted under the standby mode.")));
+
+ memset(seq_list, 0, sizeof(GTM_SeqInfo *) * 1024);
+
+ /*
+ * We must use the TopMostMemoryContext because the sequence information is
+ * not bound to a thread and can outlive any of the thread specific
+ * contextes.
+ */
+ oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
+
+ /*
+ * Store pointers to all GTM_SeqInfo in the hash buckets into an array.
+ */
+ {
+ GTM_SeqInfoHashBucket *b;
+ gtm_ListCell *elem;
+
+ for (i = 0 ; i < SEQ_HASH_TABLE_SIZE ; i++)
+ {
+ b = &GTMSequences[i];
+
+ GTM_RWLockAcquire(&b->shb_lock, GTM_LOCKMODE_READ);
+
+ gtm_foreach(elem, b->shb_list)
+ {
+ seq_list[seq_count] = (GTM_SeqInfo *) gtm_lfirst(elem);
+ seq_count++;
+ }
+
+ GTM_RWLockRelease(&b->shb_lock);
+ }
+ }
+
+ MemoryContextSwitchTo(oldContext);
+
+ pq_getmsgend(message);
+
+ pq_beginmessage(&buf, 'S');
+ pq_sendint(&buf, SEQUENCE_LIST_RESULT, 4);
+
+ if (myport->remote_type == PGXC_NODE_GTM_PROXY)
+ {
+ GTM_ProxyMsgHeader proxyhdr;
+ proxyhdr.ph_conid = myport->conn_id;
+ pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader));
+ }
+
+ /* Send a number of sequences */
+ pq_sendint(&buf, seq_count, 4);
+
+ for (i = 0 ; i < seq_count ; i++)
+ {
+ char *seq_buf;
+ size_t seq_buflen;
+
+ seq_buflen = gtm_get_sequence_size(seq_list[i]);
+ seq_buf = (char *)malloc(seq_buflen);
+
+ gtm_serialize_sequence(seq_list[i], seq_buf, seq_buflen);
+
+ elog(LOG, "seq_buflen = %ld", seq_buflen);
+
+ pq_sendint(&buf, seq_buflen, 4);
+ pq_sendbytes(&buf, seq_buf, seq_buflen);
+
+ free(seq_buf);
+ }
+
+ pq_endmessage(myport, &buf);
+
+ elog(LOG, "ProcessSequenceListCommand() done.");
+
+ if (myport->remote_type != PGXC_NODE_GTM_PROXY)
+ pq_flush(myport);
}
@@ -982,6 +1134,8 @@ ProcessSequenceGetCurrentCommand(Port *myport, StringInfo message)
(ERANGE,
errmsg("Can not get current value of the sequence")));
+ elog(LOG, "Getting current value %ld for sequence %s", seqval, seqkey.gsk_key);
+
pq_beginmessage(&buf, 'S');
pq_sendint(&buf, SEQUENCE_GET_CURRENT_RESULT, 4);
if (myport->remote_type == PGXC_NODE_GTM_PROXY)
@@ -997,6 +1151,25 @@ ProcessSequenceGetCurrentCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ GTM_Sequence loc_seq;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling get_current() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ loc_seq = get_current(GetMyThreadInfo->thr_conn->standby, &seqkey);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "get_current() returns GTM_Sequence %ld.", loc_seq);
+ }
+
+ /* FIXME: need to check errors */
}
/*
@@ -1018,6 +1191,8 @@ ProcessSequenceGetNextCommand(Port *myport, StringInfo message)
(ERANGE,
errmsg("Can not get current value of the sequence")));
+ elog(LOG, "Getting next value %ld for sequence %s", seqval, seqkey.gsk_key);
+
pq_beginmessage(&buf, 'S');
pq_sendint(&buf, SEQUENCE_GET_NEXT_RESULT, 4);
if (myport->remote_type == PGXC_NODE_GTM_PROXY)
@@ -1033,6 +1208,24 @@ ProcessSequenceGetNextCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ GTM_Sequence loc_seq;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling get_next() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+ retry:
+ loc_seq = get_next(GetMyThreadInfo->thr_conn->standby, &seqkey);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "get_next() returns GTM_Sequence %ld.", loc_seq);
+ }
+ /* FIXME: need to check errors */
}
/*
@@ -1067,7 +1260,9 @@ ProcessSequenceSetValCommand(Port *myport, StringInfo message)
*/
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
- if (GTM_SeqSetVal(&seqkey, nextval, iscalled))
+ elog(LOG, "Setting new value %ld for sequence %s", nextval, seqkey.gsk_key);
+
+ if ((errcode = GTM_SeqSetVal(&seqkey, nextval, iscalled)))
ereport(ERROR,
(errcode,
errmsg("Failed to set values of sequence")));
@@ -1090,6 +1285,27 @@ ProcessSequenceSetValCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling set_val() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ rc = set_val(GetMyThreadInfo->thr_conn->standby,
+ &seqkey,
+ nextval,
+ iscalled);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "set_val() returns rc %d.", rc);
+ }
+ /* FIXME: need to check errors */
}
/*
@@ -1105,6 +1321,8 @@ ProcessSequenceResetCommand(Port *myport, StringInfo message)
seqkey.gsk_keylen = pq_getmsgint(message, sizeof (seqkey.gsk_keylen));
seqkey.gsk_key = (char *)pq_getmsgbytes(message, seqkey.gsk_keylen);
+ elog(LOG, "Resetting sequence %s", seqkey.gsk_key);
+
if ((errcode = GTM_SeqReset(&seqkey)))
ereport(ERROR,
(errcode,
@@ -1124,6 +1342,24 @@ ProcessSequenceResetCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling reset_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ rc = reset_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "reset_sequence() returns rc %d.", rc);
+ }
+ /* FIXME: need to check errors */
}
/*
@@ -1141,6 +1377,8 @@ ProcessSequenceCloseCommand(Port *myport, StringInfo message)
memcpy(&seqkey.gsk_type, pq_getmsgbytes(message, sizeof (GTM_SequenceKeyType)),
sizeof (GTM_SequenceKeyType));
+ elog(LOG, "Closing sequence %s", seqkey.gsk_key);
+
if ((errcode = GTM_SeqClose(&seqkey)))
ereport(ERROR,
(errcode,
@@ -1160,6 +1398,24 @@ ProcessSequenceCloseCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling close_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ rc = close_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "close_sequence() returns rc %d.", rc);
+ }
+ /* FIXME: need to check errors */
}
/*
@@ -1188,6 +1444,8 @@ ProcessSequenceRenameCommand(Port *myport, StringInfo message)
*/
oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
+ elog(LOG, "Renaming sequence %s to %s", seqkey.gsk_key, newseqkey.gsk_key);
+
if ((errcode = GTM_SeqRename(&seqkey, &newseqkey)))
ereport(ERROR,
(errcode,
@@ -1212,13 +1470,32 @@ ProcessSequenceRenameCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling rename_sequence() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ rc = rename_sequence(GetMyThreadInfo->thr_conn->standby, &seqkey, &newseqkey);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "rename_sequence() returns rc %d.", rc);
+ }
+
+ /* FIXME: need to check errors */
}
void
GTM_SaveSeqInfo(int ctlfd)
{
GTM_SeqInfoHashBucket *bucket;
- ListCell *elem;
+ gtm_ListCell *elem;
GTM_SeqInfo *seqinfo = NULL;
int hash;
@@ -1228,9 +1505,9 @@ GTM_SaveSeqInfo(int ctlfd)
GTM_RWLockAcquire(&bucket->shb_lock, GTM_LOCKMODE_READ);
- foreach(elem, bucket->shb_list)
+ gtm_foreach(elem, bucket->shb_list)
{
- seqinfo = (GTM_SeqInfo *) lfirst(elem);
+ seqinfo = (GTM_SeqInfo *) gtm_lfirst(elem);
if (seqinfo == NULL)
break;
@@ -1312,6 +1589,6 @@ GTM_RestoreSeqInfo(int ctlfd)
}
GTM_SeqRestore(&seqkey, increment_by, minval, maxval, startval, curval,
- state, cycle, called);
+ state, cycle, called);
}
}
diff --git a/src/gtm/main/gtm_snap.c b/src/gtm/main/gtm_snap.c
index 1308765dbc..5e8904fa5f 100644
--- a/src/gtm/main/gtm_snap.c
+++ b/src/gtm/main/gtm_snap.c
@@ -13,17 +13,15 @@
*
*-------------------------------------------------------------------------
*/
-#include "gtm/gtm_c.h"
+#include "gtm/assert.h"
#include "gtm/elog.h"
-#include "gtm/palloc.h"
#include "gtm/gtm.h"
-#include "gtm/gtm_txn.h"
-#include "gtm/assert.h"
+#include "gtm/gtm_client.h"
+#include "gtm/gtm_standby.h"
#include "gtm/stringinfo.h"
#include "gtm/libpq.h"
+#include "gtm/libpq-int.h"
#include "gtm/pqformat.h"
-#include "gtm/gtm_msg.h"
-
/*
* Get snapshot for the given transactions. If this is the first call in the
@@ -58,7 +56,7 @@ GTM_GetTransactionSnapshot(GTM_TransactionHandle handle[], int txn_count, int *s
GlobalTransactionId xmax;
GlobalTransactionId globalxmin;
int count = 0;
- ListCell *elem = NULL;
+ gtm_ListCell *elem = NULL;
int ii;
/*
@@ -126,9 +124,9 @@ GTM_GetTransactionSnapshot(GTM_TransactionHandle handle[], int txn_count, int *s
* Spin over transaction list checking xid, xmin, and subxids. The goal is to
* gather all active xids and find the lowest xmin
*/
- foreach(elem, GTMTransactions.gt_open_transactions)
+ gtm_foreach(elem, GTMTransactions.gt_open_transactions)
{
- volatile GTM_TransactionInfo *gtm_txninfo = (GTM_TransactionInfo *)lfirst(elem);
+ volatile GTM_TransactionInfo *gtm_txninfo = (GTM_TransactionInfo *)gtm_lfirst(elem);
GlobalTransactionId xid;
/* Don't take into account LAZY VACUUMs */
@@ -445,6 +443,32 @@ ProcessGetSnapshotCommandMulti(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ int txn_count_out;
+ int status_out[GTM_MAX_GLOBAL_TRANSACTIONS];
+ GlobalTransactionId xmin_out;
+ GlobalTransactionId xmax_out;
+ GlobalTransactionId recent_global_xmin_out;
+ int32 xcnt_out;
+
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+retry:
+ elog(LOG, "calling snapshot_get_multi() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+ _rc = snapshot_get_multi(GetMyThreadInfo->thr_conn->standby,
+ txn_count, gxid, &txn_count_out, status_out,
+ &xmin_out, &xmax_out, &recent_global_xmin_out, &xcnt_out);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "snapshot_get_multi() rc=%d done.", _rc);
+ }
+
return;
}
diff --git a/src/gtm/main/gtm_standby.c b/src/gtm/main/gtm_standby.c
new file mode 100644
index 0000000000..5b9fa420ae
--- /dev/null
+++ b/src/gtm/main/gtm_standby.c
@@ -0,0 +1,511 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_standby.c
+ * Functionalities of GTM Standby
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ *
+ * IDENTIFICATION
+ * src/gtm/common/gtm_standby.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "gtm/gtm_standby.h"
+
+#include "gtm/elog.h"
+#include "gtm/gtm.h"
+#include "gtm/gtm_c.h"
+#include "gtm/standby_utils.h"
+#include "gtm/gtm_client.h"
+#include "gtm/gtm_seq.h"
+#include "gtm/gtm_serialize.h"
+#include "gtm/gtm_utils.h"
+#include "gtm/register.h"
+
+static GTM_Conn *GTM_ActiveConn = NULL;
+static char standbyNodeName[NI_MAXHOST];
+static GTM_PGXCNodeId standbyNodeNum;
+static int standbyPortNumber;
+static char *standbyDataDir;
+
+static GTM_Conn *gtm_standby_connect_to_standby_int(int *);
+
+int
+gtm_standby_start_startup(void)
+{
+ char connect_string[1024];
+ int active_port = Recovery_StandbyGetActivePort();
+ char *active_address = Recovery_StandbyGetActiveAddress();
+
+ elog(LOG, "Connecting the GTM active on %s:%d...", active_address, active_port);
+
+ sprintf(connect_string, "host=%s port=%d pgxc_node_id=1 remote_type=%d",
+ active_address, active_port, PGXC_NODE_GTM);
+
+ GTM_ActiveConn = PQconnectGTM(connect_string);
+ if (GTM_ActiveConn == NULL)
+ {
+ elog(DEBUG3, "Error in connection");
+ return 0;
+ }
+ elog(LOG, "Connection established to the GTM active.");
+
+ /* Initialize standby lock */
+ Recovery_InitStandbyLock();
+
+ return 1;
+}
+
+int
+gtm_standby_finish_startup(void)
+{
+ elog(LOG, "Closing a startup connection...");
+
+ GTMPQfinish(GTM_ActiveConn);
+
+ elog(LOG, "A startup connection closed.");
+ return 1;
+}
+
+int
+gtm_standby_restore_next_gxid(void)
+{
+ GlobalTransactionId next_gxid = InvalidGlobalTransactionId;
+
+ next_gxid = get_next_gxid(GTM_ActiveConn);
+ GTM_RestoreTxnInfo(-1, next_gxid);
+
+ elog(LOG, "Restoring the next GXID done.");
+ return 1;
+}
+
+int
+gtm_standby_restore_sequence(void)
+{
+ GTM_SeqInfo *seq_list[1024];
+ int num_seq;
+ int i;
+
+ /*
+ * Restore sequence data.
+ */
+ num_seq = get_sequence_list(GTM_ActiveConn, seq_list, 1024);
+
+ for (i = 0; i < num_seq; i++)
+ {
+ GTM_SeqRestore(seq_list[i]->gs_key,
+ seq_list[i]->gs_increment_by,
+ seq_list[i]->gs_min_value,
+ seq_list[i]->gs_max_value,
+ seq_list[i]->gs_init_value,
+ seq_list[i]->gs_value,
+ seq_list[i]->gs_state,
+ seq_list[i]->gs_cycle,
+ seq_list[i]->gs_called);
+ }
+
+ elog(LOG, "Restoring sequences done.");
+ return 1;
+}
+
+int
+gtm_standby_restore_gxid(void)
+{
+ int num_txn;
+ GTM_Transactions txn;
+ int i;
+
+ /*
+ * Restore gxid data.
+ */
+ num_txn = get_txn_gxid_list(GTM_ActiveConn, &txn);
+
+ GTM_RWLockAcquire(&GTMTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE);
+
+ GTMTransactions.gt_txn_count = txn.gt_txn_count;
+ GTMTransactions.gt_gtm_state = txn.gt_gtm_state;
+ GTMTransactions.gt_nextXid = txn.gt_nextXid;
+ GTMTransactions.gt_oldestXid = txn.gt_oldestXid;
+ GTMTransactions.gt_xidVacLimit = txn.gt_xidVacLimit;
+ GTMTransactions.gt_xidWarnLimit = txn.gt_xidWarnLimit;
+ GTMTransactions.gt_xidStopLimit = txn.gt_xidStopLimit;
+ GTMTransactions.gt_xidWrapLimit = txn.gt_xidWrapLimit;
+ GTMTransactions.gt_latestCompletedXid = txn.gt_latestCompletedXid;
+ GTMTransactions.gt_recent_global_xmin = txn.gt_recent_global_xmin;
+ GTMTransactions.gt_lastslot = txn.gt_lastslot;
+
+ for (i = 0; i < num_txn; i++)
+ {
+ GTMTransactions.gt_transactions_array[i].gti_handle = txn.gt_transactions_array[i].gti_handle;
+ GTMTransactions.gt_transactions_array[i].gti_thread_id = txn.gt_transactions_array[i].gti_thread_id;
+ GTMTransactions.gt_transactions_array[i].gti_in_use = txn.gt_transactions_array[i].gti_in_use;
+ GTMTransactions.gt_transactions_array[i].gti_gxid = txn.gt_transactions_array[i].gti_gxid;
+ GTMTransactions.gt_transactions_array[i].gti_state = txn.gt_transactions_array[i].gti_state;
+ GTMTransactions.gt_transactions_array[i].gti_coordid = txn.gt_transactions_array[i].gti_coordid;
+ GTMTransactions.gt_transactions_array[i].gti_xmin = txn.gt_transactions_array[i].gti_xmin;
+ GTMTransactions.gt_transactions_array[i].gti_isolevel = txn.gt_transactions_array[i].gti_isolevel;
+ GTMTransactions.gt_transactions_array[i].gti_readonly = txn.gt_transactions_array[i].gti_readonly;
+ GTMTransactions.gt_transactions_array[i].gti_backend_id = txn.gt_transactions_array[i].gti_backend_id;
+
+ /* data node */
+ GTMTransactions.gt_transactions_array[i].gti_datanodecount = txn.gt_transactions_array[i].gti_datanodecount;
+ if (GTMTransactions.gt_transactions_array[i].gti_datanodecount > 0)
+ {
+ GTMTransactions.gt_transactions_array[i].gti_datanodes
+ = txn.gt_transactions_array[i].gti_datanodes;
+ }
+ else
+ {
+ GTMTransactions.gt_transactions_array[i].gti_datanodes = NULL;
+ }
+
+ /* coordinator node */
+ GTMTransactions.gt_transactions_array[i].gti_coordcount = txn.gt_transactions_array[i].gti_coordcount;
+ if (GTMTransactions.gt_transactions_array[i].gti_coordcount > 0)
+ {
+ GTMTransactions.gt_transactions_array[i].gti_coordinators = txn.gt_transactions_array[i].gti_coordinators;
+ }
+ else
+ {
+ GTMTransactions.gt_transactions_array[i].gti_coordinators = NULL;
+ }
+
+ if (txn.gt_transactions_array[i].gti_gid==NULL )
+ GTMTransactions.gt_transactions_array[i].gti_gid = NULL;
+ else
+ {
+ GTMTransactions.gt_transactions_array[i].gti_gid = txn.gt_transactions_array[i].gti_gid;
+ }
+
+ /* copy GTM_SnapshotData */
+ GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xmin =
+ txn.gt_transactions_array[i].gti_current_snapshot.sn_xmin;
+ GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xmax =
+ txn.gt_transactions_array[i].gti_current_snapshot.sn_xmax;
+ GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_recent_global_xmin =
+ txn.gt_transactions_array[i].gti_current_snapshot.sn_recent_global_xmin;
+ GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xcnt =
+ txn.gt_transactions_array[i].gti_current_snapshot.sn_xcnt;
+ GTMTransactions.gt_transactions_array[i].gti_current_snapshot.sn_xip =
+ txn.gt_transactions_array[i].gti_current_snapshot.sn_xip;
+ /* end of copying GTM_SnapshotData */
+
+ GTMTransactions.gt_transactions_array[i].gti_snapshot_set =
+ txn.gt_transactions_array[i].gti_snapshot_set;
+ GTMTransactions.gt_transactions_array[i].gti_vacuum =
+ txn.gt_transactions_array[i].gti_vacuum;
+
+ /*
+ * Is this correct? Is GTM_TXN_COMMITTED transaction categorized as "open"?
+ */
+ if (GTMTransactions.gt_transactions_array[i].gti_state != GTM_TXN_ABORTED)
+ {
+ GTMTransactions.gt_open_transactions =
+ gtm_lappend(GTMTransactions.gt_open_transactions,
+ &GTMTransactions.gt_transactions_array[i]);
+ }
+ }
+
+ dump_transactions_elog(&GTMTransactions, num_txn);
+
+ GTM_RWLockRelease(&GTMTransactions.gt_XidGenLock);
+
+ elog(LOG, "Restoring %d gxid(s) done.", num_txn);
+ return 1;
+}
+
+int
+gtm_standby_restore_node(void)
+{
+ GTM_PGXCNodeInfo *data;
+ int rc, i;
+ int num_node;
+
+ elog(LOG, "Copying node information from the GTM active...");
+
+ data = (GTM_PGXCNodeInfo *) malloc(sizeof(GTM_PGXCNodeInfo) * 128);
+ memset(data, 0, sizeof(GTM_PGXCNodeInfo) * 128);
+
+ rc = get_node_list(GTM_ActiveConn, data, 128);
+ if (rc < 0)
+ {
+ elog(DEBUG3, "get_node_list() failed.");
+ rc = 0;
+ goto finished;
+ }
+
+ num_node = rc;
+
+ for (i = 0; i < num_node; i++)
+ {
+ elog(LOG, "get_node_list: nodetype=%d, nodenum=%d, datafolder=%s",
+ data[i].type, data[i].nodenum, data[i].datafolder);
+ if (Recovery_PGXCNodeRegister(data[i].type, data[i].nodenum, data[i].port,
+ data[i].proxynum, data[i].status,
+ data[i].ipaddress, data[i].datafolder, true,
+ -1 /* dummy socket */) != 0)
+ {
+ rc = 0;
+ goto finished;
+ }
+ }
+
+ elog(LOG, "Copying node information from GTM active done.");
+
+finished:
+ free(data);
+ return rc;
+}
+
+/*
+ * Register myself to the GTM (active) as a "disconnected" node.
+ *
+ * This status would be updated later after restoring completion.
+ * See gtm_standby_update_self().
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int
+gtm_standby_register_self(GTM_PGXCNodeId nodenum, int port, const char *datadir)
+{
+ int rc;
+
+ elog(LOG, "Registering standby-GTM status...");
+
+ node_get_local_addr(GTM_ActiveConn, standbyNodeName, sizeof(standbyNodeName), &rc);
+ if (rc != 0)
+ return 0;
+
+ standbyNodeNum = nodenum;
+ standbyPortNumber = port;
+ standbyDataDir= (char *)datadir;
+
+ rc = node_register_internal(GTM_ActiveConn, PGXC_NODE_GTM, standbyNodeName, standbyPortNumber,
+ standbyNodeNum, standbyDataDir, NODE_DISCONNECTED);
+ if (rc < 0)
+ {
+ elog(LOG, "Failed to register a standby-GTM status.");
+ return 0;
+ }
+
+ elog(LOG, "Registering standby-GTM done.");
+
+ return 1;
+}
+
+/*
+ * Update my node status from "disconnected" to "connected" in GTM by myself.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int
+gtm_standby_activate_self(void)
+{
+ int rc;
+
+ elog(LOG, "Updating the standby-GTM status to \"CONNECTED\"...");
+
+ rc = node_unregister(GTM_ActiveConn, PGXC_NODE_GTM, standbyNodeNum);
+ if (rc < 0)
+ {
+ elog(LOG, "Failed to unregister old standby-GTM status.");
+ return 0;
+ }
+
+ rc = node_register_internal(GTM_ActiveConn, PGXC_NODE_GTM, standbyNodeName, standbyPortNumber,
+ standbyNodeNum, standbyDataDir, NODE_CONNECTED);
+
+ if (rc < 0)
+ {
+ elog(LOG, "Failed to register a new standby-GTM status.");
+ return 0;
+ }
+
+ elog(LOG, "Updating the standby-GTM status done.");
+
+ return 1;
+}
+
+
+/*
+ * Find "one" GTM standby node info.
+ *
+ * Returns a pointer to GTM_PGXCNodeInfo on success,
+ * or returns NULL on failure.
+ */
+static GTM_PGXCNodeInfo *
+find_standby_node_info(void)
+{
+ GTM_PGXCNodeInfo *node[1024];
+ size_t n;
+ int i;
+
+ n = pgxcnode_find_by_type(PGXC_NODE_GTM, node, 1024);
+
+ for (i = 0 ; i < n ; i++)
+ {
+ elog(LOG, "pgxcnode_find_by_type: nodenum=%d, type=%d, ipaddress=%s, port=%d, status=%d",
+ node[i]->nodenum,
+ node[i]->type,
+ node[i]->ipaddress,
+ node[i]->port,
+ node[i]->status);
+
+ if (node[i]->nodenum != standbyNodeNum &&
+ node[i]->status == NODE_CONNECTED)
+ return node[i];
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Make a connection to the GTM standby node when getting connected
+ * from the client.
+ *
+ * Returns a pointer to a GTM_Conn object on success, or NULL on failure.
+ */
+GTM_Conn *
+gtm_standby_connect_to_standby(void)
+{
+ GTM_Conn *conn;
+ int report;
+
+ conn = gtm_standby_connect_to_standby_int(&report);
+
+#if 0
+ /*
+ * PGXCTODO: This portion of code needs XCM support
+ * to be able to report GTM failures to XC watcher and
+ * enable a GTM reconnection kick.
+ */
+ if (!conn && report)
+ gtm_report_failure(NULL);
+#endif
+
+ return conn;
+}
+
+static GTM_Conn *
+gtm_standby_connect_to_standby_int(int *report_needed)
+{
+ GTM_Conn *standby = NULL;
+ GTM_PGXCNodeInfo *n;
+ char conn_string[1024];
+
+ *report_needed = 0;
+
+ if (Recovery_IsStandby())
+ return NULL;
+
+ n = find_standby_node_info();
+
+ if (!n)
+ {
+ elog(LOG, "Any GTM standby node not found in registered node(s).");
+ return NULL;
+ }
+
+ elog(LOG, "GTM standby is active. Going to connect.");
+ *report_needed = 1;
+
+ snprintf(conn_string, sizeof(conn_string),
+ "host=%s port=%d pgxc_node_id=1 remote_type=4",
+ n->ipaddress, n->port);
+
+ standby = PQconnectGTM(conn_string);
+
+ if ( !standby )
+ {
+ elog(LOG, "Failed to establish a connection with GTM standby. - %p", n);
+ return NULL;
+ }
+
+ elog(LOG, "Connection established with GTM standby. - %p", n);
+
+ return standby;
+}
+
+void
+gtm_standby_disconnect_from_standby(GTM_Conn *conn)
+{
+ if (Recovery_IsStandby())
+ return;
+
+ GTMPQfinish(conn);
+}
+
+
+GTM_Conn *
+gtm_standby_reconnect_to_standby(GTM_Conn *old_conn, int retry_max)
+{
+ GTM_Conn *newconn;
+ int report;
+ int i;
+
+ if (Recovery_IsStandby())
+ return NULL;
+
+ if (old_conn != NULL)
+ gtm_standby_disconnect_from_standby(old_conn);
+
+ for (i = 0; i < retry_max; i++)
+ {
+ elog(LOG, "gtm_standby_reconnect_to_standby(): going to re-connect. retry=%d", i);
+
+ newconn = gtm_standby_connect_to_standby_int(&report);
+ if (newconn != NULL)
+ break;
+
+ elog(LOG, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=%d", i);
+ }
+
+#if 0
+ /*
+ * PGXCTODO: This portion of code needs XCM support
+ * to be able to report GTM failures to XC watcher and
+ * enable a GTM reconnection kick.
+ */
+ if (newconn)
+ gtm_report_failure(NULL);
+#endif
+
+ return newconn;
+}
+
+
+#define GTM_STANDBY_RETRY_MAX 3
+
+bool
+gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn)
+{
+ if (GetMyThreadInfo->thr_conn->standby->result->gr_status == GTM_RESULT_COMM_ERROR)
+ {
+ if (*retry_count == 0)
+ {
+ (*retry_count)++;
+
+ GetMyThreadInfo->thr_conn->standby =
+ gtm_standby_reconnect_to_standby(GetMyThreadInfo->thr_conn->standby,
+ GTM_STANDBY_RETRY_MAX);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ return true;
+ }
+
+ elog(LOG, "communication error with standby.");
+#if 0
+ /*
+ * PGXCTODO: This portion of code needs XCM support
+ * to be able to report GTM failures to XC watcher and
+ * enable a GTM reconnection kick.
+ */
+ gtm_report_failure(oldconn);
+#endif
+ }
+ return false;
+}
diff --git a/src/gtm/main/gtm_thread.c b/src/gtm/main/gtm_thread.c
index fd49e852dd..c041645521 100644
--- a/src/gtm/main/gtm_thread.c
+++ b/src/gtm/main/gtm_thread.c
@@ -265,6 +265,17 @@ GTM_ThreadCleanup(void *argp)
elog(LOG, "Cleaning up thread state");
/*
+ * Close a connection to GTM standby.
+ */
+ if (thrinfo->thr_conn->standby)
+ {
+ elog(LOG, "Closing a connection to the GTM standby.");
+
+ GTMPQfinish(thrinfo->thr_conn->standby);
+ thrinfo->thr_conn->standby = NULL;
+ }
+
+ /*
* TODO Close the open connection.
*/
StreamClose(thrinfo->thr_conn->con_port->sock);
diff --git a/src/gtm/main/gtm_txn.c b/src/gtm/main/gtm_txn.c
index c16570a3c3..7d13076486 100644
--- a/src/gtm/main/gtm_txn.c
+++ b/src/gtm/main/gtm_txn.c
@@ -13,19 +13,19 @@
*
*-------------------------------------------------------------------------
*/
-#include "gtm/gtm_c.h"
+#include "gtm/gtm_txn.h"
+
+#include <unistd.h>
+#include "gtm/assert.h"
#include "gtm/elog.h"
-#include "gtm/palloc.h"
#include "gtm/gtm.h"
-#include "gtm/gtm_txn.h"
-#include "gtm/gtm_c.h"
#include "gtm/gtm_time.h"
-#include "gtm/assert.h"
-#include "gtm/stringinfo.h"
+#include "gtm/gtm_txn.h"
+#include "gtm/gtm_serialize.h"
+#include "gtm/gtm_standby.h"
#include "gtm/libpq.h"
+#include "gtm/libpq-int.h"
#include "gtm/pqformat.h"
-#include "gtm/gtm_msg.h"
-#include <unistd.h>
/* Local functions */
static XidStatus GlobalTransactionIdGetStatus(GlobalTransactionId transactionId);
@@ -54,7 +54,7 @@ GTM_InitTxnManager(void)
* permanent storage and read it back when it's restarted. It will get
* trickier for GTM failures.
*
- * TODO We skip thia part for the prototype.
+ * TODO We skip this part for the prototype.
*/
GTMTransactions.gt_nextXid = FirstNormalGlobalTransactionId;
@@ -65,7 +65,7 @@ GTM_InitTxnManager(void)
/*
* XXX Compute various xid limits to avoid wrap-around related database
- * corruptions. Again, this is not implemeneted for the prototype
+ * corruptions. Again, this is not implemented for the prototype
*/
GTMTransactions.gt_xidVacLimit = InvalidGlobalTransactionId;
GTMTransactions.gt_xidWarnLimit = InvalidGlobalTransactionId;
@@ -87,7 +87,7 @@ GTM_InitTxnManager(void)
/*
* Initialize the list
*/
- GTMTransactions.gt_open_transactions = NIL;
+ GTMTransactions.gt_open_transactions = gtm_NIL;
GTMTransactions.gt_lastslot = -1;
GTMTransactions.gt_gtm_state = GTM_STARTING;
@@ -101,7 +101,7 @@ GTM_InitTxnManager(void)
static XidStatus
GlobalTransactionIdGetStatus(GlobalTransactionId transactionId)
{
- XidStatus xidstatus;
+ XidStatus xidstatus = TRANSACTION_STATUS_IN_PROGRESS;
/*
* Also, check to see if the transaction ID is a permanent one.
@@ -116,8 +116,10 @@ GlobalTransactionIdGetStatus(GlobalTransactionId transactionId)
}
/*
- * TODO To be implemeneted
+ * TODO To be implemented
+ * This code is not completed yet and the latter code must not be reached.
*/
+ Assert(0);
return xidstatus;
}
@@ -127,14 +129,14 @@ GlobalTransactionIdGetStatus(GlobalTransactionId transactionId)
GTM_TransactionHandle
GTM_GXIDToHandle(GlobalTransactionId gxid)
{
- ListCell *elem = NULL;
+ gtm_ListCell *elem = NULL;
GTM_TransactionInfo *gtm_txninfo = NULL;
GTM_RWLockAcquire(&GTMTransactions.gt_TransArrayLock, GTM_LOCKMODE_READ);
- foreach(elem, GTMTransactions.gt_open_transactions)
+ gtm_foreach(elem, GTMTransactions.gt_open_transactions)
{
- gtm_txninfo = (GTM_TransactionInfo *)lfirst(elem);
+ gtm_txninfo = (GTM_TransactionInfo *)gtm_lfirst(elem);
if (GlobalTransactionIdEquals(gtm_txninfo->gti_gxid, gxid))
break;
gtm_txninfo = NULL;
@@ -155,14 +157,14 @@ GTM_GXIDToHandle(GlobalTransactionId gxid)
GTM_TransactionHandle
GTM_GIDToHandle(char *gid)
{
- ListCell *elem = NULL;
+ gtm_ListCell *elem = NULL;
GTM_TransactionInfo *gtm_txninfo = NULL;
GTM_RWLockAcquire(&GTMTransactions.gt_TransArrayLock, GTM_LOCKMODE_READ);
- foreach(elem, GTMTransactions.gt_open_transactions)
+ gtm_foreach(elem, GTMTransactions.gt_open_transactions)
{
- gtm_txninfo = (GTM_TransactionInfo *)lfirst(elem);
+ gtm_txninfo = (GTM_TransactionInfo *)gtm_lfirst(elem);
if (gtm_txninfo->gti_gid && strcmp(gid,gtm_txninfo->gti_gid) == 0)
break;
gtm_txninfo = NULL;
@@ -236,16 +238,16 @@ GTM_RemoveTransInfoMulti(GTM_TransactionInfo *gtm_txninfo[], int txn_count)
if (gtm_txninfo[ii] == NULL)
continue;
- GTMTransactions.gt_open_transactions = list_delete(GTMTransactions.gt_open_transactions, gtm_txninfo[ii]);
+ GTMTransactions.gt_open_transactions = gtm_list_delete(GTMTransactions.gt_open_transactions, gtm_txninfo[ii]);
if (GlobalTransactionIdIsNormal(gtm_txninfo[ii]->gti_gxid) &&
GlobalTransactionIdFollowsOrEquals(gtm_txninfo[ii]->gti_gxid,
GTMTransactions.gt_latestCompletedXid))
GTMTransactions.gt_latestCompletedXid = gtm_txninfo[ii]->gti_gxid;
-
elog(DEBUG1, "GTM_RemoveTransInfoMulti: removing transaction id %u, %lu",
gtm_txninfo[ii]->gti_gxid, gtm_txninfo[ii]->gti_thread_id);
+
/*
* Now mark the transaction as aborted and mark the structure as not-in-use
*/
@@ -286,7 +288,7 @@ GTM_RemoveTransInfoMulti(GTM_TransactionInfo *gtm_txninfo[], int txn_count)
void
GTM_RemoveAllTransInfos(int backend_id)
{
- ListCell *cell, *prev;
+ gtm_ListCell *cell, *prev;
GTM_ThreadID thread_id;
thread_id = pthread_self();
@@ -296,10 +298,10 @@ GTM_RemoveAllTransInfos(int backend_id)
*/
GTM_RWLockAcquire(&GTMTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE);
prev = NULL;
- cell = list_head(GTMTransactions.gt_open_transactions);
+ cell = gtm_list_head(GTMTransactions.gt_open_transactions);
while (cell != NULL)
{
- GTM_TransactionInfo *gtm_txninfo = lfirst(cell);
+ GTM_TransactionInfo *gtm_txninfo = gtm_lfirst(cell);
/*
* Check if current entry is associated with the thread
* A transaction in prepared state has to be kept alive in the structure.
@@ -312,7 +314,7 @@ GTM_RemoveAllTransInfos(int backend_id)
((gtm_txninfo->gti_backend_id == backend_id) || (backend_id == -1)))
{
/* remove the entry */
- GTMTransactions.gt_open_transactions = list_delete_cell(GTMTransactions.gt_open_transactions, cell, prev);
+ GTMTransactions.gt_open_transactions = gtm_list_delete_cell(GTMTransactions.gt_open_transactions, cell, prev);
/* update the latestCompletedXid */
if (GlobalTransactionIdIsNormal(gtm_txninfo->gti_gxid) &&
@@ -349,14 +351,14 @@ GTM_RemoveAllTransInfos(int backend_id)
/* move to next cell in the list */
if (prev)
- cell = lnext(prev);
+ cell = gtm_lnext(prev);
else
- cell = list_head(GTMTransactions.gt_open_transactions);
+ cell = gtm_list_head(GTMTransactions.gt_open_transactions);
}
else
{
prev = cell;
- cell = lnext(cell);
+ cell = gtm_lnext(cell);
}
}
@@ -717,7 +719,7 @@ GTM_BeginTransactionMulti(GTM_PGXCNodeId coord_id,
* because the list is global and any memory allocation must outlive the
* thread context
*/
- GTMTransactions.gt_open_transactions = lappend(GTMTransactions.gt_open_transactions, gtm_txninfo[kk]);
+ GTMTransactions.gt_open_transactions = gtm_lappend(GTMTransactions.gt_open_transactions, gtm_txninfo[kk]);
}
GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
@@ -894,6 +896,12 @@ GTM_StartPreparedTransaction(GTM_TransactionHandle txn,
return STATUS_ERROR;
/*
+ * Check if given GID is already in use by another transaction.
+ */
+ if (GTM_GIDToHandle(gid) != InvalidTransactionHandle)
+ return STATUS_ERROR;
+
+ /*
* Mark the transaction as being prepared
*/
GTM_RWLockAcquire(&gtm_txninfo->gti_lock, GTM_LOCKMODE_WRITE);
@@ -1107,6 +1115,25 @@ ProcessBeginTransactionGetGXIDCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ GTM_Timestamp _ts;
+ GlobalTransactionId _gxid;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling begin_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _gxid = begin_transaction(GetMyThreadInfo->thr_conn->standby, txn_isolation_level, &_ts);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "begin_transaction() returns GXID %d.", _gxid);
+ }
+
return;
}
@@ -1167,6 +1194,26 @@ ProcessBeginTransactionGetGXIDAutovacuumCommand(Port *myport, StringInfo message
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ GlobalTransactionId _gxid;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling begin_transaction_autovacuum() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+ retry:
+ _gxid = begin_transaction_autovacuum(GetMyThreadInfo->thr_conn->standby,
+ txn_isolation_level);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "begin_transaction_autovacuum() GXID=%d done.", _gxid);
+ }
+
return;
}
@@ -1246,6 +1293,35 @@ ProcessBeginTransactionGetGXIDCommandMulti(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ int txn_count_out;
+ GlobalTransactionId gxid_out;
+ GTM_Timestamp ts_out;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling begin_transaction_multi() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = begin_transaction_multi(GetMyThreadInfo->thr_conn->standby,
+ txn_count,
+ txn_isolation_level,
+ txn_read_only,
+ txn_connid,
+ &txn_count_out,
+ &gxid_out,
+ &ts_out);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "begin_transaction_multi() rc=%d done.", _rc);
+ }
+
return;
}
@@ -1287,6 +1363,8 @@ ProcessCommitTransactionCommand(Port *myport, StringInfo message)
pq_getmsgend(message);
oldContext = MemoryContextSwitchTo(TopMemoryContext);
+
+ elog(LOG, "Committing transaction id %u", gxid);
/*
* Commit the transaction
@@ -1309,6 +1387,24 @@ ProcessCommitTransactionCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling commit_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = commit_transaction(GetMyThreadInfo->thr_conn->standby, gxid);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "commit_transaction() rc=%d done.", _rc);
+ }
+
return;
}
@@ -1380,6 +1476,26 @@ ProcessCommitPreparedTransactionCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling commit_prepared_transaction() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = commit_prepared_transaction(GetMyThreadInfo->thr_conn->standby,
+ gxid[0], gxid[1] /* prepared GXID */);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "commit_prepared_transaction() rc=%d done.", _rc);
+ }
+
return;
}
@@ -1397,11 +1513,10 @@ void
ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message)
{
StringInfoData buf;
- char *gid;
+ char gid[1024];
int gidlen;
GTM_IsolationLevel txn_isolation_level;
bool txn_read_only;
- MemoryContext oldContext;
GTM_TransactionHandle txn, prepared_txn;
/* Data to be sent back to client */
GlobalTransactionId gxid, prepared_gxid;
@@ -1415,7 +1530,8 @@ ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message)
/* receive GID */
gidlen = pq_getmsgint(message, sizeof (GTM_StrLen));
- gid = (char *)pq_getmsgbytes(message, gidlen);
+ memcpy(gid, (char *)pq_getmsgbytes(message, gidlen), gidlen);
+ gid[gidlen] = '\0';
pq_getmsgend(message);
@@ -1476,9 +1592,101 @@ ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling get_gid_data() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = get_gid_data(GetMyThreadInfo->thr_conn->standby,
+ txn_isolation_level,
+ gid,
+ &gxid,
+ &prepared_gxid,
+ &datanodecnt,
+ &datanodes,
+ &coordcnt,
+ &coordinators);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "get_gid_data() rc=%d done.", _rc);
+ }
+
+ return;
+}
+
+/*
+ * Process MSG_TXN_GXID_LIST
+ */
+void
+ProcessGXIDListCommand(Port *myport, StringInfo message)
+{
+ MemoryContext oldContext;
+ StringInfoData buf;
+ char *data;
+ size_t estlen, actlen; /* estimated length and actual length */
+
+ pq_getmsgend(message);
+
+ if (Recovery_IsStandby())
+ ereport(ERROR,
+ (EPERM,
+ errmsg("Operation not permitted under the standby mode.")));
+
+ /*
+ * Do something here.
+ */
+ oldContext = MemoryContextSwitchTo(TopMemoryContext);
+
+ GTM_RWLockAcquire(&GTMTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE);
+
+ estlen = gtm_get_transactions_size(&GTMTransactions);
+ data = malloc(estlen+1);
+
+ actlen = gtm_serialize_transactions(&GTMTransactions, data, estlen);
+
+ elog(LOG, "gtm_serialize_transactions: estlen=%ld, actlen=%ld", estlen, actlen);
+
+ GTM_RWLockRelease(&GTMTransactions.gt_XidGenLock);
+
+ MemoryContextSwitchTo(oldContext);
+
+ /*
+ * Send a SUCCESS message back to the client
+ */
+ pq_beginmessage(&buf, 'S');
+ pq_sendint(&buf, TXN_GXID_LIST_RESULT, 4);
+ if (myport->remote_type == PGXC_NODE_GTM_PROXY)
+ {
+ GTM_ProxyMsgHeader proxyhdr;
+ proxyhdr.ph_conid = myport->conn_id;
+ pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader));
+ }
+
+ pq_sendint(&buf, actlen, sizeof(int32)); /* size of serialized GTM_Transactions */
+ pq_sendbytes(&buf, data, actlen); /* serialized GTM_Transactions */
+ pq_endmessage(myport, &buf);
+
+ if (myport->remote_type != PGXC_NODE_GTM_PROXY)
+ {
+ pq_flush(myport);
+ elog(LOG, "pq_flush()");
+ }
+
+ elog(LOG, "ProcessGXIDListCommand() ok. %ld bytes sent. len=%d", actlen, buf.len);
+ free(data);
+
return;
}
+
/*
* Process MSG_TXN_ROLLBACK message
*/
@@ -1518,6 +1726,8 @@ ProcessRollbackTransactionCommand(Port *myport, StringInfo message)
oldContext = MemoryContextSwitchTo(TopMemoryContext);
+ elog(LOG, "Cancelling transaction id %u", gxid);
+
/*
* Commit the transaction
*/
@@ -1539,6 +1749,23 @@ ProcessRollbackTransactionCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling abort_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ abort_transaction(GetMyThreadInfo->thr_conn->standby, gxid);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "abort_transaction() GXID=%d done.", gxid);
+ }
+
return;
}
@@ -1611,6 +1838,28 @@ ProcessCommitTransactionCommandMulti(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ int txn_count_out;
+ int status_out[GTM_MAX_GLOBAL_TRANSACTIONS];
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling commit_transaction_multi() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = commit_transaction_multi(GetMyThreadInfo->thr_conn->standby,
+ txn_count, gxid, &txn_count_out, status_out);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "commit_transaction_multi() rc=%d done.", _rc);
+ }
+
return;
}
@@ -1682,6 +1931,28 @@ ProcessRollbackTransactionCommandMulti(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ int txn_count_out;
+ int status_out[GTM_MAX_GLOBAL_TRANSACTIONS];
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling abort_transaction_multi() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = abort_transaction_multi(GetMyThreadInfo->thr_conn->standby,
+ txn_count, gxid, &txn_count_out, status_out);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "abort_transaction_multi() rc=%d done.", _rc);
+ }
+
return;
}
@@ -1700,7 +1971,7 @@ ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message)
PGXC_NodeId *coordinators = NULL;
PGXC_NodeId *datanodes = NULL;
MemoryContext oldContext;
- char *gid;
+ char gid[1024];
isgxid = pq_getmsgbyte(message);
@@ -1726,7 +1997,8 @@ ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message)
/* get GID */
gidlen = pq_getmsgint(message, sizeof (GTM_StrLen));
- gid = (char *)pq_getmsgbytes(message, gidlen);
+ memcpy(gid, (char *)pq_getmsgbytes(message, gidlen), gidlen);
+ gid[gidlen] = '\0';
/* Get Datanode Count Data */
datanodecnt = pq_getmsgint(message, 4);
@@ -1780,6 +2052,28 @@ ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling start_prepared_transaction() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+ retry:
+ _rc = start_prepared_transaction(GetMyThreadInfo->thr_conn->standby,
+ gxid, gid,
+ datanodecnt, datanodes,
+ coordcnt, coordinators);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "start_prepared_transaction() rc=%d done.", _rc);
+ }
+
return;
}
@@ -1829,6 +2123,8 @@ ProcessPrepareTransactionCommand(Port *myport, StringInfo message)
MemoryContextSwitchTo(oldContext);
+ elog(LOG, "Preparing transaction id %u", gxid);
+
pq_beginmessage(&buf, 'S');
pq_sendint(&buf, TXN_PREPARE_RESULT, 4);
if (myport->remote_type == PGXC_NODE_GTM_PROXY)
@@ -1842,6 +2138,23 @@ ProcessPrepareTransactionCommand(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling prepare_transaction() for standby GTM %p.", GetMyThreadInfo->thr_conn->standby);
+
+ retry:
+ prepare_transaction(GetMyThreadInfo->thr_conn->standby, gxid);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "prepare_transaction() GXID=%d done.", gxid);
+ }
+
return;
}
@@ -1903,6 +2216,50 @@ ProcessGetGXIDTransactionCommand(Port *myport, StringInfo message)
/*
+ * Process MSG_TXN_GET_NEXT_GXID message
+ */
+void
+ProcessGetNextGXIDTransactionCommand(Port *myport, StringInfo message)
+{
+ StringInfoData buf;
+ GlobalTransactionId next_gxid;
+ MemoryContext oldContext;
+
+ elog(DEBUG3, "Inside ProcessGetNextGXIDTransactionCommand");
+
+ pq_getmsgend(message);
+
+ oldContext = MemoryContextSwitchTo(TopMemoryContext);
+
+ /*
+ * Get the next gxid.
+ */
+ GTM_RWLockAcquire(&GTMTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE);
+ next_gxid = GTMTransactions.gt_nextXid;
+ GTM_RWLockRelease(&GTMTransactions.gt_XidGenLock);
+
+ MemoryContextSwitchTo(oldContext);
+
+ elog(DEBUG3, "Sending next gxid %d", next_gxid);
+
+ pq_beginmessage(&buf, 'S');
+ pq_sendint(&buf, TXN_GET_NEXT_GXID_RESULT, 4);
+ if (myport->remote_type == PGXC_NODE_GTM_PROXY)
+ {
+ GTM_ProxyMsgHeader proxyhdr;
+ proxyhdr.ph_conid = myport->conn_id;
+ pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader));
+ }
+ pq_sendint(&buf, next_gxid, sizeof(GlobalTransactionId));
+ pq_endmessage(myport, &buf);
+
+ if (myport->remote_type != PGXC_NODE_GTM_PROXY)
+ pq_flush(myport);
+ return;
+}
+
+
+/*
* Mark GTM as shutting down. This point onwards no new GXID are issued to
* ensure that the last GXID recorded in the control file remains sane
*/
diff --git a/src/gtm/main/main.c b/src/gtm/main/main.c
index 9bfa9da475..b71bec7e5b 100644
--- a/src/gtm/main/main.c
+++ b/src/gtm/main/main.c
@@ -25,17 +25,22 @@
#include <stdio.h>
#include "gtm/gtm_c.h"
-#include "gtm/gtm.h"
#include "gtm/path.h"
+#include "gtm/gtm.h"
#include "gtm/elog.h"
#include "gtm/memutils.h"
#include "gtm/gtm_list.h"
+#include "gtm/gtm_seq.h"
+#include "gtm/standby_utils.h"
+#include "gtm/gtm_standby.h"
#include "gtm/libpq.h"
+#include "gtm/libpq-fe.h"
#include "gtm/libpq-be.h"
#include "gtm/pqsignal.h"
#include "gtm/pqformat.h"
#include "gtm/assert.h"
#include "gtm/register.h"
+#include "gtm/replication.h"
#include "gtm/gtm_txn.h"
#include "gtm/gtm_seq.h"
#include "gtm/gtm_msg.h"
@@ -69,7 +74,7 @@ static Port *ConnCreate(int serverFd);
static int ServerLoop(void);
static int initMasks(fd_set *rmask);
void *GTM_ThreadMain(void *argp);
-static int GTMAddConnection(Port *port);
+static int GTMAddConnection(Port *port, GTM_Conn *standby);
static int ReadCommand(Port *myport, StringInfo inBuf);
static void ProcessCommand(Port *myport, StringInfo input_message);
@@ -80,7 +85,6 @@ static void ProcessSequenceCommand(Port *myport, GTM_MessageType mtype, StringIn
static void ProcessQueryCommand(Port *myport, GTM_MessageType mtype, StringInfo message);
static void GTM_RegisterPGXCNode(Port *myport, GTM_PGXCNodeId pgxc_node_id);
-static void GTM_UnregisterPGXCNode(Port *myport, GTM_PGXCNodeId pgxc_node_id);
static bool CreateOptsFile(int argc, char *argv[]);
static void CreateDataDirLockFile(void);
@@ -89,6 +93,7 @@ static void SetDataDir(void);
static void ChangeToDataDir(void);
static void checkDataDir(void);
static void DeleteLockFile(const char *filename);
+static void PromoteToActive(void);
/*
* One-time initialization. It's called immediately after the main process
@@ -190,6 +195,10 @@ GTM_SigleHandler(int signal)
case SIGHUP:
break;
+ case SIGUSR1:
+ PromoteToActive();
+ return;
+
default:
fprintf(stderr, "Unknown signal %d\n", signal);
return;
@@ -220,12 +229,27 @@ help(const char *progname)
printf(_("This is the GTM server.\n\n"));
printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
printf(_("Options:\n"));
- printf(_(" -h hostname GTM server hostname/IP\n"));
- printf(_(" -p port GTM server port number\n"));
- printf(_(" -x xid Starting GXID \n"));
- printf(_(" -D directory GTM working directory\n"));
- printf(_(" -l filename GTM server log file name \n"));
+ printf(_(" -h hostname GTM server hostname/IP to listen.\n"));
+ printf(_(" -p port GTM server port number to listen.\n"));
+ printf(_(" -n nodenum Node number for GTM server.\n"));
+ printf(_(" -x xid Starting GXID \n"));
+ printf(_(" -D directory GTM working directory\n"));
+ printf(_(" -l filename GTM server log file name \n"));
+ printf(_(" -c show server status, then exit\n"));
printf(_(" --help show this help, then exit\n"));
+ printf(_("\n"));
+ printf(_("Options for Standby mode:\n"));
+ printf(_(" -s Start as a GTM standby server.\n"));
+ printf(_(" -i hostname Active GTM server hostname/IP to connect.\n"));
+ printf(_(" -q port Active GTM server port number to connect.\n"));
+ printf(_("\n"));
+}
+
+static void
+gtm_status()
+{
+ fprintf(stderr, "gtm_status(): must be implemented to scan the shmem.\n");
+ exit(0);
}
int
@@ -236,6 +260,9 @@ main(int argc, char *argv[])
int i;
GlobalTransactionId next_gxid = InvalidGlobalTransactionId;
int ctlfd;
+ char *active_addr;
+ int active_port;
+ GTM_PGXCNodeId node_num = 1001;
/*
* Catch standard options before doing much else
@@ -255,20 +282,28 @@ main(int argc, char *argv[])
/*
* Parse the command like options and set variables
*/
- while ((opt = getopt(argc, argv, "h:p:x:D:l:")) != -1)
+ while ((opt = getopt(argc, argv, "ch:n:p:x:D:l:si:q:")) != -1)
{
switch (opt)
{
+ case 'c':
+ gtm_status();
+ break; /* never reach here. */
+
case 'h':
ListenAddresses = strdup(optarg);
break;
+ case 'n':
+ node_num = atoi(optarg);
+ break;
+
case 'p':
GTMPortNumber = atoi(optarg);
break;
case 'x':
- next_gxid = (GlobalTransactionId )atoll(optarg);
+ next_gxid = (GlobalTransactionId)atoll(optarg);
break;
case 'D':
@@ -280,12 +315,37 @@ main(int argc, char *argv[])
GTMLogFile = strdup(optarg);
break;
+ case 's':
+ Recovery_StandbySetStandby(true);
+ break;
+
+ case 'i':
+ active_addr = strdup(optarg);
+ break;
+
+ case 'q':
+ active_port = atoi(optarg);
+ break;
+
default:
write_stderr("Try \"%s --help\" for more information.\n",
progname);
}
}
+ /*
+ * Check options for the standby mode.
+ */
+ if (Recovery_IsStandby())
+ {
+ if (active_addr == NULL || active_port < 1)
+ {
+ help(argv[0]);
+ exit(1);
+ }
+ Recovery_StandbySetConnInfo(active_addr, active_port);
+ }
+
if (GTMDataDir == NULL)
{
write_stderr("GTM data directory must be specified\n");
@@ -311,21 +371,80 @@ main(int argc, char *argv[])
*/
BaseInit();
+ /*
+ * Establish a connection between the active and standby.
+ */
+ if (Recovery_IsStandby())
+ {
+ if (!gtm_standby_start_startup())
+ {
+ elog(ERROR, "Failed to establish a connection to active-GTM.");
+ exit(1);
+ }
+ elog(LOG, "Startup connection established with active-GTM.");
+ }
+
elog(DEBUG3, "Starting GTM server at (%s:%d) -- control file %s", ListenAddresses, GTMPortNumber, GTMControlFile);
/*
* Read the last GXID and start from there
*/
+ if (Recovery_IsStandby())
+ {
+ if (!gtm_standby_restore_next_gxid())
+ {
+ elog(ERROR, "Failed to restore next/last gxid from the active-GTM.");
+ exit(1);
+ }
+ elog(LOG, "Restoring next/last gxid from the active-GTM succeeded.");
- ctlfd = open(GTMControlFile, O_RDONLY);
+ if (!gtm_standby_restore_gxid())
+ {
+ elog(ERROR, "Failed to restore all of gxid(s) from the active-GTM.");
+ exit(1);
+ }
+ elog(LOG, "Restoring all of gxid(s) from the active-GTM succeeded.");
- GTM_RestoreTxnInfo(ctlfd, next_gxid);
- GTM_RestoreSeqInfo(ctlfd);
+ if (!gtm_standby_restore_sequence())
+ {
+ elog(ERROR, "Failed to restore sequences from the active-GTM.");
+ exit(1);
+ }
+ elog(LOG, "Restoring sequences from the active-GTM succeeded.");
+ }
+ else
+ {
+ ctlfd = open(GTMControlFile, O_RDONLY);
+ GTM_RestoreTxnInfo(ctlfd, next_gxid);
+ GTM_RestoreSeqInfo(ctlfd);
+ close(ctlfd);
+ }
- close(ctlfd);
+ if (Recovery_IsStandby())
+ {
+ if (!gtm_standby_register_self(node_num, GTMPortNumber, GTMDataDir))
+ {
+ elog(ERROR, "Failed to register myself on the active-GTM as a GTM node.");
+ exit(1);
+ }
+ elog(LOG, "Registering myself to the active-GTM as a GTM node succeeded.");
+ }
/* Recover Data of Registered nodes. */
- Recovery_RestoreRegisterInfo();
+ if (Recovery_IsStandby())
+ {
+ if (!gtm_standby_restore_node())
+ {
+ elog(ERROR, "Failed to restore node information from the active-GTM.");
+ exit(1);
+ }
+ elog(LOG, "Restoring node information from the active-GTM succeeded.");
+ }
+ else
+ {
+ /* Recover Data of Registered nodes. */
+ Recovery_RestoreRegisterInfo();
+ }
/*
* Establish input sockets.
@@ -367,8 +486,28 @@ main(int argc, char *argv[])
pqsignal(SIGQUIT, GTM_SigleHandler);
pqsignal(SIGTERM, GTM_SigleHandler);
pqsignal(SIGINT, GTM_SigleHandler);
+ pqsignal(SIGUSR1, GTM_SigleHandler);
pqinitmask();
+
+ /*
+ * Now, activating a standby GTM...
+ */
+ if (Recovery_IsStandby())
+ {
+ if (!gtm_standby_activate_self())
+ {
+ elog(ERROR, "Failed to update the standby-GTM status as \"CONNECTED\".");
+ exit(1);
+ }
+ elog(LOG, "Updating the standby-GTM status as \"CONNECTED\" succeeded.");
+ if (!gtm_standby_finish_startup())
+ {
+ elog(ERROR, "Failed to close the initial connection to the active-GTM.");
+ exit(1);
+ }
+ elog(LOG, "Startup connection with the active-GTM closed.");
+ }
/*
* Accept any new connections. Fork a new thread for each incoming
@@ -528,12 +667,20 @@ ServerLoop(void)
port = ConnCreate(ListenSocket[i]);
if (port)
{
- if (GTMAddConnection(port) != STATUS_OK)
+ GTM_Conn *standby = NULL;
+
+ standby = gtm_standby_connect_to_standby();
+
+ if (GTMAddConnection(port, standby) != STATUS_OK)
{
elog(ERROR, "Too many connections");
+
+ gtm_standby_disconnect_from_standby(standby);
+
StreamClose(port->sock);
ConnFree(port);
}
+
}
}
}
@@ -541,6 +688,7 @@ ServerLoop(void)
}
}
+
/*
* Initialise the masks for select() for the ports we are listening on.
* Return the number of sockets to listen on.
@@ -791,9 +939,12 @@ ProcessCommand(Port *myport, StringInfo input_message)
{
case MSG_NODE_REGISTER:
case MSG_NODE_UNREGISTER:
+ case MSG_NODE_LIST:
ProcessPGXCNodeCommand(myport, mtype, input_message);
break;
+ case MSG_NODE_BEGIN_REPLICATION_INIT:
+ case MSG_NODE_END_REPLICATION_INIT:
case MSG_TXN_BEGIN:
case MSG_TXN_BEGIN_GETGXID:
case MSG_TXN_BEGIN_GETGXID_AUTOVACUUM:
@@ -807,6 +958,8 @@ ProcessCommand(Port *myport, StringInfo input_message)
case MSG_TXN_COMMIT_MULTI:
case MSG_TXN_ROLLBACK_MULTI:
case MSG_TXN_GET_GID_DATA:
+ case MSG_TXN_GET_NEXT_GXID:
+ case MSG_TXN_GXID_LIST:
ProcessTransactionCommand(myport, mtype, input_message);
break;
@@ -825,6 +978,7 @@ ProcessCommand(Port *myport, StringInfo input_message)
case MSG_SEQUENCE_CLOSE:
case MSG_SEQUENCE_RENAME:
case MSG_SEQUENCE_ALTER:
+ case MSG_SEQUENCE_LIST:
ProcessSequenceCommand(myport, mtype, input_message);
break;
@@ -849,11 +1003,12 @@ ProcessCommand(Port *myport, StringInfo input_message)
}
static int
-GTMAddConnection(Port *port)
+GTMAddConnection(Port *port, GTM_Conn *standby)
{
GTM_ConnectionInfo *conninfo = NULL;
conninfo = (GTM_ConnectionInfo *)palloc(sizeof (GTM_ConnectionInfo));
+ memset(conninfo, 0, sizeof(GTM_ConnectionInfo));
if (conninfo == NULL)
{
@@ -867,6 +1022,12 @@ GTMAddConnection(Port *port)
conninfo->con_port = port;
/*
+ * Add a connection to the standby.
+ */
+ if (standby != NULL)
+ conninfo->standby = standby;
+
+ /*
* XXX Start the thread
*/
if (GTM_ThreadCreate(conninfo, GTM_ThreadMain) == NULL)
@@ -960,6 +1121,10 @@ ProcessPGXCNodeCommand(Port *myport, GTM_MessageType mtype, StringInfo message)
ProcessPGXCNodeUnregister(myport, message);
break;
+ case MSG_NODE_LIST:
+ ProcessPGXCNodeList(myport, message);
+ break;
+
default:
Assert(0); /* Shouldn't come here.. keep compiler quite */
}
@@ -972,6 +1137,14 @@ ProcessTransactionCommand(Port *myport, GTM_MessageType mtype, StringInfo messag
switch (mtype)
{
+ case MSG_NODE_BEGIN_REPLICATION_INIT:
+ ProcessBeginReplicationInitialSyncRequest(myport, message);
+ break;
+
+ case MSG_NODE_END_REPLICATION_INIT:
+ ProcessEndReplicationInitialSyncRequest(myport, message);
+ break;
+
case MSG_TXN_BEGIN:
ProcessBeginTransactionCommand(myport, message);
break;
@@ -1022,6 +1195,15 @@ ProcessTransactionCommand(Port *myport, GTM_MessageType mtype, StringInfo messag
case MSG_TXN_GET_GID_DATA:
ProcessGetGIDDataTransactionCommand(myport, message);
+ break;
+
+ case MSG_TXN_GET_NEXT_GXID:
+ ProcessGetNextGXIDTransactionCommand(myport, message);
+ break;
+
+ case MSG_TXN_GXID_LIST:
+ ProcessGXIDListCommand(myport, message);
+ break;
default:
Assert(0); /* Shouldn't come here.. keep compiler quite */
@@ -1088,6 +1270,10 @@ ProcessSequenceCommand(Port *myport, GTM_MessageType mtype, StringInfo message)
ProcessSequenceRenameCommand(myport, message);
break;
+ case MSG_SEQUENCE_LIST:
+ ProcessSequenceListCommand(myport, message);
+ break;
+
default:
Assert(0); /* Shouldn't come here.. keep compiler quite */
}
@@ -1116,16 +1302,6 @@ GTM_RegisterPGXCNode(Port *myport, GTM_PGXCNodeId cid)
myport->pgxc_node_id = cid;
}
-
-static void
-GTM_UnregisterPGXCNode(Port *myport, GTM_PGXCNodeId cid)
-{
- /*
- * Do a clean shutdown
- */
- return;
-}
-
/*
* Validate the proposed data directory
*/
@@ -1196,10 +1372,10 @@ SetDataDir()
new = make_absolute_path(GTMDataDir);
if (!new)
ereport(FATAL,
- (errno,
- errmsg("failed to set the data directory \"%s\"",
- GTMDataDir)));
-
+ (errno,
+ errmsg("failed to set the data directory \"%s\"",
+ GTMDataDir)));
+
if (GTMDataDir)
free(GTMDataDir);
@@ -1382,8 +1558,8 @@ CreateLockFile(const char *filename, const char *refName)
/*
* Successfully created the file, now fill it.
*/
- snprintf(buffer, sizeof(buffer), "%d\n%s\n",
- (int) my_pid, GTMDataDir);
+ snprintf(buffer, sizeof(buffer), "%d\n%s\n%d\n",
+ (int) my_pid, GTMDataDir, (Recovery_IsStandby() ? 0 : 1));
errno = 0;
if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
{
@@ -1452,3 +1628,17 @@ DeleteLockFile(const char *filename)
"it could not be removed. Please remove the file "
"by hand and try again.")));
}
+
+static void
+PromoteToActive(void)
+{
+ elog(LOG, "Promote signal received. Becoming an active...");
+
+ /*
+ * Do promoting things here.
+ */
+ Recovery_StandbySetStandby(false);
+ CreateDataDirLockFile();
+
+ return;
+}
diff --git a/src/gtm/path/path.c b/src/gtm/path/path.c
index 4a3298b3d7..a3604f3b92 100644
--- a/src/gtm/path/path.c
+++ b/src/gtm/path/path.c
@@ -176,6 +176,7 @@ trim_trailing_separator(char *path)
for (p--; p > path && IS_DIR_SEP(*p); p--)
*p = '\0';
}
+
/*
* If the given pathname isn't already absolute, make it so, interpreting
* it relative to the current working directory.
@@ -186,7 +187,7 @@ trim_trailing_separator(char *path)
char *
make_absolute_path(const char *path)
{
- char *new;
+ char *new;
/* Returning null for null input is convenient for some callers */
if (path == NULL)
@@ -194,8 +195,8 @@ make_absolute_path(const char *path)
if (!is_absolute_path(path))
{
- char *buf;
- size_t buflen;
+ char *buf;
+ size_t buflen;
buflen = MAXPGPATH;
for (;;)
@@ -221,7 +222,7 @@ make_absolute_path(const char *path)
new = malloc(strlen(buf) + strlen(path) + 2);
if (!new)
- return NULL;
+ return NULL;
sprintf(new, "%s/%s", buf, path);
free(buf);
}
@@ -229,7 +230,7 @@ make_absolute_path(const char *path)
{
new = strdup(path);
if (!new)
- return NULL;
+ return NULL;
}
/* Make sure punctuation is canonical, too */
diff --git a/src/gtm/proxy/Makefile b/src/gtm/proxy/Makefile
index ebc97ea7c2..e81fd03908 100644
--- a/src/gtm/proxy/Makefile
+++ b/src/gtm/proxy/Makefile
@@ -3,7 +3,8 @@
top_build_dir=../..
include $(top_build_dir)/gtm/Makefile.global
-OBJS=proxy_main.o proxy_thread.o ../common/libgtm.a ../libpq/libpqcomm.a ../client/libgtmclient.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a
+OBJS=proxy_main.o proxy_thread.o proxy_utils.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a
+
LDFLAGS=-L$(top_build_dir)/common -L$(top_build_dir)/libpq
LIBS=-lpthread
diff --git a/src/gtm/proxy/proxy_main.c b/src/gtm/proxy/proxy_main.c
index e7632e15d8..9ad7551759 100644
--- a/src/gtm/proxy/proxy_main.c
+++ b/src/gtm/proxy/proxy_main.c
@@ -40,6 +40,8 @@
#include "gtm/gtm_seq.h"
#include "gtm/gtm_msg.h"
#include "gtm/libpq-int.h"
+#include "gtm/gtm_ip.h"
+#include "gtm/gtm_standby.h"
extern int optind;
extern char *optarg;
@@ -730,7 +732,7 @@ GTMProxy_ThreadMain(void *argp)
for (;;)
{
- ListCell *elem = NULL;
+ gtm_ListCell *elem = NULL;
GTM_Result *res = NULL;
/*
@@ -818,7 +820,7 @@ GTMProxy_ThreadMain(void *argp)
/*
* Initialize the lists
*/
- thrinfo->thr_processed_commands = NIL;
+ thrinfo->thr_processed_commands = gtm_NIL;
memset(thrinfo->thr_pending_commands, 0, sizeof (thrinfo->thr_pending_commands));
/*
@@ -907,9 +909,9 @@ GTMProxy_ThreadMain(void *argp)
* Read back the responses and put them on to the right backend
* connection.
*/
- foreach(elem, thrinfo->thr_processed_commands)
+ gtm_foreach(elem, thrinfo->thr_processed_commands)
{
- GTMProxy_CommandInfo *cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem);
+ GTMProxy_CommandInfo *cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem);
/*
* If this is a continuation of a multi-part command response, we
@@ -925,8 +927,8 @@ GTMProxy_ThreadMain(void *argp)
ProcessResponse(thrinfo, cmdinfo, res);
}
- list_free_deep(thrinfo->thr_processed_commands);
- thrinfo->thr_processed_commands = NIL;
+ gtm_list_free_deep(thrinfo->thr_processed_commands);
+ thrinfo->thr_processed_commands = gtm_NIL;
/*
* Now clean up disconnected connections
@@ -1050,7 +1052,7 @@ ProcessResponse(GTMProxy_ThreadInfo *thrinfo, GTMProxy_CommandInfo *cmdinfo,
* derive our GXID from the start GXID and the our position in the
* command queue
*/
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
if (res->gr_type != TXN_BEGIN_GETGXID_MULTI_RESULT)
elog(ERROR, "Wrong result");
@@ -1198,7 +1200,7 @@ ProcessResponse(GTMProxy_ThreadInfo *thrinfo, GTMProxy_CommandInfo *cmdinfo,
*/
switch (res->gr_status)
{
- case 0:
+ case GTM_RESULT_OK:
pq_beginmessage(&buf, 'S');
pq_sendint(&buf, res->gr_type, 4);
pq_sendbytes(&buf, res->gr_proxy_data, res->gr_msglen);
@@ -1334,6 +1336,13 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn,
sizeof (GTM_PGXCNodeType));
memcpy(&cmd_data.cd_reg.nodenum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)),
sizeof (GTM_PGXCNodeId));
+ /*
+ * Now we have to waste the following host information. It is taken from
+ * the address field in the conn.
+ */
+ len = pq_getmsgint(message, sizeof(GTM_StrLen));
+ pq_getmsgbytes(message, len);
+
memcpy(&cmd_data.cd_reg.port, pq_getmsgbytes(message, sizeof (GTM_PGXCNodePort)),
sizeof (GTM_PGXCNodePort));
memcpy(&cmd_data.cd_reg.proxynum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)),
@@ -1341,6 +1350,9 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn,
len = pq_getmsgint(message, sizeof (int));
cmd_data.cd_reg.datafolder = (char *)pq_getmsgbytes(message, len);
+
+ /* Now we have one more data to waste, "status" */
+ cmd_data.cd_reg.status = pq_getmsgint(message, sizeof(GTM_PGXCNodeStatus));
pq_getmsgend(message);
/* Copy also remote host address in data to be proxied */
@@ -1560,7 +1572,7 @@ GTMProxy_ProxyCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn,
cmdinfo->ci_mtype = mtype;
cmdinfo->ci_conn = conninfo;
cmdinfo->ci_res_index = 0;
- thrinfo->thr_processed_commands = lappend(thrinfo->thr_processed_commands, cmdinfo);
+ thrinfo->thr_processed_commands = gtm_lappend(thrinfo->thr_processed_commands, cmdinfo);
/* Finish the message. */
if (gtmpqPutMsgEnd(gtm_conn))
@@ -1587,16 +1599,28 @@ static void GTMProxy_ProxyPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo,GTM_
case MSG_NODE_REGISTER:
/* Rebuild the message */
if (gtmpqPutMsgStart('C', true, gtm_conn) ||
+ /* GTM Proxy Header */
gtmpqPutnchar((char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader), gtm_conn) ||
+ /* Message Type */
gtmpqPutInt(MSG_NODE_REGISTER, sizeof (GTM_MessageType), gtm_conn) ||
+ /* Node Type to Register */
gtmpqPutnchar((char *)&cmd_data.cd_reg.type, sizeof(GTM_PGXCNodeType), gtm_conn) ||
+ /* Node Number to Register */
gtmpqPutnchar((char *)&cmd_data.cd_reg.nodenum, sizeof(GTM_PGXCNodeId), gtm_conn) ||
- gtmpqPutnchar((char *)&cmd_data.cd_reg.port, sizeof(GTM_PGXCNodePort), gtm_conn) ||
- gtmpqPutnchar((char *)&GTMProxyID, sizeof(GTM_PGXCNodeId), gtm_conn) ||
+ /* Host Name (length) */
gtmpqPutInt(strlen(cmd_data.cd_reg.ipaddress), sizeof (GTM_StrLen), gtm_conn) ||
+ /* Host Name (var-len) */
gtmpqPutnchar(cmd_data.cd_reg.ipaddress, strlen(cmd_data.cd_reg.ipaddress), gtm_conn) ||
+ /* Port Number */
+ gtmpqPutnchar((char *)&cmd_data.cd_reg.port, sizeof(GTM_PGXCNodePort), gtm_conn) ||
+ /* Proxy ID (zero if connected to GTM directly) */
+ gtmpqPutnchar((char *)&GTMProxyID, sizeof(GTM_PGXCNodeId), gtm_conn) ||
+ /* Data Folder length */
gtmpqPutInt(strlen(cmd_data.cd_reg.datafolder), 4, gtm_conn) ||
- gtmpqPutnchar(cmd_data.cd_reg.datafolder, strlen(cmd_data.cd_reg.datafolder), gtm_conn))
+ /* Data folder name (var-len) */
+ gtmpqPutnchar(cmd_data.cd_reg.datafolder, strlen(cmd_data.cd_reg.datafolder), gtm_conn) ||
+ /* Node Status */
+ gtmpqPutInt(cmd_data.cd_reg.status, sizeof(GTM_PGXCNodeStatus), gtm_conn))
elog(ERROR, "Error proxing data");
break;
@@ -1620,7 +1644,7 @@ static void GTMProxy_ProxyPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo,GTM_
cmdinfo->ci_mtype = mtype;
cmdinfo->ci_conn = conninfo;
cmdinfo->ci_res_index = 0;
- thrinfo->thr_processed_commands = lappend(thrinfo->thr_processed_commands, cmdinfo);
+ thrinfo->thr_processed_commands = gtm_lappend(thrinfo->thr_processed_commands, cmdinfo);
/* Finish the message. */
if (gtmpqPutMsgEnd(gtm_conn))
@@ -1649,7 +1673,7 @@ GTMProxy_CommandPending(GTMProxy_ConnectionInfo *conninfo, GTM_MessageType mtype
cmdinfo->ci_conn = conninfo;
cmdinfo->ci_res_index = 0;
cmdinfo->ci_data = cmd_data;
- thrinfo->thr_pending_commands[mtype] = lappend(thrinfo->thr_pending_commands[mtype], cmdinfo);
+ thrinfo->thr_pending_commands[mtype] = gtm_lappend(thrinfo->thr_pending_commands[mtype], cmdinfo);
return;
}
@@ -1776,13 +1800,13 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo)
GTMProxy_CommandInfo *cmdinfo = NULL;
GTM_ProxyMsgHeader proxyhdr;
GTM_Conn *gtm_conn = thrinfo->thr_gtm_conn;
- ListCell *elem = NULL;
+ gtm_ListCell *elem = NULL;
for (ii = 0; ii < MSG_TYPE_COUNT; ii++)
{
int res_index = 0;
- if (list_length(thrinfo->thr_pending_commands[ii]) == 0)
+ if (gtm_list_length(thrinfo->thr_pending_commands[ii]) == 0)
continue;
/*
@@ -1797,15 +1821,15 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo)
switch (ii)
{
case MSG_TXN_BEGIN_GETGXID:
- if (list_length(thrinfo->thr_pending_commands[ii]) <=0 )
+ if (gtm_list_length(thrinfo->thr_pending_commands[ii]) <=0 )
elog(PANIC, "No pending commands of type %d", ii);
if (gtmpqPutInt(MSG_TXN_BEGIN_GETGXID_MULTI, sizeof (GTM_MessageType), gtm_conn) ||
- gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
+ gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
elog(ERROR, "Error sending data");
- foreach (elem, thrinfo->thr_pending_commands[ii])
+ gtm_foreach (elem, thrinfo->thr_pending_commands[ii])
{
- cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem);
+ cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem);
Assert(cmdinfo->ci_mtype == ii);
cmdinfo->ci_res_index = res_index++;
if (gtmpqPutInt(cmdinfo->ci_data.cd_beg.iso_level,
@@ -1823,19 +1847,19 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo)
/*
* Move the entire list to the processed command
*/
- thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands,
+ thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands,
thrinfo->thr_pending_commands[ii]);
- thrinfo->thr_pending_commands[ii] = NIL;
+ thrinfo->thr_pending_commands[ii] = gtm_NIL;
break;
case MSG_TXN_COMMIT:
if (gtmpqPutInt(MSG_TXN_COMMIT_MULTI, sizeof (GTM_MessageType), gtm_conn) ||
- gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
+ gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
elog(ERROR, "Error sending data");
- foreach (elem, thrinfo->thr_pending_commands[ii])
+ gtm_foreach (elem, thrinfo->thr_pending_commands[ii])
{
- cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem);
+ cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem);
Assert(cmdinfo->ci_mtype == ii);
cmdinfo->ci_res_index = res_index++;
if (cmdinfo->ci_data.cd_rc.isgxid)
@@ -1861,21 +1885,21 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo)
/*
* Move the entire list to the processed command
*/
- thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands,
+ thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands,
thrinfo->thr_pending_commands[ii]);
- thrinfo->thr_pending_commands[ii] = NIL;
+ thrinfo->thr_pending_commands[ii] = gtm_NIL;
break;
break;
case MSG_TXN_ROLLBACK:
if (gtmpqPutInt(MSG_TXN_ROLLBACK_MULTI, sizeof (GTM_MessageType), gtm_conn) ||
- gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
+ gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
elog(ERROR, "Error sending data");
- foreach (elem, thrinfo->thr_pending_commands[ii])
+ gtm_foreach (elem, thrinfo->thr_pending_commands[ii])
{
- cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem);
+ cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem);
Assert(cmdinfo->ci_mtype == ii);
cmdinfo->ci_res_index = res_index++;
if (cmdinfo->ci_data.cd_rc.isgxid)
@@ -1902,19 +1926,19 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo)
/*
* Move the entire list to the processed command
*/
- thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands,
+ thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands,
thrinfo->thr_pending_commands[ii]);
- thrinfo->thr_pending_commands[ii] = NIL;
+ thrinfo->thr_pending_commands[ii] = gtm_NIL;
break;
case MSG_SNAPSHOT_GET:
if (gtmpqPutInt(MSG_SNAPSHOT_GET_MULTI, sizeof (GTM_MessageType), gtm_conn) ||
- gtmpqPutInt(list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
+ gtmpqPutInt(gtm_list_length(thrinfo->thr_pending_commands[ii]), sizeof(int), gtm_conn))
elog(ERROR, "Error sending data");
- foreach (elem, thrinfo->thr_pending_commands[ii])
+ gtm_foreach (elem, thrinfo->thr_pending_commands[ii])
{
- cmdinfo = (GTMProxy_CommandInfo *)lfirst(elem);
+ cmdinfo = (GTMProxy_CommandInfo *)gtm_lfirst(elem);
Assert(cmdinfo->ci_mtype == ii);
cmdinfo->ci_res_index = res_index++;
if (cmdinfo->ci_data.cd_rc.isgxid)
@@ -1940,9 +1964,9 @@ GTMProxy_ProcessPendingCommands(GTMProxy_ThreadInfo *thrinfo)
/*
* Move the entire list to the processed command
*/
- thrinfo->thr_processed_commands = list_concat(thrinfo->thr_processed_commands,
+ thrinfo->thr_processed_commands = gtm_list_concat(thrinfo->thr_processed_commands,
thrinfo->thr_pending_commands[ii]);
- thrinfo->thr_pending_commands[ii] = NIL;
+ thrinfo->thr_pending_commands[ii] = gtm_NIL;
break;
@@ -2015,7 +2039,7 @@ retry:
* never set DataDir directly.
*/
void
-SetDataDir()
+SetDataDir(void)
{
char *new;
@@ -2319,7 +2343,7 @@ UnregisterProxy(void)
goto failed;
/* Check on node type and node number */
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_resdata.grd_node.type == type);
Assert(res->gr_resdata.grd_node.nodenum == GTMProxyID);
@@ -2358,10 +2382,13 @@ RegisterProxy(void)
gtmpqPutInt(MSG_NODE_REGISTER, sizeof (GTM_MessageType), master_conn) ||
gtmpqPutnchar((char *)&type, sizeof(GTM_PGXCNodeType), master_conn) ||
gtmpqPutnchar((char *)&GTMProxyID, sizeof(GTM_PGXCNodeId), master_conn) || /* nodenum */
+ gtmpqPutInt((int)strlen(ListenAddresses), sizeof(int), master_conn) ||
+ gtmpqPutnchar(ListenAddresses, (int)strlen(ListenAddresses), master_conn) ||
gtmpqPutnchar((char *)&port, sizeof(GTM_PGXCNodePort), master_conn) ||
gtmpqPutnchar((char *)&proxynum, sizeof(GTM_PGXCNodeId), master_conn) ||
- gtmpqPutInt(strlen(GTMProxyDataDir), 4, master_conn) ||
- gtmpqPutnchar(GTMProxyDataDir, strlen(GTMProxyDataDir), master_conn))
+ gtmpqPutInt((int)strlen(GTMProxyDataDir), 4, master_conn) ||
+ gtmpqPutnchar(GTMProxyDataDir, strlen(GTMProxyDataDir), master_conn)||
+ gtmpqPutInt(NODE_CONNECTED, sizeof(GTM_PGXCNodeStatus), master_conn))
goto failed;
/* Finish the message. */
@@ -2380,7 +2407,7 @@ RegisterProxy(void)
if ((res = GTMPQgetResult(master_conn)) == NULL)
goto failed;
- if (res->gr_status == 0)
+ if (res->gr_status == GTM_RESULT_OK)
{
Assert(res->gr_resdata.grd_node.type == type);
Assert(res->gr_resdata.grd_node.nodenum == GTMProxyID);
diff --git a/src/gtm/proxy/proxy_utils.c b/src/gtm/proxy/proxy_utils.c
new file mode 100644
index 0000000000..9b928ca4dd
--- /dev/null
+++ b/src/gtm/proxy/proxy_utils.c
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * proxy_utils.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/gtm/proxy/proxy_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "gtm/proxy_utils.h"
+
+#include "gtm/elog.h"
+#include "gtm/gtm.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+#include "gtm/gtm_seq.h"
+#include "gtm/register.h"
+
+/*
+ * This function is a dummy function of gtm_proxy module to avoid
+ * object link problem.
+ *
+ * Most of command processing functions are existing only in GTM main
+ * module, but a few are both in GTM main and GTM proxy modules, which
+ * consist of same binary objects. And all the command processing
+ * functions require calling gtm_standby_check_communication_error()
+ * for GTM main.
+ *
+ * Two options should be considered here:
+ * (1) Moving all command processing functions into the common modules, or
+ * (2) Creating a dummy function in GTM proxy module.
+ *
+ * (1) may cause another hard thing because of object and variable
+ * referencing issue. For example GetMyThreadInfo is specified in both
+ * gtm.h and gtm_proxy.h and depends on the context.
+ *
+ * This is the reason why this dummy function is needed here.
+ *
+ * The object and module structure of GTM/GTM Proxy needs review, and
+ * fix to remove this kind of tricks.
+ */
+bool
+gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn)
+{
+ return false;
+}
diff --git a/src/gtm/recovery/Makefile b/src/gtm/recovery/Makefile
index 3af7fd787a..b95b6b047b 100644
--- a/src/gtm/recovery/Makefile
+++ b/src/gtm/recovery/Makefile
@@ -8,7 +8,7 @@ NAME=gtmrecovery
SO_MAJOR_VERSION= 1
SO_MINOR_VERSION= 0
-OBJS=register.o ../client/libgtmclient.a
+OBJS=register.o replication.o standby_utils.o ../client/libgtmclient.a
all:all-lib
diff --git a/src/gtm/recovery/register.c b/src/gtm/recovery/register.c
index 163a860621..0488122b59 100644
--- a/src/gtm/recovery/register.c
+++ b/src/gtm/recovery/register.c
@@ -15,18 +15,20 @@
*/
#include <fcntl.h>
+#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
-#include "gtm/gtm_c.h"
+#include "gtm/elog.h"
#include "gtm/gtm.h"
-#include "gtm/register.h"
-#include "gtm/assert.h"
-#include <stdio.h>
+#include "gtm/gtm_client.h"
+#include "gtm/gtm_serialize.h"
+#include "gtm/gtm_standby.h"
#include "gtm/libpq.h"
+#include "gtm/libpq-int.h"
#include "gtm/pqformat.h"
-#include "gtm/gtm_msg.h"
#include "gtm/stringinfo.h"
+
#include "gtm/gtm_ip.h"
#define GTM_NODE_FILE "register.node"
@@ -35,7 +37,7 @@
typedef struct GTM_NodeInfoHashBucket
{
- List *nhb_list;
+ gtm_List *nhb_list;
GTM_RWLock nhb_lock;
} GTM_PGXCNodeInfoHashBucket;
@@ -61,6 +63,78 @@ static char *pgxcnode_copy_char(const char *str);
#define pgxcnode_nodenum_equal(num1,num2) (num1 == num2)
#define pgxcnode_port_equal(port1,port2) (port1 == port2)
+size_t
+pgxcnode_get_all(GTM_PGXCNodeInfo **data, size_t maxlen)
+{
+ GTM_PGXCNodeInfoHashBucket *bucket;
+ gtm_ListCell *elem;
+ int node = 0;
+ int i;
+
+ for (i = 0; i < NODE_HASH_TABLE_SIZE; i++)
+ {
+ bucket = &GTM_PGXCNodes[i];
+
+ GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ);
+
+ gtm_foreach(elem, bucket->nhb_list)
+ {
+ GTM_PGXCNodeInfo *curr_nodeinfo = NULL;
+
+ curr_nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem);
+ if (curr_nodeinfo != NULL)
+ {
+ data[node] = curr_nodeinfo;
+ node++;
+ }
+
+ if (node == maxlen)
+ break;
+ }
+
+ GTM_RWLockRelease(&bucket->nhb_lock);
+ }
+
+ return node;
+}
+
+size_t
+pgxcnode_find_by_type(GTM_PGXCNodeType type, GTM_PGXCNodeInfo **data, size_t maxlen)
+{
+ GTM_PGXCNodeInfoHashBucket *bucket;
+ gtm_ListCell *elem;
+ int node = 0;
+ int i;
+
+ for (i = 0; i < NODE_HASH_TABLE_SIZE; i++)
+ {
+ bucket = &GTM_PGXCNodes[i];
+
+ GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ);
+
+ gtm_foreach(elem, bucket->nhb_list)
+ {
+ GTM_PGXCNodeInfo *cur = NULL;
+
+ cur = (GTM_PGXCNodeInfo *) gtm_lfirst(elem);
+
+ if (cur != NULL && cur->type == type)
+ {
+ data[node] = cur;
+ elog(LOG, "pgxcnode_find_by_type: cur=%p, ipaddress=%s", cur, cur->ipaddress);
+ node++;
+ }
+
+ if (node == maxlen)
+ break;
+ }
+
+ GTM_RWLockRelease(&bucket->nhb_lock);
+ }
+
+ return node;
+}
+
/*
* Find the pgxcnode info structure for the given node type and number key.
*/
@@ -70,16 +144,16 @@ pgxcnode_find_info(GTM_PGXCNodeType type,
{
uint32 hash = pgxcnode_gethash(nodenum);
GTM_PGXCNodeInfoHashBucket *bucket;
- ListCell *elem;
+ gtm_ListCell *elem;
GTM_PGXCNodeInfo *curr_nodeinfo = NULL;
bucket = &GTM_PGXCNodes[hash];
GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ);
- foreach(elem, bucket->nhb_list)
+ gtm_foreach(elem, bucket->nhb_list)
{
- curr_nodeinfo = (GTM_PGXCNodeInfo *) lfirst(elem);
+ curr_nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem);
if (pgxcnode_type_equal(curr_nodeinfo->type, type) &&
pgxcnode_nodenum_equal(curr_nodeinfo->nodenum, nodenum))
break;
@@ -121,7 +195,7 @@ pgxcnode_remove_info(GTM_PGXCNodeInfo *nodeinfo)
GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_WRITE);
GTM_RWLockAcquire(&nodeinfo->node_lock, GTM_LOCKMODE_WRITE);
- bucket->nhb_list = list_delete(bucket->nhb_list, nodeinfo);
+ bucket->nhb_list = gtm_list_delete(bucket->nhb_list, nodeinfo);
GTM_RWLockRelease(&nodeinfo->node_lock);
GTM_RWLockRelease(&bucket->nhb_lock);
@@ -137,16 +211,16 @@ pgxcnode_add_info(GTM_PGXCNodeInfo *nodeinfo)
{
uint32 hash = pgxcnode_gethash(nodeinfo->nodenum);
GTM_PGXCNodeInfoHashBucket *bucket;
- ListCell *elem;
+ gtm_ListCell *elem;
bucket = &GTM_PGXCNodes[hash];
GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_WRITE);
- foreach(elem, bucket->nhb_list)
+ gtm_foreach(elem, bucket->nhb_list)
{
GTM_PGXCNodeInfo *curr_nodeinfo = NULL;
- curr_nodeinfo = (GTM_PGXCNodeInfo *) lfirst(elem);
+ curr_nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem);
/* GTM Proxy are always registered as they do not have Identification numbers yet */
if (pgxcnode_type_equal(curr_nodeinfo->type, nodeinfo->type) &&
@@ -208,7 +282,7 @@ pgxcnode_add_info(GTM_PGXCNodeInfo *nodeinfo)
/*
* Safe to add the structure to the list
*/
- bucket->nhb_list = lappend(bucket->nhb_list, nodeinfo);
+ bucket->nhb_list = gtm_lappend(bucket->nhb_list, nodeinfo);
GTM_RWLockRelease(&bucket->nhb_lock);
return 0;
@@ -228,12 +302,13 @@ pgxcnode_copy_char(const char *str)
* contextes.
*/
retstr = (char *) MemoryContextAlloc(TopMostMemoryContext,
- strlen(str));
+ strlen(str) + 1);
if (retstr == NULL)
ereport(ERROR, (ENOMEM, errmsg("Out of memory")));
memcpy(retstr, str, strlen(str));
+ retstr[strlen(str)] = '\0';
return retstr;
}
@@ -252,9 +327,6 @@ Recovery_PGXCNodeUnregister(GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum, bool
* Unregistration has to be made by the same connection as the one used for registration
* or the one that reconnected the node.
*/
- if (!in_recovery && socket != nodeinfo->socket)
- return EINVAL;
-
pgxcnode_remove_info(nodeinfo);
/* Add a record to file on disk saying that this node has been unregistered correctly */
@@ -302,6 +374,14 @@ Recovery_PGXCNodeRegister(GTM_PGXCNodeType type,
nodeinfo->status = status;
nodeinfo->socket = socket;
+ elog(LOG, "Recovery_PGXCNodeRegister Request info: type=%d, nodenum=%d, port=%d," \
+ "datafolder=%s, ipaddress=%s, status=%d",
+ type, nodenum, port, datafolder, ipaddress, status);
+ elog(LOG, "Recovery_PGXCNodeRegister Node info: type=%d, nodenum=%d, port=%d, "\
+ "datafolder=%s, ipaddress=%s, status=%d",
+ nodeinfo->type, nodeinfo->nodenum, nodeinfo->port,
+ nodeinfo->datafolder, nodeinfo->ipaddress, nodeinfo->status);
+
/* Add PGXC Node Info to the global hash table */
errcode = pgxcnode_add_info(nodeinfo);
@@ -326,41 +406,26 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message)
GTM_PGXCNodeId nodenum, proxynum;
GTM_PGXCNodePort port;
char remote_host[NI_MAXHOST];
- char remote_port[NI_MAXSERV];
- char *datafolder;
+ char datafolder[NI_MAXHOST];
char *ipaddress;
MemoryContext oldContext;
- int strlen;
+ int len;
StringInfoData buf;
+ GTM_PGXCNodeStatus status;
- /* Get the Remote node IP and port to register it */
- remote_host[0] = '\0';
- remote_port[0] = '\0';
-
- if (myport->remote_type != PGXC_NODE_GTM_PROXY)
- {
- if (gtm_getnameinfo_all(&myport->raddr.addr, myport->raddr.salen,
- remote_host, sizeof(remote_host),
- remote_port, sizeof(remote_port),
- NI_NUMERICSERV))
- {
- int ret = gtm_getnameinfo_all(&myport->raddr.addr, myport->raddr.salen,
- remote_host, sizeof(remote_host),
- remote_port, sizeof(remote_port),
- NI_NUMERICHOST | NI_NUMERICSERV);
-
- if (ret)
- ereport(WARNING,
- (errmsg_internal("gtm_getnameinfo_all() failed")));
- }
- }
-
- /* Read Node Type and number */
+ /* Read Node Type */
memcpy(&type, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeType)),
sizeof (GTM_PGXCNodeType));
+ /* Node Number */
memcpy(&nodenum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)),
sizeof (GTM_PGXCNodeId));
+ /* Read Host name */
+ len = pq_getmsgint(message, sizeof (int));
+ memcpy(remote_host, (char *)pq_getmsgbytes(message, len), len);
+ remote_host[len] = '\0';
+ ipaddress = remote_host;
+
/* Read Port Number */
memcpy(&port, pq_getmsgbytes(message, sizeof (GTM_PGXCNodePort)),
sizeof (GTM_PGXCNodePort));
@@ -368,27 +433,27 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message)
/* Read Proxy ID number (0 if no proxy used) */
memcpy(&proxynum, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeId)),
sizeof (GTM_PGXCNodeId));
-
- /*
- * Message is received from a proxy, get also the remote node address
- * In the case a proxy registering itself, the remote address
- * is directly taken from socket.
- */
- if (myport->remote_type == PGXC_NODE_GTM_PROXY &&
- !myport->is_postmaster)
- {
- strlen = pq_getmsgint(message, sizeof (GTM_StrLen));
- ipaddress = (char *)pq_getmsgbytes(message, strlen);
- }
- else
- ipaddress = remote_host;
+ elog(LOG, "ProcessPGXCNodeRegister: ipaddress = %s", ipaddress);
/*
* Finish by reading Data Folder (length and then string)
*/
+ len = pq_getmsgint(message, sizeof (GTM_StrLen));
- strlen = pq_getmsgint(message, sizeof (GTM_StrLen));
- datafolder = (char *)pq_getmsgbytes(message, strlen);
+ memcpy(datafolder, (char *)pq_getmsgbytes(message, len), len);
+ datafolder[len] = '\0';
+
+ status = pq_getmsgint(message, sizeof (GTM_PGXCNodeStatus));
+
+ if ((type!=PGXC_NODE_GTM_PROXY) &&
+ (type!=PGXC_NODE_GTM_PROXY_POSTMASTER) &&
+ (type!=PGXC_NODE_COORDINATOR) &&
+ (type!=PGXC_NODE_DATANODE) &&
+ (type!=PGXC_NODE_GTM) &&
+ (type!=PGXC_NODE_DEFAULT))
+ ereport(ERROR,
+ (EINVAL,
+ errmsg("Unknown node type.")));
/*
* We must use the TopMostMemoryContext because the Node ID information is
@@ -427,6 +492,30 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling node_register_internal() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = node_register_internal(GetMyThreadInfo->thr_conn->standby,
+ type,
+ ipaddress,
+ port,
+ nodenum,
+ datafolder,
+ status);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "node_register_internal() returns rc %d.", _rc);
+ }
}
/*
@@ -481,6 +570,112 @@ ProcessPGXCNodeUnregister(Port *myport, StringInfo message)
if (myport->remote_type != PGXC_NODE_GTM_PROXY)
pq_flush(myport);
+
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "calling node_unregister() for standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = node_unregister(GetMyThreadInfo->thr_conn->standby,
+ type,
+ nodenum);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "node_unregister() returns rc %d.", _rc);
+ }
+}
+
+/*
+ * Process MSG_NODE_LIST
+ */
+void
+ProcessPGXCNodeList(Port *myport, StringInfo message)
+{
+ MemoryContext oldContext;
+ StringInfoData buf;
+ int num_node = 13;
+ int i;
+
+ GTM_PGXCNodeInfo *data[MAX_NODES];
+ char *s_data[MAX_NODES];
+ size_t s_datalen[MAX_NODES];
+
+ /*
+ * We must use the TopMostMemoryContext because the Node ID information is
+ * not bound to a thread and can outlive any of the thread specific
+ * contextes.
+ */
+ oldContext = MemoryContextSwitchTo(TopMostMemoryContext);
+
+ memset(data, 0, sizeof(GTM_PGXCNodeInfo *) * MAX_NODES);
+ memset(s_data, 0, sizeof(char *) * MAX_NODES);
+
+ num_node = pgxcnode_get_all(data, MAX_NODES);
+
+ for (i = 0; i < num_node; i++)
+ {
+ size_t s_len;
+
+ s_len = gtm_get_pgxcnodeinfo_size(data[i]);
+
+ /*
+ * Allocate memory blocks for serialized GTM_PGXCNodeInfo data.
+ */
+ s_data[i] = (char *)malloc(s_len+1);
+ memset(s_data[i], 0, s_len+1);
+
+ s_datalen[i] = gtm_serialize_pgxcnodeinfo(data[i], s_data[i], s_len+1);
+
+ elog(LOG, "gtm_get_pgxcnodeinfo_size: s_len=%ld, s_datalen=%ld", s_len, s_datalen[i]);
+ }
+
+ MemoryContextSwitchTo(oldContext);
+
+ pq_getmsgend(message);
+
+ /*
+ * Send a SUCCESS message back to the client
+ */
+ pq_beginmessage(&buf, 'S');
+ pq_sendint(&buf, NODE_LIST_RESULT, 4);
+ if (myport->remote_type == PGXC_NODE_GTM_PROXY)
+ {
+ GTM_ProxyMsgHeader proxyhdr;
+ proxyhdr.ph_conid = myport->conn_id;
+ pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader));
+ }
+ pq_sendint(&buf, num_node, sizeof(int)); /* number of nodes */
+
+ /*
+ * Send pairs of GTM_PGXCNodeInfo size and serialized GTM_PGXCNodeInfo body.
+ */
+ for (i = 0; i < num_node; i++)
+ {
+ pq_sendint(&buf, s_datalen[i], sizeof(int));
+ pq_sendbytes(&buf, s_data[i], s_datalen[i]);
+ }
+
+ pq_endmessage(myport, &buf);
+
+ if (myport->remote_type != PGXC_NODE_GTM_PROXY)
+ pq_flush(myport);
+
+ /*
+ * Release memory blocks for the serialized data.
+ */
+ for (i = 0; i < num_node; i++)
+ {
+ free(s_data[i]);
+ }
+
+ elog(LOG, "ProcessPGXCNodeList() ok.");
}
/*
@@ -490,10 +685,10 @@ ProcessPGXCNodeUnregister(Port *myport, StringInfo message)
void
Recovery_SaveRegisterInfo(void)
{
- GTM_PGXCNodeInfoHashBucket *bucket;
- ListCell *elem;
- GTM_PGXCNodeInfo *nodeinfo = NULL;
- int hash, ctlfd;
+GTM_PGXCNodeInfoHashBucket *bucket;
+gtm_ListCell *elem;
+GTM_PGXCNodeInfo *nodeinfo = NULL;
+int hash, ctlfd;
char filebkp[GTM_NODE_FILE_MAX_PATH];
GTM_RWLockAcquire(&RegisterFileLock, GTM_LOCKMODE_WRITE);
@@ -510,18 +705,18 @@ Recovery_SaveRegisterInfo(void)
return;
}
- for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++)
+for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++)
{
bucket = &GTM_PGXCNodes[hash];
GTM_RWLockAcquire(&bucket->nhb_lock, GTM_LOCKMODE_READ);
/* Write one by one information about registered nodes */
- foreach(elem, bucket->nhb_list)
+ gtm_foreach(elem, bucket->nhb_list)
{
int len;
- nodeinfo = (GTM_PGXCNodeInfo *) lfirst(elem);
+ nodeinfo = (GTM_PGXCNodeInfo *) gtm_lfirst(elem);
if (nodeinfo == NULL)
break;
@@ -550,7 +745,7 @@ Recovery_SaveRegisterInfo(void)
}
GTM_RWLockRelease(&bucket->nhb_lock);
- }
+ }
close(ctlfd);
@@ -718,8 +913,11 @@ Recovery_PGXCNodeDisconnect(Port *myport)
if (nodeinfo != NULL)
{
- /* Disconnection cannot be made with another socket than the one used for registration */
- if (myport->sock != nodeinfo->socket)
+ /*
+ * Disconnection cannot be made with another socket than the one used for registration.
+ * socket may have a dummy value (-1) under GTM standby node.
+ */
+ if (nodeinfo->socket >= 0 && myport->sock != nodeinfo->socket)
return;
GTM_RWLockAcquire(&nodeinfo->node_lock, GTM_LOCKMODE_WRITE);
@@ -805,4 +1003,29 @@ ProcessPGXCNodeBackendDisconnect(Port *myport, StringInfo message)
}
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * Forwarding MSG_BACKEND_DISCONNECT message to GTM standby.
+ * No need to wait any response.
+ */
+ if (GetMyThreadInfo->thr_conn->standby)
+ {
+ int _rc;
+ GTM_Conn *oldconn = GetMyThreadInfo->thr_conn->standby;
+ int count = 0;
+
+ elog(LOG, "forwarding MSG_BACKEND_DISCONNECT to standby GTM %p.",
+ GetMyThreadInfo->thr_conn->standby);
+
+retry:
+ _rc = backend_disconnect(GetMyThreadInfo->thr_conn->standby,
+ is_postmaster,
+ type,
+ nodenum);
+
+ if (gtm_standby_check_communication_error(&count, oldconn))
+ goto retry;
+
+ elog(LOG, "MSG_BACKEND_DISCONNECT rc=%d done.", _rc);
+ }
}
diff --git a/src/gtm/recovery/replication.c b/src/gtm/recovery/replication.c
new file mode 100644
index 0000000000..a6789758b7
--- /dev/null
+++ b/src/gtm/recovery/replication.c
@@ -0,0 +1,121 @@
+/*-------------------------------------------------------------------------
+ *
+ * replication.c
+ * Controlling the initialization and end of replication process of GTM data
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ *
+ * IDENTIFICATION
+ * src/gtm/recovery/replication.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "gtm/replication.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "gtm/gtm_c.h"
+#include "gtm/gtm.h"
+#include "gtm/gtm_txn.h"
+#include "gtm/standby_utils.h"
+#include "gtm/gtm_standby.h"
+#include "gtm/register.h"
+#include "gtm/assert.h"
+#include <stdio.h>
+#include "gtm/libpq.h"
+#include "gtm/pqformat.h"
+#include "gtm/gtm_msg.h"
+#include "gtm/gtm_ip.h"
+
+/*
+ * Process MSG_NODE_BEGIN_REPLICATION_INIT
+ */
+void
+ProcessBeginReplicationInitialSyncRequest(Port *myport, StringInfo message)
+{
+ StringInfoData buf;
+ MemoryContext oldContext;
+
+ pq_getmsgend(message);
+
+ if (Recovery_IsStandby())
+ ereport(ERROR,
+ (EPERM,
+ errmsg("Operation not permitted under the standby mode.")));
+
+ oldContext = MemoryContextSwitchTo(TopMemoryContext);
+
+ /* Acquire global locks to copy resource data to the standby. */
+ GTM_RWLockAcquire(&GTMTransactions.gt_XidGenLock, GTM_LOCKMODE_WRITE);
+ GTM_RWLockAcquire(&GTMTransactions.gt_TransArrayLock, GTM_LOCKMODE_WRITE);
+ elog(LOG, "Prepared for copying data with holding XidGenLock and TransArrayLock.");
+
+ MemoryContextSwitchTo(oldContext);
+
+ pq_beginmessage(&buf, 'S');
+ pq_sendint(&buf, NODE_BEGIN_REPLICATION_INIT_RESULT, 4);
+ if (myport->remote_type == PGXC_NODE_GTM_PROXY)
+ {
+ GTM_ProxyMsgHeader proxyhdr;
+ proxyhdr.ph_conid = myport->conn_id;
+ pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader));
+ }
+ pq_endmessage(myport, &buf);
+
+ if (myport->remote_type != PGXC_NODE_GTM_PROXY)
+ pq_flush(myport);
+
+ elog(LOG, "ProcessBeginReplicationInitialSyncRequest() ok.");
+
+ return;
+}
+
+/*
+ * Process MSG_NODE_END_REPLICATION_INIT
+ */
+void
+ProcessEndReplicationInitialSyncRequest(Port *myport, StringInfo message)
+{
+ StringInfoData buf;
+ MemoryContext oldContext;
+
+ pq_getmsgend(message);
+
+ if (Recovery_IsStandby())
+ ereport(ERROR,
+ (EPERM,
+ errmsg("Operation not permitted under the standby mode.")));
+
+ oldContext = MemoryContextSwitchTo(TopMemoryContext);
+
+ /*
+ * Release global locks after copying resource data to the standby.
+ */
+ GTM_RWLockRelease(&GTMTransactions.gt_TransArrayLock);
+ GTM_RWLockRelease(&GTMTransactions.gt_XidGenLock);
+ elog(LOG, "XidGenLock and TransArrayLock released.");
+
+ MemoryContextSwitchTo(oldContext);
+
+ pq_beginmessage(&buf, 'S');
+ pq_sendint(&buf, NODE_END_REPLICATION_INIT_RESULT, 4);
+ if (myport->remote_type == PGXC_NODE_GTM_PROXY)
+ {
+ GTM_ProxyMsgHeader proxyhdr;
+ proxyhdr.ph_conid = myport->conn_id;
+ pq_sendbytes(&buf, (char *)&proxyhdr, sizeof (GTM_ProxyMsgHeader));
+ }
+ pq_endmessage(myport, &buf);
+
+ if (myport->remote_type != PGXC_NODE_GTM_PROXY)
+ pq_flush(myport);
+
+ elog(LOG, "ProcessEndReplicationInitialSyncRequest() ok.");
+
+ return;
+}
diff --git a/src/gtm/recovery/standby_utils.c b/src/gtm/recovery/standby_utils.c
new file mode 100644
index 0000000000..4acf81c2b3
--- /dev/null
+++ b/src/gtm/recovery/standby_utils.c
@@ -0,0 +1,86 @@
+/*-------------------------------------------------------------------------
+ *
+ * standby_utils.c
+ * Utilities for GTM standby global values
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ *
+ * IDENTIFICATION
+ * src/gtm/recovery/standby_utils.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "gtm/gtm_c.h"
+#include "gtm/standby_utils.h"
+#include "gtm/gtm_lock.h"
+
+/*
+ * Variables to interact with GTM active under GTM standby mode.
+ */
+static bool GTM_StandbyMode = false;
+static char *GTM_ActiveAddress;
+static int GTM_ActivePort;
+
+/* For thread safety, values above are protected by a lock */
+static GTM_RWLock StandbyLock;
+
+bool
+Recovery_IsStandby(void)
+{
+ bool res;
+ GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_READ);
+ res = GTM_StandbyMode;
+ GTM_RWLockRelease(&StandbyLock);
+ return GTM_StandbyMode;
+}
+
+void
+Recovery_StandbySetStandby(bool standby)
+{
+ GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_WRITE);
+ GTM_StandbyMode = standby;
+ GTM_RWLockRelease(&StandbyLock);
+}
+
+void
+Recovery_StandbySetConnInfo(const char *addr, int port)
+{
+ GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_WRITE);
+ GTM_ActiveAddress = strdup(addr);
+ GTM_ActivePort = port;
+ GTM_RWLockRelease(&StandbyLock);
+}
+
+int
+Recovery_StandbyGetActivePort(void)
+{
+ int res;
+
+ GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_READ);
+ res = GTM_ActivePort;
+ GTM_RWLockRelease(&StandbyLock);
+
+ return res;
+}
+
+char *
+Recovery_StandbyGetActiveAddress(void)
+{
+ char *res;
+
+ GTM_RWLockAcquire(&StandbyLock, GTM_LOCKMODE_READ);
+ res = GTM_ActiveAddress;
+ GTM_RWLockRelease(&StandbyLock);
+
+ return res;
+}
+
+void
+Recovery_InitStandbyLock(void)
+{
+ GTM_RWLockInit(&StandbyLock);
+}
diff --git a/src/gtm/test/Makefile b/src/gtm/test/Makefile
new file mode 100644
index 0000000000..e6155ea7f6
--- /dev/null
+++ b/src/gtm/test/Makefile
@@ -0,0 +1,53 @@
+# Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+
+top_build_dir=../..
+include $(top_build_dir)/gtm/Makefile.global
+
+override CPPFLAGS := -I$(top_build_dir)/gtm/client $(CPPFLAGS)
+
+SRCS=test_serialize.c test_connect.c test_node.c test_node5.c test_txn.c test_txn4.c test_txn5.c test_repli.c test_repli2.c test_seq.c test_seq4.c test_seq5.c test_scenario.c test_startup.c test_standby.c test_common.c
+
+PROGS=test_serialize test_connect test_txn test_txn4 test_txn5 test_repli test_repli2 test_seq test_seq4 test_seq5 test_scenario test_startup test_node test_node5 test_standby
+
+OBJS=$(SRCS:.c=.o)
+LIBS=$(top_build_dir)/gtm/client/libgtmclient.a \
+ $(top_build_dir)/gtm/common/libgtm.a \
+ $(top_build_dir)/gtm/libpq/libpqcomm.a
+
+LOADLIBES=-lpthread
+CFLAGS=-g -O0
+
+all: $(PROGS)
+
+test_serialize: test_serialize.o test_common.o $(LIBS)
+
+test_connect: test_connect.o test_common.o $(LIBS)
+test_startup: test_startup.o test_common.o $(LIBS)
+
+test_node: test_node.o test_common.o $(LIBS)
+test_node5: test_node5.o test_common.o $(LIBS)
+
+test_txn: test_txn.o test_common.o $(LIBS)
+
+test_txn4: test_txn4.o test_common.o $(LIBS)
+test_txn5: test_txn5.o test_common.o $(LIBS)
+
+test_repli: test_repli.o test_common.o $(LIBS)
+
+test_standby: test_standby.o test_common.o $(LIBS)
+
+test_repli2: test_repli2.o test_common.o $(LIBS)
+
+test_seq: test_seq.o test_common.o $(LIBS)
+test_seq4: test_seq4.o test_common.o $(LIBS)
+test_seq5: test_seq5.o test_common.o $(LIBS)
+
+test_scenario: test_scenario.o test_common.o $(LIBS)
+
+clean:
+ rm -f $(OBJS) *~
+ rm -f $(PROGS)
+
+distclean: clean
+
+maintainer-clean: distclean
diff --git a/src/gtm/test/clean.sh b/src/gtm/test/clean.sh
new file mode 100644
index 0000000000..87c452b3ce
--- /dev/null
+++ b/src/gtm/test/clean.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# GTM start script for test
+
+pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd
+
+export PATH=/tmp/pgxc/bin:$PATH
+
+# -------------------------------
+# starting standby...
+# -------------------------------
+echo "cleaning standby..."
+export DATA=/tmp/pgxc/data/gtm_standby
+
+pushd $DATA
+rm -rf gtm.control gtm.opts gtm.pid register.node
+cat /dev/null > gtm.log
+popd
+
+# -------------------------------
+# starting active...
+# -------------------------------
+echo "cleaning active..."
+export DATA=/tmp/pgxc/data/gtm
+
+pushd $DATA
+rm -rf gtm.control gtm.opts gtm.pid register.node
+cat /dev/null > gtm.log
+popd
diff --git a/src/gtm/test/promote.sh b/src/gtm/test/promote.sh
new file mode 100644
index 0000000000..ae60e9ccb3
--- /dev/null
+++ b/src/gtm/test/promote.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# GTM start script for test
+
+pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd
+
+export PATH=/tmp/pgxc/bin:$PATH
+
+# -------------------------------
+# promoting standby...
+# -------------------------------
+echo "promoting standby..."
+export DATA=/tmp/pgxc/data/gtm_standby
+
+gtm_ctl -D ${DATA} -S gtm_standby promote
+
+# -------------------------------
+# process check
+# -------------------------------
+echo "checking process..."
+ps -aef |grep gtm
diff --git a/src/gtm/test/regress.sh b/src/gtm/test/regress.sh
new file mode 100644
index 0000000000..314d17cda9
--- /dev/null
+++ b/src/gtm/test/regress.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+cat /dev/null>regress.log
+
+./test_serialize | tee -a regress.log 2>&1
+
+./stop.sh
+./start_a.sh
+./test_connect 2>&1 | tee -a regress.log
+./test_node 2>&1 | tee -a regress.log
+./test_txn 2>&1 | tee -a regress.log
+./test_seq 2>&1 | tee -a regress.log
+
+echo ""
+echo "=========== SUMMARY ============"
+date
+echo -n "Assert: "
+grep -c ASSERT regress.log
diff --git a/src/gtm/test/start.sh b/src/gtm/test/start.sh
new file mode 100644
index 0000000000..24951ff77a
--- /dev/null
+++ b/src/gtm/test/start.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# GTM start script for test
+
+pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd
+
+./start_a.sh
+
+echo "sleeping 3 seconds..."
+sleep 3;
+
+./start_s.sh
diff --git a/src/gtm/test/start_a.sh b/src/gtm/test/start_a.sh
new file mode 100644
index 0000000000..4a6c1c16fa
--- /dev/null
+++ b/src/gtm/test/start_a.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# GTM start script for test
+
+pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd
+
+export PATH=/tmp/pgxc/bin:$PATH
+export DATA=/tmp/pgxc/data/gtm
+
+# -------------------------------
+# starting active...
+# -------------------------------
+echo "starting active..."
+
+gtm_ctl -D ${DATA} -S gtm stop
+rm -rf ${DATA}/gtm.opts ${DATA}/gtm.pid ${DATA}/register.node
+
+gtm_ctl -D ${DATA} -S gtm -o "-n 101" start
+
+# -------------------------------
+# process check
+# -------------------------------
+echo "checking process..."
+ps -aef |grep gtm
diff --git a/src/gtm/test/start_s.sh b/src/gtm/test/start_s.sh
new file mode 100644
index 0000000000..b016bcee32
--- /dev/null
+++ b/src/gtm/test/start_s.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# GTM start script for test
+
+pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd
+
+export PATH=/tmp/pgxc/bin:$PATH
+
+# -------------------------------
+# starting standby...
+# -------------------------------
+echo "starting standby..."
+
+export DATA=/tmp/pgxc/data/gtm_standby
+
+gtm_ctl -D ${DATA} -S gtm stop
+rm -rf ${DATA}/gtm.opts ${DATA}/gtm.pid ${DATA}/register.node
+
+gtm_ctl -D ${DATA} -S gtm_standby -o "-n 102 -s -p 6667 -i 127.0.0.1 -q 6666" start
+
+# -------------------------------
+# process check
+# -------------------------------
+echo "checking process..."
+ps -aef |grep gtm
diff --git a/src/gtm/test/stop.sh b/src/gtm/test/stop.sh
new file mode 100644
index 0000000000..4660ecc725
--- /dev/null
+++ b/src/gtm/test/stop.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# GTM start script for test
+
+pushd /tmp/pgxc/bin; ln -fs gtm gtm_standby; popd
+
+export PATH=/tmp/pgxc/bin:$PATH
+
+# -------------------------------
+# starting standby...
+# -------------------------------
+echo "stopping standby..."
+export DATA=/tmp/pgxc/data/gtm_standby
+
+gtm_ctl -D ${DATA} -S gtm stop
+
+# -------------------------------
+# starting active...
+# -------------------------------
+echo "stopping active..."
+export DATA=/tmp/pgxc/data/gtm
+
+gtm_ctl -D ${DATA} -S gtm stop
+
+killall -9 gtm gtm_standby
+
+# -------------------------------
+# process check
+# -------------------------------
+echo "checking process..."
+ps -aef |grep gtm
diff --git a/src/gtm/test/test_common.c b/src/gtm/test/test_common.c
new file mode 100644
index 0000000000..df8b5817e3
--- /dev/null
+++ b/src/gtm/test/test_common.c
@@ -0,0 +1,80 @@
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+GTM_ThreadID TopMostThreadID;
+
+GTM_Conn *conn = NULL;
+GTM_Conn *conn2 = NULL;
+GTM_Timestamp *timestamp = NULL;
+char connect_string[100];
+
+void
+print_nodeinfo(GTM_PGXCNodeInfo d)
+{
+ client_log(("type=%d, nodenum=%d, proxynum=%d, ipaddress=%s, port=%d, datafolder=%s, status=%d\n",
+ d.type,
+ d.nodenum,
+ d.proxynum,
+ d.ipaddress,
+ d.port,
+ d.datafolder,
+ d.status));
+}
+
+
+/*
+ * Connect to active GTM.
+ */
+void
+connect1()
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+/*
+ * Connect to standby GTM.
+ */
+void
+connect2()
+{
+ sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=102 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+
+/*
+ * Get a word count with using grep command in a log file.
+ */
+int
+grep_count(const char *file, const char *key)
+{
+ FILE *fp;
+ int count;
+ char cmd[1024];
+
+ snprintf(cmd, sizeof(cmd), "grep -c '%s' %s", key, file);
+
+ fp = popen(cmd, "r");
+ fscanf(fp, "%d", &count);
+ pclose(fp);
+
+ return count;
+}
diff --git a/src/gtm/test/test_connect.c b/src/gtm/test/test_connect.c
new file mode 100644
index 0000000000..cb25378455
--- /dev/null
+++ b/src/gtm/test/test_connect.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+#define client_log(x) printf x
+
+void
+setUp()
+{
+}
+
+void
+tearDown()
+{
+}
+
+void
+test01()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM_PROXY);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test02()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM_PROXY_POSTMASTER);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test03()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_COORDINATOR);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test04()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_DATANODE);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test05()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test06()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_DEFAULT);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test07()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ 12);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test08()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+
+ SETUP();
+
+ sprintf(connect_string, "host=localhost port=6668 pgxc_node_id=1 remote_type=%d",
+ 12);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+
+ printf("conn = %p\n", conn);
+
+ client_log(("PGconnectGTM() ok.\n"));
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+ test06();
+ test07(); /* must fail */
+ test08(); /* must fail */
+
+ return 0;
+}
diff --git a/src/gtm/test/test_node.c b/src/gtm/test/test_node.c
new file mode 100644
index 0000000000..c83ac552eb
--- /dev/null
+++ b/src/gtm/test/test_node.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ connect1();
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_node_01()
+{
+ int rc;
+
+ SETUP();
+
+ rc = node_register(conn, PGXC_NODE_DATANODE, 6666, 1, "/tmp/pgxc/data/gtm");
+ _ASSERT( rc >= 0 );
+
+ TEARDOWN();
+}
+
+void
+test_node_02()
+{
+ int rc;
+
+ SETUP();
+
+ rc = node_unregister(conn, PGXC_NODE_DATANODE, 1);
+ _ASSERT( rc >= 0 );
+
+ TEARDOWN();
+}
+
+void
+test_node_03()
+{
+ GTM_PGXCNodeInfo *data;
+ int rc, i;
+
+ SETUP();
+
+ data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo)*128 );
+ memset(data, 0, sizeof(GTM_PGXCNodeInfo)*128);
+
+ rc = get_node_list(conn, data, 128);
+ _ASSERT( rc>=0 );
+
+ for (i=0 ; i<rc ; i++)
+ {
+ print_nodeinfo(data[i]);
+ }
+
+ free(data);
+
+ TEARDOWN();
+}
+
+void
+test_node_04()
+{
+ GTM_PGXCNodeInfo *data;
+ int rc, i;
+
+ SETUP();
+
+ data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo)*128 );
+ memset(data, 0, sizeof(GTM_PGXCNodeInfo)*128);
+
+ rc = node_register(conn, PGXC_NODE_DATANODE, 6666, 1, "/tmp/pgxc/data/gtm");
+ _ASSERT( rc>=0 );
+
+ rc = get_node_list(conn, data, 128);
+ _ASSERT( rc>=0 );
+
+ for (i=0 ; i<rc ; i++)
+ {
+ print_nodeinfo(data[i]);
+ }
+
+ free(data);
+
+ TEARDOWN();
+}
+
+
+void
+test_node_05()
+{
+ int rc, i;
+
+ SETUP();
+
+ rc = node_unregister(conn, PGXC_NODE_DATANODE, 101);
+
+ rc = node_register(conn, PGXC_NODE_DATANODE, 6666, 101, "/tmp/pgxc/data/gtm");
+ _ASSERT( rc>=0 );
+
+ sleep(5);
+
+ rc = backend_disconnect(conn, true, PGXC_NODE_DATANODE, 101);
+ _ASSERT( rc>=0 );
+
+ rc = node_unregister(conn, PGXC_NODE_DATANODE, 101);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ test_node_01();
+ test_node_02();
+
+ test_node_03();
+ test_node_04();
+
+ test_node_05();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_node5.c b/src/gtm/test/test_node5.c
new file mode 100644
index 0000000000..7454b8795a
--- /dev/null
+++ b/src/gtm/test/test_node5.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ system("./stop.sh");
+ system("./clean.sh");
+ system("./start.sh");
+ sleep(1);
+}
+
+void
+tearDown()
+{
+}
+
+void
+test_node5_01()
+{
+ int rc;
+
+ SETUP();
+
+ /*
+ * active
+ */
+ connect1();
+
+ rc = node_register(conn, PGXC_NODE_DATANODE, 16666, 1001, "/tmp/pgxc/data/gtm");
+ _ASSERT( rc >= 0 );
+
+ GTMPQfinish(conn);
+ sleep(3);
+
+ /*
+ * standby
+ */
+ connect2();
+
+ rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001);
+ _ASSERT( rc >= 0 );
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test_node5_02()
+{
+ int rc;
+
+ SETUP();
+
+ /*
+ * active
+ */
+ connect1();
+
+ rc = node_register(conn, PGXC_NODE_DATANODE, 16666, 1001, "/tmp/pgxc/data/gtm");
+ _ASSERT( rc >= 0 );
+
+ rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001);
+ _ASSERT( rc >= 0 );
+
+ GTMPQfinish(conn);
+ sleep(3);
+
+ /*
+ * standby
+ */
+ connect2();
+
+ rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001);
+ _ASSERT( rc<0 );
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+void
+test_node5_03()
+{
+ int rc;
+
+ SETUP();
+
+ /*
+ * active
+ */
+ connect1();
+
+ rc = node_register(conn, PGXC_NODE_DATANODE, 16666, 1001, "/tmp/pgxc/data/gtm");
+ _ASSERT( rc >= 0 );
+
+ system("killall -9 gtm");
+ system("./promote.sh");
+ sleep(1);
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = node_unregister(conn, PGXC_NODE_DATANODE, 1001);
+ _ASSERT( rc >= 0 );
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test_node5_01();
+ test_node5_02();
+
+ test_node5_03();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_repli.c b/src/gtm/test/test_repli.c
new file mode 100644
index 0000000000..7edbbab3b7
--- /dev/null
+++ b/src/gtm/test/test_repli.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+#include "gtm/register.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+
+ _ASSERT( conn!=NULL );
+
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ rc = begin_replication_initial_sync(conn);
+ if ( rc>=0 )
+ client_log(("begin_replication_initial_sync() ok.\n"));
+ else
+ client_log(("begin_replication_initial_sync() failed.\n"));
+
+ rc = end_replication_initial_sync(conn);
+ if ( rc>=0 )
+ client_log(("end_replication_initial_sync() ok.\n"));
+ else
+ client_log(("end_replication_initial_sync() failed.\n"));
+
+ TEARDOWN();
+}
+
+void
+test04()
+{
+ GTM_PGXCNodeInfo *data;
+ int rc, i;
+ GTM_Transactions txn;
+ GlobalTransactionId tmp;
+ GTM_Timestamp ts;
+
+ // data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo)*128 );
+ // memset(data, 0, sizeof(GTM_PGXCNodeInfo)*128);
+
+ SETUP();
+
+ tmp = begin_transaction(conn, GTM_ISOLATION_RC, &ts);
+
+ rc = get_txn_gxid_list(conn, &txn);
+ if ( rc>=0 )
+ client_log(("get_txn_gxid_list() ok.\n"));
+ else
+ client_log(("get_txn_gxid_list() failed.\n"));
+
+ abort_transaction(conn, tmp);
+
+ TEARDOWN();
+}
+
+void
+test05()
+{
+ GlobalTransactionId next_gxid;
+ int rc, i;
+
+ SETUP();
+
+ next_gxid = get_next_gxid(conn);
+ if ( next_gxid!=InvalidGlobalTransactionId )
+ client_log(("get_next_gxid() ok. - %ld\n", next_gxid));
+ else
+ client_log(("get_txn_gxid_list() failed.\n"));
+
+ TEARDOWN();
+}
+
+void
+test08()
+{
+ GlobalTransactionId next_gxid;
+ GTM_SequenceKeyData key1;
+ GTM_SequenceKeyData key2;
+ GTM_SequenceKeyData key3;
+ int rc, i;
+ GTM_SeqInfo *seq_list[1024];
+
+ SETUP();
+
+ key1.gsk_keylen = strlen("seq1");
+ key1.gsk_key = strdup("seq1");
+ key2.gsk_keylen = strlen("seq2");
+ key2.gsk_key = strdup("seq2");
+ key3.gsk_keylen = strlen("seq3");
+ key3.gsk_key = strdup("seq3");
+
+ close_sequence(conn, &key1);
+ close_sequence(conn, &key2);
+ close_sequence(conn, &key3);
+
+ for (i=0 ; i<1024 ; i++)
+ seq_list[i] = NULL;
+
+ rc = get_sequence_list(conn, seq_list, 1024);
+ if ( rc>=0 )
+ {
+ client_log(("get_seq_list() ok. - %d\n", rc));
+
+ for (i=0 ; i<rc ; i++)
+ {
+ client_log(("key = %s\n", seq_list[i]->gs_key->gsk_key));
+ }
+ }
+ else
+ client_log(("get_seq_list() failed.\n"));
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test01();
+ test04();
+ /*
+ test05();
+ test08();
+ */
+
+ return 0;
+}
diff --git a/src/gtm/test/test_repli2.c b/src/gtm/test/test_repli2.c
new file mode 100644
index 0000000000..46c64d42e7
--- /dev/null
+++ b/src/gtm/test/test_repli2.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+#include "gtm/register.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+
+ _ASSERT( conn!=NULL );
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test01()
+{
+ int rc;
+ char host[1024];
+
+ SETUP();
+
+ node_get_local_addr(conn, host, sizeof(host));
+
+ rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 101, "/tmp/pgxc/data/gtm_standby", NODE_DISCONNECTED);
+ _ASSERT(rc == 0);
+
+ rc = node_unregister(conn, PGXC_NODE_GTM, 101);
+ _ASSERT(rc == 0);
+
+ TEARDOWN();
+}
+
+void
+test02()
+{
+ int rc;
+ char host[1024];
+
+ SETUP();
+
+ node_get_local_addr(conn, host, sizeof(host));
+
+ /*
+ * If a node already registered as a "DISCONNECTED" node,
+ * node_register() would not detect conflict of node information.
+ *
+ * See pgxcnode_add_info() for more details.
+ */
+ rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 101, "/tmp/pgxc/data/gtm_standby", NODE_CONNECTED);
+ _ASSERT(rc == 0);
+
+ rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 101, "/tmp/pgxc/data/gtm_standby", NODE_CONNECTED);
+ _ASSERT(rc != 0);
+
+ TEARDOWN();
+}
+
+void
+test03()
+{
+ int rc;
+
+ SETUP();
+
+ rc = node_unregister(conn, PGXC_NODE_GTM, 101);
+ _ASSERT( rc==0 );
+
+ TEARDOWN();
+}
+
+void
+test04()
+{
+ int rc;
+
+ SETUP();
+
+ rc = node_unregister(conn, PGXC_NODE_GTM, 101);
+ _ASSERT( rc!=0 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test01();
+ test02();
+ test03();
+ test04();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_scenario.c b/src/gtm/test/test_scenario.c
new file mode 100644
index 0000000000..91c146ed3e
--- /dev/null
+++ b/src/gtm/test/test_scenario.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+#include "gtm/register.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+
+ _ASSERT( conn!=NULL );
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+ char host[1024];
+
+ printf("\n=== test01:node_register ===\n");
+
+ setUp();
+
+ node_get_local_addr(conn, host, sizeof(host));
+
+ /*
+ * starting
+ */
+ rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 102, "/tmp/pgxc/data/gtm_standby", NODE_DISCONNECTED);
+ _ASSERT(rc == 0);
+ rc = node_unregister(conn, PGXC_NODE_GTM, 102);
+ _ASSERT(rc == 0);
+
+ rc = node_register_internal(conn, PGXC_NODE_GTM, host, 6667, 102, "/tmp/pgxc/data/gtm_standby", NODE_CONNECTED);
+ _ASSERT(rc == 0);
+
+ sleep(10);
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid!=InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc!=-1 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc!=-1 );
+
+ sleep(10);
+
+ /*
+ * closing
+ */
+ rc = node_unregister(conn, PGXC_NODE_GTM, 102);
+ _ASSERT( rc==0 );
+
+ tearDown();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test01();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_seq.c b/src/gtm/test/test_seq.c
new file mode 100644
index 0000000000..e86e9ede4f
--- /dev/null
+++ b/src/gtm/test/test_seq.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+#include "gtm/register.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ int i;
+
+ connect1();
+
+ /* delete old stuffs */
+ for (i=0 ; i<10 ; i++)
+ {
+ GTM_SequenceKeyData seqkey;
+ char key[16];
+
+ snprintf(key, sizeof(key), "seq%d", i);
+ seqkey.gsk_key = key;
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ close_sequence(conn, &seqkey);
+ }
+
+ /* Create the first one. */
+ {
+ GTM_SequenceKeyData seqkey;
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+ int rc;
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ increment = 1;
+ minval = 0;
+ maxval = 10000;
+ startval = 0;
+ cycle = true;
+
+ rc = open_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ cycle);
+
+ _ASSERT( rc>=0 );
+ }
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_seq_01()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq2");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ increment = 1;
+ minval = 0;
+ maxval = 10000;
+ startval = 0;
+ cycle = true;
+
+ rc = open_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ cycle);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_seq_02()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ rc = close_sequence(conn, &seqkey);
+
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_seq_03()
+{
+ GTM_SequenceKeyData seqkey;
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ cur = get_current(conn, &seqkey);
+ _ASSERT( cur==0 );
+
+ TEARDOWN();
+}
+
+void
+test_seq_04()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==0 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==2 );
+
+ TEARDOWN();
+}
+
+void
+test_seq_05()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ rc = set_val(conn, &seqkey, 1000, true);
+ _ASSERT( rc>=0 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1001 );
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1002 );
+
+ /*
+ * FIXME: When `iscalled' is false, set_val() does not affect the sequence value.
+ */
+ rc = set_val(conn, &seqkey, 1000, false);
+ _ASSERT( rc>=0 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==0 ); /* FIXME: */
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1 ); /* FIXME: */
+
+ TEARDOWN();
+}
+
+
+void
+test_seq_06()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ int i;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ for (i=0 ; i<1000 ; i++)
+ {
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==i );
+ }
+ /* get_next() x 1000 done. */
+
+ rc = reset_sequence(conn, &seqkey);
+ _ASSERT( rc>=0 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1 );
+
+ TEARDOWN();
+}
+
+void
+test_seq_07()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_SequenceKeyData newseqkey;
+ int i;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ /* create a key */
+ newseqkey.gsk_key = strdup("seqnew");
+ newseqkey.gsk_keylen = strlen(newseqkey.gsk_key);
+ newseqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ rc = rename_sequence(conn, &seqkey, &newseqkey);
+ _ASSERT( rc>=0 );
+
+ cur = get_next(conn, &newseqkey);
+ _ASSERT( cur==0 );
+
+ cur = get_next(conn, &newseqkey);
+ _ASSERT( cur==1 );
+
+ rc = close_sequence(conn, &newseqkey);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+
+void
+test_seq_08()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_SequenceKeyData newseqkey;
+ int i;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /* create a key */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==0 );
+
+ {
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ increment = 1;
+ minval = 100;
+ maxval = 10000;
+ startval = 100;
+ cycle = true;
+
+ rc = alter_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ 256, /* lastval */
+ cycle,
+ true);
+
+ _ASSERT( rc>=0 );
+ }
+
+ cur = get_current(conn, &seqkey);
+ _ASSERT( cur==100 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==101 );
+
+ TEARDOWN();
+}
+
+
+void
+test_seq_09()
+{
+ GlobalTransactionId next_gxid;
+ GTM_SequenceKeyData key1;
+ GTM_SequenceKeyData key2;
+ GTM_SequenceKeyData key3;
+ int rc, i;
+ GTM_SeqInfo *seq_list[1024];
+
+ SETUP();
+
+ key1.gsk_keylen = strlen("seq1");
+ key1.gsk_key = strdup("seq1");
+ key1.gsk_type = GTM_SEQ_FULL_NAME;
+
+ key2.gsk_keylen = strlen("seq2");
+ key2.gsk_key = strdup("seq2");
+ key3.gsk_type = GTM_SEQ_FULL_NAME;
+ key3.gsk_keylen = strlen("seq3");
+ key3.gsk_key = strdup("seq3");
+ key3.gsk_type = GTM_SEQ_FULL_NAME;
+
+ /* name, increment, min, max, start, cycle */
+ rc = open_sequence(conn, &key2, 1, 1, 10000, 5, true);
+ _ASSERT( rc==0 );
+ rc = open_sequence(conn, &key3, 1, 1, 10000, 7, true);
+ _ASSERT( rc==0 );
+
+ memset(seq_list, 0, sizeof(GTM_SeqInfo *) * 1024);
+
+ rc = get_sequence_list(conn, seq_list, 1024);
+ _ASSERT( rc==3 );
+
+ _ASSERT( strncmp(seq_list[0]->gs_key->gsk_key, key1.gsk_key, 4)==0 );
+ _ASSERT( strncmp(seq_list[1]->gs_key->gsk_key, key2.gsk_key, 4)==0 );
+ _ASSERT( strncmp(seq_list[2]->gs_key->gsk_key, key3.gsk_key, 4)==0 );
+
+ {
+ GTM_Sequence cur;
+
+ cur = get_next(conn, seq_list[0]->gs_key);
+ fprintf(stderr, "key=%s, cur=%d\n", seq_list[0]->gs_key->gsk_key, cur);
+ _ASSERT( cur==0 );
+
+ cur = get_next(conn, seq_list[1]->gs_key);
+ fprintf(stderr, "key=%s, cur=%d\n", seq_list[1]->gs_key->gsk_key, cur);
+ _ASSERT( cur==5 );
+
+ cur = get_next(conn, seq_list[2]->gs_key);
+ fprintf(stderr, "key=%s, cur=%d\n", seq_list[2]->gs_key->gsk_key, cur);
+ _ASSERT( cur==7 );
+ }
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test_seq_01();
+ test_seq_02();
+
+ test_seq_03();
+ test_seq_04();
+
+ test_seq_05(); /* set_val */
+ test_seq_06(); /* reset_sequence */
+
+ test_seq_07(); /* rename_sequence */
+
+ test_seq_08(); /* alter_sequence */
+
+ test_seq_09(); /* get_sequence_list */
+
+ return 0;
+}
diff --git a/src/gtm/test/test_seq4.c b/src/gtm/test/test_seq4.c
new file mode 100644
index 0000000000..1b0c4b9d7d
--- /dev/null
+++ b/src/gtm/test/test_seq4.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+#include "gtm/register.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ connect1();
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_seq4_01()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /*
+ * open sequence
+ */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ increment = 1;
+ minval = 0;
+ maxval = 10000;
+ startval = 0;
+ cycle = true;
+
+ rc = open_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ cycle);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Opening sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Opening sequence seq1")==1 );
+
+ /*
+ * get current
+ */
+ cur = get_current(conn, &seqkey);
+ _ASSERT( cur==0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 0 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting current value 0 for sequence seq1")==1 );
+
+ cur = get_current(conn, &seqkey);
+ _ASSERT( cur==0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 0 for sequence seq1")==2 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting current value 0 for sequence seq1")==2 );
+
+ /*
+ * get next
+ */
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 1 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 1 for sequence seq1")==1 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==2 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 2 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 2 for sequence seq1")==1 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==3 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 3 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 3 for sequence seq1")==1 );
+
+ /*
+ * set value
+ */
+ rc = set_val(conn, &seqkey, 13, true);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Setting new value 13 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Setting new value 13 for sequence seq1")==1 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==14 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 14 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 14 for sequence seq1")==1 );
+
+#ifdef NOT_USED /* FIXME: snaga
+ /*
+ * FIXME: When `iscalled' is false, set_val() does not affect the sequence value.
+ */
+ rc = set_val(conn, &seqkey, 1000, false);
+#endif
+
+ /*
+ * reset
+ */
+ rc = reset_sequence(conn, &seqkey);
+ _ASSERT( cur>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Resetting sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Resetting sequence seq1")==1 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 1 for sequence seq1")==2 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 1 for sequence seq1")==2 );
+
+ /*
+ * close
+ */
+ rc = close_sequence(conn, &seqkey);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Closing sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Closing sequence seq1")==1 );
+
+ TEARDOWN();
+}
+
+
+void
+test_seq4_02()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_SequenceKeyData newseqkey;
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ SETUP();
+
+ /*
+ * create a sequence
+ */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ increment = 1;
+ minval = 0;
+ maxval = 10000;
+ startval = 0;
+ cycle = true;
+
+ rc = open_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ cycle);
+ _ASSERT( rc>=0 );
+
+ /* create a new key */
+ newseqkey.gsk_key = strdup("seqnew");
+ newseqkey.gsk_keylen = strlen(newseqkey.gsk_key);
+ newseqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ /*
+ * rename key
+ */
+ rc = rename_sequence(conn, &seqkey, &newseqkey);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Renaming sequence seq1 to seqnew")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Renaming sequence seq1 to seqnew")==1 );
+
+ rc = close_sequence(conn, &newseqkey);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Closing sequence seqnew")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Closing sequence seqnew")==1 );
+
+ TEARDOWN();
+}
+
+
+void
+test_seq4_03()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_SequenceKeyData newseqkey;
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /*
+ * create a sequence
+ */
+ seqkey.gsk_key = strdup("seq2");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ increment = 1;
+ minval = 0;
+ maxval = 10000;
+ startval = 0;
+ cycle = true;
+
+ rc = open_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ cycle);
+ _ASSERT( rc>=0 );
+
+ {
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ increment = 1;
+ minval = 100;
+ maxval = 10000;
+ startval = 100;
+ cycle = true;
+
+ rc = alter_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ 256, /* lastval */
+ cycle,
+ true);
+
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Altering sequence key seq2")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Altering sequence key seq2")==1 );
+ }
+
+ cur = get_current(conn, &seqkey);
+ _ASSERT( cur==100 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 100 for sequence seq2")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting current value 100 for sequence seq2")==1 );
+
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==101 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 101 for sequence seq2")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 101 for sequence seq2")==1 );
+
+ TEARDOWN();
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ system("./stop.sh");
+ system("./clean.sh");
+ system("./start.sh");
+
+ test_seq4_01();
+ test_seq4_02(); /* rename */
+ test_seq4_03(); /* alter */
+
+ return 0;
+}
diff --git a/src/gtm/test/test_seq5.c b/src/gtm/test/test_seq5.c
new file mode 100644
index 0000000000..f14f14a23b
--- /dev/null
+++ b/src/gtm/test/test_seq5.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+#include "gtm/register.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ connect1();
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_seq5_01()
+{
+ int rc;
+ GTM_SequenceKeyData seqkey;
+ GTM_Sequence increment;
+ GTM_Sequence minval;
+ GTM_Sequence maxval;
+ GTM_Sequence startval;
+ bool cycle;
+
+ GTM_Sequence cur;
+
+ SETUP();
+
+ /*
+ * open sequence
+ */
+ seqkey.gsk_key = strdup("seq1");
+ seqkey.gsk_keylen = strlen(seqkey.gsk_key);
+ seqkey.gsk_type = GTM_SEQ_FULL_NAME;
+
+ increment = 1;
+ minval = 0;
+ maxval = 10000;
+ startval = 0;
+ cycle = true;
+
+ rc = open_sequence(conn,
+ &seqkey,
+ increment,
+ minval,
+ maxval,
+ startval,
+ cycle);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Opening sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Opening sequence seq1")==1 );
+
+ /* get current */
+ cur = get_current(conn, &seqkey);
+ _ASSERT( cur==0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting current value 0 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting current value 0 for sequence seq1")==1 );
+
+ /* get next */
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Getting next value 1 for sequence seq1")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 1 for sequence seq1")==1 );
+
+ system("killall -9 gtm");
+ system("./promote.sh");
+ sleep(1);
+
+ GTMPQfinish(conn);
+ connect2();
+
+ /* get current */
+ cur = get_current(conn, &seqkey);
+ _ASSERT( cur==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting current value 1 for sequence seq1")==1 );
+
+ /* get next */
+ cur = get_next(conn, &seqkey);
+ _ASSERT( cur==2 );
+ _ASSERT( grep_count(LOG_STANDBY, "Getting next value 2 for sequence seq1")==1 );
+
+ /*
+ * close
+ */
+ rc = close_sequence(conn, &seqkey);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_STANDBY, "Closing sequence seq1")==1 );
+
+ TEARDOWN();
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ system("./stop.sh");
+ system("./clean.sh");
+ system("./start.sh");
+
+ test_seq5_01();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_serialize.c b/src/gtm/test/test_serialize.c
new file mode 100644
index 0000000000..95afef3ac6
--- /dev/null
+++ b/src/gtm/test/test_serialize.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "gtm/gtm_c.h"
+#include "gtm/elog.h"
+#include "gtm/palloc.h"
+#include "gtm/gtm.h"
+#include "gtm/gtm_txn.h"
+#include "gtm/assert.h"
+#include "gtm/stringinfo.h"
+#include "gtm/register.h"
+#include "gtm/libpq.h"
+#include "gtm/pqformat.h"
+#include "gtm/gtm_msg.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+}
+
+void
+tearDown()
+{
+}
+
+int
+test_snapshotdata_1(void)
+{
+ GTM_SnapshotData *data, *data2;
+ char *buf;
+ int buflen;
+
+ SETUP();
+
+ /* build a dummy GTM_SnapshotData data. */
+ data = (GTM_SnapshotData *)malloc(sizeof(GTM_SnapshotData));
+
+ data->sn_xmin = 128;
+ data->sn_xmax = 256;
+ data->sn_recent_global_xmin = 512;
+ data->sn_xcnt = 1024;
+ data->sn_xip = 2048;
+
+ printf("sn_xmin=%d, sn_xmax=%d, sn_recent_global_xmin=%d, sn_xcnt=%d, sn_xip=%d\n",
+ data->sn_xmin, data->sn_xmax, data->sn_recent_global_xmin, data->sn_xcnt, data->sn_xip);
+
+ /* serialize */
+ buflen = sizeof(GTM_SnapshotData);
+ buf = (char *)malloc(buflen);
+ gtm_serialize_snapshotdata(data, buf, buflen);
+
+ /* destroy old buf */
+ memset(data, 0, sizeof(GTM_SnapshotData));
+ free(data);
+
+ /* deserialize */
+ data2 = (GTM_SnapshotData *)malloc(sizeof(GTM_SnapshotData));
+ gtm_deserialize_snapshotdata(data2, buf, buflen);
+ printf("sn_xmin=%d, sn_xmax=%d, sn_recent_global_xmin=%d, sn_xcnt=%d, sn_xip=%d\n",
+ data2->sn_xmin, data2->sn_xmax, data2->sn_recent_global_xmin, data2->sn_xcnt, data2->sn_xip);
+
+ TEARDOWN();
+
+ return 0;
+}
+
+
+GTM_TransactionInfo *
+build_dummy_gtm_transactioninfo()
+{
+ GTM_TransactionInfo *data;
+
+ data = (GTM_TransactionInfo *)malloc( sizeof(GTM_TransactionInfo) );
+
+ data->gti_handle = 3;
+ data->gti_backend_id = 13;
+ data->gti_datanodecount = 0;
+ data->gti_datanodes = NULL;
+ data->gti_coordcount = 0;
+ data->gti_coordinators = NULL;
+
+ data->gti_gid = "hoge";
+
+ data->gti_current_snapshot.sn_xmin = 128;
+ data->gti_current_snapshot.sn_xmax = 256;
+ data->gti_current_snapshot.sn_recent_global_xmin = 512;
+ data->gti_current_snapshot.sn_xcnt = 1024;
+ data->gti_current_snapshot.sn_xip = 2048;
+
+ return data;
+}
+
+
+int
+test_transactioninfo_1(void)
+{
+ GTM_TransactionInfo *data,*data2;
+ char *buf;
+ int buflen;
+
+ PGXC_NodeId datanode[3];
+ PGXC_NodeId coordnode[5];
+
+ SETUP();
+
+ /* build a dummy GTM_SnapshotData data. */
+ data = build_dummy_gtm_transactioninfo();
+ data->gti_datanodecount = 3;
+ data->gti_datanodes = datanode;
+ data->gti_coordcount = 5;
+ data->gti_coordinators = coordnode;
+
+ /* serialize */
+ buflen = sizeof( GTM_TransactionInfo );
+ buf = (char *)malloc(buflen);
+ gtm_serialize_transactioninfo(data, buf, buflen);
+
+ /* destroy old buf */
+ memset(data, 0, sizeof(GTM_TransactionInfo));
+ free(data);
+
+ /* deserialize */
+ data2 = (GTM_TransactionInfo *)malloc(sizeof(GTM_TransactionInfo));
+ gtm_deserialize_transactioninfo(data2, buf, buflen);
+
+ printf("deserialized.\n");
+
+ _ASSERT(data2->gti_handle==3);
+ _ASSERT(data2->gti_backend_id==13);
+ _ASSERT(data2->gti_datanodecount==3);
+ _ASSERT(data2->gti_coordcount==5);
+ _ASSERT(data2->gti_current_snapshot.sn_xmin==128);
+ _ASSERT(data2->gti_current_snapshot.sn_xmax==256);
+ _ASSERT(data2->gti_current_snapshot.sn_recent_global_xmin==512);
+ _ASSERT(data2->gti_current_snapshot.sn_xcnt==1024);
+ _ASSERT(data2->gti_current_snapshot.sn_xip==2048);
+
+ TEARDOWN();
+
+ return 0;
+}
+
+int
+test_transactions_1(void)
+{
+ GTM_Transactions *data,*data2;
+ GTM_TransactionInfo *d;
+ char *buf;
+ int buflen;
+ PGXC_NodeId datanode[3];
+ PGXC_NodeId coordnode[5];
+
+ SETUP();
+
+ data = (GTM_Transactions *)malloc( sizeof(GTM_Transactions) );
+ data->gt_lastslot = 13;
+
+ /* build a dummy GTM_TransactionInfo data. */
+ d = build_dummy_gtm_transactioninfo();
+ d->gti_datanodecount = 3;
+ d->gti_datanodes = datanode;
+ d->gti_coordcount = 5;
+ d->gti_coordinators = coordnode;
+
+ memcpy(&data->gt_transactions_array[0], d, sizeof(data->gt_transactions_array[0]));
+
+ printf("gt_lastslot=%d\n",
+ data->gt_lastslot);
+
+ /* serialize */
+ buflen = gtm_get_transactions_size( data );
+ buf = (char *)malloc(buflen);
+
+ if ( !gtm_serialize_transactions(data, buf, buflen) )
+ {
+ printf("error.\n");
+ exit(1);
+ }
+
+ /* destroy old buf */
+ memset(data, 0, sizeof(GTM_Transactions));
+ free(data);
+
+ /* deserialize */
+ data2 = (GTM_Transactions *)malloc(sizeof(GTM_Transactions));
+ gtm_deserialize_transactions(data2, buf, buflen);
+
+ printf("deserialized.\n");
+
+ printf("gt_lastslot=%d\n",
+ data2->gt_lastslot);
+
+ printf("gti_handle=%d, gti_backend_id=%d\n",
+ data2->gt_transactions_array[0].gti_handle,
+ data2->gt_transactions_array[0].gti_backend_id);
+
+ TEARDOWN();
+
+ return 0;
+}
+
+
+void
+test_pgxcnodeinfo_1()
+{
+ GTM_PGXCNodeInfo *data,*data2;
+ char *buf;
+ size_t buflen;
+
+ SETUP();
+
+ data = (GTM_PGXCNodeInfo *)malloc( sizeof(GTM_PGXCNodeInfo) );
+ data->type = 2;
+ data->nodenum = 3;
+ data->port = 7;
+ data->ipaddress = "foo";
+ data->datafolder = "bar";
+
+ printf("type=%d, nodenum=%d, port=%d, ipaddress=%s, datafolder=%s\n",
+ data->type, data->nodenum, data->port,
+ data->ipaddress, data->datafolder);
+
+ /* serialize */
+ buflen = gtm_get_pgxcnodeinfo_size( data );
+ buf = (char *)malloc(buflen);
+
+ if ( !gtm_serialize_pgxcnodeinfo(data, buf, buflen) )
+ {
+ printf("error.\n");
+ exit(1);
+ }
+
+ /* destroy old buf */
+ memset(data, 0, sizeof(GTM_PGXCNodeInfo));
+ free(data);
+
+ /* deserialize */
+ data2 = (GTM_PGXCNodeInfo *)malloc(sizeof(GTM_PGXCNodeInfo));
+ gtm_deserialize_pgxcnodeinfo(data2, buf, buflen);
+
+ printf("deserialized.\n");
+
+ printf("type=%d, nodenum=%d, port=%d, ipaddress=%s, datafolder=%s\n",
+ data2->type, data2->nodenum, data2->port,
+ data2->ipaddress, data2->datafolder);
+
+ TEARDOWN();
+
+ return 0;
+}
+
+int
+test_seqinfo_1(void)
+{
+ GTM_SeqInfo *d1, *d2;
+ char *buf;
+ int buflen;
+ GTM_SequenceKeyData gs_key;
+
+ SETUP();
+
+ /* build a dummy GTM_SnapshotData data. */
+ d1 = (GTM_SeqInfo *)malloc(sizeof(GTM_SeqInfo));
+
+ gs_key.gsk_keylen = 0;
+ gs_key.gsk_key = "";
+ gs_key.gsk_type = GTM_SEQ_DB_NAME;
+
+ d1->gs_key = &gs_key;
+ d1->gs_value = 3;
+ d1->gs_init_value = 5;
+ d1->gs_last_value = 7;
+ d1->gs_increment_by = 11;
+ d1->gs_min_value = 13;
+ d1->gs_max_value = 17;
+ d1->gs_cycle = true;
+ d1->gs_called = true;
+ d1->gs_ref_count = 19;
+ d1->gs_state = 23;
+
+ /* serialize */
+ buflen = gtm_get_sequence_size(d1);
+ buf = (char *)malloc(buflen);
+ gtm_serialize_sequence(d1, buf, buflen);
+
+ /* destroy old buf */
+ memset(d1, 0, sizeof(GTM_SeqInfo));
+ free(d1);
+
+ /* deserialize */
+ // d2 = (GTM_SeqInfo *)malloc(sizeof(GTM_SeqInfo));
+ d2 = gtm_deserialize_sequence(buf, buflen);
+
+ _ASSERT( d2->gs_key->gsk_keylen==0 );
+ _ASSERT( strcmp(d2->gs_key->gsk_key,"")==0 );
+ _ASSERT( d2->gs_key->gsk_type==GTM_SEQ_DB_NAME );
+
+ TEARDOWN();
+
+ return 0;
+}
+
+int
+main(void)
+{
+ test_snapshotdata_1();
+ test_transactioninfo_1();
+ test_transactions_1();
+ test_pgxcnodeinfo_1();
+ test_seqinfo_1();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_standby.c b/src/gtm/test/test_standby.c
new file mode 100644
index 0000000000..de53255d71
--- /dev/null
+++ b/src/gtm/test/test_standby.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ system("./stop.sh > /dev/null");
+ system("./clean.sh > /dev/null");
+ system("./start.sh > /dev/null");
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_standby_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ system("killall -9 gtm_standby");
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+
+ _ASSERT( conn!=NULL );
+ _ASSERT( grep_count(LOG_ACTIVE, "Failed to establish a connection with GTM standby")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+
+ TEARDOWN();
+}
+
+void
+test_standby_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ connect1();
+ _ASSERT( conn!=NULL );
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 );
+
+ system("killall -9 gtm_standby");
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby.")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 );
+
+ TEARDOWN();
+}
+
+void
+test_standby_03()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ connect1();
+ _ASSERT( conn!=NULL );
+
+ system("./status_a.sh > status");
+ _ASSERT( grep_count("status", "active: 1")==1 );
+
+ system("./status_s.sh > status");
+ _ASSERT( grep_count("status", "active: 0")==1 );
+
+ TEARDOWN();
+}
+
+void
+test_standby_04()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ connect1();
+ _ASSERT( conn!=NULL );
+
+ system("./status_a.sh > status");
+ _ASSERT( grep_count("status", "active: 1")==1 );
+ system("./status_s.sh > status");
+ _ASSERT( grep_count("status", "active: 0")==1 );
+
+ system("killall -9 gtm");
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+ _ASSERT( conn!=NULL );
+
+ system("./status_s.sh > status");
+ _ASSERT( grep_count("status", "active: 1")==1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test_standby_01();
+ test_standby_02();
+
+ test_standby_03(); /* get status */
+
+ test_standby_04(); /* promote */
+
+ return 0;
+}
diff --git a/src/gtm/test/test_startup.c b/src/gtm/test/test_startup.c
new file mode 100644
index 0000000000..f6d3906f66
--- /dev/null
+++ b/src/gtm/test/test_startup.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+#define client_log(x) printf x
+
+GlobalTransactionId gxid;
+
+void
+setUp()
+{
+}
+
+void
+tearDown()
+{
+}
+
+void
+test_startup_01()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+ int rc;
+
+ SETUP();
+
+ system("./start_a.sh");
+ sleep(1);
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d",
+ PGXC_NODE_DEFAULT);
+
+ conn = PQconnectGTM(connect_string);
+ _ASSERT(conn != NULL);
+
+ /*
+ * create a transaction data on active GTM
+ */
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Preparing transaction id 3")==1 );
+
+ fprintf(stderr, "gxid=%d\n", gxid);
+
+ GTMPQfinish(conn);
+
+ /*
+ * start standby
+ */
+ system("./start_s.sh");
+ sleep(1);
+
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring next/last gxid from the active-GTM succeeded.")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring 1 gxid(s) done.")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring all of gxid(s) from the active-GTM succeeded.")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring sequences done.")==1 );
+
+ /*
+ * connecting to the standby
+ */
+ sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=102 remote_type=%d",
+ PGXC_NODE_DEFAULT);
+
+ conn = PQconnectGTM(connect_string);
+ _ASSERT(conn != NULL);
+
+ /*
+ * abort the replicated transaction
+ */
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Cancelling transaction id 3")==0 );
+ _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 3")==1 );
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ system("./stop.sh");
+ system("./clean.sh");
+
+ test_startup_01();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_txn.c b/src/gtm/test/test_txn.c
new file mode 100644
index 0000000000..5c73e237ee
--- /dev/null
+++ b/src/gtm/test/test_txn.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_03()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction_autovacuum(conn, GTM_ISOLATION_SERIALIZABLE);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_11()
+{
+ GTM_IsolationLevel isolevel = GTM_ISOLATION_SERIALIZABLE;
+ char *gid = "test";
+ GlobalTransactionId gxid =InvalidGlobalTransactionId;
+ GlobalTransactionId prepared_gxid =InvalidGlobalTransactionId;
+ int datanodecnt = 0;
+ PGXC_NodeId *datanodes = NULL;
+ int coordcnt = 0;
+ PGXC_NodeId *coordinators = NULL;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, isolevel, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ printf("test_txn_11: gxid=%d\n", gxid);
+
+ rc = start_prepared_transaction(conn, gxid, gid,
+ datanodecnt, datanodes, coordcnt, coordinators);
+ _ASSERT( rc>=0 );
+
+ rc = get_gid_data(conn, isolevel, gid,
+ &gxid, &prepared_gxid, &datanodecnt, datanodes, &coordcnt, coordinators);
+ printf("test_txn_11: prepared_gxid=%d\n", prepared_gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_prepared_transaction(conn, gxid, prepared_gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+
+void
+test_txn_51()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid == InvalidGlobalTransactionId );
+
+ TEARDOWN();
+}
+
+void
+test_txn_52()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ rc = prepare_transaction(conn, InvalidGlobalTransactionId);
+ _ASSERT( rc==-1 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_53()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ rc = commit_transaction(conn, InvalidGlobalTransactionId);
+ _ASSERT( rc==-1 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_54()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ rc = abort_transaction(conn, InvalidGlobalTransactionId);
+ _ASSERT( rc==-1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ test_txn_01();
+ test_txn_02();
+ test_txn_03();
+
+ test_txn_11();
+
+ /*
+ * connect to standby. must be prevented.
+ */
+ sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ test_txn_51();
+ test_txn_52();
+ test_txn_53();
+ test_txn_54();
+
+ /*
+ * promote GTM standby to become GTM active.
+ */
+ system("./promote.sh");
+ sleep(1);
+
+ test_txn_01();
+ test_txn_02();
+ test_txn_03();
+
+ test_txn_11();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_txn4.c b/src/gtm/test/test_txn4.c
new file mode 100644
index 0000000000..e5312cf460
--- /dev/null
+++ b/src/gtm/test/test_txn4.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ connect1();
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn4_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==1 );
+
+ TEARDOWN();
+}
+
+void
+test_txn4_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 4")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 4")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 4")==1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ system("./stop.sh");
+ system("./clean.sh");
+ system("./start.sh");
+
+ test_txn4_01();
+ test_txn4_02();
+
+ return 0;
+}
diff --git a/src/gtm/test/test_txn5.c b/src/gtm/test/test_txn5.c
new file mode 100644
index 0000000000..04ca7b3a49
--- /dev/null
+++ b/src/gtm/test/test_txn5.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ system("./stop.sh > /dev/null");
+ system("./clean.sh > /dev/null");
+ system("./start.sh > /dev/null");
+
+ connect1();
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn5_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_03()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_04()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_05()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_06()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+
+void
+test_txn5_11()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ sleep(3);
+
+ system("killall -9 gtm_standby");
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_12()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ sleep(3);
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ system("killall -9 gtm_standby");
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test_txn5_01();
+ test_txn5_02();
+ test_txn5_03();
+ test_txn5_04();
+
+ test_txn5_05();
+ test_txn5_06();
+
+ /* detecting communication failure, and dropping standby. */
+ test_txn5_11();
+ test_txn5_12();
+
+ return 0;
+}
diff --git a/src/gtm/test2/Makefile b/src/gtm/test2/Makefile
new file mode 100644
index 0000000000..e6155ea7f6
--- /dev/null
+++ b/src/gtm/test2/Makefile
@@ -0,0 +1,53 @@
+# Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+
+top_build_dir=../..
+include $(top_build_dir)/gtm/Makefile.global
+
+override CPPFLAGS := -I$(top_build_dir)/gtm/client $(CPPFLAGS)
+
+SRCS=test_serialize.c test_connect.c test_node.c test_node5.c test_txn.c test_txn4.c test_txn5.c test_repli.c test_repli2.c test_seq.c test_seq4.c test_seq5.c test_scenario.c test_startup.c test_standby.c test_common.c
+
+PROGS=test_serialize test_connect test_txn test_txn4 test_txn5 test_repli test_repli2 test_seq test_seq4 test_seq5 test_scenario test_startup test_node test_node5 test_standby
+
+OBJS=$(SRCS:.c=.o)
+LIBS=$(top_build_dir)/gtm/client/libgtmclient.a \
+ $(top_build_dir)/gtm/common/libgtm.a \
+ $(top_build_dir)/gtm/libpq/libpqcomm.a
+
+LOADLIBES=-lpthread
+CFLAGS=-g -O0
+
+all: $(PROGS)
+
+test_serialize: test_serialize.o test_common.o $(LIBS)
+
+test_connect: test_connect.o test_common.o $(LIBS)
+test_startup: test_startup.o test_common.o $(LIBS)
+
+test_node: test_node.o test_common.o $(LIBS)
+test_node5: test_node5.o test_common.o $(LIBS)
+
+test_txn: test_txn.o test_common.o $(LIBS)
+
+test_txn4: test_txn4.o test_common.o $(LIBS)
+test_txn5: test_txn5.o test_common.o $(LIBS)
+
+test_repli: test_repli.o test_common.o $(LIBS)
+
+test_standby: test_standby.o test_common.o $(LIBS)
+
+test_repli2: test_repli2.o test_common.o $(LIBS)
+
+test_seq: test_seq.o test_common.o $(LIBS)
+test_seq4: test_seq4.o test_common.o $(LIBS)
+test_seq5: test_seq5.o test_common.o $(LIBS)
+
+test_scenario: test_scenario.o test_common.o $(LIBS)
+
+clean:
+ rm -f $(OBJS) *~
+ rm -f $(PROGS)
+
+distclean: clean
+
+maintainer-clean: distclean
diff --git a/src/gtm/test2/regress.log b/src/gtm/test2/regress.log
new file mode 100644
index 0000000000..127fe9a893
--- /dev/null
+++ b/src/gtm/test2/regress.log
@@ -0,0 +1,415 @@
+make: Nothing to be done for `all'.
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+stopping standby...
+waiting for server to shut down... done
+server stopped
+stopping active...
+gtm_ctl: could not send stop signal (PID: 24704): No such process
+gtm: no process killed
+gtm_standby: no process killed
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24750 24744 0 16:50 pts/2 00:00:00 grep gtm
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning standby...
+/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning active...
+/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting active...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24763 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+gxid=3
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting standby...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24763 1 1 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24775 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+=== test_startup_01 in ===
+=== test_startup_01 out ===
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+stopping standby...
+waiting for server to shut down.... done
+server stopped
+stopping active...
+waiting for server to shut down.... done
+server stopped
+gtm: no process killed
+gtm_standby: no process killed
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning standby...
+/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning active...
+/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting active...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24808 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24810 24802 0 16:50 pts/2 00:00:00 grep gtm
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+sleeping 3 seconds...
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting standby...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24808 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24818 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+stopping standby...
+waiting for server to shut down.... done
+server stopped
+stopping active...
+waiting for server to shut down.... done
+server stopped
+gtm: no process killed
+gtm_standby: no process killed
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning standby...
+/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning active...
+/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting active...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24846 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24848 24840 0 16:50 pts/2 00:00:00 grep gtm
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+sleeping 3 seconds...
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting standby...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24846 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24856 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+=== test_node5_01 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_node5_01 out ===
+=== test_node5_02 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_node5_02 out ===
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+stopping standby...
+waiting for server to shut down... done
+server stopped
+stopping active...
+waiting for server to shut down... done
+server stopped
+gtm: no process killed
+gtm_standby: no process killed
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24870 24864 0 16:50 pts/2 00:00:00 grep gtm
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning standby...
+/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning active...
+/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting active...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24885 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24887 24879 0 16:50 pts/2 00:00:00 grep gtm
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+sleeping 3 seconds...
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting standby...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24885 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24895 1 0 16:50 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+=== test_txn4_01 in ===
+PGconnectGTM() ok.
+=== test_txn4_01 out ===
+=== test_txn4_02 in ===
+PGconnectGTM() ok.
+=== test_txn4_02 out ===
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+stopping standby...
+waiting for server to shut down.... done
+server stopped
+stopping active...
+waiting for server to shut down.... done
+server stopped
+gtm: no process killed
+gtm_standby: no process killed
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24917 24910 0 16:50 pts/2 00:00:00 grep gtm
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning standby...
+/tmp/pgxc/data/gtm_standby ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+cleaning active...
+/tmp/pgxc/data/gtm ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting active...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24932 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24934 24926 0 16:50 pts/2 00:00:00 grep gtm
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+sleeping 3 seconds...
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+starting standby...
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+server starting
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 24932 1 0 16:50 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 24942 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+=== test_seq4_01 in ===
+PGconnectGTM() ok.
+=== test_seq4_01 out ===
+=== test_seq4_02 in ===
+PGconnectGTM() ok.
+=== test_seq4_02 out ===
+=== test_seq4_03 in ===
+PGconnectGTM() ok.
+=== test_seq4_03 out ===
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+gtm_ctl: could not send stop signal (PID: 25019): No such process
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+gtm_ctl: could not send stop signal (PID: 25061): No such process
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+promoting standby...
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+snaga 25158 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+=== test_standby_01 in ===
+=== test_standby_01 out ===
+=== test_standby_02 in ===
+PGconnectGTM() ok.
+=== test_standby_02 out ===
+=== test_standby_03 in ===
+PGconnectGTM() ok.
+=== test_standby_03 out ===
+=== test_standby_04 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_standby_04 out ===
+gtm_ctl: could not send stop signal (PID: 25147): No such process
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+promoting standby...
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+snaga 25208 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 25218 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25228 25224 0 16:51 pts/2 00:00:00 grep gtm
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+promoting standby...
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+snaga 25254 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 25264 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25274 25270 0 16:51 pts/2 00:00:00 grep gtm
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+promoting standby...
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+snaga 25300 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 25310 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25320 25316 0 16:51 pts/2 00:00:00 grep gtm
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+promoting standby...
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+snaga 25346 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 25356 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25366 25362 0 16:51 pts/2 00:00:00 grep gtm
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+promoting standby...
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+snaga 25392 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 25402 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25412 25408 0 16:51 pts/2 00:00:00 grep gtm
+gtm: no process killed
+gtm_standby: no process killed
+gtm_ctl: PID file "/tmp/pgxc/data/gtm/gtm.pid" does not exist
+Is server running?
+gtm_ctl: PID file "/tmp/pgxc/data/gtm_standby/gtm.pid" does not exist
+Is server running?
+/tmp/pgxc/bin ~/pgxc/postgres-xc/src/gtm/test
+~/pgxc/postgres-xc/src/gtm/test
+promoting standby...
+checking process...
+snaga 18449 18384 0 Mar22 pts/3 00:00:00 tail -f /tmp/pgxc/data/gtm/gtm.log
+snaga 25124 18416 0 Mar22 pts/4 00:00:00 tail -f /tmp/pgxc/data/gtm_standby/gtm.log
+snaga 25438 1 0 16:51 pts/2 00:00:00 gtm -D /tmp/pgxc/data/gtm -n 101
+snaga 25448 1 0 16:51 pts/2 00:00:00 gtm_standby -D /tmp/pgxc/data/gtm_standby -n 102 -s -p 6667 -i 127.0.0.1 -q 6666
+snaga 25458 25454 0 16:51 pts/2 00:00:00 grep gtm
+=== test_txn5_01 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_txn5_01 out ===
+=== test_txn5_02 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_txn5_02 out ===
+=== test_txn5_03 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_txn5_03 out ===
+=== test_txn5_04 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_txn5_04 out ===
+=== test_txn5_05 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_txn5_05 out ===
+=== test_txn5_06 in ===
+PGconnectGTM() ok.
+PGconnectGTM() ok.
+=== test_txn5_06 out ===
diff --git a/src/gtm/test2/regress2.sh b/src/gtm/test2/regress2.sh
new file mode 100644
index 0000000000..835055ac7e
--- /dev/null
+++ b/src/gtm/test2/regress2.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+make
+
+./test_startup
+./test_node5
+./test_txn4
+./test_seq4
+
+./test_standby
+
+./test_txn5
+
diff --git a/src/gtm/test2/run.sh b/src/gtm/test2/run.sh
new file mode 100644
index 0000000000..98566eb6fb
--- /dev/null
+++ b/src/gtm/test2/run.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+. ./regress2.sh 2>&1 | tee regress.log
+
+echo ""
+echo "=========== SUMMARY ============"
+date
+echo -n "Assert: "
+grep -c ASSERT regress.log
diff --git a/src/gtm/test2/status b/src/gtm/test2/status
new file mode 100644
index 0000000000..ef55611eaa
--- /dev/null
+++ b/src/gtm/test2/status
@@ -0,0 +1,3 @@
+pid: 19879
+data: /tmp/pgxc/data/gtm_standby
+active: 1
diff --git a/src/gtm/test2/status_a.sh b/src/gtm/test2/status_a.sh
new file mode 100644
index 0000000000..d120fd0b7a
--- /dev/null
+++ b/src/gtm/test2/status_a.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# GTM start script for test
+
+export PATH=/tmp/pgxc/bin:$PATH
+export DATA=/tmp/pgxc/data/gtm
+
+# -------------------------------
+# starting active...
+# -------------------------------
+gtm_ctl -D ${DATA} -S gtm status
+
diff --git a/src/gtm/test2/status_s.sh b/src/gtm/test2/status_s.sh
new file mode 100644
index 0000000000..badcf1590b
--- /dev/null
+++ b/src/gtm/test2/status_s.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# GTM start script for test
+
+export PATH=/tmp/pgxc/bin:$PATH
+export DATA=/tmp/pgxc/data/gtm_standby
+
+# -------------------------------
+# starting active...
+# -------------------------------
+gtm_ctl -D ${DATA} -S gtm_standby status
+
diff --git a/src/gtm/test2/test.c b/src/gtm/test2/test.c
new file mode 100644
index 0000000000..f7d6f945c1
--- /dev/null
+++ b/src/gtm/test2/test.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+int
+main(void)
+{
+ char *ptr[1024];
+ int i;
+
+ for (i=0 ; i<1024 ; i++)
+ {
+ fprintf(stderr, "ptr[%d] = %p\n", i, ptr[i]);
+ }
+
+ return 0;
+}
diff --git a/src/gtm/test2/test_connect2.c b/src/gtm/test2/test_connect2.c
new file mode 100644
index 0000000000..9b53d332a9
--- /dev/null
+++ b/src/gtm/test2/test_connect2.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#define client_log(x) printf x
+
+void
+setUp()
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+
+void
+test11()
+{
+ clean_active();
+ clean_standby();
+
+ start_active();
+ sleep(10);
+ start_standby();
+ sleep(10);
+ // stop_active();
+ kill_standby();
+ sleep(10);
+ kill_active();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test11();
+
+ return 0;
+}
+
diff --git a/src/gtm/test2/test_standby.c b/src/gtm/test2/test_standby.c
new file mode 100644
index 0000000000..de53255d71
--- /dev/null
+++ b/src/gtm/test2/test_standby.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ system("./stop.sh > /dev/null");
+ system("./clean.sh > /dev/null");
+ system("./start.sh > /dev/null");
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_standby_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ system("killall -9 gtm_standby");
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+
+ _ASSERT( conn!=NULL );
+ _ASSERT( grep_count(LOG_ACTIVE, "Failed to establish a connection with GTM standby")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+
+ TEARDOWN();
+}
+
+void
+test_standby_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ connect1();
+ _ASSERT( conn!=NULL );
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 );
+
+ system("killall -9 gtm_standby");
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby.")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 );
+
+ TEARDOWN();
+}
+
+void
+test_standby_03()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ connect1();
+ _ASSERT( conn!=NULL );
+
+ system("./status_a.sh > status");
+ _ASSERT( grep_count("status", "active: 1")==1 );
+
+ system("./status_s.sh > status");
+ _ASSERT( grep_count("status", "active: 0")==1 );
+
+ TEARDOWN();
+}
+
+void
+test_standby_04()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ connect1();
+ _ASSERT( conn!=NULL );
+
+ system("./status_a.sh > status");
+ _ASSERT( grep_count("status", "active: 1")==1 );
+ system("./status_s.sh > status");
+ _ASSERT( grep_count("status", "active: 0")==1 );
+
+ system("killall -9 gtm");
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+ _ASSERT( conn!=NULL );
+
+ system("./status_s.sh > status");
+ _ASSERT( grep_count("status", "active: 1")==1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test_standby_01();
+ test_standby_02();
+
+ test_standby_03(); /* get status */
+
+ test_standby_04(); /* promote */
+
+ return 0;
+}
diff --git a/src/gtm/test2/test_startup.c b/src/gtm/test2/test_startup.c
new file mode 100644
index 0000000000..f6d3906f66
--- /dev/null
+++ b/src/gtm/test2/test_startup.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+#define client_log(x) printf x
+
+GlobalTransactionId gxid;
+
+void
+setUp()
+{
+}
+
+void
+tearDown()
+{
+}
+
+void
+test_startup_01()
+{
+ GTM_Conn *conn;
+ char connect_string[100];
+ int rc;
+
+ SETUP();
+
+ system("./start_a.sh");
+ sleep(1);
+
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=101 remote_type=%d",
+ PGXC_NODE_DEFAULT);
+
+ conn = PQconnectGTM(connect_string);
+ _ASSERT(conn != NULL);
+
+ /*
+ * create a transaction data on active GTM
+ */
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Preparing transaction id 3")==1 );
+
+ fprintf(stderr, "gxid=%d\n", gxid);
+
+ GTMPQfinish(conn);
+
+ /*
+ * start standby
+ */
+ system("./start_s.sh");
+ sleep(1);
+
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring next/last gxid from the active-GTM succeeded.")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring 1 gxid(s) done.")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring all of gxid(s) from the active-GTM succeeded.")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Restoring sequences done.")==1 );
+
+ /*
+ * connecting to the standby
+ */
+ sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=102 remote_type=%d",
+ PGXC_NODE_DEFAULT);
+
+ conn = PQconnectGTM(connect_string);
+ _ASSERT(conn != NULL);
+
+ /*
+ * abort the replicated transaction
+ */
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Cancelling transaction id 3")==0 );
+ _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 3")==1 );
+
+ GTMPQfinish(conn);
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ system("./stop.sh");
+ system("./clean.sh");
+
+ test_startup_01();
+
+ return 0;
+}
diff --git a/src/gtm/test2/test_txn.c b/src/gtm/test2/test_txn.c
new file mode 100644
index 0000000000..5c73e237ee
--- /dev/null
+++ b/src/gtm/test2/test_txn.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_03()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction_autovacuum(conn, GTM_ISOLATION_SERIALIZABLE);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_11()
+{
+ GTM_IsolationLevel isolevel = GTM_ISOLATION_SERIALIZABLE;
+ char *gid = "test";
+ GlobalTransactionId gxid =InvalidGlobalTransactionId;
+ GlobalTransactionId prepared_gxid =InvalidGlobalTransactionId;
+ int datanodecnt = 0;
+ PGXC_NodeId *datanodes = NULL;
+ int coordcnt = 0;
+ PGXC_NodeId *coordinators = NULL;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, isolevel, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ printf("test_txn_11: gxid=%d\n", gxid);
+
+ rc = start_prepared_transaction(conn, gxid, gid,
+ datanodecnt, datanodes, coordcnt, coordinators);
+ _ASSERT( rc>=0 );
+
+ rc = get_gid_data(conn, isolevel, gid,
+ &gxid, &prepared_gxid, &datanodecnt, datanodes, &coordcnt, coordinators);
+ printf("test_txn_11: prepared_gxid=%d\n", prepared_gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_prepared_transaction(conn, gxid, prepared_gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+
+void
+test_txn_51()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid == InvalidGlobalTransactionId );
+
+ TEARDOWN();
+}
+
+void
+test_txn_52()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ rc = prepare_transaction(conn, InvalidGlobalTransactionId);
+ _ASSERT( rc==-1 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_53()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ rc = commit_transaction(conn, InvalidGlobalTransactionId);
+ _ASSERT( rc==-1 );
+
+ TEARDOWN();
+}
+
+void
+test_txn_54()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ rc = abort_transaction(conn, InvalidGlobalTransactionId);
+ _ASSERT( rc==-1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ test_txn_01();
+ test_txn_02();
+ test_txn_03();
+
+ test_txn_11();
+
+ /*
+ * connect to standby. must be prevented.
+ */
+ sprintf(connect_string, "host=localhost port=6667 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ test_txn_51();
+ test_txn_52();
+ test_txn_53();
+ test_txn_54();
+
+ /*
+ * promote GTM standby to become GTM active.
+ */
+ system("./promote.sh");
+ sleep(1);
+
+ test_txn_01();
+ test_txn_02();
+ test_txn_03();
+
+ test_txn_11();
+
+ return 0;
+}
diff --git a/src/gtm/test2/test_txn2.c b/src/gtm/test2/test_txn2.c
new file mode 100644
index 0000000000..803611ea7d
--- /dev/null
+++ b/src/gtm/test2/test_txn2.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn_21()
+{
+ /* request parameters */
+ int txn_count = 2;
+ GTM_IsolationLevel txn_isolation_level[GTM_MAX_GLOBAL_TRANSACTIONS];
+ bool txn_read_only[GTM_MAX_GLOBAL_TRANSACTIONS];
+ GTMProxy_ConnID txn_connid[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ /* results */
+ int txn_count2;
+ GlobalTransactionId start_gxid;
+ GTM_Timestamp start_ts;
+
+ /* request parameters */
+ GlobalTransactionId gxid[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ /* results */
+ int status[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ int rc, i;
+
+ for (i=0 ; i<txn_count ; i++)
+ {
+ txn_isolation_level[i] = GTM_ISOLATION_SERIALIZABLE;
+ txn_read_only[i] = false;
+ // txn_connid[i] = InvalidGTMProxyConnID;
+ txn_connid[i] = 0;
+ }
+
+ SETUP();
+
+ rc = begin_transaction_multi(conn,
+ txn_count, txn_isolation_level, txn_read_only, txn_connid,
+ &txn_count2, &start_gxid, &start_ts);
+ _ASSERT( rc>=0 );
+ _ASSERT( txn_count2==2 );
+
+ /*
+ * commit test
+ */
+ txn_count = txn_count2;
+
+ for (i=0 ; i<txn_count ; i++)
+ {
+ gxid[i] = start_gxid + i;
+ }
+
+ rc = commit_transaction_multi(conn, txn_count, gxid, &txn_count2, &status);
+ _ASSERT( rc>=0 );
+ _ASSERT( txn_count2==2 );
+
+ tearDown();
+}
+
+void
+test_txn_22()
+{
+ /* request parameters */
+ int txn_count = 2;
+ GTM_IsolationLevel txn_isolation_level[GTM_MAX_GLOBAL_TRANSACTIONS];
+ bool txn_read_only[GTM_MAX_GLOBAL_TRANSACTIONS];
+ GTMProxy_ConnID txn_connid[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ /* results */
+ int txn_count2;
+ GlobalTransactionId start_gxid;
+ GTM_Timestamp start_ts;
+
+ /* request parameters */
+ GlobalTransactionId gxid[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ /* results */
+ int status[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ int rc, i;
+
+ for (i=0 ; i<txn_count ; i++)
+ {
+ txn_isolation_level[i] = GTM_ISOLATION_SERIALIZABLE;
+ txn_read_only[i] = false;
+ // txn_connid[i] = InvalidGTMProxyConnID;
+ txn_connid[i] = 0;
+ }
+
+ SETUP();
+
+ rc = begin_transaction_multi(conn,
+ txn_count, txn_isolation_level, txn_read_only, txn_connid,
+ &txn_count2, &start_gxid, &start_ts);
+ _ASSERT( rc>=0 );
+ _ASSERT( txn_count2==2 );
+
+ /*
+ * abort test
+ */
+ txn_count = txn_count2;
+
+ for (i=0 ; i<txn_count ; i++)
+ {
+ gxid[i] = start_gxid + i;
+ }
+
+ rc = abort_transaction_multi(conn, txn_count, gxid, &txn_count2, &status);
+ _ASSERT( rc>=0 );
+ _ASSERT( txn_count2==2 );
+
+ tearDown();
+}
+
+void
+test_txn_23()
+{
+ /* request parameters */
+ int txn_count = 2;
+ GTM_IsolationLevel txn_isolation_level[GTM_MAX_GLOBAL_TRANSACTIONS];
+ bool txn_read_only[GTM_MAX_GLOBAL_TRANSACTIONS];
+ GTMProxy_ConnID txn_connid[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ /* results */
+ int txn_count2;
+ GlobalTransactionId start_gxid;
+ GTM_Timestamp start_ts;
+
+ /* request parameters */
+ GlobalTransactionId gxid[GTM_MAX_GLOBAL_TRANSACTIONS];
+
+ /* results */
+ int status[GTM_MAX_GLOBAL_TRANSACTIONS];
+ GlobalTransactionId xmin_out;
+ GlobalTransactionId xmax_out;
+ GlobalTransactionId recent_global_xmin_out;
+ int32 xcnt_out;
+
+ int rc, i;
+
+ for (i=0 ; i<txn_count ; i++)
+ {
+ txn_isolation_level[i] = GTM_ISOLATION_SERIALIZABLE;
+ txn_read_only[i] = false;
+ // txn_connid[i] = InvalidGTMProxyConnID;
+ txn_connid[i] = 0;
+ }
+
+ SETUP();
+
+ rc = begin_transaction_multi(conn,
+ txn_count, txn_isolation_level, txn_read_only, txn_connid,
+ &txn_count2, &start_gxid, &start_ts);
+ _ASSERT( rc>=0 );
+ _ASSERT( txn_count2==2 );
+
+ /*
+ * snapshot get test
+ */
+ txn_count = txn_count2;
+
+ for (i=0 ; i<txn_count ; i++)
+ {
+ gxid[i] = start_gxid + i;
+ }
+
+ rc = snapshot_get_multi(conn, txn_count, gxid,
+ &txn_count2, &status,
+ &xmin_out, &xmax_out, &recent_global_xmin_out, &xcnt_out);
+ _ASSERT( rc>=0 );
+ _ASSERT( txn_count2==2 );
+
+ tearDown();
+}
+
+
+void
+test_txn_31()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ sleep(10); /* kill standby while sleeping. */
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ tearDown();
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ test_txn2_01();
+
+ return 0;
+}
diff --git a/src/gtm/test2/test_txn3.c b/src/gtm/test2/test_txn3.c
new file mode 100644
index 0000000000..a711dcae63
--- /dev/null
+++ b/src/gtm/test2/test_txn3.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ sprintf(connect_string, "host=localhost port=6666 pgxc_node_id=1 remote_type=%d",
+ PGXC_NODE_GTM);
+
+ conn = PQconnectGTM(connect_string);
+ if (conn == NULL)
+ {
+ client_log(("Error in connection\n"));
+ exit(1);
+ }
+ client_log(("PGconnectGTM() ok.\n"));
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn3_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ sleep(2);
+ system("killall -9 gtm_standby");
+ sleep(1);
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ tearDown();
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ test_txn2_01();
+
+ return 0;
+}
diff --git a/src/gtm/test2/test_txn4.c b/src/gtm/test2/test_txn4.c
new file mode 100644
index 0000000000..e5312cf460
--- /dev/null
+++ b/src/gtm/test2/test_txn4.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ connect1();
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn4_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==1 );
+
+ TEARDOWN();
+}
+
+void
+test_txn4_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ _ASSERT( grep_count(LOG_STANDBY, "Sending transaction id 4")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Preparing transaction id 4")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Cancelling transaction id 4")==1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ system("./stop.sh");
+ system("./clean.sh");
+ system("./start.sh");
+
+ test_txn4_01();
+ test_txn4_02();
+
+ return 0;
+}
diff --git a/src/gtm/test2/test_txn5.c b/src/gtm/test2/test_txn5.c
new file mode 100644
index 0000000000..04ca7b3a49
--- /dev/null
+++ b/src/gtm/test2/test_txn5.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtm/libpq-fe.h"
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_client.h"
+
+#include "test_common.h"
+
+pthread_key_t threadinfo_key;
+
+void
+setUp()
+{
+ system("./stop.sh > /dev/null");
+ system("./clean.sh > /dev/null");
+ system("./start.sh > /dev/null");
+
+ connect1();
+}
+
+void
+tearDown()
+{
+ GTMPQfinish(conn);
+}
+
+void
+test_txn5_01()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_02()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_03()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_04()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_05()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_06()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ rc = prepare_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ system("./promote.sh");
+
+ GTMPQfinish(conn);
+ connect2();
+
+ rc = abort_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+
+ TEARDOWN();
+}
+
+
+void
+test_txn5_11()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ sleep(3);
+
+ system("killall -9 gtm_standby");
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Sending transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Committing transaction id 3")==1 );
+ _ASSERT( grep_count(LOG_STANDBY, "Committing transaction id 3")==0 );
+
+ TEARDOWN();
+}
+
+void
+test_txn5_12()
+{
+ GlobalTransactionId gxid;
+ int rc;
+
+ SETUP();
+ sleep(3);
+
+ gxid = begin_transaction(conn, GTM_ISOLATION_SERIALIZABLE, timestamp);
+ _ASSERT( gxid != InvalidGlobalTransactionId );
+
+ system("killall -9 gtm_standby");
+
+ rc = commit_transaction(conn, gxid);
+ _ASSERT( rc>=0 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=0")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=1")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "gtm_standby_reconnect_to_standby(): re-connect failed. retry=2")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "communication error with standby")==1 );
+ _ASSERT( grep_count(LOG_ACTIVE, "Calling report_xcwatch_gtm_failure()...")==1 );
+
+ TEARDOWN();
+}
+
+int
+main(int argc, char *argv[])
+{
+ test_txn5_01();
+ test_txn5_02();
+ test_txn5_03();
+ test_txn5_04();
+
+ test_txn5_05();
+ test_txn5_06();
+
+ /* detecting communication failure, and dropping standby. */
+ test_txn5_11();
+ test_txn5_12();
+
+ return 0;
+}
diff --git a/src/include/gen_alloc.h b/src/include/gen_alloc.h
new file mode 100644
index 0000000000..1229ba524d
--- /dev/null
+++ b/src/include/gen_alloc.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * gen_alloc.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gen_alloc.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef GEN_ALLOC_H
+#define GEN_ALLOC_H
+
+/*
+ * Common memory allocation binary interface both for Postgres and GTM processes.
+ * Especially needed by gtm_serialize.c and gtm_serialize_debug.c
+ */
+
+typedef struct Gen_Alloc
+{
+ void * (* alloc) (void *, size_t);
+ void * (* alloc0) (void *, size_t);
+ void * (* realloc) (void *, size_t);
+ void (* free) (void *);
+ void * (* current_memcontext) (void);
+} Gen_Alloc;
+
+extern Gen_Alloc genAlloc_class;
+
+#define genAlloc(x) genAlloc_class.alloc(genAlloc_class.current_memcontext(), x)
+#define genRealloc(x, y) genAlloc_class.realloc(x, y);
+#define genFree(x) genAlloc_class.free(x);
+#define genAlloc0(x) genAlloc_class.alloc0(genAlloc_class.current_memcontext(), x)
+
+#endif /* GEN_ALLOC_H */
diff --git a/src/include/gtm/assert.h b/src/include/gtm/assert.h
index b46df46a5e..97cfc2dfb2 100644
--- a/src/include/gtm/assert.h
+++ b/src/include/gtm/assert.h
@@ -14,6 +14,8 @@
#ifndef GTM_ASSERT_H
#define GTM_ASSERT_H
+#include "c.h"
+
extern bool assert_enabled;
/*
diff --git a/src/include/gtm/elog.h b/src/include/gtm/elog.h
index 115ea1fd9b..e9b7391502 100644
--- a/src/include/gtm/elog.h
+++ b/src/include/gtm/elog.h
@@ -15,6 +15,8 @@
#ifndef ELOG_H
#define ELOG_H
+#include "c.h"
+
/* Error level codes */
#define DEBUG5 10 /* Debugging messages, in categories of
* decreasing detail. */
diff --git a/src/include/gtm/gtm.h b/src/include/gtm/gtm.h
index 91eff992fa..ad5882bd1b 100644
--- a/src/include/gtm/gtm.h
+++ b/src/include/gtm/gtm.h
@@ -64,7 +64,7 @@ typedef struct GTM_ThreadInfo
GTM_ConnectionInfo *thr_conn;
GTM_RWLock thr_lock;
- List *thr_cached_txninfo;
+ gtm_List *thr_cached_txninfo;
} GTM_ThreadInfo;
@@ -117,10 +117,10 @@ extern GTM_ThreadID TopMostThreadID;
#define IsMainThread() (GetMyThreadInfo->thr_id == TopMostThreadID)
#define GTM_CachedTransInfo (GetMyThreadInfo->thr_cached_txninfo)
-#define GTM_HaveFreeCachedTransInfo() (list_length(GTM_CachedTransInfo))
+#define GTM_HaveFreeCachedTransInfo() (gtm_list_length(GTM_CachedTransInfo))
#define GTM_MAX_CACHED_TRANSINFO 0
-#define GTM_HaveEnoughCachedTransInfo() (list_length(GTM_CachedTransInfo) >= GTM_MAX_CACHED_TRANSINFO)
+#define GTM_HaveEnoughCachedTransInfo() (gtm_list_length(GTM_CachedTransInfo) >= GTM_MAX_CACHED_TRANSINFO)
#define START_CRIT_SECTION() (CritSectionCount++)
diff --git a/src/include/gtm/gtm_c.h b/src/include/gtm/gtm_c.h
index e243317a47..4f050f6d4b 100644
--- a/src/include/gtm/gtm_c.h
+++ b/src/include/gtm/gtm_c.h
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * c.h
+ * gtm_c.h
* Fundamental C definitions. This is included by every .c file in
* PostgreSQL (via either postgres.h or postgres_fe.h, as appropriate).
*
@@ -53,6 +53,7 @@ typedef enum GTM_PGXCNodeType
PGXC_NODE_GTM_PROXY_POSTMASTER, /* Used by Proxy to communicate with GTM and not use Proxy headers */
PGXC_NODE_COORDINATOR,
PGXC_NODE_DATANODE,
+ PGXC_NODE_GTM,
PGXC_NODE_DEFAULT /* In case nothing is associated to connection */
} GTM_PGXCNodeType;
diff --git a/src/include/gtm/gtm_client.h b/src/include/gtm/gtm_client.h
index 0159212367..ca1d92edb5 100644
--- a/src/include/gtm/gtm_client.h
+++ b/src/include/gtm/gtm_client.h
@@ -15,7 +15,10 @@
#define GTM_CLIENT_H
#include "gtm/gtm_c.h"
+#include "gtm/gtm_seq.h"
+#include "gtm/gtm_txn.h"
#include "gtm/gtm_msg.h"
+#include "gtm/register.h"
#include "gtm/libpq-fe.h"
typedef union GTM_ResultData
@@ -35,6 +38,8 @@ typedef union GTM_ResultData
* TXN_ROLLBACK
*/
+ GlobalTransactionId grd_next_gxid;
+
struct
{
GTM_TransactionHandle txnhandle;
@@ -50,6 +55,11 @@ typedef union GTM_ResultData
GTM_Sequence seqval;
} grd_seq; /* SEQUENCE_GET_CURRENT
SEQUENCE_GET_NEXT */
+ struct
+ {
+ int seq_count;
+ GTM_SeqInfo **seq;
+ } grd_seq_list; /* SEQUENCE_GET_LIST */
struct
{
@@ -84,10 +94,22 @@ typedef union GTM_ResultData
struct
{
+ char *ptr;
+ size_t len;
+ } grd_txn_gid_list; /* TXN_GXID_LIST_RESULT */
+
+ struct
+ {
GTM_PGXCNodeType type; /* NODE_REGISTER */
GTM_PGXCNodeId nodenum; /* NODE_UNREGISTER */
} grd_node;
+ struct
+ {
+ int num_node;
+ GTM_PGXCNodeInfo *nodeinfo[MAX_NODES];
+ } grd_node_list;
+
/*
* TODO
* TXN_GET_STATUS
@@ -95,6 +117,16 @@ typedef union GTM_ResultData
*/
} GTM_ResultData;
+#define GTM_RESULT_COMM_ERROR (-2) /* Communication error */
+#define GTM_RESULT_ERROR (-1)
+#define GTM_RESULT_OK (0)
+/*
+ * This error is used ion the case where allocated buffer is not large
+ * enough to store the errors. It may happen of an allocation failed
+ * so it's status is considered as unknown.
+ */
+#define GTM_RESULT_UNKNOWN (1)
+
typedef struct GTM_Result
{
GTM_ResultType gr_type;
@@ -123,6 +155,14 @@ typedef struct GTM_Result
GTM_Conn *connect_gtm(const char *connect_string);
void disconnect_gtm(GTM_Conn *conn);
+int begin_replication_initial_sync(GTM_Conn *);
+int end_replication_initial_sync(GTM_Conn *);
+
+size_t get_node_list(GTM_Conn *, GTM_PGXCNodeInfo *, size_t);
+GlobalTransactionId get_next_gxid(GTM_Conn *);
+uint32 get_txn_gxid_list(GTM_Conn *, GTM_Transactions *);
+size_t get_sequence_list(GTM_Conn *, GTM_SeqInfo **, size_t);
+
/*
* Transaction Management API
*/
@@ -141,6 +181,25 @@ int get_gid_data(GTM_Conn *conn, GTM_IsolationLevel isolevel, char *gid,
PGXC_NodeId **coordinators);
/*
+ * Multiple Transaction Management API
+ */
+int
+begin_transaction_multi(GTM_Conn *conn, int txn_count, GTM_IsolationLevel *txn_isolation_level,
+ bool *txn_read_only, GTMProxy_ConnID *txn_connid,
+ int *txn_count_out, GlobalTransactionId *gxid_out, GTM_Timestamp *ts_out);
+int
+commit_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid,
+ int *txn_count_out, int *status_out);
+int
+abort_transaction_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid,
+ int *txn_count_out, int *status_out);
+int
+snapshot_get_multi(GTM_Conn *conn, int txn_count, GlobalTransactionId *gxid,
+ int *txn_count_out, int *status_out,
+ GlobalTransactionId *xmin_out, GlobalTransactionId *xmax_out,
+ GlobalTransactionId *recent_global_xmin_out, int32 *xcnt_out);
+
+/*
* Snapshot Management API
*/
GTM_SnapshotData *get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid,
@@ -151,7 +210,12 @@ GTM_SnapshotData *get_snapshot(GTM_Conn *conn, GlobalTransactionId gxid,
*/
int node_register(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum,
GTM_PGXCNodePort port, char *datafolder);
+int node_register_internal(GTM_Conn *conn, GTM_PGXCNodeType type, const char *host,
+ GTM_PGXCNodePort port, GTM_PGXCNodeId nodenum, char *datafolder,
+ GTM_PGXCNodeStatus status);
int node_unregister(GTM_Conn *conn, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum);
+int backend_disconnect(GTM_Conn *conn, bool is_postmaster, GTM_PGXCNodeType type, GTM_PGXCNodeId nodenum);
+char *node_get_local_addr(GTM_Conn *conn, char *buf, size_t buflen, int *rc);
/*
* Sequence Management API
@@ -169,5 +233,4 @@ GTM_Sequence get_next(GTM_Conn *conn, GTM_SequenceKey key);
int set_val(GTM_Conn *conn, GTM_SequenceKey key, GTM_Sequence nextval, bool is_called);
int reset_sequence(GTM_Conn *conn, GTM_SequenceKey key);
-
#endif
diff --git a/src/include/gtm/gtm_conn.h b/src/include/gtm/gtm_conn.h
index c000497a27..40256be5fe 100644
--- a/src/include/gtm/gtm_conn.h
+++ b/src/include/gtm/gtm_conn.h
@@ -15,6 +15,8 @@
#define GTM_CONN_H
#include "gtm/libpq-be.h"
+#include "gtm/libpq-fe.h"
+#include "gtm/libpq-int.h"
struct GTM_ThreadInfo;
@@ -24,6 +26,9 @@ typedef struct GTM_ConnectionInfo
Port *con_port;
struct GTM_ThreadInfo *con_thrinfo;
bool con_authenticated;
+
+ /* a connection object to the standby */
+ GTM_Conn *standby;
} GTM_ConnectionInfo;
typedef struct GTM_Connections
diff --git a/src/include/gtm/gtm_ip.h b/src/include/gtm/gtm_ip.h
index 2f89418af6..bc64ba0aa0 100644
--- a/src/include/gtm/gtm_ip.h
+++ b/src/include/gtm/gtm_ip.h
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * ip.h
+ * gtm_ip.h
* Definitions for IPv6-aware network access.
*
* These definitions are used by both frontend and backend code. Be careful
diff --git a/src/include/gtm/gtm_list.h b/src/include/gtm/gtm_list.h
index c5dbd93766..6a7e5ab73f 100644
--- a/src/include/gtm/gtm_list.h
+++ b/src/include/gtm/gtm_list.h
@@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
- * pg_list.h
+ * gtm_list.h
* interface for PostgreSQL generic linked list package
*
* This package implements singly-linked homogeneous lists.
@@ -39,31 +39,31 @@
#define GTM_LIST_H
-typedef struct ListCell ListCell;
+typedef struct gtm_ListCell gtm_ListCell;
-typedef struct List
+typedef struct gtm_List
{
int length;
- ListCell *head;
- ListCell *tail;
-} List;
+ gtm_ListCell *head;
+ gtm_ListCell *tail;
+} gtm_List;
-struct ListCell
+struct gtm_ListCell
{
union
{
void *ptr_value;
int int_value;
} data;
- ListCell *next;
+ gtm_ListCell *next;
};
/*
- * The *only* valid representation of an empty list is NIL; in other
- * words, a non-NIL list is guaranteed to have length >= 1 and
+ * The *only* valid representation of an empty list is gtm_NIL; in other
+ * words, a non-gtm_NIL list is guaranteed to have length >= 1 and
* head/tail != NULL
*/
-#define NIL ((List *) NULL)
+#define gtm_NIL ((gtm_List *) NULL)
/*
* These routines are used frequently. However, we can't implement
@@ -73,150 +73,150 @@ struct ListCell
*/
#ifdef __GNUC__
-static __inline__ ListCell *
-list_head(List *l)
+static __inline__ gtm_ListCell *
+gtm_list_head(gtm_List *l)
{
return l ? l->head : NULL;
}
-static __inline__ ListCell *
-list_tail(List *l)
+static __inline__ gtm_ListCell *
+gtm_list_tail(gtm_List *l)
{
return l ? l->tail : NULL;
}
static __inline__ int
-list_length(List *l)
+gtm_list_length(gtm_List *l)
{
return l ? l->length : 0;
}
#else
-extern ListCell *list_head(List *l);
-extern ListCell *list_tail(List *l);
-extern int list_length(List *l);
+extern gtm_ListCell *gtm_list_head(gtm_List *l);
+extern gtm_ListCell *gtm_list_tail(gtm_List *l);
+extern int gtm_list_length(gtm_List *l);
#endif /* __GNUC__ */
/*
* NB: There is an unfortunate legacy from a previous incarnation of
- * the List API: the macro lfirst() was used to mean "the data in this
- * cons cell". To avoid changing every usage of lfirst(), that meaning
- * has been kept. As a result, lfirst() takes a ListCell and returns
+ * the gtm_List API: the macro gtm_lfirst() was used to mean "the data in this
+ * cons cell". To avoid changing every usage of gtm_lfirst(), that meaning
+ * has been kept. As a result, gtm_lfirst() takes a gtm_ListCell and returns
* the data it contains; to get the data in the first cell of a
- * List, use linitial(). Worse, lsecond() is more closely related to
- * linitial() than lfirst(): given a List, lsecond() returns the data
+ * gtm_List, use gtm_linitial(). Worse, gtm_lsecond() is more closely related to
+ * gtm_linitial() than gtm_lfirst(): given a gtm_List, gtm_lsecond() returns the data
* in the second cons cell.
*/
-#define lnext(lc) ((lc)->next)
-#define lfirst(lc) ((lc)->data.ptr_value)
-#define lfirst_int(lc) ((lc)->data.int_value)
+#define gtm_lnext(lc) ((lc)->next)
+#define gtm_lfirst(lc) ((lc)->data.ptr_value)
+#define gtm_lfirst_int(lc) ((lc)->data.int_value)
-#define linitial(l) lfirst(list_head(l))
-#define linitial_int(l) lfirst_int(list_head(l))
+#define gtm_linitial(l) gtm_lfirst(gtm_list_head(l))
+#define gtm_linitial_int(l) gtm_lfirst_int(gtm_list_head(l))
-#define lsecond(l) lfirst(lnext(list_head(l)))
-#define lsecond_int(l) lfirst_int(lnext(list_head(l)))
+#define gtm_lsecond(l) gtm_lfirst(gtm_lnext(gtm_list_head(l)))
+#define gtm_lsecond_int(l) gtm_lfirst_int(gtm_lnext(gtm_list_head(l)))
-#define lthird(l) lfirst(lnext(lnext(list_head(l))))
-#define lthird_int(l) lfirst_int(lnext(lnext(list_head(l))))
+#define gtm_lthird(l) gtm_lfirst(gtm_lnext(gtm_lnext(gtm_list_head(l))))
+#define gtm_lthird_int(l) gtm_lfirst_int(gtm_lnext(gtm_lnext(gtm_list_head(l))))
-#define lfourth(l) lfirst(lnext(lnext(lnext(list_head(l)))))
-#define lfourth_int(l) lfirst_int(lnext(lnext(lnext(list_head(l)))))
+#define gtm_lfourth(l) gtm_lfirst(gtm_lnext(gtm_lnext(gtm_lnext(gtm_list_head(l)))))
+#define gtm_lfourth_int(l) gtm_lfirst_int(gtm_lnext(gtm_lnext(gtm_lnext(gtm_list_head(l)))))
-#define llast(l) lfirst(list_tail(l))
-#define llast_int(l) lfirst_int(list_tail(l))
+#define gtm_llast(l) gtm_lfirst(gtm_list_tail(l))
+#define gtm_llast_int(l) gtm_lfirst_int(gtm_list_tail(l))
/*
* Convenience macros for building fixed-length lists
*/
-#define list_make1(x1) lcons(x1, NIL)
-#define list_make2(x1,x2) lcons(x1, list_make1(x2))
-#define list_make3(x1,x2,x3) lcons(x1, list_make2(x2, x3))
-#define list_make4(x1,x2,x3,x4) lcons(x1, list_make3(x2, x3, x4))
+#define gtm_list_make1(x1) gtm_lcons(x1, gtm_NIL)
+#define gtm_list_make2(x1,x2) gtm_lcons(x1, gtm_list_make1(x2))
+#define gtm_list_make3(x1,x2,x3) gtm_lcons(x1, gtm_list_make2(x2, x3))
+#define gtm_list_make4(x1,x2,x3,x4) gtm_lcons(x1, gtm_list_make3(x2, x3, x4))
-#define list_make1_int(x1) lcons_int(x1, NIL)
-#define list_make2_int(x1,x2) lcons_int(x1, list_make1_int(x2))
-#define list_make3_int(x1,x2,x3) lcons_int(x1, list_make2_int(x2, x3))
-#define list_make4_int(x1,x2,x3,x4) lcons_int(x1, list_make3_int(x2, x3, x4))
+#define gtm_list_make1_int(x1) gtm_lcons_int(x1, gtm_NIL)
+#define gtm_list_make2_int(x1,x2) gtm_lcons_int(x1, gtm_list_make1_int(x2))
+#define gtm_list_make3_int(x1,x2,x3) gtm_lcons_int(x1, gtm_list_make2_int(x2, x3))
+#define gtm_list_make4_int(x1,x2,x3,x4) gtm_lcons_int(x1, gtm_list_make3_int(x2, x3, x4))
/*
- * foreach -
+ * gtm_foreach -
* a convenience macro which loops through the list
*/
-#define foreach(cell, l) \
- for ((cell) = list_head(l); (cell) != NULL; (cell) = lnext(cell))
+#define gtm_foreach(cell, l) \
+ for ((cell) = gtm_list_head(l); (cell) != NULL; (cell) = gtm_lnext(cell))
/*
- * for_each_cell -
+ * gtm_for_each_cell -
* a convenience macro which loops through a list starting from a
* specified cell
*/
-#define for_each_cell(cell, initcell) \
- for ((cell) = (initcell); (cell) != NULL; (cell) = lnext(cell))
+#define gtm_for_each_cell(cell, initcell) \
+ for ((cell) = (initcell); (cell) != NULL; (cell) = gtm_lnext(cell))
/*
- * forboth -
+ * gtm_forboth -
* a convenience macro for advancing through two linked lists
* simultaneously. This macro loops through both lists at the same
* time, stopping when either list runs out of elements. Depending
* on the requirements of the call site, it may also be wise to
- * assert that the lengths of the two lists are equal.
+ * assert that the lengths of the two lists are gtm_equal.
*/
-#define forboth(cell1, list1, cell2, list2) \
- for ((cell1) = list_head(list1), (cell2) = list_head(list2); \
+#define gtm_forboth(cell1, list1, cell2, list2) \
+ for ((cell1) = gtm_list_head(list1), (cell2) = gtm_list_head(list2); \
(cell1) != NULL && (cell2) != NULL; \
- (cell1) = lnext(cell1), (cell2) = lnext(cell2))
+ (cell1) = gtm_lnext(cell1), (cell2) = gtm_lnext(cell2))
-extern List *lappend(List *list, void *datum);
-extern List *lappend_int(List *list, int datum);
+extern gtm_List *gtm_lappend(gtm_List *list, void *datum);
+extern gtm_List *gtm_lappend_int(gtm_List *list, int datum);
-extern ListCell *lappend_cell(List *list, ListCell *prev, void *datum);
-extern ListCell *lappend_cell_int(List *list, ListCell *prev, int datum);
+extern gtm_ListCell *gtm_lappend_cell(gtm_List *list, gtm_ListCell *prev, void *datum);
+extern gtm_ListCell *gtm_lappend_cell_int(gtm_List *list, gtm_ListCell *prev, int datum);
-extern List *lcons(void *datum, List *list);
-extern List *lcons_int(int datum, List *list);
+extern gtm_List *gtm_lcons(void *datum, gtm_List *list);
+extern gtm_List *gtm_lcons_int(int datum, gtm_List *list);
-extern List *list_concat(List *list1, List *list2);
-extern List *list_truncate(List *list, int new_size);
+extern gtm_List *gtm_list_concat(gtm_List *list1, gtm_List *list2);
+extern gtm_List *gtm_list_truncate(gtm_List *list, int new_size);
-extern void *list_nth(List *list, int n);
-extern int list_nth_int(List *list, int n);
+extern void *gtm_list_nth(gtm_List *list, int n);
+extern int gtm_list_nth_int(gtm_List *list, int n);
-extern bool list_member(List *list, void *datum);
-extern bool list_member_ptr(List *list, void *datum);
-extern bool list_member_int(List *list, int datum);
+extern bool gtm_list_member(gtm_List *list, void *datum);
+extern bool gtm_list_member_ptr(gtm_List *list, void *datum);
+extern bool gtm_list_member_int(gtm_List *list, int datum);
-extern List *list_delete(List *list, void *datum);
-extern List *list_delete_ptr(List *list, void *datum);
-extern List *list_delete_int(List *list, int datum);
-extern List *list_delete_first(List *list);
-extern List *list_delete_cell(List *list, ListCell *cell, ListCell *prev);
+extern gtm_List *gtm_list_delete(gtm_List *list, void *datum);
+extern gtm_List *gtm_list_delete_ptr(gtm_List *list, void *datum);
+extern gtm_List *gtm_list_delete_int(gtm_List *list, int datum);
+extern gtm_List *gtm_list_delete_first(gtm_List *list);
+extern gtm_List *gtm_list_delete_cell(gtm_List *list, gtm_ListCell *cell, gtm_ListCell *prev);
-extern List *list_union(List *list1, List *list2);
-extern List *list_union_ptr(List *list1, List *list2);
-extern List *list_union_int(List *list1, List *list2);
+extern gtm_List *gtm_list_union(gtm_List *list1, gtm_List *list2);
+extern gtm_List *gtm_list_union_ptr(gtm_List *list1, gtm_List *list2);
+extern gtm_List *gtm_list_union_int(gtm_List *list1, gtm_List *list2);
-extern List *list_intersection(List *list1, List *list2);
+extern gtm_List *gtm_list_intersection(gtm_List *list1, gtm_List *list2);
/* currently, there's no need for list_intersection_int etc */
-extern List *list_difference(List *list1, List *list2);
-extern List *list_difference_ptr(List *list1, List *list2);
-extern List *list_difference_int(List *list1, List *list2);
+extern gtm_List *gtm_list_difference(gtm_List *list1, gtm_List *list2);
+extern gtm_List *gtm_list_difference_ptr(gtm_List *list1, gtm_List *list2);
+extern gtm_List *gtm_list_difference_int(gtm_List *list1, gtm_List *list2);
-extern List *list_append_unique(List *list, void *datum);
-extern List *list_append_unique_ptr(List *list, void *datum);
-extern List *list_append_unique_int(List *list, int datum);
+extern gtm_List *gtm_list_append_unique(gtm_List *list, void *datum);
+extern gtm_List *gtm_list_append_unique_ptr(gtm_List *list, void *datum);
+extern gtm_List *gtm_list_append_unique_int(gtm_List *list, int datum);
-extern List *list_concat_unique(List *list1, List *list2);
-extern List *list_concat_unique_ptr(List *list1, List *list2);
-extern List *list_concat_unique_int(List *list1, List *list2);
+extern gtm_List *gtm_list_concat_unique(gtm_List *list1, gtm_List *list2);
+extern gtm_List *gtm_list_concat_unique_ptr(gtm_List *list1, gtm_List *list2);
+extern gtm_List *gtm_list_concat_unique_int(gtm_List *list1, gtm_List *list2);
-extern void list_free(List *list);
-extern void list_free_deep(List *list);
+extern void gtm_list_free(gtm_List *list);
+extern void gtm_list_free_deep(gtm_List *list);
-extern List *list_copy(List *list);
-extern List *list_copy_tail(List *list, int nskip);
+extern gtm_List *gtm_list_copy(gtm_List *list);
+extern gtm_List *gtm_list_copy_tail(gtm_List *list, int nskip);
/*
* To ease migration to the new list API, a set of compatibility
@@ -227,54 +227,54 @@ extern List *list_copy_tail(List *list, int nskip);
*/
#ifdef ENABLE_LIST_COMPAT
-#define lfirsti(lc) lfirst_int(lc)
+#define gtm_lfirsti(lc) gtm_lfirst_int(lc)
-#define makeList1(x1) list_make1(x1)
-#define makeList2(x1, x2) list_make2(x1, x2)
-#define makeList3(x1, x2, x3) list_make3(x1, x2, x3)
-#define makeList4(x1, x2, x3, x4) list_make4(x1, x2, x3, x4)
+#define gtm_makeList1(x1) gtm_list_make1(x1)
+#define gtm_makeList2(x1, x2) gtm_list_make2(x1, x2)
+#define gtm_makeList3(x1, x2, x3) gtm_list_make3(x1, x2, x3)
+#define gtm_makeList4(x1, x2, x3, x4) gtm_list_make4(x1, x2, x3, x4)
-#define makeListi1(x1) list_make1_int(x1)
-#define makeListi2(x1, x2) list_make2_int(x1, x2)
+#define gtm_makeListi1(x1) gtm_list_make1_int(x1)
+#define gtm_makeListi2(x1, x2) gtm_list_make2_int(x1, x2)
-#define lconsi(datum, list) lcons_int(datum, list)
+#define gtm_lconsi(datum, list) gtm_lcons_int(datum, list)
-#define lappendi(list, datum) lappend_int(list, datum)
+#define gtm_lappendi(list, datum) gtm_lappend_int(list, datum)
-#define nconc(l1, l2) list_concat(l1, l2)
+#define gtm_nconc(l1, l2) gtm_list_concat(l1, l2)
-#define nth(n, list) list_nth(list, n)
+#define gtm_nth(n, list) gtm_list_nth(list, n)
-#define member(datum, list) list_member(list, datum)
-#define ptrMember(datum, list) list_member_ptr(list, datum)
-#define intMember(datum, list) list_member_int(list, datum)
+#define gtm_member(datum, list) gtm_list_member(list, datum)
+#define gtm_ptrMember(datum, list) gtm_list_member_ptr(list, datum)
+#define gtm_intMember(datum, list) gtm_list_member_int(list, datum)
/*
- * Note that the old lremove() determined equality via pointer
- * comparison, whereas the new list_delete() uses equal(); in order to
- * keep the same behavior, we therefore need to map lremove() calls to
- * list_delete_ptr() rather than list_delete()
+ * Note that the old gtm_lremove() determined equality via pointer
+ * comparison, whereas the new gtm_list_delete() uses gtm_equal(); in order to
+ * keep the same behavior, we therefore need to map gtm_lremove() calls to
+ * gtm_list_delete_ptr() rather than gtm_list_delete()
*/
-#define lremove(elem, list) list_delete_ptr(list, elem)
-#define LispRemove(elem, list) list_delete(list, elem)
-#define lremovei(elem, list) list_delete_int(list, elem)
+#define gtm_lremove(elem, list) gtm_list_delete_ptr(list, elem)
+#define gtm_LispRemove(elem, list) gtm_list_delete(list, elem)
+#define gtm_lremovei(elem, list) gtm_list_delete_int(list, elem)
-#define ltruncate(n, list) list_truncate(list, n)
+#define gtm_ltruncate(n, list) gtm_list_truncate(list, n)
-#define set_union(l1, l2) list_union(l1, l2)
-#define set_ptrUnion(l1, l2) list_union_ptr(l1, l2)
+#define gtm_set_union(l1, l2) gtm_list_union(l1, l2)
+#define gtm_set_ptrUnion(l1, l2) gtm_list_union_ptr(l1, l2)
-#define set_difference(l1, l2) list_difference(l1, l2)
-#define set_ptrDifference(l1, l2) list_difference_ptr(l1, l2)
+#define gtm_set_difference(l1, l2) gtm_list_difference(l1, l2)
+#define gtm_set_ptrDifference(l1, l2) gtm_list_difference_ptr(l1, l2)
-#define equali(l1, l2) equal(l1, l2)
-#define equalo(l1, l2) equal(l1, l2)
+#define gtm_equali(l1, l2) gtm_equal(l1, l2)
+#define gtm_equalo(l1, l2) gtm_equal(l1, l2)
-#define freeList(list) list_free(list)
+#define gtm_freeList(list) gtm_list_free(list)
-#define listCopy(list) list_copy(list)
+#define gtm_listCopy(list) gtm_list_copy(list)
-extern int length(List *list);
+extern int gtm_length(gtm_List *list);
#endif /* ENABLE_LIST_COMPAT */
#endif /* GTM_LIST_H */
diff --git a/src/include/gtm/gtm_msg.h b/src/include/gtm/gtm_msg.h
index aeee09569b..4ec7dacb28 100644
--- a/src/include/gtm/gtm_msg.h
+++ b/src/include/gtm/gtm_msg.h
@@ -19,6 +19,9 @@ typedef enum GTM_MessageType
MSG_TYPE_INVALID,
MSG_NODE_REGISTER, /* Register a PGXC Node with GTM */
MSG_NODE_UNREGISTER, /* Unregister a PGXC Node with GTM */
+ MSG_NODE_LIST,
+ MSG_NODE_BEGIN_REPLICATION_INIT,
+ MSG_NODE_END_REPLICATION_INIT,
MSG_TXN_BEGIN, /* Start a new transaction */
MSG_TXN_BEGIN_GETGXID, /* Start a new transaction and get GXID */
MSG_TXN_BEGIN_GETGXID_MULTI, /* Start multiple new transactions and get GXIDs */
@@ -31,6 +34,8 @@ typedef enum GTM_MessageType
MSG_TXN_ROLLBACK_MULTI, /* Rollback multiple transactions */
MSG_TXN_GET_GID_DATA, /* Get info associated with a GID, and get a GXID */
MSG_TXN_GET_GXID, /* Get a GXID for a transaction */
+ MSG_TXN_GET_NEXT_GXID, /* Get next GXID */
+ MSG_TXN_GXID_LIST,
MSG_SNAPSHOT_GET, /* Get a global snapshot */
MSG_SNAPSHOT_GET_MULTI, /* Get multiple global snapshots */
MSG_SNAPSHOT_GXID_GET, /* Get GXID and snapshot together */
@@ -43,6 +48,7 @@ typedef enum GTM_MessageType
MSG_SEQUENCE_CLOSE, /* Close a previously inited sequence */
MSG_SEQUENCE_RENAME, /* Rename a sequence */
MSG_SEQUENCE_ALTER, /* Alter a sequence */
+ MSG_SEQUENCE_LIST, /* Get a list of sequences */
MSG_TXN_GET_STATUS, /* Get status of a given transaction */
MSG_TXN_GET_ALL_PREPARED, /* Get information about all outstanding
* prepared transactions */
@@ -60,6 +66,9 @@ typedef enum GTM_ResultType
{
NODE_REGISTER_RESULT,
NODE_UNREGISTER_RESULT,
+ NODE_LIST_RESULT,
+ NODE_BEGIN_REPLICATION_INIT_RESULT,
+ NODE_END_REPLICATION_INIT_RESULT,
TXN_BEGIN_RESULT,
TXN_BEGIN_GETGXID_RESULT,
TXN_BEGIN_GETGXID_MULTI_RESULT,
@@ -72,6 +81,8 @@ typedef enum GTM_ResultType
TXN_ROLLBACK_MULTI_RESULT,
TXN_GET_GID_DATA_RESULT,
TXN_GET_GXID_RESULT,
+ TXN_GET_NEXT_GXID_RESULT,
+ TXN_GXID_LIST_RESULT,
SNAPSHOT_GET_RESULT,
SNAPSHOT_GET_MULTI_RESULT,
SNAPSHOT_GXID_GET_RESULT,
@@ -84,6 +95,7 @@ typedef enum GTM_ResultType
SEQUENCE_CLOSE_RESULT,
SEQUENCE_RENAME_RESULT,
SEQUENCE_ALTER_RESULT,
+ SEQUENCE_LIST_RESULT,
TXN_GET_STATUS_RESULT,
TXN_GET_ALL_PREPARED_RESULT,
TXN_BEGIN_GETGXID_AUTOVACUUM_RESULT,
diff --git a/src/include/gtm/gtm_proxy.h b/src/include/gtm/gtm_proxy.h
index 0031d6e83b..ead825320b 100644
--- a/src/include/gtm/gtm_proxy.h
+++ b/src/include/gtm/gtm_proxy.h
@@ -110,11 +110,16 @@ typedef struct GTMProxy_ThreadInfo
/* connection array */
GTMProxy_ConnectionInfo *thr_all_conns[GTM_PROXY_MAX_CONNECTIONS];
struct pollfd thr_poll_fds[GTM_PROXY_MAX_CONNECTIONS];
- List *thr_processed_commands;
- List *thr_pending_commands[MSG_TYPE_COUNT];
- GTM_Conn *thr_gtm_conn;
+ /* Command backup */
+ short thr_any_backup[GTM_PROXY_MAX_CONNECTIONS];
+ int thr_qtype[GTM_PROXY_MAX_CONNECTIONS];
+ StringInfoData thr_inBufData[GTM_PROXY_MAX_CONNECTIONS];
+
+ gtm_List *thr_processed_commands;
+ gtm_List *thr_pending_commands[MSG_TYPE_COUNT];
+ GTM_Conn *thr_gtm_conn;
} GTMProxy_ThreadInfo;
typedef struct GTMProxy_Threads
@@ -173,6 +178,7 @@ typedef union GTMProxy_CommandData
GTM_PGXCNodeId proxynum;
char *datafolder;
char *ipaddress;
+ GTM_PGXCNodeStatus status;
} cd_reg;
} GTMProxy_CommandData;
diff --git a/src/include/gtm/gtm_seq.h b/src/include/gtm/gtm_seq.h
index 6ac8092ef5..92174eecc5 100644
--- a/src/include/gtm/gtm_seq.h
+++ b/src/include/gtm/gtm_seq.h
@@ -15,6 +15,8 @@
#define GTM_SEQ_H
#include "gtm/stringinfo.h"
+#include "gtm/gtm_lock.h"
+#include "gtm/libpq-be.h"
/* Global sequence related structures */
@@ -83,7 +85,18 @@ void ProcessSequenceCloseCommand(Port *myport, StringInfo message);
void ProcessSequenceRenameCommand(Port *myport, StringInfo message);
void ProcessSequenceAlterCommand(Port *myport, StringInfo message);
+void ProcessSequenceListCommand(Port *myport, StringInfo message);
+
void GTM_SaveSeqInfo(int ctlfd);
void GTM_RestoreSeqInfo(int ctlfd);
+int GTM_SeqRestore(GTM_SequenceKey seqkey,
+ GTM_Sequence increment_by,
+ GTM_Sequence minval,
+ GTM_Sequence maxval,
+ GTM_Sequence startval,
+ GTM_Sequence curval,
+ int32 state,
+ bool cycle,
+ bool called);
#endif
diff --git a/src/include/gtm/gtm_serialize.h b/src/include/gtm/gtm_serialize.h
new file mode 100644
index 0000000000..1fe80cd707
--- /dev/null
+++ b/src/include/gtm/gtm_serialize.h
@@ -0,0 +1,48 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_serialize.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gtm/gtm_serialize.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef GTM_SERIALIZE_H
+#define GTM_SERIALIZE_H
+
+#include <sys/types.h>
+
+#include "gtm/gtm_c.h"
+#include "gtm/gtm_txn.h"
+#include "gtm/register.h"
+#include "gtm/gtm_seq.h"
+
+size_t gtm_get_snapshotdata_size(GTM_SnapshotData *);
+size_t gtm_serialize_snapshotdata(GTM_SnapshotData *, char *, size_t);
+size_t gtm_deserialize_snapshotdata(GTM_SnapshotData *, const char *, size_t);
+
+size_t gtm_get_transactioninfo_size(GTM_TransactionInfo *);
+size_t gtm_serialize_transactioninfo(GTM_TransactionInfo *, char *, size_t);
+size_t gtm_deserialize_transactioninfo(GTM_TransactionInfo *, const char *, size_t);
+
+size_t gtm_get_transactions_size(GTM_Transactions *);
+size_t gtm_serialize_transactions(GTM_Transactions *, char *, size_t);
+size_t gtm_deserialize_transactions(GTM_Transactions *, const char *, size_t);
+
+size_t gtm_get_pgxcnodeinfo_size(GTM_PGXCNodeInfo *);
+size_t gtm_serialize_pgxcnodeinfo(GTM_PGXCNodeInfo *, char *, size_t);
+size_t gtm_deserialize_pgxcnodeinfo(GTM_PGXCNodeInfo *, const char *, size_t);
+
+size_t gtm_get_sequence_size(GTM_SeqInfo *);
+size_t gtm_serialize_sequence(GTM_SeqInfo *, char *, size_t);
+GTM_SeqInfo *gtm_deserialize_sequence(const char *, size_t);
+
+void dump_transactions_elog(GTM_Transactions *, int);
+void dump_transactioninfo_elog(GTM_TransactionInfo *);
+
+#endif /* GTM_SERIALIZE_H */
diff --git a/src/include/gtm/gtm_serialize_debug.h b/src/include/gtm/gtm_serialize_debug.h
new file mode 100644
index 0000000000..ba9d89dc53
--- /dev/null
+++ b/src/include/gtm/gtm_serialize_debug.h
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_serialize_debug.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gtm/gtm_serialize_debug.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef GTM_SERIALIZE_DEBUG_H
+#define GTM_SERIALIZE_DEBUG_H
+
+#include <sys/types.h>
+
+#include "gtm/gtm_txn.h"
+
+void dump_transactions_elog(GTM_Transactions *, int);
+void dump_transactioninfo_elog(GTM_TransactionInfo *);
+
+#endif /* GTM_SERIALIZE_DEBUG_H */
diff --git a/src/include/gtm/gtm_standby.h b/src/include/gtm/gtm_standby.h
new file mode 100644
index 0000000000..299f65c454
--- /dev/null
+++ b/src/include/gtm/gtm_standby.h
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_standby.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gtm/gtm_standby.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef GTM_STANDBY_H
+#define GTM_STANDBY_H
+
+#include "c.h"
+#include "gtm/gtm_c.h"
+#include "gtm/libpq-fe.h"
+
+/*
+ * Variables to interact with GTM active under GTM standby mode.
+ */
+bool gtm_is_standby(void);
+void gtm_set_standby(bool standby);
+void gtm_set_active_conninfo(const char *addr, int port);
+
+int gtm_standby_start_startup(void);
+int gtm_standby_finish_startup(void);
+
+int gtm_standby_restore_next_gxid(void);
+int gtm_standby_restore_gxid(void);
+int gtm_standby_restore_sequence(void);
+int gtm_standby_restore_node(void);
+
+int gtm_standby_register_self(GTM_PGXCNodeId nodenum, int port, const char *datadir);
+int gtm_standby_activate_self(void);
+
+GTM_Conn *gtm_standby_connect_to_standby(void);
+void gtm_standby_disconnect_from_standby(GTM_Conn *conn);
+GTM_Conn *gtm_standby_reconnect_to_standby(GTM_Conn *old_conn, int retry_max);
+bool gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn);
+
+#endif /* GTM_STANDBY_H */
diff --git a/src/include/gtm/gtm_txn.h b/src/include/gtm/gtm_txn.h
index 9b2fb83aa1..86a0d4919b 100644
--- a/src/include/gtm/gtm_txn.h
+++ b/src/include/gtm/gtm_txn.h
@@ -14,6 +14,7 @@
#ifndef _GTM_TXN_H
#define _GTM_TXN_H
+#include "gtm/libpq-be.h"
#include "gtm/gtm_c.h"
#include "gtm/gtm_lock.h"
#include "gtm/gtm_list.h"
@@ -163,14 +164,14 @@ typedef struct GTM_Transactions
int32 gt_lastslot;
GTM_TransactionInfo gt_transactions_array[GTM_MAX_GLOBAL_TRANSACTIONS];
- List *gt_open_transactions;
+ gtm_List *gt_open_transactions;
GTM_RWLock gt_TransArrayLock;
} GTM_Transactions;
extern GTM_Transactions GTMTransactions;
-#define GTM_CountOpenTransactions() (list_length(GTMTransactions.gt_open_transactions))
+#define GTM_CountOpenTransactions() (gtm_list_length(GTMTransactions.gt_open_transactions))
/*
* Two hash tables will be maintained to quickly find the
@@ -239,6 +240,8 @@ void ProcessStartPreparedTransactionCommand(Port *myport, StringInfo message);
void ProcessPrepareTransactionCommand(Port *myport, StringInfo message);
void ProcessGetGIDDataTransactionCommand(Port *myport, StringInfo message);
void ProcessGetGXIDTransactionCommand(Port *myport, StringInfo message);
+void ProcessGXIDListCommand(Port *myport, StringInfo message);
+void ProcessGetNextGXIDTransactionCommand(Port *myport, StringInfo message);
void ProcessBeginTransactionGetGXIDAutovacuumCommand(Port *myport, StringInfo message);
void ProcessBeginTransactionGetGXIDCommandMulti(Port *myport, StringInfo message);
diff --git a/src/include/gtm/gtm_utils.h b/src/include/gtm/gtm_utils.h
new file mode 100644
index 0000000000..245daa9114
--- /dev/null
+++ b/src/include/gtm/gtm_utils.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * gtm_utils.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gtm/gtm_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef GTM_UTILS_H
+#define GTM_UTILS_H
+
+#include "gtm/libpq-int.h"
+
+#if 0
+/*
+ * PGXCTODO: This portion of code needs XCM support
+ * to be able to report GTM failures to XC watcher and
+ * enable a GTM reconnection kick.
+ */
+void gtm_report_failure(GTM_Conn *);
+#endif
+
+#endif /* GTM_UTIL_H */
diff --git a/src/include/gtm/libpq-be.h b/src/include/gtm/libpq-be.h
index d5a5df5ed0..eaea0adcf0 100644
--- a/src/include/gtm/libpq-be.h
+++ b/src/include/gtm/libpq-be.h
@@ -19,6 +19,9 @@
#ifndef LIBPQ_BE_H
#define LIBPQ_BE_H
+#include "gtm/pqcomm.h"
+#include "gtm/gtm_c.h"
+
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
@@ -26,8 +29,6 @@
#include <netinet/tcp.h>
#endif
-#include "gtm/pqcomm.h"
-
/*
* This is used by the postmaster in its communication with frontends. It
* contains all state information needed during this communication before the
@@ -72,6 +73,13 @@ typedef struct Port
int keepalives_idle;
int keepalives_interval;
int keepalives_count;
+
+ /*
+ * GTM communication error handling. See libpq-int.h for details.
+ */
+ int connErr_WaitOpt;
+ int connErr_WaitSecs;
+ int connErr_WaitCount;
} Port;
/* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
diff --git a/src/include/gtm/libpq-int.h b/src/include/gtm/libpq-int.h
index 5c6714bc9a..2961f70dd0 100644
--- a/src/include/gtm/libpq-int.h
+++ b/src/include/gtm/libpq-int.h
@@ -86,6 +86,11 @@ struct gtm_conn
/* Buffer for receiving various parts of messages */
PQExpBufferData workBuffer; /* expansible string */
+ /* Options to handle GTM communication error */
+ int gtmErrorWaitOpt; /* If true, wait reconnect signal. */
+ int gtmErrorWaitSecs; /* Duration of the wait time in second */
+ int gtmErrorWaitCount; /* How many durations to wait */
+
/* Pointer to the result of last operation */
GTM_Result *result;
};
diff --git a/src/include/gtm/palloc.h b/src/include/gtm/palloc.h
index e81e9d14ac..0c82e7362b 100644
--- a/src/include/gtm/palloc.h
+++ b/src/include/gtm/palloc.h
@@ -87,4 +87,12 @@ extern char *pgport_pstrdup(const char *str);
extern void pgport_pfree(void *pointer);
#endif
+#ifdef PGXC
+/*
+ * The following part provides common palloc binary interface. This
+ * is needed especially for gtm_serialize.c and gtm_serialize_debug.c.
+ */
+#include "gen_alloc.h"
+#endif
+
#endif /* PALLOC_H */
diff --git a/src/include/gtm/pqcomm.h b/src/include/gtm/pqcomm.h
index df655a5c5b..010952e287 100644
--- a/src/include/gtm/pqcomm.h
+++ b/src/include/gtm/pqcomm.h
@@ -39,14 +39,6 @@ typedef struct
(port))
/*
- * Packet lengths are 4 bytes in network byte order.
- *
- * The initial length is omitted from the packet layouts appearing below.
- */
-
-typedef uint32 PacketLen;
-
-/*
* In protocol 3.0 and later, the startup packet length is not fixed, but
* we set an arbitrary limit on it anyway. This is just to prevent simple
* denial-of-service attacks via sending enough data to run the server
diff --git a/src/include/gtm/proxy_utils.h b/src/include/gtm/proxy_utils.h
new file mode 100644
index 0000000000..04b14e95d2
--- /dev/null
+++ b/src/include/gtm/proxy_utils.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * proxy_utils.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gtm/proxy_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef PROXY_UTILS_H
+#define PROXY_UTILS_H
+
+#include "gtm/libpq-int.h"
+
+bool gtm_standby_check_communication_error(int *, GTM_Conn *);
+
+#endif /* PROXY_UTILS_H */
+
diff --git a/src/include/gtm/register.h b/src/include/gtm/register.h
index 9b11d1d6cb..0421d2bbb9 100644
--- a/src/include/gtm/register.h
+++ b/src/include/gtm/register.h
@@ -14,6 +14,7 @@
#ifndef _REGISTER_H
#define _REGISTER_H
+#include "gtm/libpq-be.h"
#include "gtm/gtm_c.h"
#include "gtm/gtm_lock.h"
#include "gtm/gtm_list.h"
@@ -51,6 +52,12 @@ typedef struct GTM_PGXCNodeInfo
int socket; /* socket number used for registration */
} GTM_PGXCNodeInfo;
+/* Maximum number of nodes that can be registered */
+#define MAX_NODES 1024
+
+size_t pgxcnode_get_all(GTM_PGXCNodeInfo **data, size_t maxlen);
+size_t pgxcnode_find_by_type(GTM_PGXCNodeType type, GTM_PGXCNodeInfo **data, size_t maxlen);
+
int Recovery_PGXCNodeRegister(GTM_PGXCNodeType type,
GTM_PGXCNodeId nodenum,
GTM_PGXCNodePort port,
@@ -75,5 +82,6 @@ void Recovery_SaveRegisterFileName(char *dir);
void ProcessPGXCNodeRegister(Port *myport, StringInfo message);
void ProcessPGXCNodeUnregister(Port *myport, StringInfo message);
void ProcessPGXCNodeBackendDisconnect(Port *myport, StringInfo message);
+void ProcessPGXCNodeList(Port *myport, StringInfo message);
#endif /* GTM_NODE_H */
diff --git a/src/include/gtm/replication.h b/src/include/gtm/replication.h
new file mode 100644
index 0000000000..93ff885aff
--- /dev/null
+++ b/src/include/gtm/replication.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * replication.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gtm/replication.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef REPLICATION_H
+#define REPLICATION_H
+
+#include "c.h"
+
+#include "gtm/libpq.h"
+#include "gtm/stringinfo.h"
+
+void ProcessBeginReplicationInitialSyncRequest(Port *, StringInfo);
+void ProcessEndReplicationInitialSyncRequest(Port *, StringInfo);
+
+#endif /* REPLICATION_H */
diff --git a/src/include/gtm/standby_utils.h b/src/include/gtm/standby_utils.h
new file mode 100644
index 0000000000..db05adc674
--- /dev/null
+++ b/src/include/gtm/standby_utils.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * standby_utils.h
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation
+ *
+ * src/include/gtm/standby_utils.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef STANDBY_UTILS_H
+#define STANDBY_UTILS_H
+
+#include "gtm/gtm_lock.h"
+
+bool Recovery_IsStandby(void);
+void Recovery_StandbySetStandby(bool standby);
+void Recovery_StandbySetConnInfo(const char *addr, int port);
+int Recovery_StandbyGetActivePort(void);
+char* Recovery_StandbyGetActiveAddress(void);
+void Recovery_InitStandbyLock(void);
+
+#endif
diff --git a/src/include/gtm/stringinfo.h b/src/include/gtm/stringinfo.h
index c34e6a32fb..62bb45631a 100644
--- a/src/include/gtm/stringinfo.h
+++ b/src/include/gtm/stringinfo.h
@@ -146,4 +146,17 @@ extern void appendBinaryStringInfo(StringInfo str,
*/
extern void enlargeStringInfo(StringInfo str, int needed);
+/*-----------------------
+ * dupStringInfo
+ * Get new StringInfo and copy the original to it.
+ */
+extern StringInfo dupStringInfo(StringInfo orig);
+
+/*------------------------
+ * copyStringInfo
+ * Copy StringInfo. Deep copy: Data will be copied too.
+ * cursor of "to" will be initialized to zero.
+ */
+extern void copyStringInfo(StringInfo to, StringInfo from);
+
#endif /* STRINGINFO_H */