pg_xlogdump

Started by Alvaro Herreraalmost 13 years ago19 messages
#1Alvaro Herrera
alvherre@2ndquadrant.com
2 attachment(s)

Here's an updated version of pg_xlogdump. This is rebased on top of the
committed xlogreader, palloc restructuring and libpgcommon, PG_RMGR
stuff, and is basically a revamped version of what Andres submitted in
/messages/by-id/1357672187-7693-5-git-send-email-andres@2ndquadrant.com

I also attach a patch to move the relpathbackend() function to
src/common. On top of that, it's trivial to change pg_xlogdump and
remove the "uninplemented" stub we're currently using. (I also tried it
with a real implementation of relpath() that was mostly a duplicate of
the backend's relpath(), but I didn't like the duplication at all even
though I stripped the unnecessary bits).

(The more adventorous might think about moving timestamp_to_str to
src/common, as well, but this isn't a simple task: it depends on some
backend global state variables such as GUC vars, so it requires detailed
surgery. I think it's a worthy goal nonetheless, because it'd allow us
to reduce useless duplication in ECPG, but it's not a 9.3 project)

--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

xlogdump-v5.patchtext/x-diff; charset=us-asciiDownload
*** /dev/null
--- b/contrib/pg_xlogdump/Makefile
***************
*** 0 ****
--- 1,32 ----
+ # contrib/pg_xlogdump/Makefile
+ 
+ PGFILEDESC = "pg_xlogdump"
+ PGAPPICON=win32
+ 
+ PROGRAM = pg_xlogdump
+ OBJS = pg_xlogdump.o compat.o xlogreader.o rmgrdesc.o \
+ 	$(RMGRDESCOBJS) $(WIN32RES)
+ 
+ RMGRDESCSOURCES = $(notdir $(wildcard $(top_srcdir)/src/backend/access/rmgrdesc/*desc.c))
+ RMGRDESCOBJS = $(patsubst %.c,%.o,$(RMGRDESCSOURCES))
+ 
+ EXTRA_CLEAN = $(RMGRDESCSOURCES) xlogreader.c rmgrdesc.c
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_xlogdump
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+ 
+ override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
+ 
+ rmgrdesc.c xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/%
+ 	rm -f $@ && $(LN_S) $< .
+ 
+ $(RMGRDESCSOURCES): % : $(top_srcdir)/src/backend/access/rmgrdesc/%
+ 	rm -f $@ && $(LN_S) $< .
*** /dev/null
--- b/contrib/pg_xlogdump/compat.c
***************
*** 0 ****
--- 1,83 ----
+ /*-------------------------------------------------------------------------
+  *
+  * compat.c
+  *		Reimplementations of various backend functions.
+  *
+  * Portions Copyright (c) 2012, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *		contrib/pg_xlogdump/compat.c
+  *
+  * This file contains client-side implementations for various backend
+  * functions that the rm_desc functions in *desc.c files rely on.
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /* ugly hack, same as in e.g pg_controldata */
+ #define FRONTEND 1
+ #include "postgres.h"
+ 
+ #include "catalog/catalog.h"
+ #include "catalog/pg_tablespace.h"
+ #include "common/fe_memutils.h"
+ #include "datatype/timestamp.h"
+ #include "lib/stringinfo.h"
+ #include "storage/relfilenode.h"
+ 
+ const char *
+ timestamptz_to_str(TimestampTz dt)
+ {
+ 	return "unimplemented-timestamp";
+ }
+ 
+ /*
+  * Table of fork names.
+  *
+  * needs to be synced with src/backend/catalog/catalog.c
+  */
+ const char *forkNames[] = {
+ 	"main",						/* MAIN_FORKNUM */
+ 	"fsm",						/* FSM_FORKNUM */
+ 	"vm",						/* VISIBILITYMAP_FORKNUM */
+ 	"init"						/* INIT_FORKNUM */
+ };
+ #define FORKNAMECHARS	4
+ 
+ /*
+  * relpathbackend
+  *
+  * A pg_xlogdump implementation of the backend function of the same name.
+  * This one doesn't accept non-permanent relfilenodes.
+  */
+ char *
+ relpathbackend(RelFileNode rnode, BackendId backend, ForkNumber forknum)
+ {
+ 	return pg_strdup("unimplemented-relpath");
+ }
+ 
+ /*
+  * Provide a hacked up compat layer for StringInfos so xlog desc functions can
+  * be linked/called.
+  */
+ void
+ appendStringInfo(StringInfo str, const char *fmt, ...)
+ {
+ 	va_list		args;
+ 
+ 	va_start(args, fmt);
+ 	vprintf(fmt, args);
+ 	va_end(args);
+ }
+ 
+ void
+ appendStringInfoString(StringInfo str, const char *string)
+ {
+ 	appendStringInfo(str, "%s", string);
+ }
+ 
+ void
+ appendStringInfoChar(StringInfo str, char ch)
+ {
+ 	appendStringInfo(str, "%c", ch);
+ }
*** /dev/null
--- b/contrib/pg_xlogdump/pg_xlogdump.c
***************
*** 0 ****
--- 1,674 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_xlogdump.c - decode and display WAL
+  *
+  * Copyright (c) 2012, PostgreSQL Global Development Group
+  *
+  * IDENTIFICATION
+  *		  contrib/pg_xlogdump/pg_xlogdump.c
+  *-------------------------------------------------------------------------
+  */
+ 
+ #define FRONTEND 1
+ #include "postgres.h"
+ 
+ #include <unistd.h>
+ 
+ #include "rmgrdesc.h"
+ 
+ #include "access/xlog.h"
+ #include "access/xlogreader.h"
+ #include "access/transam.h"
+ #include "catalog/catalog.h"
+ #include "common/fe_memutils.h"
+ #include "getopt_long.h"
+ 
+ 
+ static const char *progname;
+ 
+ typedef struct XLogDumpPrivateData
+ {
+ 	TimeLineID	timeline;
+ 	char	   *inpath;
+ 	XLogRecPtr	startptr;
+ 	XLogRecPtr	endptr;
+ 
+ 	/* display options */
+ 	bool		bkp_details;
+ 	int			stop_after_records;
+ 	int			already_displayed_records;
+ 
+ 	/* filter options */
+ 	int			filter_by_rmgr;
+ 	TransactionId filter_by_xid;
+ }	XLogDumpPrivateData;
+ 
+ static void
+ fatal_error(const char *fmt,...)
+ __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
+ 
+ static void
+ fatal_error(const char *fmt,...)
+ {
+ 	va_list		args;
+ 
+ 	fflush(stdout);
+ 
+ 	fprintf(stderr, "%s: FATAL:  ", progname);
+ 	va_start(args, fmt);
+ 	vfprintf(stderr, fmt, args);
+ 	va_end(args);
+ 	fputc('\n', stderr);
+ 
+ 	exit(EXIT_FAILURE);
+ }
+ 
+ /*
+  * Check whether directory exists and whether we can open it.
+  * errno is kept set so that the caller can report errors.
+  */
+ static bool
+ verify_directory(const char *directory)
+ {
+ 	int			fd = open(directory, O_DIRECTORY | O_RDONLY);
+ 
+ 	if (fd < 0)
+ 		return false;
+ 
+ 	close(fd);
+ 	errno = 0;					/* ignore errors in this case */
+ 	return true;
+ }
+ 
+ /*
+  * Split a pathname as dirname(1) and basename(1) would.
+  *
+  * XXX this probably doesn't do very well on Windows.  We probably need to
+  * apply canonicalize_path(), at the very least.
+  */
+ static void
+ split_path(const char *path, char **dir, char **fname)
+ {
+ 	char	   *sep;
+ 
+ 	/* split filepath into directory & filename */
+ 	sep = strrchr(path, '/');
+ 
+ 	/* directory path */
+ 	if (sep != NULL)
+ 	{
+ 		*dir = pg_strdup(path);
+ 		(*dir)[(sep - path) + 1] = '\0';	/* no strndup */
+ 		*fname = pg_strdup(sep + 1);
+ 	}
+ 	/* local directory */
+ 	else
+ 	{
+ 		*dir = NULL;
+ 		*fname = pg_strdup(path);
+ 	}
+ }
+ 
+ /*
+  * Try to find the file in several places:
+  * if directory == NULL:
+  *	 fname
+  *	 XLOGDIR / fname
+  *	 $PGDATA / XLOGDIR / fname
+  * else
+  *	 directory / fname
+  *	 directory / XLOGDIR / fname
+  *
+  * return a read only fd
+  */
+ static int
+ fuzzy_open_file(const char *directory, const char *fname)
+ {
+ 	int			fd = -1;
+ 	char		fpath[MAXPGPATH];
+ 
+ 	if (directory == NULL)
+ 	{
+ 		const char *datadir;
+ 
+ 		/* fname */
+ 		fd = open(fname, O_RDONLY | PG_BINARY, 0);
+ 		if (fd < 0 && errno != ENOENT)
+ 			return -1;
+ 		else if (fd > 0)
+ 			return fd;
+ 
+ 		/* XLOGDIR / fname */
+ 		snprintf(fpath, MAXPGPATH, "%s/%s",
+ 				 XLOGDIR, fname);
+ 		fd = open(fpath, O_RDONLY | PG_BINARY, 0);
+ 		if (fd < 0 && errno != ENOENT)
+ 			return -1;
+ 		else if (fd > 0)
+ 			return fd;
+ 
+ 		datadir = getenv("PGDATA");
+ 		/* $PGDATA / XLOGDIR / fname */
+ 		if (datadir != NULL)
+ 		{
+ 			snprintf(fpath, MAXPGPATH, "%s/%s/%s",
+ 					 datadir, XLOGDIR, fname);
+ 			fd = open(fpath, O_RDONLY | PG_BINARY, 0);
+ 			if (fd < 0 && errno != ENOENT)
+ 				return -1;
+ 			else if (fd > 0)
+ 				return fd;
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* directory / fname */
+ 		snprintf(fpath, MAXPGPATH, "%s/%s",
+ 				 directory, fname);
+ 		fd = open(fpath, O_RDONLY | PG_BINARY, 0);
+ 		if (fd < 0 && errno != ENOENT)
+ 			return -1;
+ 		else if (fd > 0)
+ 			return fd;
+ 
+ 		/* directory / XLOGDIR / fname */
+ 		snprintf(fpath, MAXPGPATH, "%s/%s/%s",
+ 				 directory, XLOGDIR, fname);
+ 		fd = open(fpath, O_RDONLY | PG_BINARY, 0);
+ 		if (fd < 0 && errno != ENOENT)
+ 			return -1;
+ 		else if (fd > 0)
+ 			return fd;
+ 	}
+ 	return -1;
+ }
+ 
+ /* 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];
+ 
+ 			/* Switch to another logfile segment */
+ 			if (sendFile >= 0)
+ 				close(sendFile);
+ 
+ 			XLByteToSeg(recptr, sendSegNo);
+ 
+ 			XLogFileName(fname, timeline_id, sendSegNo);
+ 
+ 			sendFile = fuzzy_open_file(directory, fname);
+ 
+ 			if (sendFile < 0)
+ 				fatal_error("could not find file \"%s\": %s",
+ 							fname, strerror(errno));
+ 			sendOff = 0;
+ 		}
+ 
+ 		/* Need to seek in the file? */
+ 		if (sendOff != startoff)
+ 		{
+ 			if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0)
+ 			{
+ 				int			err = errno;
+ 				char		fname[MAXPGPATH];
+ 
+ 				XLogFileName(fname, timeline_id, sendSegNo);
+ 
+ 				fatal_error("could not seek in log segment %s to offset %u: %s",
+ 							fname, startoff, strerror(err));
+ 			}
+ 			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)
+ 		{
+ 			int			err = errno;
+ 			char		fname[MAXPGPATH];
+ 
+ 			XLogFileName(fname, timeline_id, sendSegNo);
+ 
+ 			fatal_error("could not read from log segment %s, offset %d, length %d: %s",
+ 						fname, sendOff, segbytes, strerror(err));
+ 		}
+ 
+ 		/* Update state for read */
+ 		recptr += readbytes;
+ 
+ 		sendOff += readbytes;
+ 		nbytes -= readbytes;
+ 		p += readbytes;
+ 	}
+ }
+ 
+ static int
+ XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
+ 				 XLogRecPtr targetPtr, char *readBuff, TimeLineID *curFileTLI)
+ {
+ 	XLogDumpPrivateData *private = state->private_data;
+ 	int			count = XLOG_BLCKSZ;
+ 
+ 	if (private->endptr != InvalidXLogRecPtr)
+ 	{
+ 		if (targetPagePtr + XLOG_BLCKSZ <= private->endptr)
+ 			count = XLOG_BLCKSZ;
+ 		else if (targetPagePtr + reqLen <= private->endptr)
+ 			count = private->endptr - targetPagePtr;
+ 		else
+ 			return -1;
+ 	}
+ 
+ 	XLogDumpXLogRead(private->inpath, private->timeline, targetPagePtr,
+ 					 readBuff, count);
+ 
+ 	return count;
+ }
+ 
+ static void
+ XLogDumpDisplayRecord(XLogReaderState *state, XLogRecord *record)
+ {
+ 	XLogDumpPrivateData *config = (XLogDumpPrivateData *) state->private_data;
+ 	const RmgrDescData *desc = &RmgrDescTable[record->xl_rmid];
+ 
+ 	if (config->filter_by_rmgr != -1 &&
+ 		config->filter_by_rmgr != record->xl_rmid)
+ 		return;
+ 
+ 	if (TransactionIdIsValid(config->filter_by_xid) &&
+ 		config->filter_by_xid != record->xl_xid)
+ 		return;
+ 
+ 	config->already_displayed_records++;
+ 
+ 	printf("xlog record: rmgr: %-11s, record_len: %6u, tot_len: %6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, bkp: %u%u%u%u, desc:",
+ 		   desc->rm_name,
+ 		   record->xl_len, record->xl_tot_len,
+ 		   record->xl_xid,
+ 		   (uint32) (state->ReadRecPtr >> 32), (uint32) state->ReadRecPtr,
+ 		   (uint32) (record->xl_prev >> 32), (uint32) record->xl_prev,
+ 		   !!(XLR_BKP_BLOCK(0) & record->xl_info),
+ 		   !!(XLR_BKP_BLOCK(1) & record->xl_info),
+ 		   !!(XLR_BKP_BLOCK(2) & record->xl_info),
+ 		   !!(XLR_BKP_BLOCK(3) & record->xl_info));
+ 
+ 	/* the desc routine will printf the description directly to stdout */
+ 	desc->rm_desc(NULL, record->xl_info, XLogRecGetData(record));
+ 
+ 	putchar('\n');
+ 
+ 	if (config->bkp_details)
+ 	{
+ 		int			bkpnum;
+ 		char	   *blk = (char *) XLogRecGetData(record) + record->xl_len;
+ 
+ 		for (bkpnum = 0; bkpnum < XLR_MAX_BKP_BLOCKS; bkpnum++)
+ 		{
+ 			BkpBlock	bkpb;
+ 
+ 			if (!(XLR_BKP_BLOCK(bkpnum) & record->xl_info))
+ 				continue;
+ 
+ 			memcpy(&bkpb, blk, sizeof(BkpBlock));
+ 			blk += sizeof(BkpBlock);
+ 			blk += BLCKSZ - bkpb.hole_length;
+ 
+ 			printf("\tbackup bkp #%u; rel %u/%u/%u; fork: %s; block: %u; hole: offset: %u, length: %u\n",
+ 				   bkpnum,
+ 				   bkpb.node.spcNode, bkpb.node.dbNode, bkpb.node.relNode,
+ 				   forkNames[bkpb.fork],
+ 				   bkpb.block, bkpb.hole_offset, bkpb.hole_length);
+ 		}
+ 	}
+ }
+ 
+ static void
+ usage(void)
+ {
+ 	printf("%s decodes and displays PostgreSQL transaction logs for debugging.\n\n",
+ 		   progname);
+ 	printf("Usage:\n");
+ 	printf("  %s [OPTION] [STARTSEG [ENDSEG]] \n", progname);
+ 	printf("\nGeneral options:\n");
+ 	printf("  -V, --version          output version information, then exit\n");
+ 	printf("  -?, --help             show this help, then exit\n");
+ 	printf("\nContent options:\n");
+ 	printf("  -b, --bkp-details      output detailed information about backup blocks\n");
+ 	printf("  -e, --end=RECPTR       stop reading at log position RECPTR\n");
+ 	printf("  -n, --limit=N          number of records to display");
+ 	printf("  -p, --path=PATH        directory in which to find log segment files\n");
+ 	printf("                         (default: ./pg_xlog)");
+ 	printf("  -r, --rmgr=RMGR        only show records generated by resource manager RMGR\n");
+ 	printf("  -s, --start=RECPTR     stop reading at log position RECPTR");
+ 	printf("  -t, --timeline=TLI     timeline from which to read log records (default: 1)\n");
+ 	printf("  -x, --xid=XID          only show records with TransactionId XID\n");
+ }
+ 
+ int
+ main(int argc, char **argv)
+ {
+ 	uint32		xlogid;
+ 	uint32		xrecoff;
+ 	XLogReaderState *xlogreader_state;
+ 	XLogDumpPrivateData private;
+ 	XLogRecord *record;
+ 	XLogRecPtr	first_record;
+ 	char	   *errormsg;
+ 
+ 	static struct option long_options[] = {
+ 		{"bkp-details", no_argument, NULL, 'b'},
+ 		{"end", required_argument, NULL, 'e'},
+ 		{"help", no_argument, NULL, '?'},
+ 		{"limit", required_argument, NULL, 'n'},
+ 		{"path", required_argument, NULL, 'p'},
+ 		{"rmgr", required_argument, NULL, 'r'},
+ 		{"start", required_argument, NULL, 's'},
+ 		{"timeline", required_argument, NULL, 't'},
+ 		{"xid", required_argument, NULL, 'x'},
+ 		{"version", no_argument, NULL, 'V'},
+ 		{NULL, 0, NULL, 0}
+ 	};
+ 
+ 	int			option;
+ 	int			optindex = 0;
+ 
+ 	progname = get_progname(argv[0]);
+ 
+ 	memset(&private, 0, sizeof(XLogDumpPrivateData));
+ 
+ 	private.timeline = 1;
+ 	private.bkp_details = false;
+ 	private.startptr = InvalidXLogRecPtr;
+ 	private.endptr = InvalidXLogRecPtr;
+ 	private.stop_after_records = -1;
+ 	private.already_displayed_records = 0;
+ 	private.filter_by_rmgr = -1;
+ 	private.filter_by_xid = InvalidTransactionId;
+ 
+ 	if (argc <= 1)
+ 	{
+ 		fprintf(stderr, "%s: no arguments specified\n", progname);
+ 		goto bad_argument;
+ 	}
+ 
+ 	while ((option = getopt_long(argc, argv, "be:?n:p:r:s:t:Vx:",
+ 								 long_options, &optindex)) != -1)
+ 	{
+ 		switch (option)
+ 		{
+ 			case 'b':
+ 				private.bkp_details = true;
+ 				break;
+ 			case 'e':
+ 				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
+ 				{
+ 					fprintf(stderr, "%s: could not parse end log position \"%s\"\n",
+ 							progname, optarg);
+ 					goto bad_argument;
+ 				}
+ 				private.endptr = (uint64) xlogid << 32 | xrecoff;
+ 				break;
+ 			case '?':
+ 				usage();
+ 				exit(EXIT_SUCCESS);
+ 				break;
+ 			case 'n':
+ 				if (sscanf(optarg, "%d", &private.stop_after_records) != 1)
+ 				{
+ 					fprintf(stderr, "%s: could not parse limit \"%s\"\n",
+ 							progname, optarg);
+ 					goto bad_argument;
+ 				}
+ 				break;
+ 			case 'p':
+ 				private.inpath = pg_strdup(optarg);
+ 				break;
+ 			case 'r':
+ 				{
+ 					int			i;
+ 
+ 					for (i = 0; i < RM_MAX_ID; i++)
+ 					{
+ 						if (strcmp(optarg, RmgrDescTable[i].rm_name) == 0)
+ 						{
+ 							private.filter_by_rmgr = i;
+ 							break;
+ 						}
+ 					}
+ 
+ 					if (private.filter_by_rmgr == -1)
+ 					{
+ 						fprintf(stderr, "%s: resource manager \"%s\" does not exist\n",
+ 								progname, optarg);
+ 						goto bad_argument;
+ 					}
+ 				}
+ 				break;
+ 			case 's':
+ 				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
+ 				{
+ 					fprintf(stderr, "%s: could not parse end log position \"%s\"\n",
+ 							progname, optarg);
+ 					goto bad_argument;
+ 				}
+ 				else
+ 					private.startptr = (uint64) xlogid << 32 | xrecoff;
+ 				break;
+ 			case 't':
+ 				if (sscanf(optarg, "%d", &private.timeline) != 1)
+ 				{
+ 					fprintf(stderr, "%s: could not parse timeline \"%s\"\n",
+ 							progname, optarg);
+ 					goto bad_argument;
+ 				}
+ 				break;
+ 			case 'V':
+ 				puts("pg_xlogdump (PostgreSQL) " PG_VERSION);
+ 				exit(EXIT_SUCCESS);
+ 				break;
+ 			case 'x':
+ 				if (sscanf(optarg, "%u", &private.filter_by_xid) != 1)
+ 				{
+ 					fprintf(stderr, "%s: could not parse \"%s\" as a valid xid\n",
+ 							progname, optarg);
+ 					goto bad_argument;
+ 				}
+ 				break;
+ 			default:
+ 				goto bad_argument;
+ 		}
+ 	}
+ 
+ 	if ((optind + 2) < argc)
+ 	{
+ 		fprintf(stderr,
+ 				"%s: too many command-line arguments (first is \"%s\")\n",
+ 				progname, argv[optind + 2]);
+ 		goto bad_argument;
+ 	}
+ 
+ 	if (private.inpath != NULL)
+ 	{
+ 		/* validate path points to directory */
+ 		if (!verify_directory(private.inpath))
+ 		{
+ 			fprintf(stderr,
+ 					"%s: path \"%s\" cannot be opened: %s",
+ 					progname, private.inpath, strerror(errno));
+ 			goto bad_argument;
+ 		}
+ 	}
+ 
+ 	/* parse files as start/end boundaries, extract path if not specified */
+ 	if (optind < argc)
+ 	{
+ 		char	   *directory = NULL;
+ 		char	   *fname = NULL;
+ 		int			fd;
+ 		XLogSegNo	segno;
+ 
+ 		split_path(argv[optind], &directory, &fname);
+ 
+ 		if (private.inpath == NULL && directory != NULL)
+ 		{
+ 			private.inpath = directory;
+ 
+ 			if (!verify_directory(private.inpath))
+ 				fatal_error("cannot open directory \"%s\": %s",
+ 							private.inpath, strerror(errno));
+ 		}
+ 
+ 		fd = fuzzy_open_file(private.inpath, fname);
+ 		if (fd < 0)
+ 			fatal_error("could not open file \"%s\"", fname);
+ 		close(fd);
+ 
+ 		/* parse position from file */
+ 		XLogFromFileName(fname, &private.timeline, &segno);
+ 
+ 		if (XLogRecPtrIsInvalid(private.startptr))
+ 			XLogSegNoOffsetToRecPtr(segno, 0, private.startptr);
+ 		else if (!XLByteInSeg(private.startptr, segno))
+ 		{
+ 			fprintf(stderr,
+ 					"%s: end log position %X/%X is not inside file \"%s\"\n",
+ 					progname,
+ 					(uint32) (private.startptr >> 32),
+ 					(uint32) private.startptr,
+ 					fname);
+ 			goto bad_argument;
+ 		}
+ 
+ 		/* no second file specified, set end position */
+ 		if (!(optind + 1 < argc) && XLogRecPtrIsInvalid(private.endptr))
+ 			XLogSegNoOffsetToRecPtr(segno + 1, 0, private.endptr);
+ 
+ 		/* parse ENDSEG if passed */
+ 		if (optind + 1 < argc)
+ 		{
+ 			XLogSegNo	endsegno;
+ 
+ 			/* ignore directory, already have that */
+ 			split_path(argv[optind + 1], &directory, &fname);
+ 
+ 			fd = fuzzy_open_file(private.inpath, fname);
+ 			if (fd < 0)
+ 				fatal_error("could not open file \"%s\"", fname);
+ 			close(fd);
+ 
+ 			/* parse position from file */
+ 			XLogFromFileName(fname, &private.timeline, &endsegno);
+ 
+ 			if (endsegno < segno)
+ 				fatal_error("ENDSEG %s is before STARTSEG %s",
+ 							argv[optind + 1], argv[optind]);
+ 
+ 			if (XLogRecPtrIsInvalid(private.endptr))
+ 				XLogSegNoOffsetToRecPtr(endsegno + 1, 0, private.endptr);
+ 
+ 			/* set segno to endsegno for check of --end */
+ 			segno = endsegno;
+ 		}
+ 
+ 
+ 		if (!XLByteInSeg(private.endptr, segno) &&
+ 			private.endptr != (segno + 1) * XLogSegSize)
+ 		{
+ 			fprintf(stderr,
+ 					"%s: end log position %X/%X is not inside file \"%s\"\n",
+ 					progname,
+ 					(uint32) (private.endptr >> 32),
+ 					(uint32) private.endptr,
+ 					argv[argc - 1]);
+ 			goto bad_argument;
+ 		}
+ 	}
+ 
+ 	/* we don't know what to print */
+ 	if (XLogRecPtrIsInvalid(private.startptr))
+ 	{
+ 		fprintf(stderr, "%s: no start log position given in range mode.\n", progname);
+ 		goto bad_argument;
+ 	}
+ 
+ 	/* done with argument parsing, do the actual work */
+ 
+ 	/* we have everything we need, start reading */
+ 	xlogreader_state = XLogReaderAllocate(XLogDumpReadPage, &private);
+ 	if (!xlogreader_state)
+ 		fatal_error("out of memory");
+ 
+ 	/* first find a valid recptr to start from */
+ 	first_record = XLogFindNextRecord(xlogreader_state, private.startptr);
+ 
+ 	if (first_record == InvalidXLogRecPtr)
+ 		fatal_error("could not find a valid record after %X/%X",
+ 					(uint32) (private.startptr >> 32),
+ 					(uint32) private.startptr);
+ 
+ 	/*
+ 	 * Display a message that we're skipping data if `from` wasn't a pointer to
+ 	 * the start of a record and also wasn't a pointer to the beginning of a
+ 	 * segment (e.g. we were used in file mode).
+ 	 */
+ 	if (first_record != private.startptr && (private.startptr % XLogSegSize) != 0)
+ 		printf("first record is after %X/%X, at %X/%X, skipping over %u bytes\n",
+ 			   (uint32) (private.startptr >> 32), (uint32) private.startptr,
+ 			   (uint32) (first_record >> 32), (uint32) first_record,
+ 			   (uint32) (first_record - private.startptr));
+ 
+ 	while ((record = XLogReadRecord(xlogreader_state, first_record, &errormsg)))
+ 	{
+ 		/* continue after the last record */
+ 		first_record = InvalidXLogRecPtr;
+ 		XLogDumpDisplayRecord(xlogreader_state, record);
+ 
+ 		/* check whether we printed enough */
+ 		if (private.stop_after_records > 0 &&
+ 			private.already_displayed_records >= private.stop_after_records)
+ 			break;
+ 	}
+ 
+ 	if (errormsg)
+ 		fatal_error("error in WAL record at %X/%X: %s\n",
+ 					(uint32) (xlogreader_state->ReadRecPtr >> 32),
+ 					(uint32) xlogreader_state->ReadRecPtr,
+ 					errormsg);
+ 
+ 	XLogReaderFree(xlogreader_state);
+ 
+ 	return EXIT_SUCCESS;
+ 
+ bad_argument:
+ 	fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
+ 	return EXIT_FAILURE;
+ }
*** /dev/null
--- b/contrib/pg_xlogdump/rmgrdesc.c
***************
*** 0 ****
--- 1,36 ----
+ /*
+  * rmgrdesc.c
+  *
+  * pg_xlogdump resource managers definition
+  *
+  * contrib/pg_xlogdump/rmgrdesc.c
+  */
+ #define FRONTEND 1
+ #include "postgres.h"
+ 
+ #include "access/clog.h"
+ #include "access/gin.h"
+ #include "access/gist_private.h"
+ #include "access/hash.h"
+ #include "access/heapam_xlog.h"
+ #include "access/multixact.h"
+ #include "access/nbtree.h"
+ #include "access/rmgr.h"
+ #include "access/spgist.h"
+ #include "access/xact.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/storage_xlog.h"
+ #include "commands/dbcommands.h"
+ #include "commands/sequence.h"
+ #include "commands/tablespace.h"
+ #include "rmgrdesc.h"
+ #include "storage/standby.h"
+ #include "utils/relmapper.h"
+ 
+ #define PG_RMGR(symname,name,redo,desc,startup,cleanup,restartpoint) \
+ 	{ name, desc, },
+ 
+ const RmgrDescData RmgrDescTable[RM_MAX_ID + 1] = {
+ #include "access/rmgrlist.h"
+ };
+ 
*** /dev/null
--- b/contrib/pg_xlogdump/rmgrdesc.h
***************
*** 0 ****
--- 1,21 ----
+ /*
+  * rmgrdesc.h
+  *
+  * pg_xlogdump resource managers declaration
+  *
+  * contrib/pg_xlogdump/rmgrdesc.h
+  */
+ #ifndef RMGRDESC_H
+ #define RMGRDESC_H
+ 
+ #include "lib/stringinfo.h"
+ 
+ typedef struct RmgrDescData
+ {
+ 	const char *rm_name;
+ 	void	  (*rm_desc) (StringInfo buf, uint8 xl_info, char *rec);
+ } RmgrDescData;
+ 
+ extern const RmgrDescData RmgrDescTable[];
+ 
+ #endif /* RMGRDESC_H */
*** /dev/null
--- b/doc/src/sgml/ref/pg_xlogdump.sgml
***************
*** 0 ****
--- 1,76 ----
+ <!--
+ doc/src/sgml/ref/pg_xlogdump.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="APP-PGXLOGDUMP">
+  <refmeta>
+   <refentrytitle><application>pg_xlogdump</application></refentrytitle>
+   <manvolnum>1</manvolnum>
+   <refmiscinfo>Application</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>pg_xlogdump</refname>
+   <refpurpose>Display the write-ahead log of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="app-pgxlogdump">
+   <primary>pg_xlogdump</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+   <cmdsynopsis>
+    <command>pg_xlogdump</command>
+    <arg choice="opt"><option>-b</option></arg>
+    <arg choice="opt"><option>-e</option> <replaceable class="parameter">xlogrecptr</replaceable></arg>
+    <arg choice="opt"><option>-f</option> <replaceable class="parameter">filename</replaceable></arg>
+    <arg choice="opt"><option>-h</option></arg>
+    <arg choice="opt"><option>-p</option> <replaceable class="parameter">directory</replaceable></arg>
+    <arg choice="opt"><option>-s</option> <replaceable class="parameter">xlogrecptr</replaceable></arg>
+    <arg choice="opt"><option>-t</option> <replaceable class="parameter">timelineid</replaceable></arg>
+    <arg choice="opt"><option>-v</option></arg>
+   </cmdsynopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1 id="R1-APP-PGXLOGDUMP-1">
+   <title>Description</title>
+   <para>
+    <command>pg_xlogdump</command> display the write-ahead log (WAL) and is only
+    useful for debugging or educational purposes.
+   </para>
+ 
+   <para>
+    This utility can only be run by the user who installed the server, because
+    it requires read access to the data directory. It does not perform any
+    modifications.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Options</title>
+ 
+    <para>
+     The following command-line options control the location and format of the
+     output.
+ 
+     <variablelist>
+      <varlistentry>
+       <term><option>-p <replaceable class="parameter">directory</replaceable></option></term>
+       <listitem>
+        <para>
+         Directory to find xlog files in.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Notes</title>
+   <para>
+     Can give wrong results when the server is running.
+   </para>
+  </refsect1>
+ </refentry>
*** a/src/include/utils/palloc.h
--- b/src/include/utils/palloc.h
***************
*** 28,35 ****
  #ifndef PALLOC_H
  #define PALLOC_H
  
- #ifndef FRONTEND
- 
  /*
   * Type MemoryContextData is declared in nodes/memnodes.h.	Most users
   * of memory allocation should just treat it as an abstract type, so we
--- 28,33 ----
***************
*** 37,42 ****
--- 35,42 ----
   */
  typedef struct MemoryContextData *MemoryContext;
  
+ #ifndef FRONTEND
+ 
  /*
   * CurrentMemoryContext is the default allocation context for palloc().
   * We declare it here so that palloc() can be a macro.	Avoid accessing it
commonalize-relpath.patchtext/x-diff; charset=us-asciiDownload
*** a/src/backend/access/rmgrdesc/smgrdesc.c
--- b/src/backend/access/rmgrdesc/smgrdesc.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "catalog/catalog.h"
  #include "catalog/storage_xlog.h"
+ #include "common/relpath.h"
  
  
  void
*** a/src/backend/access/rmgrdesc/xactdesc.c
--- b/src/backend/access/rmgrdesc/xactdesc.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "access/xact.h"
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "storage/sinval.h"
  #include "utils/timestamp.h"
  
*** a/src/backend/access/transam/xlogutils.c
--- b/src/backend/access/transam/xlogutils.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "access/xlog.h"
  #include "access/xlogutils.h"
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "storage/smgr.h"
  #include "utils/guc.h"
  #include "utils/hsearch.h"
*** a/src/backend/catalog/catalog.c
--- b/src/backend/catalog/catalog.c
***************
*** 37,42 ****
--- 37,43 ----
  #include "catalog/pg_shseclabel.h"
  #include "catalog/pg_tablespace.h"
  #include "catalog/toasting.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "storage/fd.h"
  #include "utils/fmgroids.h"
***************
*** 44,64 ****
  #include "utils/tqual.h"
  
  
- #define FORKNAMECHARS	4		/* max chars for a fork name */
- 
- /*
-  * Lookup table of fork name by fork number.
-  *
-  * If you add a new entry, remember to update the errhint below, and the
-  * documentation for pg_relation_size(). Also keep FORKNAMECHARS above
-  * up-to-date.
-  */
- const char *forkNames[] = {
- 	"main",						/* MAIN_FORKNUM */
- 	"fsm",						/* FSM_FORKNUM */
- 	"vm",						/* VISIBILITYMAP_FORKNUM */
- 	"init"						/* INIT_FORKNUM */
- };
  
  /*
   * forkname_to_number - look up fork number by name
--- 45,50 ----
***************
*** 80,209 **** forkname_to_number(char *forkName)
  }
  
  /*
-  * forkname_chars
-  *		We use this to figure out whether a filename could be a relation
-  *		fork (as opposed to an oddly named stray file that somehow ended
-  *		up in the database directory).	If the passed string begins with
-  *		a fork name (other than the main fork name), we return its length,
-  *		and set *fork (if not NULL) to the fork number.  If not, we return 0.
-  *
-  * Note that the present coding assumes that there are no fork names which
-  * are prefixes of other fork names.
-  */
- int
- forkname_chars(const char *str, ForkNumber *fork)
- {
- 	ForkNumber	forkNum;
- 
- 	for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
- 	{
- 		int			len = strlen(forkNames[forkNum]);
- 
- 		if (strncmp(forkNames[forkNum], str, len) == 0)
- 		{
- 			if (fork)
- 				*fork = forkNum;
- 			return len;
- 		}
- 	}
- 	return 0;
- }
- 
- /*
-  * relpathbackend - construct path to a relation's file
-  *
-  * Result is a palloc'd string.
-  */
- char *
- relpathbackend(RelFileNode rnode, BackendId backend, ForkNumber forknum)
- {
- 	int			pathlen;
- 	char	   *path;
- 
- 	if (rnode.spcNode == GLOBALTABLESPACE_OID)
- 	{
- 		/* Shared system relations live in {datadir}/global */
- 		Assert(rnode.dbNode == 0);
- 		Assert(backend == InvalidBackendId);
- 		pathlen = 7 + OIDCHARS + 1 + FORKNAMECHARS + 1;
- 		path = (char *) palloc(pathlen);
- 		if (forknum != MAIN_FORKNUM)
- 			snprintf(path, pathlen, "global/%u_%s",
- 					 rnode.relNode, forkNames[forknum]);
- 		else
- 			snprintf(path, pathlen, "global/%u", rnode.relNode);
- 	}
- 	else if (rnode.spcNode == DEFAULTTABLESPACE_OID)
- 	{
- 		/* The default tablespace is {datadir}/base */
- 		if (backend == InvalidBackendId)
- 		{
- 			pathlen = 5 + OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "base/%u/%u_%s",
- 						 rnode.dbNode, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "base/%u/%u",
- 						 rnode.dbNode, rnode.relNode);
- 		}
- 		else
- 		{
- 			/* OIDCHARS will suffice for an integer, too */
- 			pathlen = 5 + OIDCHARS + 2 + OIDCHARS + 1 + OIDCHARS + 1
- 				+ FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "base/%u/t%d_%u_%s",
- 						 rnode.dbNode, backend, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "base/%u/t%d_%u",
- 						 rnode.dbNode, backend, rnode.relNode);
- 		}
- 	}
- 	else
- 	{
- 		/* All other tablespaces are accessed via symlinks */
- 		if (backend == InvalidBackendId)
- 		{
- 			pathlen = 9 + 1 + OIDCHARS + 1
- 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 1
- 				+ OIDCHARS + 1 + FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u_%s",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, rnode.relNode);
- 		}
- 		else
- 		{
- 			/* OIDCHARS will suffice for an integer, too */
- 			pathlen = 9 + 1 + OIDCHARS + 1
- 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 2
- 				+ OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u_%s",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, backend, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, backend, rnode.relNode);
- 		}
- 	}
- 	return path;
- }
- 
- /*
   * GetDatabasePath			- construct path to a database dir
   *
   * Result is a palloc'd string.
--- 66,71 ----
*** a/src/backend/commands/tablespace.c
--- b/src/backend/commands/tablespace.c
***************
*** 64,69 ****
--- 64,70 ----
  #include "commands/comment.h"
  #include "commands/seclabel.h"
  #include "commands/tablespace.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "postmaster/bgwriter.h"
  #include "storage/fd.h"
*** a/src/backend/storage/buffer/bufmgr.c
--- b/src/backend/storage/buffer/bufmgr.c
***************
*** 34,39 ****
--- 34,40 ----
  #include <unistd.h>
  
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "executor/instrument.h"
  #include "miscadmin.h"
  #include "pg_trace.h"
*** a/src/backend/storage/buffer/localbuf.c
--- b/src/backend/storage/buffer/localbuf.c
***************
*** 16,21 ****
--- 16,22 ----
  #include "postgres.h"
  
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "executor/instrument.h"
  #include "storage/buf_internals.h"
  #include "storage/bufmgr.h"
*** a/src/backend/storage/file/fd.c
--- b/src/backend/storage/file/fd.c
***************
*** 71,76 ****
--- 71,77 ----
  #include "access/xact.h"
  #include "catalog/catalog.h"
  #include "catalog/pg_tablespace.h"
+ #include "common/relpath.h"
  #include "pgstat.h"
  #include "storage/fd.h"
  #include "storage/ipc.h"
*** a/src/backend/storage/file/reinit.c
--- b/src/backend/storage/file/reinit.c
***************
*** 17,22 ****
--- 17,23 ----
  #include <unistd.h>
  
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "storage/copydir.h"
  #include "storage/fd.h"
  #include "storage/reinit.h"
*** a/src/backend/storage/smgr/md.c
--- b/src/backend/storage/smgr/md.c
***************
*** 21,26 ****
--- 21,27 ----
  #include "miscadmin.h"
  #include "access/xlog.h"
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "portability/instr_time.h"
  #include "postmaster/bgwriter.h"
  #include "storage/fd.h"
*** a/src/backend/utils/adt/dbsize.c
--- b/src/backend/utils/adt/dbsize.c
***************
*** 21,26 ****
--- 21,27 ----
  #include "catalog/pg_tablespace.h"
  #include "commands/dbcommands.h"
  #include "commands/tablespace.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "storage/fd.h"
  #include "utils/acl.h"
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
***************
*** 24,29 ****
--- 24,30 ----
  #include "catalog/pg_tablespace.h"
  #include "catalog/pg_type.h"
  #include "commands/dbcommands.h"
+ #include "common/relpath.h"
  #include "funcapi.h"
  #include "miscadmin.h"
  #include "parser/keywords.h"
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
***************
*** 56,61 ****
--- 56,62 ----
  #include "catalog/schemapg.h"
  #include "catalog/storage.h"
  #include "commands/trigger.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "optimizer/clauses.h"
  #include "optimizer/planmain.h"
*** a/src/common/Makefile
--- b/src/common/Makefile
***************
*** 23,29 **** include $(top_builddir)/src/Makefile.global
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! OBJS_COMMON =
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o
  
--- 23,29 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! OBJS_COMMON = relpath.o
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o
  
*** /dev/null
--- b/src/common/relpath.c
***************
*** 0 ****
--- 1,150 ----
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+ 
+ #include "catalog/pg_tablespace.h"
+ #include "common/relpath.h"
+ #include "storage/backendid.h"
+ 
+ #define FORKNAMECHARS	4		/* max chars for a fork name */
+ 
+ /*
+  * Lookup table of fork name by fork number.
+  *
+  * If you add a new entry, remember to update the errhint below, and the
+  * documentation for pg_relation_size(). Also keep FORKNAMECHARS above
+  * up-to-date.
+  */
+ const char *forkNames[] = {
+ 	"main",						/* MAIN_FORKNUM */
+ 	"fsm",						/* FSM_FORKNUM */
+ 	"vm",						/* VISIBILITYMAP_FORKNUM */
+ 	"init"						/* INIT_FORKNUM */
+ };
+ 
+ /*
+  * forkname_chars
+  *		We use this to figure out whether a filename could be a relation
+  *		fork (as opposed to an oddly named stray file that somehow ended
+  *		up in the database directory).	If the passed string begins with
+  *		a fork name (other than the main fork name), we return its length,
+  *		and set *fork (if not NULL) to the fork number.  If not, we return 0.
+  *
+  * Note that the present coding assumes that there are no fork names which
+  * are prefixes of other fork names.
+  */
+ int
+ forkname_chars(const char *str, ForkNumber *fork)
+ {
+ 	ForkNumber	forkNum;
+ 
+ 	for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
+ 	{
+ 		int			len = strlen(forkNames[forkNum]);
+ 
+ 		if (strncmp(forkNames[forkNum], str, len) == 0)
+ 		{
+ 			if (fork)
+ 				*fork = forkNum;
+ 			return len;
+ 		}
+ 	}
+ 	return 0;
+ }
+ 
+ /*
+  * relpathbackend - construct path to a relation's file
+  *
+  * Result is a palloc'd string.
+  */
+ char *
+ relpathbackend(RelFileNode rnode, BackendId backend, ForkNumber forknum)
+ {
+ 	int			pathlen;
+ 	char	   *path;
+ 
+ 	if (rnode.spcNode == GLOBALTABLESPACE_OID)
+ 	{
+ 		/* Shared system relations live in {datadir}/global */
+ 		Assert(rnode.dbNode == 0);
+ 		Assert(backend == InvalidBackendId);
+ 		pathlen = 7 + OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 		path = (char *) palloc(pathlen);
+ 		if (forknum != MAIN_FORKNUM)
+ 			snprintf(path, pathlen, "global/%u_%s",
+ 					 rnode.relNode, forkNames[forknum]);
+ 		else
+ 			snprintf(path, pathlen, "global/%u", rnode.relNode);
+ 	}
+ 	else if (rnode.spcNode == DEFAULTTABLESPACE_OID)
+ 	{
+ 		/* The default tablespace is {datadir}/base */
+ 		if (backend == InvalidBackendId)
+ 		{
+ 			pathlen = 5 + OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "base/%u/%u_%s",
+ 						 rnode.dbNode, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "base/%u/%u",
+ 						 rnode.dbNode, rnode.relNode);
+ 		}
+ 		else
+ 		{
+ 			/* OIDCHARS will suffice for an integer, too */
+ 			pathlen = 5 + OIDCHARS + 2 + OIDCHARS + 1 + OIDCHARS + 1
+ 				+ FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "base/%u/t%d_%u_%s",
+ 						 rnode.dbNode, backend, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "base/%u/t%d_%u",
+ 						 rnode.dbNode, backend, rnode.relNode);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* All other tablespaces are accessed via symlinks */
+ 		if (backend == InvalidBackendId)
+ 		{
+ 			pathlen = 9 + 1 + OIDCHARS + 1
+ 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 1
+ 				+ OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u_%s",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, rnode.relNode);
+ 		}
+ 		else
+ 		{
+ 			/* OIDCHARS will suffice for an integer, too */
+ 			pathlen = 9 + 1 + OIDCHARS + 1
+ 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 2
+ 				+ OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u_%s",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, backend, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, backend, rnode.relNode);
+ 		}
+ 	}
+ 	return path;
+ }
+ 
*** a/src/include/catalog/catalog.h
--- b/src/include/catalog/catalog.h
***************
*** 14,47 ****
  #ifndef CATALOG_H
  #define CATALOG_H
  
- /*
-  *	'pgrminclude ignore' needed here because CppAsString2() does not throw
-  *	an error if the symbol is not defined.
-  */
- #include "catalog/catversion.h" /* pgrminclude ignore */
  #include "catalog/pg_class.h"
  #include "storage/relfilenode.h"
  #include "utils/relcache.h"
  
- #define OIDCHARS		10		/* max chars printed by %u */
- #define TABLESPACE_VERSION_DIRECTORY	"PG_" PG_MAJORVERSION "_" \
- 									CppAsString2(CATALOG_VERSION_NO)
- 
- extern const char *forkNames[];
  extern ForkNumber forkname_to_number(char *forkName);
- extern int	forkname_chars(const char *str, ForkNumber *);
  
- extern char *relpathbackend(RelFileNode rnode, BackendId backend,
- 			   ForkNumber forknum);
  extern char *GetDatabasePath(Oid dbNode, Oid spcNode);
  
- /* First argument is a RelFileNodeBackend */
- #define relpath(rnode, forknum) \
- 		relpathbackend((rnode).node, (rnode).backend, (forknum))
- 
- /* First argument is a RelFileNode */
- #define relpathperm(rnode, forknum) \
- 		relpathbackend((rnode), InvalidBackendId, (forknum))
  
  extern bool IsSystemRelation(Relation relation);
  extern bool IsToastRelation(Relation relation);
--- 14,27 ----
*** /dev/null
--- b/src/include/common/relpath.h
***************
*** 0 ****
--- 1,28 ----
+ #ifndef COMMON_RELPATH_H
+ #define COMMON_RELPATH_H
+ 
+ /*
+  *	'pgrminclude ignore' needed here because CppAsString2() does not throw
+  *	an error if the symbol is not defined.
+  */
+ #include "catalog/catversion.h" /* pgrminclude ignore */
+ #include "storage/relfilenode.h"
+ 
+ #define OIDCHARS		10		/* max chars printed by %u */
+ #define TABLESPACE_VERSION_DIRECTORY	"PG_" PG_MAJORVERSION "_" \
+ 									CppAsString2(CATALOG_VERSION_NO)
+ 
+ extern const char *forkNames[];
+ extern int	forkname_chars(const char *str, ForkNumber *);
+ extern char *relpathbackend(RelFileNode rnode, BackendId backend,
+ 			   ForkNumber forknum);
+ 
+ /* First argument is a RelFileNodeBackend */
+ #define relpath(rnode, forknum) \
+ 		relpathbackend((rnode).node, (rnode).backend, (forknum))
+ 
+ /* First argument is a RelFileNode */
+ #define relpathperm(rnode, forknum) \
+ 		relpathbackend((rnode), InvalidBackendId, (forknum))
+ 
+ #endif
#2Andres Freund
andres@2ndquadrant.com
In reply to: Alvaro Herrera (#1)
1 attachment(s)
Re: pg_xlogdump

On 2013-02-13 12:09:37 -0300, Alvaro Herrera wrote:

Here's an updated version of pg_xlogdump. This is rebased on top of the
committed xlogreader, palloc restructuring and libpgcommon, PG_RMGR
stuff, and is basically a revamped version of what Andres submitted in
/messages/by-id/1357672187-7693-5-git-send-email-andres@2ndquadrant.com

Two tiny followup bits, I had fixed since:
* one copy-and-paste-o in an error message
* replace stupid directory verification implementation
* fix include in compat.c to include utils/timestamp.h instead of
datatype/

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

followup-xlogdump-corrections.patchtext/x-patch; charset=us-asciiDownload
diff --git a/contrib/pg_xlogdump/compat.c b/contrib/pg_xlogdump/compat.c
index a3c98c6..dc54bad 100644
--- a/contrib/pg_xlogdump/compat.c
+++ b/contrib/pg_xlogdump/compat.c
@@ -21,7 +21,7 @@
 #include "catalog/catalog.h"
 #include "catalog/pg_tablespace.h"
 #include "common/fe_memutils.h"
-#include "datatype/timestamp.h"
+#include "utils/timestamp.h"
 #include "lib/stringinfo.h"
 #include "storage/relfilenode.h"
 
diff --git a/contrib/pg_xlogdump/pg_xlogdump.c b/contrib/pg_xlogdump/pg_xlogdump.c
index c2ed1b8..9d8597f 100644
--- a/contrib/pg_xlogdump/pg_xlogdump.c
+++ b/contrib/pg_xlogdump/pg_xlogdump.c
@@ -12,6 +12,7 @@
 #define FRONTEND 1
 #include "postgres.h"
 
+#include <dirent.h>
 #include <unistd.h>
 
 #include "rmgrdesc.h"
@@ -64,19 +65,16 @@ fatal_error(const char *fmt,...)
 }
 
 /*
- * Check whether directory exists and whether we can open it.
- * errno is kept set so that the caller can report errors.
+ * Check whether directory exists and whether we can open it. Keep errno set so
+ * that the caller can report errors somewhat more accurate.
  */
 static bool
 verify_directory(const char *directory)
 {
-	int			fd = open(directory, O_DIRECTORY | O_RDONLY);
-
-	if (fd < 0)
+	DIR *dir = opendir(directory);
+	if (dir == NULL)
 		return false;
-
-	close(fd);
-	errno = 0;					/* ignore errors in this case */
+	closedir(dir);
 	return true;
 }
 
@@ -560,7 +558,7 @@ main(int argc, char **argv)
 		else if (!XLByteInSeg(private.startptr, segno))
 		{
 			fprintf(stderr,
-					"%s: end log position %X/%X is not inside file \"%s\"\n",
+					"%s: start log position %X/%X is not inside file \"%s\"\n",
 					progname,
 					(uint32) (private.startptr >> 32),
 					(uint32) private.startptr,
#3Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Alvaro Herrera (#1)
1 attachment(s)
relpath() to src/common

Just a heads up: I intend to push this shortly, and after it has seen
some BF activity, pg_xlogdump as well.

--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

commonalize-relpath-2.patchtext/x-diff; charset=us-asciiDownload
*** a/src/backend/Makefile
--- b/src/backend/Makefile
***************
*** 35,44 **** LOCALOBJS += utils/probes.o
  endif
  endif
  
! OBJS = $(SUBDIROBJS) $(LOCALOBJS) $(top_builddir)/src/port/libpgport_srv.a
  
! # We put libpgport into OBJS, so remove it from LIBS; also add libldap
! LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE)
  
  # The backend doesn't need everything that's in LIBS, however
  LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS))
--- 35,46 ----
  endif
  endif
  
! OBJS = $(SUBDIROBJS) $(LOCALOBJS) $(top_builddir)/src/port/libpgport_srv.a \
!        $(top_builddir)/src/common/libpgcommon_srv.a
  
! # We put libpgport and libpgcommon into OBJS, so remove it from LIBS; also add
! # libldap
! LIBS := $(filter-out -lpgport -lpgcommon, $(LIBS)) $(LDAP_LIBS_BE)
  
  # The backend doesn't need everything that's in LIBS, however
  LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS))
*** a/src/backend/access/rmgrdesc/smgrdesc.c
--- b/src/backend/access/rmgrdesc/smgrdesc.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "catalog/catalog.h"
  #include "catalog/storage_xlog.h"
+ #include "common/relpath.h"
  
  
  void
*** a/src/backend/access/rmgrdesc/xactdesc.c
--- b/src/backend/access/rmgrdesc/xactdesc.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "access/xact.h"
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "storage/sinval.h"
  #include "utils/timestamp.h"
  
*** a/src/backend/access/transam/xlogutils.c
--- b/src/backend/access/transam/xlogutils.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "access/xlog.h"
  #include "access/xlogutils.h"
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "storage/smgr.h"
  #include "utils/guc.h"
  #include "utils/hsearch.h"
*** a/src/backend/catalog/catalog.c
--- b/src/backend/catalog/catalog.c
***************
*** 37,42 ****
--- 37,43 ----
  #include "catalog/pg_shseclabel.h"
  #include "catalog/pg_tablespace.h"
  #include "catalog/toasting.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "storage/fd.h"
  #include "utils/fmgroids.h"
***************
*** 44,64 ****
  #include "utils/tqual.h"
  
  
