Commit 8bdd12bb authored by Robert Haas's avatar Robert Haas

Add pg_recvlogical, a tool to receive data logical decoding data.

This is fairly basic at the moment, but it's at least useful for
testing and debugging, and possibly more.

Andres Freund
parent 250f8a7b
/pg_basebackup /pg_basebackup
/pg_receivexlog /pg_receivexlog
/pg_recvlogical
...@@ -20,7 +20,7 @@ override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) ...@@ -20,7 +20,7 @@ override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
OBJS=receivelog.o streamutil.o $(WIN32RES) OBJS=receivelog.o streamutil.o $(WIN32RES)
all: pg_basebackup pg_receivexlog all: pg_basebackup pg_receivexlog pg_recvlogical
pg_basebackup: pg_basebackup.o $(OBJS) | submake-libpq submake-libpgport pg_basebackup: pg_basebackup.o $(OBJS) | submake-libpq submake-libpgport
$(CC) $(CFLAGS) pg_basebackup.o $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) $(CC) $(CFLAGS) pg_basebackup.o $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
...@@ -28,9 +28,13 @@ pg_basebackup: pg_basebackup.o $(OBJS) | submake-libpq submake-libpgport ...@@ -28,9 +28,13 @@ pg_basebackup: pg_basebackup.o $(OBJS) | submake-libpq submake-libpgport
pg_receivexlog: pg_receivexlog.o $(OBJS) | submake-libpq submake-libpgport pg_receivexlog: pg_receivexlog.o $(OBJS) | submake-libpq submake-libpgport
$(CC) $(CFLAGS) pg_receivexlog.o $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) $(CC) $(CFLAGS) pg_receivexlog.o $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
pg_recvlogical: pg_recvlogical.o $(OBJS) | submake-libpq submake-libpgport
$(CC) $(CFLAGS) pg_recvlogical.o $(OBJS) $(libpq_pgport) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
install: all installdirs install: all installdirs
$(INSTALL_PROGRAM) pg_basebackup$(X) '$(DESTDIR)$(bindir)/pg_basebackup$(X)' $(INSTALL_PROGRAM) pg_basebackup$(X) '$(DESTDIR)$(bindir)/pg_basebackup$(X)'
$(INSTALL_PROGRAM) pg_receivexlog$(X) '$(DESTDIR)$(bindir)/pg_receivexlog$(X)' $(INSTALL_PROGRAM) pg_receivexlog$(X) '$(DESTDIR)$(bindir)/pg_receivexlog$(X)'
$(INSTALL_PROGRAM) pg_recvlogical$(X) '$(DESTDIR)$(bindir)/pg_recvlogical$(X)'
installdirs: installdirs:
$(MKDIR_P) '$(DESTDIR)$(bindir)' $(MKDIR_P) '$(DESTDIR)$(bindir)'
...@@ -38,6 +42,9 @@ installdirs: ...@@ -38,6 +42,9 @@ installdirs:
uninstall: uninstall:
rm -f '$(DESTDIR)$(bindir)/pg_basebackup$(X)' rm -f '$(DESTDIR)$(bindir)/pg_basebackup$(X)'
rm -f '$(DESTDIR)$(bindir)/pg_receivexlog$(X)' rm -f '$(DESTDIR)$(bindir)/pg_receivexlog$(X)'
rm -f '$(DESTDIR)$(bindir)/pg_recvlogical$(X)'
clean distclean maintainer-clean: clean distclean maintainer-clean:
rm -f pg_basebackup$(X) pg_receivexlog$(X) $(OBJS) pg_basebackup.o pg_receivexlog.o rm -f pg_basebackup$(X) pg_receivexlog$(X) pg_recvlogical$(X) \
pg_basebackup.o pg_receivexlog.o pg_recvlogical.o \
$(OBJS)
# src/bin/pg_basebackup/nls.mk # src/bin/pg_basebackup/nls.mk
CATALOG_NAME = pg_basebackup CATALOG_NAME = pg_basebackup
AVAIL_LANGUAGES = cs de es fr it ja pl pt_BR ru zh_CN AVAIL_LANGUAGES = cs de es fr it ja pl pt_BR ru zh_CN
GETTEXT_FILES = pg_basebackup.c pg_receivexlog.c receivelog.c streamutil.c ../../common/fe_memutils.c GETTEXT_FILES = pg_basebackup.c pg_receivexlog.c pg_recvlogical.c receivelog.c streamutil.c ../../common/fe_memutils.c
This diff is collapsed.
...@@ -11,22 +11,19 @@ ...@@ -11,22 +11,19 @@
* src/bin/pg_basebackup/receivelog.c * src/bin/pg_basebackup/receivelog.c
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> #include <unistd.h>
/* for ntohl/htonl */
#include <netinet/in.h>
#include <arpa/inet.h>
#include "libpq-fe.h"
#include "access/xlog_internal.h"
/* local includes */
#include "receivelog.h" #include "receivelog.h"
#include "streamutil.h" #include "streamutil.h"
#include "libpq-fe.h"
#include "access/xlog_internal.h"
/* fd and filename for currently open WAL file */ /* fd and filename for currently open WAL file */
static int walfile = -1; static int walfile = -1;
...@@ -194,63 +191,6 @@ close_walfile(char *basedir, char *partial_suffix, XLogRecPtr pos) ...@@ -194,63 +191,6 @@ close_walfile(char *basedir, char *partial_suffix, XLogRecPtr pos)
} }
/*
* Local version of GetCurrentTimestamp(), since we are not linked with
* backend code. The protocol always uses integer timestamps, regardless of
* server setting.
*/
static int64
localGetCurrentTimestamp(void)
{
int64 result;
struct timeval tp;
gettimeofday(&tp, NULL);
result = (int64) tp.tv_sec -
((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
result = (result * USECS_PER_SEC) + tp.tv_usec;
return result;
}
/*
* Local version of TimestampDifference(), since we are not linked with
* backend code.
*/
static void
localTimestampDifference(int64 start_time, int64 stop_time,
long *secs, int *microsecs)
{
int64 diff = stop_time - start_time;
if (diff <= 0)
{
*secs = 0;
*microsecs = 0;
}
else
{
*secs = (long) (diff / USECS_PER_SEC);
*microsecs = (int) (diff % USECS_PER_SEC);
}
}
/*
* Local version of TimestampDifferenceExceeds(), since we are not
* linked with backend code.
*/
static bool
localTimestampDifferenceExceeds(int64 start_time,
int64 stop_time,
int msec)
{
int64 diff = stop_time - start_time;
return (diff >= msec * INT64CONST(1000));
}
/* /*
* Check if a timeline history file exists. * Check if a timeline history file exists.
*/ */
...@@ -370,47 +310,6 @@ writeTimeLineHistoryFile(char *basedir, TimeLineID tli, char *filename, char *co ...@@ -370,47 +310,6 @@ writeTimeLineHistoryFile(char *basedir, TimeLineID tli, char *filename, char *co
return true; return true;
} }
/*
* Converts an int64 to network byte order.
*/
static void
sendint64(int64 i, char *buf)
{
uint32 n32;
/* High order half first, since we're doing MSB-first */
n32 = (uint32) (i >> 32);
n32 = htonl(n32);
memcpy(&buf[0], &n32, 4);
/* Now the low order half */
n32 = (uint32) i;
n32 = htonl(n32);
memcpy(&buf[4], &n32, 4);
}
/*
* Converts an int64 from network byte order to native format.
*/
static int64
recvint64(char *buf)
{
int64 result;
uint32 h32;
uint32 l32;
memcpy(&h32, buf, 4);
memcpy(&l32, buf + 4, 4);
h32 = ntohl(h32);
l32 = ntohl(l32);
result = h32;
result <<= 32;
result |= l32;
return result;
}
/* /*
* Send a Standby Status Update message to server. * Send a Standby Status Update message to server.
*/ */
...@@ -422,16 +321,16 @@ sendFeedback(PGconn *conn, XLogRecPtr blockpos, int64 now, bool replyRequested) ...@@ -422,16 +321,16 @@ sendFeedback(PGconn *conn, XLogRecPtr blockpos, int64 now, bool replyRequested)
replybuf[len] = 'r'; replybuf[len] = 'r';
len += 1; len += 1;
sendint64(blockpos, &replybuf[len]); /* write */ fe_sendint64(blockpos, &replybuf[len]); /* write */
len += 8; len += 8;
if (reportFlushPosition) if (reportFlushPosition)
sendint64(lastFlushPosition, &replybuf[len]); /* flush */ fe_sendint64(lastFlushPosition, &replybuf[len]); /* flush */
else else
sendint64(InvalidXLogRecPtr, &replybuf[len]); /* flush */ fe_sendint64(InvalidXLogRecPtr, &replybuf[len]); /* flush */
len += 8; len += 8;
sendint64(InvalidXLogRecPtr, &replybuf[len]); /* apply */ fe_sendint64(InvalidXLogRecPtr, &replybuf[len]); /* apply */
len += 8; len += 8;
sendint64(now, &replybuf[len]); /* sendTime */ fe_sendint64(now, &replybuf[len]); /* sendTime */
len += 8; len += 8;
replybuf[len] = replyRequested ? 1 : 0; /* replyRequested */ replybuf[len] = replyRequested ? 1 : 0; /* replyRequested */
len += 1; len += 1;
...@@ -864,9 +763,9 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, ...@@ -864,9 +763,9 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline,
/* /*
* Potentially send a status message to the master * Potentially send a status message to the master
*/ */
now = localGetCurrentTimestamp(); now = feGetCurrentTimestamp();
if (still_sending && standby_message_timeout > 0 && if (still_sending && standby_message_timeout > 0 &&
localTimestampDifferenceExceeds(last_status, now, feTimestampDifferenceExceeds(last_status, now,
standby_message_timeout)) standby_message_timeout))
{ {
/* Time to send feedback! */ /* Time to send feedback! */
...@@ -895,10 +794,10 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, ...@@ -895,10 +794,10 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline,
int usecs; int usecs;
targettime = last_status + (standby_message_timeout - 1) * ((int64) 1000); targettime = last_status + (standby_message_timeout - 1) * ((int64) 1000);
localTimestampDifference(now, feTimestampDifference(now,
targettime, targettime,
&secs, &secs,
&usecs); &usecs);
if (secs <= 0) if (secs <= 0)
timeout.tv_sec = 1; /* Always sleep at least 1 sec */ timeout.tv_sec = 1; /* Always sleep at least 1 sec */
else else
...@@ -1002,7 +901,7 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, ...@@ -1002,7 +901,7 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline,
/* If the server requested an immediate reply, send one. */ /* If the server requested an immediate reply, send one. */
if (replyRequested && still_sending) if (replyRequested && still_sending)
{ {
now = localGetCurrentTimestamp(); now = feGetCurrentTimestamp();
if (!sendFeedback(conn, blockpos, now, false)) if (!sendFeedback(conn, blockpos, now, false))
goto error; goto error;
last_status = now; last_status = now;
...@@ -1032,7 +931,7 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline, ...@@ -1032,7 +931,7 @@ HandleCopyStream(PGconn *conn, XLogRecPtr startpos, uint32 timeline,
progname, r); progname, r);
goto error; goto error;
} }
blockpos = recvint64(&copybuf[1]); blockpos = fe_recvint64(&copybuf[1]);
/* Extract WAL location for this block */ /* Extract WAL location for this block */
xlogoff = blockpos % XLOG_SEG_SIZE; xlogoff = blockpos % XLOG_SEG_SIZE;
......
#include "libpq-fe.h"
#include "access/xlogdefs.h" #include "access/xlogdefs.h"
/* /*
......
...@@ -12,10 +12,23 @@ ...@@ -12,10 +12,23 @@
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
#include "streamutil.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
/* for ntohl/htonl */
#include <netinet/in.h>
#include <arpa/inet.h>
/* local includes */
#include "receivelog.h"
#include "streamutil.h"
#include "common/fe_memutils.h"
#include "datatype/timestamp.h"
const char *progname; const char *progname;
char *connection_string = NULL; char *connection_string = NULL;
...@@ -23,6 +36,7 @@ char *dbhost = NULL; ...@@ -23,6 +36,7 @@ char *dbhost = NULL;
char *dbuser = NULL; char *dbuser = NULL;
char *dbport = NULL; char *dbport = NULL;
char *replication_slot = NULL; char *replication_slot = NULL;
char *dbname = NULL;
int dbgetpassword = 0; /* 0=auto, -1=never, 1=always */ int dbgetpassword = 0; /* 0=auto, -1=never, 1=always */
static char *dbpassword = NULL; static char *dbpassword = NULL;
PGconn *conn = NULL; PGconn *conn = NULL;
...@@ -87,10 +101,10 @@ GetConnection(void) ...@@ -87,10 +101,10 @@ GetConnection(void)
} }
keywords[i] = "dbname"; keywords[i] = "dbname";
values[i] = "replication"; values[i] = dbname == NULL ? "replication" : dbname;
i++; i++;
keywords[i] = "replication"; keywords[i] = "replication";
values[i] = "true"; values[i] = dbname == NULL ? "true" : "database";
i++; i++;
keywords[i] = "fallback_application_name"; keywords[i] = "fallback_application_name";
values[i] = progname; values[i] = progname;
...@@ -212,3 +226,102 @@ GetConnection(void) ...@@ -212,3 +226,102 @@ GetConnection(void)
return tmpconn; return tmpconn;
} }
/*
* Frontend version of GetCurrentTimestamp(), since we are not linked with
* backend code. The protocol always uses integer timestamps, regardless of
* server setting.
*/
int64
feGetCurrentTimestamp(void)
{
int64 result;
struct timeval tp;
gettimeofday(&tp, NULL);
result = (int64) tp.tv_sec -
((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
result = (result * USECS_PER_SEC) + tp.tv_usec;
return result;
}
/*
* Frontend version of TimestampDifference(), since we are not linked with
* backend code.
*/
void
feTimestampDifference(int64 start_time, int64 stop_time,
long *secs, int *microsecs)
{
int64 diff = stop_time - start_time;
if (diff <= 0)
{
*secs = 0;
*microsecs = 0;
}
else
{
*secs = (long) (diff / USECS_PER_SEC);
*microsecs = (int) (diff % USECS_PER_SEC);
}
}
/*
* Frontend version of TimestampDifferenceExceeds(), since we are not
* linked with backend code.
*/
bool
feTimestampDifferenceExceeds(int64 start_time,
int64 stop_time,
int msec)
{
int64 diff = stop_time - start_time;
return (diff >= msec * INT64CONST(1000));
}
/*
* Converts an int64 to network byte order.
*/
void
fe_sendint64(int64 i, char *buf)
{
uint32 n32;
/* High order half first, since we're doing MSB-first */
n32 = (uint32) (i >> 32);
n32 = htonl(n32);
memcpy(&buf[0], &n32, 4);
/* Now the low order half */
n32 = (uint32) i;
n32 = htonl(n32);
memcpy(&buf[4], &n32, 4);
}
/*
* Converts an int64 from network byte order to native format.
*/
int64
fe_recvint64(char *buf)
{
int64 result;
uint32 h32;
uint32 l32;
memcpy(&h32, buf, 4);
memcpy(&l32, buf + 4, 4);
h32 = ntohl(h32);
l32 = ntohl(l32);
result = h32;
result <<= 32;
result |= l32;
return result;
}
...@@ -5,6 +5,7 @@ extern char *connection_string; ...@@ -5,6 +5,7 @@ extern char *connection_string;
extern char *dbhost; extern char *dbhost;
extern char *dbuser; extern char *dbuser;
extern char *dbport; extern char *dbport;
extern char *dbname;
extern int dbgetpassword; extern int dbgetpassword;
extern char *replication_slot; extern char *replication_slot;
...@@ -12,3 +13,12 @@ extern char *replication_slot; ...@@ -12,3 +13,12 @@ extern char *replication_slot;
extern PGconn *conn; extern PGconn *conn;
extern PGconn *GetConnection(void); extern PGconn *GetConnection(void);
extern int64 feGetCurrentTimestamp(void);
extern void feTimestampDifference(int64 start_time, int64 stop_time,
long *secs, int *microsecs);
extern bool feTimestampDifferenceExceeds(int64 start_time, int64 stop_time,
int msec);
extern void fe_sendint64(int64 i, char *buf);
extern int64 fe_recvint64(char *buf);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment