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
http://www.postgresql.org/message-id/[email protected]
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
*** /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
*** 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
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers