---
 src/bin/Makefile            |   2 +-
 src/bin/xlogdump/Makefile   |  25 +++
 src/bin/xlogdump/xlogdump.c | 468 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 494 insertions(+), 1 deletion(-)
 create mode 100644 src/bin/xlogdump/Makefile
 create mode 100644 src/bin/xlogdump/xlogdump.c

diff --git a/src/bin/Makefile b/src/bin/Makefile
index b4dfdba..9992f7a 100644
--- a/src/bin/Makefile
+++ b/src/bin/Makefile
@@ -14,7 +14,7 @@ top_builddir = ../..
 include $(top_builddir)/src/Makefile.global
 
 SUBDIRS = initdb pg_ctl pg_dump \
-	psql scripts pg_config pg_controldata pg_resetxlog pg_basebackup
+	psql scripts pg_config pg_controldata pg_resetxlog pg_basebackup xlogdump
 
 ifeq ($(PORTNAME), win32)
 SUBDIRS += pgevent
diff --git a/src/bin/xlogdump/Makefile b/src/bin/xlogdump/Makefile
new file mode 100644
index 0000000..d54640a
--- /dev/null
+++ b/src/bin/xlogdump/Makefile
@@ -0,0 +1,25 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/bin/xlogdump
+#
+# Copyright (c) 1998-2012, PostgreSQL Global Development Group
+#
+# src/bin/pg_resetxlog/Makefile
+#
+#-------------------------------------------------------------------------
+
+PGFILEDESC = "xlogdump"
+PGAPPICON=win32
+
+subdir = src/bin/xlogdump
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS= xlogdump.o \
+	 $(WIN32RES)
+
+all: xlogdump
+
+
+xlogdump: $(OBJS) $(shell find ../../backend ../../timezone -name objfiles.txt|xargs cat|tr -s " " "\012"|grep -v /main.o|sed 's/^/..\/..\/..\//')
+	$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
diff --git a/src/bin/xlogdump/xlogdump.c b/src/bin/xlogdump/xlogdump.c
new file mode 100644
index 0000000..0f984e4
--- /dev/null
+++ b/src/bin/xlogdump/xlogdump.c
@@ -0,0 +1,468 @@
+#include "postgres.h"
+
+#include <unistd.h>
+
+#include "access/xlogreader.h"
+#include "access/rmgr.h"
+#include "miscadmin.h"
+#include "storage/ipc.h"
+#include "utils/memutils.h"
+#include "utils/guc.h"
+
+#include "getopt_long.h"
+
+/*
+ * needs to be declared because otherwise its defined in main.c which we cannot
+ * link from here.
+ */
+const char *progname = "xlogdump";
+
+typedef struct XLogDumpPrivateData {
+	TimeLineID timeline;
+	char* outpath;
+	char* inpath;
+} XLogDumpPrivateData;
+
+static void
+XLogDumpXLogRead(const char *directory, TimeLineID timeline_id,
+                 XLogRecPtr startptr, char *buf, Size count);
+
+static void
+XLogDumpXLogWrite(const char *directory, TimeLineID timeline_id,
+                  XLogRecPtr startptr, const char *buf, Size count);
+
+#define XLogFilePathWrite(path, base, tli, logSegNo)			\
+	snprintf(path, MAXPGPATH, "%s/%08X%08X%08X", base, tli,		\
+			 (uint32) ((logSegNo) / XLogSegmentsPerXLogId),		\
+			 (uint32) ((logSegNo) % XLogSegmentsPerXLogId))
+
+static void
+XLogDumpXLogWrite(const char *directory, TimeLineID timeline_id,
+                  XLogRecPtr startptr, const char *buf, Size count)
+{
+	const char	   *p;
+	XLogRecPtr	recptr;
+	Size		nbytes;
+
+	static int	sendFile = -1;
+	static XLogSegNo sendSegNo = 0;
+	static uint32 sendOff = 0;
+
+	p = buf;
+	recptr = startptr;
+	nbytes = count;
+
+	while (nbytes > 0)
+	{
+		uint32		startoff;
+		int			segbytes;
+		int			writebytes;
+
+		startoff = recptr % XLogSegSize;
+
+		if (sendFile < 0 || !XLByteInSeg(recptr, sendSegNo))
+		{
+			char		path[MAXPGPATH];
+
+			/* Switch to another logfile segment */
+			if (sendFile >= 0)
+				close(sendFile);
+
+			XLByteToSeg(recptr, sendSegNo);
+			XLogFilePathWrite(path, directory, timeline_id, sendSegNo);
+
+			sendFile = open(path, O_WRONLY|O_CREAT, S_IRUSR | S_IWUSR);
+			if (sendFile < 0)
+			{
+				ereport(ERROR,
+				        (errcode_for_file_access(),
+				         errmsg("could not open file \"%s\": %m",
+				                path)));
+			}
+			sendOff = 0;
+		}
+
+		/* Need to seek in the file? */
+		if (sendOff != startoff)
+		{
+			if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0){
+				char fname[MAXPGPATH];
+				XLogFileName(fname, timeline_id, sendSegNo);
+
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not seek in log segment %s to offset %u: %m",
+						        fname,
+								startoff)));
+			}
+			sendOff = startoff;
+		}
+
+		/* How many bytes are within this segment? */
+		if (nbytes > (XLogSegSize - startoff))
+			segbytes = XLogSegSize - startoff;
+		else
+			segbytes = nbytes;
+
+		writebytes = write(sendFile, p, segbytes);
+		if (writebytes <= 0)
+		{
+			char fname[MAXPGPATH];
+			XLogFileName(fname, timeline_id, sendSegNo);
+
+			ereport(ERROR,
+					(errcode_for_file_access(),
+			errmsg("could not write to log segment %s, offset %u, length %lu: %m",
+				   fname,
+				   sendOff, (unsigned long) segbytes)));
+		}
+
+		/* Update state for read */
+		XLByteAdvance(recptr, writebytes);
+
+		sendOff += writebytes;
+		nbytes -= writebytes;
+		p += writebytes;
+	}
+}
+
+/* this should probably be put in a general implementation */
+static void
+XLogDumpXLogRead(const char *directory, TimeLineID timeline_id,
+                 XLogRecPtr startptr, char *buf, Size count)
+{
+	char	   *p;
+	XLogRecPtr	recptr;
+	Size		nbytes;
+
+	static int	sendFile = -1;
+	static XLogSegNo sendSegNo = 0;
+	static uint32 sendOff = 0;
+
+	p = buf;
+	recptr = startptr;
+	nbytes = count;
+
+	while (nbytes > 0)
+	{
+		uint32		startoff;
+		int			segbytes;
+		int			readbytes;
+
+		startoff = recptr % XLogSegSize;
+
+		if (sendFile < 0 || !XLByteInSeg(recptr, sendSegNo))
+		{
+			char		fname[MAXFNAMELEN];
+			char		fpath[MAXPGPATH];
+
+			/* Switch to another logfile segment */
+			if (sendFile >= 0)
+				close(sendFile);
+
+			XLByteToSeg(recptr, sendSegNo);
+
+			XLogFileName(fname, timeline_id, sendSegNo);
+
+			snprintf(fpath, MAXPGPATH, "%s/%s",
+			         directory == NULL ? XLOGDIR : directory, fname);
+
+			sendFile = open(fpath, O_RDONLY, 0);
+			if (sendFile < 0)
+			{
+				/*
+				 * If the file is not found, assume it's because the standby
+				 * asked for a too old WAL segment that has already been
+				 * removed or recycled.
+				 */
+				if (errno == ENOENT)
+					ereport(ERROR,
+							(errcode_for_file_access(),
+							 errmsg("requested WAL segment %s has already been removed",
+									fname)));
+				else
+					ereport(ERROR,
+							(errcode_for_file_access(),
+							 errmsg("could not open file \"%s\": %m",
+									fpath)));
+			}
+			sendOff = 0;
+		}
+
+		/* Need to seek in the file? */
+		if (sendOff != startoff)
+		{
+			if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0){
+				char fname[MAXPGPATH];
+				XLogFileName(fname, timeline_id, sendSegNo);
+
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not seek in log segment %s to offset %u: %m",
+						        fname,
+								startoff)));
+			}
+			sendOff = startoff;
+		}
+
+		/* How many bytes are within this segment? */
+		if (nbytes > (XLogSegSize - startoff))
+			segbytes = XLogSegSize - startoff;
+		else
+			segbytes = nbytes;
+
+		readbytes = read(sendFile, p, segbytes);
+		if (readbytes <= 0)
+		{
+			char fname[MAXPGPATH];
+			XLogFileName(fname, timeline_id, sendSegNo);
+
+			ereport(ERROR,
+					(errcode_for_file_access(),
+			errmsg("could not read from log segment %s, offset %u, length %lu: %m",
+				   fname,
+				   sendOff, (unsigned long) segbytes)));
+		}
+
+		/* Update state for read */
+		XLByteAdvance(recptr, readbytes);
+
+		sendOff += readbytes;
+		nbytes -= readbytes;
+		p += readbytes;
+	}
+}
+
+static void
+XLogDumpReadPage(XLogReaderState* state, char* cur_page, XLogRecPtr startptr)
+{
+    XLogPageHeader page_header;
+	XLogDumpPrivateData *private = state->private_data;
+
+    Assert((startptr % XLOG_BLCKSZ) == 0);
+
+    XLogDumpXLogRead(private->inpath, private->timeline, startptr,
+                     cur_page, XLOG_BLCKSZ);
+
+    page_header = (XLogPageHeader)cur_page;
+
+    if (page_header->xlp_magic != XLOG_PAGE_MAGIC)
+    {
+        elog(FATAL, "page header magic %x, should be %x at %X/%X", page_header->xlp_magic,
+             XLOG_PAGE_MAGIC, (uint32)(startptr << 32), (uint32)startptr);
+    }
+}
+
+static void
+XLogDumpWrite(XLogReaderState* state, char* data, Size len)
+{
+	static char zero[XLOG_BLCKSZ];
+	XLogDumpPrivateData *private = state->private_data;
+
+	if (data == NULL)
+		data = zero;
+
+	if (private->outpath == NULL)
+		return;
+
+	XLogDumpXLogWrite(private->outpath, private->timeline, state->curptr,
+	                  data, len);
+}
+
+static void
+XLogDumpFinishedRecord(XLogReaderState* state, XLogRecordBuffer* buf)
+{
+	XLogRecord *record = &buf->record;
+	const RmgrData *rmgr = &RmgrTable[record->xl_rmid];
+
+	StringInfo str = makeStringInfo();
+	initStringInfo(str);
+
+	rmgr->rm_desc(str, state->buf.record.xl_info, buf->record_data);
+
+	fprintf(stdout, "xlog record: rmgr: %-11s, record_len: %6u, tot_len: %6u, tx: %10u, lsn: %X/%-8X, prev %X/%-8X, bkp: %u%u%u%u, desc: %s\n",
+			rmgr->rm_name,
+			record->xl_len, record->xl_tot_len,
+			record->xl_xid,
+			(uint32)(buf->origptr >> 32), (uint32)buf->origptr,
+			(uint32)(record->xl_prev >> 32), (uint32)record->xl_prev,
+			!!(XLR_BKP_BLOCK(0) & buf->record.xl_info),
+			!!(XLR_BKP_BLOCK(1) & buf->record.xl_info),
+			!!(XLR_BKP_BLOCK(2) & buf->record.xl_info),
+			!!(XLR_BKP_BLOCK(3) & buf->record.xl_info),
+			str->data);
+
+}
+
+
+static void init()
+{
+	MemoryContextInit();
+	IsPostmasterEnvironment = false;
+	log_min_messages = DEBUG1;
+	Log_error_verbosity = PGERROR_TERSE;
+	pg_timezone_initialize();
+}
+
+static void
+usage(void)
+{
+	printf(_("%s reads/writes postgres transaction logs for debugging.\n\n"),
+		   progname);
+	printf(_("Usage:\n"));
+	printf(_("  %s [OPTION]...\n"), progname);
+	printf(_("\nOptions:\n"));
+	printf(_("  -v, --version          output version information, then exit\n"));
+	printf(_("  -h, --help             show this help, then exit\n"));
+	printf(_("  -s, --start            from where recptr onwards to read\n"));
+	printf(_("  -e, --end              up to which recptr to read\n"));
+	printf(_("  -t, --timeline         which timeline do we want to read\n"));
+	printf(_("  -i, --inpath           from where do we want to read? cwd/pg_xlog is the default\n"));
+	printf(_("  -o, --output           where to write [start, end]\n"));
+	printf(_("  -f, --file             wal file to parse\n"));
+}
+
+int main(int argc, char **argv)
+{
+	uint32 xlogid;
+	uint32 xrecoff;
+	XLogReaderState *xlogreader_state;
+	XLogDumpPrivateData private;
+	XLogRecPtr from = InvalidXLogRecPtr;
+	XLogRecPtr to = InvalidXLogRecPtr;
+	bool bad_argument = false;
+
+	static struct option long_options[] = {
+		{"help", no_argument, NULL, 'h'},
+		{"version", no_argument, NULL, 'v'},
+		{"start", required_argument, NULL, 's'},
+		{"end", required_argument, NULL, 'e'},
+		{"timeline", required_argument, NULL, 't'},
+		{"inpath", required_argument, NULL, 'i'},
+		{"outpath", required_argument, NULL, 'o'},
+		{"file", required_argument, NULL, 'f'},
+		{NULL, 0, NULL, 0}
+	};
+	int			c;
+	int			option_index;
+
+	memset(&private, 0, sizeof(XLogDumpPrivateData));
+
+	while ((c = getopt_long(argc, argv, "hvs:e:t:i:o:f:",
+							long_options, &option_index)) != -1)
+	{
+		switch (c)
+		{
+			case 'h':
+				usage();
+				exit(0);
+				break;
+			case 'v':
+				printf("Version: 0.1\n");
+				exit(0);
+				break;
+			case 's':
+				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
+				{
+					bad_argument = true;
+					fprintf(stderr, "couldn't parse -s\n");
+				}
+				else
+					from = (uint64)xlogid << 32 | xrecoff;
+				break;
+			case 'e':
+				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
+				{
+					bad_argument = true;
+					fprintf(stderr, "couldn't parse -e\n");
+				}
+				else
+					to = (uint64)xlogid << 32 | xrecoff;
+				break;
+			case 't':
+				if (sscanf(optarg, "%d", &private.timeline) != 1)
+				{
+					bad_argument = true;
+					fprintf(stderr, "couldn't parse timeline -t\n");
+				}
+				break;
+			case 'i':
+				private.inpath = strdup(optarg);
+				break;
+			case 'o':
+				private.outpath = strdup(optarg);
+				break;
+			case 'f':
+				fprintf(stderr, "--file is not yet implemented\n");
+				bad_argument = true;
+				break;
+			default:
+				bad_argument = true;
+				break;
+		}
+	}
+
+	if (optind < argc)
+	{
+		bad_argument = true;
+		fprintf(stderr,
+		        _("%s: too many command-line arguments (first is \"%s\")\n"),
+		        progname, argv[optind]);
+	}
+
+	if (XLByteEQ(from, InvalidXLogRecPtr))
+	{
+		bad_argument = true;
+		fprintf(stderr,
+		        _("%s: -s invalid or missing: %s\n"),
+		        progname, argv[optind]);
+	}
+	else if (XLByteEQ(to, InvalidXLogRecPtr))
+	{
+		bad_argument = true;
+		fprintf(stderr,
+		        _("%s: -e invalid or missing: %s\n"),
+		        progname, argv[optind]);
+	}
+	else if (private.timeline == 0)
+	{
+		bad_argument = true;
+		fprintf(stderr,
+		        _("%s: -t invalid or missing: %s\n"),
+		        progname, argv[optind]);
+	}
+
+	if (bad_argument)
+	{
+		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
+				progname);
+		exit(-1);
+	}
+
+	init();
+
+	xlogreader_state = XLogReaderAllocate();
+
+	/*
+	 * not set because we want all records, perhaps we want filtering later?
+	 * xlogreader_state->is_record_interesting =
+	 */
+	xlogreader_state->finished_record = XLogDumpFinishedRecord;
+
+	/*
+	 * not set because we do not want to copy data to somewhere yet
+	 * xlogreader_state->writeout_data = ;
+	 */
+	xlogreader_state->writeout_data = XLogDumpWrite;
+
+	xlogreader_state->read_page = XLogDumpReadPage;
+
+	xlogreader_state->private_data = &private;
+
+	xlogreader_state->startptr = from;
+	xlogreader_state->endptr = to;
+
+	XLogReaderRead(xlogreader_state);
+	XLogReaderFree(xlogreader_state);
+	return 0;
+}
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to