- #define FORKNAMECHARS	4		/* max chars for a fork name */
- 
- /*
-  * Lookup table of fork name by fork number.
-  *
-  * If you add a new entry, remember to update the errhint below, and the
-  * documentation for pg_relation_size(). Also keep FORKNAMECHARS above
-  * up-to-date.
-  */
- const char *forkNames[] = {
- 	"main",						/* MAIN_FORKNUM */
- 	"fsm",						/* FSM_FORKNUM */
- 	"vm",						/* VISIBILITYMAP_FORKNUM */
- 	"init"						/* INIT_FORKNUM */
- };
  
  /*
   * forkname_to_number - look up fork number by name
--- 45,50 ----
***************
*** 80,209 **** forkname_to_number(char *forkName)
  }
  
  /*
-  * forkname_chars
-  *		We use this to figure out whether a filename could be a relation
-  *		fork (as opposed to an oddly named stray file that somehow ended
-  *		up in the database directory).	If the passed string begins with
-  *		a fork name (other than the main fork name), we return its length,
-  *		and set *fork (if not NULL) to the fork number.  If not, we return 0.
-  *
-  * Note that the present coding assumes that there are no fork names which
-  * are prefixes of other fork names.
-  */
- int
- forkname_chars(const char *str, ForkNumber *fork)
- {
- 	ForkNumber	forkNum;
- 
- 	for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
- 	{
- 		int			len = strlen(forkNames[forkNum]);
- 
- 		if (strncmp(forkNames[forkNum], str, len) == 0)
- 		{
- 			if (fork)
- 				*fork = forkNum;
- 			return len;
- 		}
- 	}
- 	return 0;
- }
- 
- /*
-  * relpathbackend - construct path to a relation's file
-  *
-  * Result is a palloc'd string.
-  */
- char *
- relpathbackend(RelFileNode rnode, BackendId backend, ForkNumber forknum)
- {
- 	int			pathlen;
- 	char	   *path;
- 
- 	if (rnode.spcNode == GLOBALTABLESPACE_OID)
- 	{
- 		/* Shared system relations live in {datadir}/global */
- 		Assert(rnode.dbNode == 0);
- 		Assert(backend == InvalidBackendId);
- 		pathlen = 7 + OIDCHARS + 1 + FORKNAMECHARS + 1;
- 		path = (char *) palloc(pathlen);
- 		if (forknum != MAIN_FORKNUM)
- 			snprintf(path, pathlen, "global/%u_%s",
- 					 rnode.relNode, forkNames[forknum]);
- 		else
- 			snprintf(path, pathlen, "global/%u", rnode.relNode);
- 	}
- 	else if (rnode.spcNode == DEFAULTTABLESPACE_OID)
- 	{
- 		/* The default tablespace is {datadir}/base */
- 		if (backend == InvalidBackendId)
- 		{
- 			pathlen = 5 + OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "base/%u/%u_%s",
- 						 rnode.dbNode, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "base/%u/%u",
- 						 rnode.dbNode, rnode.relNode);
- 		}
- 		else
- 		{
- 			/* OIDCHARS will suffice for an integer, too */
- 			pathlen = 5 + OIDCHARS + 2 + OIDCHARS + 1 + OIDCHARS + 1
- 				+ FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "base/%u/t%d_%u_%s",
- 						 rnode.dbNode, backend, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "base/%u/t%d_%u",
- 						 rnode.dbNode, backend, rnode.relNode);
- 		}
- 	}
- 	else
- 	{
- 		/* All other tablespaces are accessed via symlinks */
- 		if (backend == InvalidBackendId)
- 		{
- 			pathlen = 9 + 1 + OIDCHARS + 1
- 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 1
- 				+ OIDCHARS + 1 + FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u_%s",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, rnode.relNode);
- 		}
- 		else
- 		{
- 			/* OIDCHARS will suffice for an integer, too */
- 			pathlen = 9 + 1 + OIDCHARS + 1
- 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 2
- 				+ OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
- 			path = (char *) palloc(pathlen);
- 			if (forknum != MAIN_FORKNUM)
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u_%s",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, backend, rnode.relNode,
- 						 forkNames[forknum]);
- 			else
- 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u",
- 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
- 						 rnode.dbNode, backend, rnode.relNode);
- 		}
- 	}
- 	return path;
- }
- 
- /*
   * GetDatabasePath			- construct path to a database dir
   *
   * Result is a palloc'd string.
--- 66,71 ----
*** a/src/backend/commands/tablespace.c
--- b/src/backend/commands/tablespace.c
***************
*** 64,69 ****
--- 64,70 ----
  #include "commands/comment.h"
  #include "commands/seclabel.h"
  #include "commands/tablespace.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "postmaster/bgwriter.h"
  #include "storage/fd.h"
*** a/src/backend/storage/buffer/bufmgr.c
--- b/src/backend/storage/buffer/bufmgr.c
***************
*** 34,39 ****
--- 34,40 ----
  #include <unistd.h>
  
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "executor/instrument.h"
  #include "miscadmin.h"
  #include "pg_trace.h"
*** a/src/backend/storage/buffer/localbuf.c
--- b/src/backend/storage/buffer/localbuf.c
***************
*** 16,21 ****
--- 16,22 ----
  #include "postgres.h"
  
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "executor/instrument.h"
  #include "storage/buf_internals.h"
  #include "storage/bufmgr.h"
*** a/src/backend/storage/file/fd.c
--- b/src/backend/storage/file/fd.c
***************
*** 71,76 ****
--- 71,77 ----
  #include "access/xact.h"
  #include "catalog/catalog.h"
  #include "catalog/pg_tablespace.h"
+ #include "common/relpath.h"
  #include "pgstat.h"
  #include "storage/fd.h"
  #include "storage/ipc.h"
*** a/src/backend/storage/file/reinit.c
--- b/src/backend/storage/file/reinit.c
***************
*** 17,22 ****
--- 17,23 ----
  #include <unistd.h>
  
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "storage/copydir.h"
  #include "storage/fd.h"
  #include "storage/reinit.h"
*** a/src/backend/storage/smgr/md.c
--- b/src/backend/storage/smgr/md.c
***************
*** 21,26 ****
--- 21,27 ----
  #include "miscadmin.h"
  #include "access/xlog.h"
  #include "catalog/catalog.h"
+ #include "common/relpath.h"
  #include "portability/instr_time.h"
  #include "postmaster/bgwriter.h"
  #include "storage/fd.h"
*** a/src/backend/utils/adt/dbsize.c
--- b/src/backend/utils/adt/dbsize.c
***************
*** 21,26 ****
--- 21,27 ----
  #include "catalog/pg_tablespace.h"
  #include "commands/dbcommands.h"
  #include "commands/tablespace.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "storage/fd.h"
  #include "utils/acl.h"
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
***************
*** 24,29 ****
--- 24,30 ----
  #include "catalog/pg_tablespace.h"
  #include "catalog/pg_type.h"
  #include "commands/dbcommands.h"
+ #include "common/relpath.h"
  #include "funcapi.h"
  #include "miscadmin.h"
  #include "parser/keywords.h"
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
***************
*** 56,61 ****
--- 56,62 ----
  #include "catalog/schemapg.h"
  #include "catalog/storage.h"
  #include "commands/trigger.h"
+ #include "common/relpath.h"
  #include "miscadmin.h"
  #include "optimizer/clauses.h"
  #include "optimizer/planmain.h"
*** a/src/common/Makefile
--- b/src/common/Makefile
***************
*** 23,35 **** include $(top_builddir)/src/Makefile.global
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! OBJS_COMMON =
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o
  
  OBJS_SRV = $(OBJS_COMMON:%.o=%_srv.o)
  
! all: libpgcommon.a
  
  # libpgcommon is needed by some contrib
  install: all installdirs
--- 23,35 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! OBJS_COMMON = relpath.o
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o
  
  OBJS_SRV = $(OBJS_COMMON:%.o=%_srv.o)
  
! all: libpgcommon.a libpgcommon_srv.a
  
  # libpgcommon is needed by some contrib
  install: all installdirs
***************
*** 60,64 **** libpgcommon_srv.a: $(OBJS_SRV)
--- 60,71 ----
  %_srv.o: %.c %.o
  	$(CC) $(CFLAGS) $(subst -DFRONTEND,, $(CPPFLAGS)) -c $< -o $@
  
+ $(OBJS_SRV): | submake-errcodes
+ 
+ .PHONY: submake-errcodes
+ 
+ submake-errcodes:
+ 	$(MAKE) -C ../backend submake-errcodes
+ 
  clean distclean maintainer-clean:
  	rm -f libpgcommon.a libpgcommon_srv.a $(OBJS_FRONTEND) $(OBJS_SRV)
*** /dev/null
--- b/src/common/relpath.c
***************
*** 0 ****
--- 1,162 ----
+ /*-------------------------------------------------------------------------
+  * relpath.c
+  * 		Shared frontend/backend code to find out pathnames of relation files
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *    src/common/relpath.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+ 
+ #include "catalog/pg_tablespace.h"
+ #include "common/relpath.h"
+ #include "storage/backendid.h"
+ 
+ #define FORKNAMECHARS	4		/* max chars for a fork name */
+ 
+ /*
+  * Lookup table of fork name by fork number.
+  *
+  * If you add a new entry, remember to update the errhint below, and the
+  * documentation for pg_relation_size(). Also keep FORKNAMECHARS above
+  * up-to-date.
+  */
+ const char *forkNames[] = {
+ 	"main",						/* MAIN_FORKNUM */
+ 	"fsm",						/* FSM_FORKNUM */
+ 	"vm",						/* VISIBILITYMAP_FORKNUM */
+ 	"init"						/* INIT_FORKNUM */
+ };
+ 
+ /*
+  * forkname_chars
+  *		We use this to figure out whether a filename could be a relation
+  *		fork (as opposed to an oddly named stray file that somehow ended
+  *		up in the database directory).	If the passed string begins with
+  *		a fork name (other than the main fork name), we return its length,
+  *		and set *fork (if not NULL) to the fork number.  If not, we return 0.
+  *
+  * Note that the present coding assumes that there are no fork names which
+  * are prefixes of other fork names.
+  */
+ int
+ forkname_chars(const char *str, ForkNumber *fork)
+ {
+ 	ForkNumber	forkNum;
+ 
+ 	for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
+ 	{
+ 		int			len = strlen(forkNames[forkNum]);
+ 
+ 		if (strncmp(forkNames[forkNum], str, len) == 0)
+ 		{
+ 			if (fork)
+ 				*fork = forkNum;
+ 			return len;
+ 		}
+ 	}
+ 	return 0;
+ }
+ 
+ /*
+  * relpathbackend - construct path to a relation's file
+  *
+  * Result is a palloc'd string.
+  */
+ char *
+ relpathbackend(RelFileNode rnode, BackendId backend, ForkNumber forknum)
+ {
+ 	int			pathlen;
+ 	char	   *path;
+ 
+ 	if (rnode.spcNode == GLOBALTABLESPACE_OID)
+ 	{
+ 		/* Shared system relations live in {datadir}/global */
+ 		Assert(rnode.dbNode == 0);
+ 		Assert(backend == InvalidBackendId);
+ 		pathlen = 7 + OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 		path = (char *) palloc(pathlen);
+ 		if (forknum != MAIN_FORKNUM)
+ 			snprintf(path, pathlen, "global/%u_%s",
+ 					 rnode.relNode, forkNames[forknum]);
+ 		else
+ 			snprintf(path, pathlen, "global/%u", rnode.relNode);
+ 	}
+ 	else if (rnode.spcNode == DEFAULTTABLESPACE_OID)
+ 	{
+ 		/* The default tablespace is {datadir}/base */
+ 		if (backend == InvalidBackendId)
+ 		{
+ 			pathlen = 5 + OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "base/%u/%u_%s",
+ 						 rnode.dbNode, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "base/%u/%u",
+ 						 rnode.dbNode, rnode.relNode);
+ 		}
+ 		else
+ 		{
+ 			/* OIDCHARS will suffice for an integer, too */
+ 			pathlen = 5 + OIDCHARS + 2 + OIDCHARS + 1 + OIDCHARS + 1
+ 				+ FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "base/%u/t%d_%u_%s",
+ 						 rnode.dbNode, backend, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "base/%u/t%d_%u",
+ 						 rnode.dbNode, backend, rnode.relNode);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		/* All other tablespaces are accessed via symlinks */
+ 		if (backend == InvalidBackendId)
+ 		{
+ 			pathlen = 9 + 1 + OIDCHARS + 1
+ 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 1
+ 				+ OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u_%s",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, rnode.relNode);
+ 		}
+ 		else
+ 		{
+ 			/* OIDCHARS will suffice for an integer, too */
+ 			pathlen = 9 + 1 + OIDCHARS + 1
+ 				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 2
+ 				+ OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
+ 			path = (char *) palloc(pathlen);
+ 			if (forknum != MAIN_FORKNUM)
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u_%s",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, backend, rnode.relNode,
+ 						 forkNames[forknum]);
+ 			else
+ 				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u",
+ 						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
+ 						 rnode.dbNode, backend, rnode.relNode);
+ 		}
+ 	}
+ 	return path;
+ }
+ 
*** a/src/include/catalog/catalog.h
--- b/src/include/catalog/catalog.h
***************
*** 14,47 ****
  #ifndef CATALOG_H
  #define CATALOG_H
  
- /*
-  *	'pgrminclude ignore' needed here because CppAsString2() does not throw
-  *	an error if the symbol is not defined.
-  */
- #include "catalog/catversion.h" /* pgrminclude ignore */
  #include "catalog/pg_class.h"
  #include "storage/relfilenode.h"
  #include "utils/relcache.h"
  
- #define OIDCHARS		10		/* max chars printed by %u */
- #define TABLESPACE_VERSION_DIRECTORY	"PG_" PG_MAJORVERSION "_" \
- 									CppAsString2(CATALOG_VERSION_NO)
- 
- extern const char *forkNames[];
  extern ForkNumber forkname_to_number(char *forkName);
- extern int	forkname_chars(const char *str, ForkNumber *);
  
- extern char *relpathbackend(RelFileNode rnode, BackendId backend,
- 			   ForkNumber forknum);
  extern char *GetDatabasePath(Oid dbNode, Oid spcNode);
  
- /* First argument is a RelFileNodeBackend */
- #define relpath(rnode, forknum) \
- 		relpathbackend((rnode).node, (rnode).backend, (forknum))
- 
- /* First argument is a RelFileNode */
- #define relpathperm(rnode, forknum) \
- 		relpathbackend((rnode), InvalidBackendId, (forknum))
  
  extern bool IsSystemRelation(Relation relation);
  extern bool IsToastRelation(Relation relation);
--- 14,27 ----
*** /dev/null
--- b/src/include/common/relpath.h
***************
*** 0 ****
--- 1,41 ----
+ /*-------------------------------------------------------------------------
+  *
+  * relpath.h
+  * 		Declarations for relpath() and friends
+  *
+  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/common/relpath.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef RELPATH_H
+ #define RELPATH_H
+ 
+ /*
+  *	'pgrminclude ignore' needed here because CppAsString2() does not throw
+  *	an error if the symbol is not defined.
+  */
+ #include "catalog/catversion.h" /* pgrminclude ignore */
+ #include "storage/relfilenode.h"
+ 
+ 
+ #define OIDCHARS		10		/* max chars printed by %u */
+ #define TABLESPACE_VERSION_DIRECTORY	"PG_" PG_MAJORVERSION "_" \
+ 									CppAsString2(CATALOG_VERSION_NO)
+ 
+ extern const char *forkNames[];
+ extern int	forkname_chars(const char *str, ForkNumber *fork);
+ extern char *relpathbackend(RelFileNode rnode, BackendId backend,
+ 			   ForkNumber forknum);
+ 
+ /* First argument is a RelFileNodeBackend */
+ #define relpath(rnode, forknum) \
+ 		relpathbackend((rnode).node, (rnode).backend, (forknum))
+ 
+ /* First argument is a RelFileNode */
+ #define relpathperm(rnode, forknum) \
+ 		relpathbackend((rnode), InvalidBackendId, (forknum))
+ 
+ #endif		/* RELPATH_H */
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 69,78 **** sub mkvcbuild
  	  sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c rint.c win32env.c
  	  win32error.c win32setlocale.c);
  
! 	our @pgcommonfiles = qw(
! 		fe_memutils.c);
  
! 	our @pgcommonbkndfiles = qw();
  
  	$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
  	$libpgport->AddDefine('FRONTEND');
--- 69,81 ----
  	  sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c rint.c win32env.c
  	  win32error.c win32setlocale.c);
  
! 	our @pgcommonallfiles = qw(
! 		relpath.c);
  
! 	our @pgcommonfrontendfiles = (@pgcommonallfiles,
! 		qw(fe_memutils.c));
! 
! 	our @pgcommonbkndfiles = @pgcommonallfiles;
  
  	$libpgport = $solution->AddProject('libpgport', 'lib', 'misc');
  	$libpgport->AddDefine('FRONTEND');
***************
*** 80,86 **** sub mkvcbuild
  
  	$libpgcommon = $solution->AddProject('libpgcommon', 'lib', 'misc');
  	$libpgcommon->AddDefine('FRONTEND');
! 	$libpgcommon->AddFiles('src\common', @pgcommonfiles);
  
  	$postgres = $solution->AddProject('postgres', 'exe', '', 'src\backend');
  	$postgres->AddIncludeDir('src\backend');
--- 83,89 ----
  
  	$libpgcommon = $solution->AddProject('libpgcommon', 'lib', 'misc');
  	$libpgcommon->AddDefine('FRONTEND');
! 	$libpgcommon->AddFiles('src\common', @pgcommonfrontendfiles);
  
  	$postgres = $solution->AddProject('postgres', 'exe', '', 'src\backend');
  	$postgres->AddIncludeDir('src\backend');
#4Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andres Freund (#2)
Re: pg_xlogdump

Andres Freund wrote:

On 2013-02-13 12:09:37 -0300, Alvaro Herrera wrote:

Here's an updated version of pg_xlogdump. This is rebased on top of the
committed xlogreader, palloc restructuring and libpgcommon, PG_RMGR
stuff, and is basically a revamped version of what Andres submitted in
/messages/by-id/1357672187-7693-5-git-send-email-andres@2ndquadrant.com

Two tiny followup bits, I had fixed since:
* one copy-and-paste-o in an error message
* replace stupid directory verification implementation
* fix include in compat.c to include utils/timestamp.h instead of
datatype/

Applied with some additional fixes.

--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Andres Freund
andres@2ndquadrant.com
In reply to: Alvaro Herrera (#4)
Re: pg_xlogdump

On 2013-02-22 16:58:37 -0300, Alvaro Herrera wrote:

Andres Freund wrote:

On 2013-02-13 12:09:37 -0300, Alvaro Herrera wrote:

Here's an updated version of pg_xlogdump. This is rebased on top of the
committed xlogreader, palloc restructuring and libpgcommon, PG_RMGR
stuff, and is basically a revamped version of what Andres submitted in
/messages/by-id/1357672187-7693-5-git-send-email-andres@2ndquadrant.com

Two tiny followup bits, I had fixed since:
* one copy-and-paste-o in an error message
* replace stupid directory verification implementation
* fix include in compat.c to include utils/timestamp.h instead of
datatype/

Applied with some additional fixes.

Thanks!

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Jeff Janes
jeff.janes@gmail.com
In reply to: Alvaro Herrera (#4)
Re: pg_xlogdump

On Fri, Feb 22, 2013 at 11:58 AM, Alvaro Herrera
<alvherre@2ndquadrant.com>wrote:

Andres Freund wrote:

On 2013-02-13 12:09:37 -0300, Alvaro Herrera wrote:

Here's an updated version of pg_xlogdump. This is rebased on top of

the

committed xlogreader, palloc restructuring and libpgcommon, PG_RMGR
stuff, and is basically a revamped version of what Andres submitted in

/messages/by-id/1357672187-7693-5-git-send-email-andres@2ndquadrant.com

Two tiny followup bits, I had fixed since:
* one copy-and-paste-o in an error message
* replace stupid directory verification implementation
* fix include in compat.c to include utils/timestamp.h instead of
datatype/

Applied with some additional fixes.

Hi Álvaro,

If I run "make clean", or "make maintainer-clean", this deletes the file
contrib/pg_xlogdump/rmgrdesc.c. And then config/make doesn't know how to
get it back again.

I don't know if the Makefile needs to be taught not to delete it, or taught
how to recreate it once deleted.

Thanks,

Jeff

#7Andres Freund
andres@2ndquadrant.com
In reply to: Jeff Janes (#6)
1 attachment(s)
Re: pg_xlogdump

On 2013-02-23 14:54:51 -0800, Jeff Janes wrote:

If I run "make clean", or "make maintainer-clean", this deletes the file
contrib/pg_xlogdump/rmgrdesc.c. And then config/make doesn't know how to
get it back again.

I don't know if the Makefile needs to be taught not to delete it, or taught
how to recreate it once deleted.

It shouldn't be deleted, I think neither Alvaro nor me did notice this
because it only matters in non-vpath builds...

Patch attached.

I independently wonder whether we should remove the PGXS stub from
xlogdump, given it relies on a full sourcetree available?

Andres

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

xlogdump-dont-remove-rmgrdesc.c.patchtext/x-patch; charset=us-asciiDownload
diff --git a/contrib/pg_xlogdump/Makefile b/contrib/pg_xlogdump/Makefile
index f381967..b454c02 100644
--- a/contrib/pg_xlogdump/Makefile
+++ b/contrib/pg_xlogdump/Makefile
@@ -1,7 +1,7 @@
 # contrib/pg_xlogdump/Makefile
 
 PGFILEDESC = "pg_xlogdump"
-PGAPPICON=win32
+PGAPPICON = win32
 
 PROGRAM = pg_xlogdump
 OBJS = pg_xlogdump.o compat.o xlogreader.o rmgrdesc.o \
@@ -10,7 +10,7 @@ OBJS = pg_xlogdump.o compat.o xlogreader.o rmgrdesc.o \
 RMGRDESCSOURCES = $(notdir $(wildcard $(top_srcdir)/src/backend/access/rmgrdesc/*desc.c))
 RMGRDESCOBJS = $(patsubst %.c,%.o,$(RMGRDESCSOURCES))
 
-EXTRA_CLEAN = $(RMGRDESCSOURCES) xlogreader.c rmgrdesc.c
+EXTRA_CLEAN = $(RMGRDESCSOURCES) xlogreader.c
 
 ifdef USE_PGXS
 PG_CONFIG = pg_config
@@ -25,7 +25,7 @@ endif
 
 override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
 
-rmgrdesc.c xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/%
+xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/%
 	rm -f $@ && $(LN_S) $< .
 
 $(RMGRDESCSOURCES): % : $(top_srcdir)/src/backend/access/rmgrdesc/%
#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#7)
Re: pg_xlogdump

Andres Freund <andres@2ndquadrant.com> writes:

On 2013-02-23 14:54:51 -0800, Jeff Janes wrote:

I don't know if the Makefile needs to be taught not to delete it, or taught
how to recreate it once deleted.

It shouldn't be deleted,

I came to the same conclusion and committed that a few minutes before
you posted.

I independently wonder whether we should remove the PGXS stub from
xlogdump, given it relies on a full sourcetree available?

I'd just as soon keep its Makefile looking like all the others.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#8)
Re: pg_xlogdump

On Sun, 2013-02-24 at 09:34 -0500, Tom Lane wrote:

I independently wonder whether we should remove the PGXS stub from
xlogdump, given it relies on a full sourcetree available?

I'd just as soon keep its Makefile looking like all the others.

If the code doesn't actually work, it should be deleted. There is no
value in keeping the Makefile looking like the others, because it
doesn't in fact look like the others.

I for one wonder why we even have PGXS support in contrib at all. It's
not documented or tested anywhere, so it might as well not exist.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Peter Eisentraut (#9)
Re: pg_xlogdump

Peter Eisentraut <peter_e@gmx.net> writes:

I for one wonder why we even have PGXS support in contrib at all. It's
not documented or tested anywhere, so it might as well not exist.

I think I did about the same comment back when cooking the extension
patch, and the answer then was all about providing PGXS usage examples.
Now if none of the buildfarm animals are actually building our contribs
out of tree, maybe we should just remove those examples.

The cost of keeping them is that they double-up the Makefile content and
lots of users do think they need their extension's Makefile to be
structured the same. The common effect before the extension availability
was for people to provide extensions that would only build in tree.

I don't want to kill cleaning up those Makefiles, but I still want to
make a strong correlation in between that point and providing core
maintained extensions. I don't think extensions should have support for
being built in-tree at all.

My proposal: paint them extension rather than contrib modules, then
cleanup Makefiles so as to stop building them in-tree.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Dimitri Fontaine (#10)
Re: pg_xlogdump

Dimitri Fontaine <dimitri@2ndQuadrant.fr> writes:

Peter Eisentraut <peter_e@gmx.net> writes:

I for one wonder why we even have PGXS support in contrib at all. It's
not documented or tested anywhere, so it might as well not exist.

I think I did about the same comment back when cooking the extension
patch, and the answer then was all about providing PGXS usage examples.
Now if none of the buildfarm animals are actually building our contribs
out of tree, maybe we should just remove those examples.

The cost of keeping them is that they double-up the Makefile content and
lots of users do think they need their extension's Makefile to be
structured the same. The common effect before the extension availability
was for people to provide extensions that would only build in tree.

I don't want to kill cleaning up those Makefiles, but I still want to
make a strong correlation in between that point and providing core
maintained extensions. I don't think extensions should have support for
being built in-tree at all.

My proposal: paint them extension rather than contrib modules, then
cleanup Makefiles so as to stop building them in-tree.

[ Sigh... ] Why this eagerness to fix what isn't broken?

Leave the Makefiles alone. They're not broken and they provide useful
examples, plus a sense of continuity between in-tree and not-in-tree
extensions. Any change here will likely break build scenarios that
work today --- in particular, this proposal will break building contrib
before the main tree has been installed.

If somebody wants to set up a buildfarm member that occasionally tests
PGXS building of contrib/, that's fine with me. But it isn't, and never
will be, the main build scenario for contrib/ IMO.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Tom Lane (#11)
Re: pg_xlogdump

Tom Lane <tgl@sss.pgh.pa.us> writes:

work today --- in particular, this proposal will break building contrib
before the main tree has been installed.

True that.

If somebody wants to set up a buildfarm member that occasionally tests
PGXS building of contrib/, that's fine with me. But it isn't, and never
will be, the main build scenario for contrib/ IMO.

I guess you just made a final vote on the whole idea of making contrib a
set of extensions maintained by the PostgreSQL commiters, then. It might
be good to document that position, so that the topic isn't raised again
each year?

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Andres Freund
andres@2ndquadrant.com
In reply to: Dimitri Fontaine (#10)
Re: pg_xlogdump

On 2013-02-26 17:23:01 +0100, Dimitri Fontaine wrote:

Peter Eisentraut <peter_e@gmx.net> writes:

I for one wonder why we even have PGXS support in contrib at all. It's
not documented or tested anywhere, so it might as well not exist.

Agreed. I personally think we just should build contrib/ as part of the
normal build and be done with it.

There's currently the argument that building them separately is
interesting because you can do it from a separate sourcetree if you have
a precompiled postgres around. But whats the usecase for that?

I would much rather designate one example module that is always built
with PGXS (possibly only during regress's install?), so we reliably test that.

I think I did about the same comment back when cooking the extension
patch, and the answer then was all about providing PGXS usage examples.
Now if none of the buildfarm animals are actually building our contribs
out of tree, maybe we should just remove those examples.

The cost of keeping them is that they double-up the Makefile content and
lots of users do think they need their extension's Makefile to be
structured the same. The common effect before the extension availability
was for people to provide extensions that would only build in tree.

I don't want to kill cleaning up those Makefiles, but I still want to
make a strong correlation in between that point and providing core
maintained extensions. I don't think extensions should have support for
being built in-tree at all.

Wait what? So I need to make install before I can compile extensions?
That doesn't seem to be something realistic.

My proposal: paint them extension rather than contrib modules, then
cleanup Makefiles so as to stop building them in-tree.

Imo painting them extensions is something unrelated. Which doesn't apply
to everything in contrib/, there are several modules that don't make
sense as extensions (chkpass, oid2name, passwordcheck,
pg_archivecleanup, pgbench, ...).

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Andres Freund
andres@2ndquadrant.com
In reply to: Tom Lane (#11)
Re: pg_xlogdump

On 2013-02-26 11:33:48 -0500, Tom Lane wrote:

[ Sigh... ] Why this eagerness to fix what isn't broken?

Well, it is broken for xlogdump because it needs a sourcetree arround.

All I personally really want to do is to replace the usual stanza for
pg_xlogdump with something like:

ifdef USE_PGXS
$(error Building pg_xlogdump with PGXS is not supported)
include $(PGXS)
else
...

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andres Freund (#14)
Re: pg_xlogdump

Andres Freund <andres@2ndquadrant.com> writes:

Well, it is broken for xlogdump because it needs a sourcetree arround.

All I personally really want to do is to replace the usual stanza for
pg_xlogdump with something like:

ifdef USE_PGXS
$(error Building pg_xlogdump with PGXS is not supported)
else
...

Seems reasonable. But let's not break the cases that do work. One
of the functions of contrib/ is to serve as models/skeletons for
external modules. If we pull out the "useless" PGXS support then we'll
just be making it that much harder to copy a contrib module and start
doing something useful.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Dimitri Fontaine
dimitri@2ndQuadrant.fr
In reply to: Andres Freund (#13)
Re: pg_xlogdump

Andres Freund <andres@2ndquadrant.com> writes:

Wait what? So I need to make install before I can compile extensions?
That doesn't seem to be something realistic.

You know, any extension that's not in our source tree out there is
maintained in a way to target all supported versions of PostgreSQL from
the same sources. I understand that we want to continue applying our
versioning rules to contribs, and I think that it's a good idea.

But now that we're making a real cut decision about that, I think
contribs should be documented as NOT extensions.

Regards,
--
Dimitri Fontaine
http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#15)
Re: pg_xlogdump

On 2/26/13 11:45 AM, Tom Lane wrote:

But let's not break the cases that do work. One
of the functions of contrib/ is to serve as models/skeletons for
external modules. If we pull out the "useless" PGXS support then we'll
just be making it that much harder to copy a contrib module and start
doing something useful.

Well, this is exactly the problem. Because of this skeleton idea, most
external extension modules do not build unless you set USE_PGXS=1 before
building, because they think that they live in contrib by default, which
is completely bizarre and user-unfriendly.

We could have an actual example or skeleton whose purpose is to teach
extension authors. The actual contrib module makefiles should just do
their job and don't pretend to teach things that are misguided and/or
don't work.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Peter Geoghegan
peter.geoghegan86@gmail.com
In reply to: Peter Eisentraut (#17)
Re: pg_xlogdump

On 26 February 2013 17:02, Peter Eisentraut <peter_e@gmx.net> wrote:

Well, this is exactly the problem. Because of this skeleton idea, most
external extension modules do not build unless you set USE_PGXS=1 before
building, because they think that they live in contrib by default, which
is completely bizarre and user-unfriendly.

repmgr is a popular external extension module. The README actually
suggests moving the entire source tree into /contrib as an alternative
to setting USE_PGXS=1. That does seem kind of weird, and yet I can
understand the train of thought.

My advice to others working on external modules would not be to
generalise from the example of /contrib, but to generalise from the
example of popular external modules. This is particularly important
when targeting multiple Postgres versions.

--
Regards,
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#17)
Re: pg_xlogdump

On Tue, Feb 26, 2013 at 12:02 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/26/13 11:45 AM, Tom Lane wrote:

But let's not break the cases that do work. One
of the functions of contrib/ is to serve as models/skeletons for
external modules. If we pull out the "useless" PGXS support then we'll
just be making it that much harder to copy a contrib module and start
doing something useful.

Well, this is exactly the problem. Because of this skeleton idea, most
external extension modules do not build unless you set USE_PGXS=1 before
building, because they think that they live in contrib by default, which
is completely bizarre and user-unfriendly.

We could have an actual example or skeleton whose purpose is to teach
extension authors. The actual contrib module makefiles should just do
their job and don't pretend to teach things that are misguided and/or
don't work.

I couldn't agree more. I can't think how much time I've wasted by
forgetting (or remembering) to type USE_PGXS=1 over the years, or not
realizing that I needed to. I'm sure there are many other people in
the same boat.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers