lesslog_contrib.patch
text/plain
Filename: lesslog_contrib.patch
Type: text/plain
Part: 1
Patch
Format: context
| File | + | − |
|---|---|---|
| contrib/lesslog/debug.c | 125 | 0 |
| contrib/lesslog/debug.h | 28 | 0 |
| contrib/lesslog/file.c | 180 | 0 |
| contrib/lesslog/file.h | 28 | 0 |
| contrib/lesslog/Makefile | 3 | 0 |
| contrib/lesslog/Makefile.pg_compresslog | 19 | 0 |
| contrib/lesslog/Makefile.pg_decompresslog | 19 | 0 |
| contrib/lesslog/pg_compresslog.c | 450 | 0 |
| contrib/lesslog/pg_decompresslog.c | 543 | 0 |
| contrib/lesslog/README.lesslog | 71 | 0 |
| contrib/Makefile | 1 | 0 |
| contrib/README | 4 | 0 |
diff -Ncr postgresql-8.2.1.org/contrib/Makefile postgresql-8.2.1/contrib/Makefile
*** postgresql-8.2.1.org/contrib/Makefile 2006-09-09 13:07:51.000000000 +0900
--- postgresql-8.2.1/contrib/Makefile 2007-04-06 17:14:21.000000000 +0900
***************
*** 16,21 ****
--- 16,22 ----
intagg \
intarray \
isn \
+ lesslog \
lo \
ltree \
oid2name \
diff -Ncr postgresql-8.2.1.org/contrib/README postgresql-8.2.1/contrib/README
*** postgresql-8.2.1.org/contrib/README 2006-09-09 13:07:52.000000000 +0900
--- postgresql-8.2.1/contrib/README 2007-04-06 17:14:21.000000000 +0900
***************
*** 68,73 ****
--- 68,77 ----
PostgreSQL type extensions for ISBN, ISSN, ISMN, EAN13 product numbers
by Germán Méndez Bravo (Kronuz) <kronuz@hotmail.com>
+ lesslog -
+ Reduce archive log file size by removing unnecessary physical log.
+ by Koichi Suzuki <suzuki.koichi@oss.ntt.co.jp>
+
lo -
Large Object maintenance
by Peter Mount <peter@retep.org.uk>
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/Makefile postgresql-8.2.1/contrib/lesslog/Makefile
*** postgresql-8.2.1.org/contrib/lesslog/Makefile 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/Makefile 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,3 ----
+ all install clean:
+ $(MAKE) -f Makefile.pg_compresslog $@
+ $(MAKE) -f Makefile.pg_decompresslog $@
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/Makefile.pg_compresslog postgresql-8.2.1/contrib/lesslog/Makefile.pg_compresslog
*** postgresql-8.2.1.org/contrib/lesslog/Makefile.pg_compresslog 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/Makefile.pg_compresslog 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,19 ----
+ PROGRAM = pg_compresslog
+ OBJS = pg_compresslog.o file.o debug.o
+
+ PG_CPPFLAGS = -I$(libpq_srcdir)
+ PG_LIBS = $(libpq_pgport) $(top_builddir)/src/backend/utils/hash/pg_crc.o
+
+ DOCS = README.lesslog
+
+ ifdef USE_PGXS
+ PGXS := $(shell pg_config --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_compresslog
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+
+ $(OBJS): Makefile.pg_compresslog
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/Makefile.pg_decompresslog postgresql-8.2.1/contrib/lesslog/Makefile.pg_decompresslog
*** postgresql-8.2.1.org/contrib/lesslog/Makefile.pg_decompresslog 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/Makefile.pg_decompresslog 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,19 ----
+ PROGRAM = pg_decompresslog
+ OBJS = pg_decompresslog.o file.o debug.o
+
+ PG_CPPFLAGS = -I$(libpq_srcdir)
+ PG_LIBS = $(libpq_pgport) $(top_builddir)/src/backend/utils/hash/pg_crc.o
+
+ DOCS =
+
+ ifdef USE_PGXS
+ PGXS := $(shell pg_config --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_decompresslog
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
+
+ $(OBJS): Makefile.pg_decompresslog
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/README.lesslog postgresql-8.2.1/contrib/lesslog/README.lesslog
*** postgresql-8.2.1.org/contrib/lesslog/README.lesslog 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/README.lesslog 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,71 ----
+ lesslog README 2006/04/06
+
+ ** What is lesslog?
+
+ lesslog is a set of tools to reduce the size of PostgreSQL archive log. lesslog consists of the following materials.
+
+ - pg_compresslog
+ This is a command to remove physical log records with "removable" mark.
+ This command should be specified as archive_command in postgresql.conf.
+ This command also removes page headers by changing page size from 8kB to
+ 16MB, which are restored by pg_decompresslog.
+
+ - pg_decompresslog
+ This command restores page headers and add dummy data to make up for
+ physical log record, finally restores LSN of each log record and restores
+ the page size to be used in the archive recovery. This command should be
+ specified as restore_command in recovery.conf.
+
+
+ ** How to use lesslog
+ 1. Build and install the additional tools.
+ Move to contrib/lesslog directory, then make and make install.
+ pg_compresslog and pg_decompresslog will be installed to PostgreSQL install
+ directory.
+ 2. Edit postgresql.conf
+ Edit postgresql.conf which is copied to DB cluster by initdb and edit
+ parameters as follows.
+
+ full_page_writes = on
+ wal_add_optimization_info = on
+ archive_command = 'pg_compresslog "%p" <archive directory>/"%f"'
+
+ ** How to use pg_compresslog
+ Synopsis
+ pg_compresslog [from [to]]
+
+ Explanation
+ pg_compresslog removes physical log from the WAL segment file specified by
+ <from> and archives as <to> file name.
+
+ if <from> is omitted or specfied as "-", it reads setment file from stdin.
+ If <to> is omitted or specified as "-", it means stdout.
+
+ Physical log records removed by pg_compresslog are those written while
+ online backup is not running and both full_page_writes and
+ wal_add_optimization_info are "on".
+
+ To use the output of pg_compresslog command in archive recovery, it must be
+ restored using pg_decompresslog command.
+
+ Return value
+ pg_compresslog returns zero if no error occurs, 0 if error occurs.
+
+ ** How to use pg_decompresslog
+ Synopsis
+ pg_decompresslog [from [to]]
+
+ Explanation
+ pg_decompresslog reads archive log file specified by <from> argument and
+ restores an area corresponds to the removed physical log, which restores
+ LSN of each log record, and writes them to the file specified by <to>
+ argument.
+
+ If <from> is omitted or specified as "-", it reads from stdin. If <to> is
+ omitted of specified as "-", it writes to stdout.
+
+ You can specifiy the file written by pg_compresslog as <from> argument.
+
+ Return value
+ It returns zero if no error occurs, 1 if error occurs.
+
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/debug.c postgresql-8.2.1/contrib/lesslog/debug.c
*** postgresql-8.2.1.org/contrib/lesslog/debug.c 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/debug.c 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,125 ----
+ /*
+ * debug.c
+ * Debug dump function implementation.
+ */
+ #include <stdio.h>
+ #include <errno.h>
+
+ #include "postgres.h"
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+
+ void get_segment_id(const char *filename);
+ void dump_record(XLogRecPtr *ptr, size_t off, XLogRecord *precord);
+ void dump_page_header(int num, XLogPageHeader pheader);
+ void dumpXLogRecord(XLogRecPtr *ptr, size_t off, XLogRecord *record);
+
+ /* Current segment ID */
+ static uint32 segment_id;
+
+ /* List for the resource manager. */
+ static const char * const RM_names[RM_MAX_ID + 1] = {
+ "XLOG ", /* 0 */
+ "XACT ", /* 1 */
+ "SMGR ", /* 2 */
+ "CLOG ", /* 3 */
+ "DBASE", /* 4 */
+ "TBSPC", /* 5 */
+ "MXACT", /* 6 */
+ "RM 7", /* 7 */
+ "RM 8", /* 8 */
+ "RM 9", /* 9 */
+ "HEAP ", /* 10 */
+ "BTREE", /* 11 */
+ "HASH ", /* 12 */
+ "RTREE", /* 13 */
+ "GIST ", /* 14 */
+ "SEQ " /* 15 */
+ };
+
+ /*
+ * Obtain segment ID from WAL segment file name.
+ *
+ * Parameters:
+ * filename: WAL segment file name.
+ *
+ * Note: If no slash mark (path delimiter) is included in the argument, or if
+ * the argument does not follow WAL segment file name format, nothing will
+ * happen.
+ */
+ void
+ get_segment_id(const char *filename)
+ {
+ TimeLineID tli;
+ uint32 xlogid;
+ char *p;
+ p = strrchr(filename, '/');
+ if (!p)
+ return;
+ p++;
+ if (sscanf(p, "%08X%08X%08X", &tli, &xlogid, &segment_id) != 3)
+ return;
+ }
+
+ /*
+ * Dump the page header content.
+ *
+ * Paramters:
+ * num: Page number to be included in the dump output.
+ * page: Target page.
+ */
+ void
+ dump_page_header(int num, XLogPageHeader page)
+ {
+ printf("=[%04d]==================================================\n", num);
+ printf("PAGE: xlp_magic=%02X\n", page->xlp_magic);
+ printf("PAGE: xlp_info=%02X\n", page->xlp_info);
+ printf("PAGE: xlp_tli=%u\n", page->xlp_tli);
+ printf("PAGE: xlogid=%u\n", page->xlp_pageaddr.xlogid);
+ printf("PAGE: xrecoff=%u\n", page->xlp_pageaddr.xrecoff);
+ if (page->xlp_info & XLP_FIRST_IS_CONTRECORD)
+ {
+ XLogContRecord *cont =
+ (XLogContRecord *)((char *)page + XLogPageHeaderSize(page));
+ printf("PAGE: rem_len=%u\n", cont->xl_rem_len);
+ }
+ printf("=========================================================\n");
+ }
+
+ /*
+ * Dump record header content in xlogdump format.
+ *
+ * Parameters:
+ * ptr: Record position information (only log ID will be used).
+ * off: Record offset within the segment.
+ * record: Pointer to the record.
+ *
+ * Note: the source is copied and modified using xlogdump source.
+ */
+ void
+ dumpXLogRecord(XLogRecPtr *ptr, size_t off, XLogRecord *record)
+ {
+ static XLogRecPtr prevRecPtr = { 0, 0};
+
+ printf("%u/%08X: prv %u/%08X",
+ ptr->xlogid, (uint32)off + segment_id * XLOG_SEG_SIZE,
+ record->xl_prev.xlogid, record->xl_prev.xrecoff);
+
+ if (!XLByteEQ(record->xl_prev, prevRecPtr))
+ printf("(?)");
+ prevRecPtr.xlogid = ptr->xlogid;
+ prevRecPtr.xrecoff = (uint32)off + segment_id * XLOG_SEG_SIZE;
+
+ printf("; xid %u; ", record->xl_xid);
+
+ if (record->xl_rmid <= RM_MAX_ID)
+ printf("%s", RM_names[record->xl_rmid]);
+ else
+ printf("RM %2d", record->xl_rmid);
+
+ printf(" info %02X len %u tot_len %u\n", record->xl_info,
+ record->xl_len, record->xl_tot_len);
+
+ fflush(stdout);
+ }
+
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/debug.h postgresql-8.2.1/contrib/lesslog/debug.h
*** postgresql-8.2.1.org/contrib/lesslog/debug.h 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/debug.h 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,28 ----
+ /*
+ * debug.h
+ * Interface for debug dump function.
+ */
+ #ifndef DEBUG_H_INCLUDED
+ #define DEBUG_H_INCLUDED
+
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+
+ /*
+ * In the release, debug function call itself will be eliminated.
+ */
+ #ifdef DEBUG
+
+ void get_segment_id(const char *filename);
+ void dump_page_header(int num, XLogPageHeader pheader);
+ void dumpXLogRecord(XLogRecPtr *ptr, size_t off, XLogRecord *record);
+
+ #else
+
+ #define get_segment_id(a)
+ #define dump_page_header(a, b)
+ #define dumpXLogRecord(a, b, c)
+
+ #endif
+
+ #endif
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/file.c postgresql-8.2.1/contrib/lesslog/file.c
*** postgresql-8.2.1.org/contrib/lesslog/file.c 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/file.c 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,180 ----
+ /*
+ * file.c
+ * Common I/O routine implementation used in archive/restoration
+ */
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <errno.h>
+
+ #include "postgres.h"
+ #include "access/xlog_internal.h"
+
+ #include "file.h"
+
+ /*
+ * Read the file data and return the size actually read.
+ * The length to read is specified by an argument.
+ *
+ * Parameters:
+ * fd: File descriptor.
+ * buff: Buffer to read.
+ * len: Size to read.
+ *
+ * Note: If error occurd, exit(2) will be called here and will not return to the
+ * caller in this case.
+ */
+ int
+ read_buff(int fd, char *buff, size_t len)
+ {
+ int ret;
+ size_t read_len = 0;
+
+ do
+ {
+ ret = read(fd, buff + read_len, len - read_len);
+ if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "failed to read : %s\n", strerror(errno));
+ exit(1);
+ }
+ else if (ret == 0)
+ break;
+ read_len += ret;
+ } while (read_len < len);
+
+ return read_len;
+ }
+
+ /*
+ * Write to the file and return length actually written.
+ * The length to write should be specified by an argument.
+ *
+ * Parameter:
+ * fd: File descriptor.
+ * buff: Buffer to write.
+ * len: Size to write.
+ *
+ * Note: If an error occurs, exit(2) will be called in this function and will
+ * not return to the caller in this case.
+ */
+ void
+ write_buff(int fd, const char *buff, size_t len)
+ {
+ int ret;
+ int written_len = 0;
+
+ do
+ {
+ ret = write(fd, buff + written_len, len - written_len);
+ if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "failed to write : %s\n", strerror(errno));
+ exit(1);
+ }
+ written_len += ret;
+ } while (written_len < len);
+ }
+
+ /*
+ * Copy the contents of the file.
+ *
+ * Parameter:
+ * from_fd: File descriptor of the file to copy from.
+ * to_fd: File descriptor of the file to copy to.
+ *
+ * Note: If error occurs in this function, exit(2) will be called here and will
+ * not return to the caller in this case.
+ */
+ void
+ copy_file(int from_fd, int to_fd)
+ {
+ int read_len = 0;
+ char buff[8 * 1024]; /* 8KB buffer */
+
+ while (1)
+ {
+ /* Read to the buffer. */
+ read_len = read(from_fd, buff, sizeof(buff));
+ if (read_len < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "failed to read : %s\n", strerror(errno));
+ exit(1);
+ }
+ else if (read_len == 0)
+ break;
+ /* Write all the buffer content. */
+ write_buff(to_fd, buff, read_len);
+ }
+
+ return;
+ }
+
+ /*
+ * Validate the record by comparing CRC value.
+ *
+ * CRC value will be calculated in the following order.
+ * - Logical Log
+ * - Full page write (if exists)
+ * - Record header (exluding the CRC area)
+ *
+ * Parameters:
+ * precord: Pointer to the target record.
+ */
+ bool
+ is_valid_record(XLogRecord *precord)
+ {
+ pg_crc32 crc;
+ BkpBlock *pblk;
+ int i;
+
+ /* Calculate CRC for a logical log. */
+ INIT_CRC32(crc);
+ COMP_CRC32(crc, XLogRecGetData(precord), precord->xl_len);
+
+ /*
+ * If full page writes exist, calculate CRC for each full page write.
+ */
+ pblk = (BkpBlock *)((char *)XLogRecGetData(precord) + precord->xl_len);
+ for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++)
+ {
+ uint32 blen;
+
+ if (!(precord->xl_info & XLR_SET_BKP_BLOCK(i)))
+ continue;
+
+ if (pblk->hole_offset + pblk->hole_length > BLCKSZ)
+ {
+ fprintf(stderr, "incorrect hole size in record.\n");
+ return false;
+ }
+
+ blen = sizeof(BkpBlock) + BLCKSZ - pblk->hole_length;
+ COMP_CRC32(crc, (char *)pblk, blen);
+ pblk = (BkpBlock *)((char *)pblk + blen);
+ }
+
+ /* Calculate record header CRC value. */
+ COMP_CRC32(crc, (char *)precord + sizeof(pg_crc32),
+ SizeOfXLogRecord - sizeof(pg_crc32));
+
+ /* Examine if the final CRC is the same as the value found in the record. */
+ FIN_CRC32(crc);
+ if (!EQ_CRC32(precord->xl_crc, crc))
+ {
+ fprintf(stderr, "incorrect resource manager data checksum.\n");
+ return false;
+ }
+
+ return true;
+ }
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/file.h postgresql-8.2.1/contrib/lesslog/file.h
*** postgresql-8.2.1.org/contrib/lesslog/file.h 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/file.h 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,28 ----
+ /*
+ * file.h
+ * Common file I/O routines for pg_archive and pg_restore.
+ */
+ #ifndef FILE_H_INCLUDED
+ #define FILE_H_INCLUDED
+
+ #include "postgres.h"
+ #include "access/xlog.h"
+
+ int read_buff(int fd, char *buff, size_t len);
+ void write_buff(int fd, const char *buff, size_t len);
+ void copy_file(int from_fd, int to_fd);
+ bool is_valid_record(XLogRecord *precord);
+
+ /*
+ * Check if the page header in the buffer is valid.
+ */
+ #define IS_WAL_FILE(buff) \
+ (((XLogPageHeader)(buff))->xlp_magic == XLOG_PAGE_MAGIC)
+
+ /*
+ * Check if the record is log switch WAL record.
+ */
+ #define IS_XLOG_SWITCH(rec) \
+ ((rec)->xl_rmid == RM_XLOG_ID && (rec)->xl_info == XLOG_SWITCH)
+
+ #endif /* !FILE_H_INCLUDED */
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/pg_compresslog.c postgresql-8.2.1/contrib/lesslog/pg_compresslog.c
*** postgresql-8.2.1.org/contrib/lesslog/pg_compresslog.c 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/pg_compresslog.c 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,450 ----
+ /*
+ * pg_compresslog.c
+ * Implementation of the archive command (pg_compresslog).
+ */
+ #include <stdio.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+ #include <errno.h>
+
+ #include "postgres.h"
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+
+ #include "file.h"
+ #include "debug.h"
+
+ /* =============================================================================
+ * Global variables
+ * ===========================================================================*/
+
+ /* Buffer to read WAL segment file. */
+ static char xlog_buff[XLogSegSize];
+ /* Buffer to hold archive log file image with physical log removed. */
+ static char arch_buff[XLogSegSize];
+
+ static int cont_log_size; /* Log size considering the former segment. */
+ static int logical_log_size; /* Total size of the logical log. */
+ static int physical_log_size; /* Total size of the physical log. */
+
+ /* =============================================================================
+ * Prototype declaration
+ * ===========================================================================*/
+ static void print_usage(int code);
+ static int open_xlog_file(int argc, char *argv[]);
+ static int open_arch_file(int argc, char *argv[]);
+ int create_arch_image(const char *from, char *to);
+ static bool remove_bkp_block(XLogRecord *record);
+
+ /* =============================================================================
+ * Macros
+ * ===========================================================================*/
+ /* Check if the physical log can be removed. */
+ #define IS_REMOVABLE(record) \
+ (((record)->xl_info & XLR_BKP_BLOCK_MASK) && \
+ ((record)->xl_info & XLR_BKP_REMOVABLE))
+
+ /* =============================================================================
+ * Function definitions
+ * ===========================================================================*/
+ /*
+ * Entry point of pg_compresslog command.
+ */
+ int
+ main(int argc, char *argv[])
+ {
+ int from_fd = -1;
+ int to_fd = -1;
+ size_t xlog_len;
+ size_t arch_len;
+
+ /* Error if there are more argument(s) other than from and to. */
+ if (argc > 3)
+ print_usage(1);
+
+ /* Open WAL segment file to archive. */
+ from_fd = open_xlog_file(argc, argv);
+
+ /*
+ * If input file is not stdin, check the size of the file.
+ * If the size is not 16MB, then the specified file is not the WAL
+ * segment file. We're not sure if we can scan the file to find
+ * removable physical log, copy the whole file and then exits.
+ */
+ if (from_fd != fileno(stdin))
+ {
+ struct stat st;
+
+ if (fstat(from_fd, &st) < 0)
+ {
+ fprintf(stderr, "failed to stat `%s': %s\n", argv[1],
+ strerror(errno));
+ exit(1);
+ }
+ if (st.st_size != XLogSegSize)
+ {
+ to_fd = open_arch_file(argc, argv);
+ copy_file(from_fd, to_fd);
+ exit(0);
+ }
+ }
+
+ /*
+ * Read all the data from WAL segment file to archive.
+ * If the amount of the data is not sufficient (less than 16MB: XLogSegSize)
+ * or header is not valid, specified input file is not a WAL segment file.
+ * Copy the whole input to the output and then exit.
+ */
+ xlog_len = read_buff(from_fd, xlog_buff, XLogSegSize);
+ if (xlog_len != XLogSegSize || !IS_WAL_FILE(xlog_buff))
+ {
+ /* Write the checked header part and then copy the rest of the input file. */
+ to_fd = open_arch_file(argc, argv);
+ write_buff(to_fd, xlog_buff, xlog_len);
+ copy_file(from_fd, to_fd);
+ if (close(from_fd) < 0)
+ {
+ fprintf(stderr, "failed to close `%s': %s\n", argv[1],
+ strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ if (close(from_fd) < 0)
+ {
+ fprintf(stderr, "failed to close `%s': %s\n", argv[1], strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Build the entire compressed output file image on the buffer,
+ * removing physical logs, then write the whole compressed file image.
+ */
+ arch_len = create_arch_image(xlog_buff, arch_buff);
+ to_fd = open_arch_file(argc, argv);
+ write_buff(to_fd, arch_buff, arch_len);
+ if (close(to_fd) < 0)
+ {
+ fprintf(stderr, "failed to close `%s': %s\n", argv[2], strerror(errno));
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ /*
+ * Show the usage of the command and then exits with specified code.
+ */
+ static void
+ print_usage(int code)
+ {
+ printf(
+ "usage: pg_compresslog [from [to]]\n"
+ " from - Input file name (stdin if omitted or '-' is given)\n"
+ " to - Output file name (stdiout if omitted or '-' is given)\n"
+ );
+ exit(code);
+ }
+
+ /*
+ * Build archive log file image of 16MB, from WAL segment buffer image of
+ * 8kB pages. And return size of archive log file image.
+ *
+ * Parameters:
+ * from: WAL segment buffer page
+ * to: Archive log file image
+ *
+ * Note: exit() will be called here when a error is detected. In the case of
+ * error, the control will not be given to the caller.
+ */
+ int
+ create_arch_image(const char *from, char *to)
+ {
+ const char *read_pos = from;
+ char *write_pos = to;
+ const char *crrpage = from;
+ XLogPageHeader page = (XLogPageHeader)from;
+ XLogRecord *rec = NULL;
+ XLogRecord *write_rec = NULL;
+
+ /*
+ * Copy the first page header of the segment to the buffer.
+ * If the record is the successor of the last record of the former segment,
+ * then copies XLogContRecord too.
+ * XLogContRecord.xl_rem_len means the total data length of the
+ * continuation record, not the length of the record in the given page.
+ * Therefore, this value is not influenced by the change of the page size.
+ */
+ read_pos = crrpage = from;
+ memcpy(write_pos, read_pos, XLogPageHeaderSize(page));
+ write_pos += XLogPageHeaderSize(page);
+ read_pos += XLogPageHeaderSize(page);
+ if (page->xlp_info & XLP_FIRST_IS_CONTRECORD)
+ {
+ memcpy(write_pos, read_pos, SizeOfXLogContRecord);
+ write_pos += SizeOfXLogContRecord;
+ }
+
+ /*
+ * Loop page by page.
+ */
+ for (crrpage = from; crrpage < from + XLogSegSize; crrpage += XLOG_BLCKSZ)
+ {
+ XLogRecPtr ptr;
+
+ /* Parse the page header. */
+ page = (XLogPageHeader)crrpage;
+ read_pos = crrpage + XLogPageHeaderSize(page);
+ ptr = page->xlp_pageaddr;
+
+ /* If there is a continuous data, copy them to the write buffer. */
+ if (page->xlp_info & XLP_FIRST_IS_CONTRECORD)
+ {
+ int cont_len = ((XLogContRecord *)read_pos)->xl_rem_len;
+ int copy_len = cont_len;
+ int free_len = XLOG_BLCKSZ -
+ (read_pos + SizeOfXLogContRecord - crrpage);
+ /*
+ * Copy the continuous data within this page.
+ * xl_rem_len specifies the length of the continuous data after this page,
+ * so this may be larger than the length of the rest of this page.
+ */
+ if (copy_len > free_len)
+ copy_len = free_len;
+ memcpy(write_pos, read_pos + SizeOfXLogContRecord, copy_len);
+ read_pos += MAXALIGN(SizeOfXLogContRecord + copy_len);
+ write_pos += copy_len;
+ if (!rec)
+ cont_log_size += copy_len;
+
+ /*
+ * If the data continues to the next page and no record header
+ * exists in this file, then switch to the next page.
+ */
+ if (cont_len != copy_len)
+ continue;
+
+ /*
+ * Set the write position to the end of the current record,
+ * considering alignment.
+ */
+ write_pos = to + MAXALIGN(write_pos - to);
+
+ /*
+ * If the record should have a header in this segment (not a continuous
+ * record from the last segment), perform CRC check and check if
+ * physical log record can be removed.
+ */
+ if (write_rec)
+ {
+ /* Check if the record is valid. */
+ if (!is_valid_record(write_rec))
+ exit(1);
+
+ /*
+ * Determine if the physical log can be removed.
+ * If it can be removed, then rewind the position for the next log record
+ * to the position of the physical log (plus padding).
+ */
+ if (remove_bkp_block(write_rec))
+ write_pos = (char *)write_rec +
+ MAXALIGN(SizeOfXLogRecord + rec->xl_len);
+ }
+ }
+
+ /* Read the data within the page record by record. */
+ while(read_pos <= crrpage + XLOG_BLCKSZ - SizeOfXLogRecord)
+ {
+ int freespace = XLOG_BLCKSZ - (read_pos - crrpage);
+
+ /* Obtain the record header info. */
+ rec = (XLogRecord *)read_pos;
+ write_rec = (XLogRecord *)write_pos;
+ logical_log_size += rec->xl_len;
+ physical_log_size +=
+ rec->xl_tot_len - (SizeOfXLogRecord + rec->xl_len);
+ dumpXLogRecord(&ptr, read_pos - from, rec);
+
+ /*
+ * If the record continues to the following pages, copy only the portion
+ * in this page and then switch to the next page.
+ */
+ if (rec->xl_tot_len > freespace)
+ {
+ /* Copy the log data only in the current page. */
+ memcpy(write_pos, read_pos, freespace);
+ /* read_pos will be overwritten at the next loop. We don't need to update this here. */
+ write_pos += freespace;
+ break;
+ }
+
+ /* Copy the record data to the archive buffer. */
+ memcpy(write_pos, read_pos, rec->xl_tot_len);
+ read_pos += MAXALIGN(rec->xl_tot_len);
+ write_pos += MAXALIGN(rec->xl_tot_len);
+
+ /* Check if the record is valid using CRC in the record header. */
+ if (!is_valid_record(write_rec))
+ exit(1);
+
+ /*
+ * Log record other than log switch must have it's logical data.
+ * See the comment around the line 3065 of src/backend/access/transam/xlog.c
+ * (8.2.0).a
+ */
+ if (IS_XLOG_SWITCH(write_rec))
+ {
+ if (write_rec->xl_len != 0)
+ {
+ fprintf(stderr, "invalid xlog switch record.\n");
+ exit(1);
+ }
+ }
+ else if (write_rec->xl_len == 0)
+ {
+ fprintf(stderr, "invalid record length.\n");
+ exit(1);
+ }
+
+ /* If the log record is the log switch record, then no more log record exists
+ * in * the input file. Exit.
+ */
+ if (IS_XLOG_SWITCH(write_rec))
+ return write_pos - to;
+
+ /*
+ * If the physical log is removable, then rewind the position of the next
+ * record to
+ * the physical log start position (and padding).
+ */
+ if (remove_bkp_block(write_rec))
+ write_pos = (char *)write_rec +
+ MAXALIGN(SizeOfXLogRecord + write_rec->xl_len);
+ else
+ write_pos = to + MAXALIGN(write_pos - to);
+
+ }
+ }
+
+ return (write_pos - to);
+ }
+
+ /*
+ * Remove the physical log which was marked `REMOVABLE'.
+ * Return true if the physical record has been removed, false otherwise.
+ */
+ static bool
+ remove_bkp_block(XLogRecord *record)
+ {
+ pg_crc32 crc;
+
+ /*
+ * If no record is specified or the physical log is not removable, just
+ * return.
+ */
+ if (!record || !IS_REMOVABLE(record))
+ return false;
+
+ /*
+ * Reset XLR_BKP_BLOCK_MASK.
+ * We need the flag to show the physical log is removable to restore
+ * removed physical log with a dummy. It is not reset.
+ */
+ record->xl_info &= ~XLR_BKP_BLOCK_MASK;
+
+ /*
+ * Record contents changes by physical log removal and CRC has to be
+ * recalculated.
+ * CRC will be accumulated as follows:
+ * 1. Logical log
+ * 2. Physical log (It has ben removed and we don't calculate its CRC here).
+ * 3. WAL record header excluding CRC part
+ * Please refer to the line 2817 of RecordIsValid(), src/backend/access/transam/xlog.c.
+ */
+ INIT_CRC32(crc);
+ COMP_CRC32(crc, XLogRecGetData(record), record->xl_len);
+ COMP_CRC32(crc, (char *)record + sizeof(pg_crc32),
+ SizeOfXLogRecord - sizeof(pg_crc32));
+ FIN_CRC32(crc);
+ record->xl_crc = crc;
+
+ return true;
+ }
+
+ /*
+ * Open the WAL segment file to archive and return file descriptor.
+ *
+ * The first argument of pg_compresslog will be regarded as an input file.
+ * If omitted or specified as "-", stdin will be used as an input file.
+ *
+ * Parameters:
+ * argc: Number of arguments (argument to main() will be passed as is).
+ * argv: Array of pointers to argument strings (argument to main() will be
+ * passed as is).
+ *
+ * Note: exit() will be called here if error occurs. Will not return to the
+ * caller in this case.
+ */
+ static int
+ open_xlog_file(int argc, char *argv[])
+ {
+ int from_fd = -1;
+
+ if (argc > 1 && strcmp(argv[1], "-") != 0)
+ {
+ /* Open WAL segment file to archive. */
+ from_fd = open(argv[1], O_RDONLY, 0);
+ if (from_fd < 0)
+ {
+ fprintf(stderr, "failed to open `%s': %s\n", argv[1],
+ strerror(errno));
+ exit(1);
+ }
+
+ /* Obtain segment ID from the file name (for record dump). */
+ get_segment_id(argv[1]);
+ }
+ else
+ from_fd = fileno(stdin);
+
+ return from_fd;
+ }
+
+ /*
+ * Open the archive segment file to write the result and return file descriptor.
+ *
+ * The second argument to pg_compresslog will be regarded as an output file.
+ * If omitted or specified as "-", stdout will be used as an output file.
+ *
+ * Parameters:
+ * argc: Number of arguments (argument to main() will be passed as is).
+ * argv: Array of pointers to argument strings (argument to main() will be
+ *
+ * Note: When an error occurs within this function, exit() will be called here
+ * and will not return to the caller in this case.
+ */
+ static int
+ open_arch_file(int argc, char *argv[])
+ {
+ int to_fd = -1;
+
+ if (argc > 2 && strcmp(argv[2], "-") != 0)
+ {
+ /* Open the archive log file. */
+ to_fd = open(argv[2], O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
+ S_IRUSR | S_IWUSR);
+ if (to_fd < 0)
+ {
+ fprintf(stderr, "failed to open `%s': %s\n", argv[2],
+ strerror(errno));
+ exit(1);
+ }
+ }
+ else
+ to_fd = fileno(stdout);
+
+ return to_fd;
+ }
diff -Ncr postgresql-8.2.1.org/contrib/lesslog/pg_decompresslog.c postgresql-8.2.1/contrib/lesslog/pg_decompresslog.c
*** postgresql-8.2.1.org/contrib/lesslog/pg_decompresslog.c 1970-01-01 09:00:00.000000000 +0900
--- postgresql-8.2.1/contrib/lesslog/pg_decompresslog.c 2007-04-06 17:14:21.000000000 +0900
***************
*** 0 ****
--- 1,543 ----
+ /*
+ * file pg_decompresslog.c
+ * Implementation of the archive restore command (pg_decompresslog).
+ */
+ #include <stdio.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <unistd.h>
+
+ #include "postgres.h"
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+
+ #include "file.h"
+ #include "debug.h"
+
+ /* =============================================================================
+ * Global variables
+ * ===========================================================================*/
+
+ /* Buffer to hold restored WAL segment file image. */
+ static char xlog_buff[XLogSegSize];
+ /* Buffer to read an archive log file. */
+ static char arch_buff[XLogSegSize];
+ /* Position to write a record data. */
+ static char *write_pos = xlog_buff;
+ /* Position to read record data in the archive log buffer. */
+ static char *read_pos = arch_buff;
+ /* This holds data in the first page header of the segment. */
+ static XLogPageHeaderData baseheader;
+
+ /* =============================================================================
+ * Prototype declaration
+ * ===========================================================================*/
+ static void print_usage(int code);
+ int create_wal_image(int arch_len);
+ static int write_record(char *record_buff, int rem_len, bool isFromPrevSeg);
+ static int get_freespace(void);
+ static void insert_XLogContRecord(char *write_pos, int rem_len);
+ static void insert_pageheader(char *write_pos, XLogPageHeader pheader,
+ bool hasContRecord);
+ static int open_arch_file(int argc, char *argv[]);
+ static int open_xlog_file(int argc, char *argv[]);
+
+ /* =============================================================================
+ * Function definitions
+ * ===========================================================================*/
+
+ /*
+ * Entry point of pg_decompresslog command.
+ */
+ int
+ main(int argc, char *argv[])
+ {
+ int from_fd = -1;
+ int to_fd = -1;
+ size_t arch_len;
+
+ /* Error if argument(s) other than <from>, and <to> are given. */
+ if (argc > 3)
+ print_usage(1);
+
+ /* Open the archive log file to restore. */
+ from_fd = open_arch_file(argc, argv);
+
+ /*
+ * Read all the data in the input archive log file.
+ * If the header at the first page is not valid, it is not a WAL segment file
+ * and then copy the whole input file to the output file.
+ */
+ arch_len = read_buff(from_fd, arch_buff, XLogSegSize);
+ if (!IS_WAL_FILE(arch_buff))
+ {
+ /* Write what is read for header validation check and then copy the rest of the input file. */
+ to_fd = open_xlog_file(argc, argv);
+ write_buff(to_fd, arch_buff, arch_len);
+ copy_file(from_fd, to_fd);
+ if (close(from_fd) < 0)
+ {
+ fprintf(stderr, "failed to close `%s': %s\n", argv[1],
+ strerror(errno));
+ exit(1);
+ }
+ exit(0);
+ }
+ if (close(from_fd) < 0)
+ {
+ fprintf(stderr, "failed to close `%s': %s\n", argv[1], strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Build the restored WAL segment file image.
+ * Write all the restored WAL segment file image.
+ */
+ if (create_wal_image((int)arch_len))
+ {
+ fprintf(stderr, "failed to create the image of `%s'\n", argv[1]);
+ exit(1);
+ }
+ to_fd = open_xlog_file(argc, argv);
+ write_buff(to_fd, xlog_buff, XLogSegSize);
+ if (close(to_fd) < 0)
+ {
+ fprintf(stderr, "failed to close `%s': %s\n", argv[2], strerror(errno));
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ /*
+ * Show the usage of the command and exit with specified code.
+ */
+ static void
+ print_usage(int code)
+ {
+ printf(
+ "usage: pg_decompresslog [from [to]]\n"
+ " from - Iput file name (stdin if omitted or specified as '-')\n"
+ " to - Output file name (stdout if omitted or specified as '-')\n"
+ );
+ exit(code);
+ }
+
+ /*
+ * Restore 8KB page WAl segment file image from 16MB page archive log build by
+ * pg_compresslog command.
+ *
+ * Parameters:
+ * arch_len: Size of the archive log file.
+ */
+ int
+ create_wal_image(int arch_len)
+ {
+ /* Buffer holding one record data. */
+ static char record_buff[XLogSegSize];
+ XLogPageHeader pheader;
+ XLogContRecord *pcontrec = NULL;
+ XLogRecord *precord = NULL;
+ char *rec_write_pos = record_buff;
+ int rec_len = 0;
+ bool isFromPrevSeg = false;
+
+ /*
+ * Copy the archive log file page header and hold info in the header.
+ * They are used to restore page headers of WAL segment file.
+ */
+ pheader = (XLogPageHeader)arch_buff;
+ if (XLogPageHeaderSize(pheader) != SizeOfXLogLongPHD)
+ {
+ fprintf(stderr, "invalid pageheader size.\n");
+ return -1;
+ }
+ memcpy(write_pos, (char *)pheader, SizeOfXLogLongPHD);
+ read_pos += SizeOfXLogLongPHD;
+ write_pos += SizeOfXLogLongPHD;
+
+ baseheader.xlp_magic = pheader->xlp_magic;
+ baseheader.xlp_info &= ~XLP_ALL_FLAGS;
+ baseheader.xlp_tli = pheader->xlp_tli;
+ baseheader.xlp_pageaddr.xlogid = pheader->xlp_pageaddr.xlogid;
+ baseheader.xlp_pageaddr.xrecoff = pheader->xlp_pageaddr.xrecoff;
+
+ /*
+ * Copy XLogContRecord and the continuous record to the record buffer, if there is
+ * a continuous record from the last segment file. Then move them to WAL segment
+ * file image buffer.
+ */
+ if (pheader->xlp_info & XLP_FIRST_IS_CONTRECORD)
+ {
+ pcontrec = (XLogContRecord *)read_pos;
+
+ /* If the size of the continue record is not valid, it's an error. */
+ if (pcontrec->xl_rem_len == 0)
+ {
+ printf("invalid continue record length : xl_rem_len = %u\n",
+ pcontrec->xl_rem_len);
+ return -1;
+ }
+
+ memcpy(write_pos, read_pos, SizeOfXLogContRecord);
+ write_pos += SizeOfXLogContRecord;
+ rec_len = pcontrec->xl_rem_len;
+ memcpy(rec_write_pos, (read_pos + SizeOfXLogContRecord), rec_len);
+ read_pos += MAXALIGN(SizeOfXLogContRecord + rec_len);
+ isFromPrevSeg = true;
+
+ /* Write the continuous data to WAL segment file image buffer. */
+ if (write_record(record_buff, rec_len, isFromPrevSeg))
+ return 0;
+ }
+ isFromPrevSeg = false;
+ dump_page_header(((write_pos - xlog_buff) / XLOG_BLCKSZ),
+ (XLogPageHeader)xlog_buff);
+
+ /*
+ * Loop record by record, and build each record image in the record buffer.
+ */
+ while ((read_pos - arch_buff) < arch_len)
+ {
+ /* Set the write position of the record data. */
+ rec_write_pos = record_buff;
+ precord = (XLogRecord *)read_pos;
+
+ /*
+ * If the record data fits in the current segment, validate the record.
+ * If WAL record cotinues to the next segment, we cannot calculate CRC for
+ * the whole record and skip the validation.
+ */
+ if ((char *)precord - arch_buff + precord->xl_tot_len <= arch_len)
+ if (!is_valid_record(precord))
+ exit(1);
+
+ /*
+ * Record other than the log switch must have corresponding logical data.
+ * Refer to the comment around the line 3056 in src/backend/access/transam/xlog.c (8.2.0).
+ */
+ if (IS_XLOG_SWITCH(precord))
+ {
+ if (precord->xl_len != 0)
+ {
+ fprintf(stderr, "invalid xlog switch record.\n");
+ exit(1);
+ }
+ }
+ else if (precord->xl_len == 0)
+ {
+ fprintf(stderr, "invalid record length.\n");
+ exit(1);
+ }
+
+ /*
+ * Copy the record header and the logical log to the record buffer.
+ * We don't move read_pos here to cauculate the alignment considering
+ * physical log.
+ */
+ memcpy(rec_write_pos, read_pos, (SizeOfXLogRecord + precord->xl_len));
+ rec_write_pos += (SizeOfXLogRecord + precord->xl_len);
+ rec_len = precord->xl_tot_len;
+
+ /* Copy the physical log, or restore it. */
+ if (precord->xl_tot_len > SizeOfXLogRecord + precord->xl_len)
+ {
+ /*
+ * If physical log does exist (not removed), then simply copy it.
+ * If physical log is removed, then build a dummy.
+ */
+ if (precord->xl_info & XLR_BKP_BLOCK_MASK)
+ {
+ memcpy(rec_write_pos,
+ XLogRecGetData((XLogRecord *)read_pos) + precord->xl_len,
+ precord->xl_tot_len - (SizeOfXLogRecord + precord->xl_len));
+ read_pos += MAXALIGN(precord->xl_tot_len);
+ }
+ else
+ {
+ /*
+ * Because full page write flag will be omitted during the archiving, CRC check
+ * should be performed only against the record header and the logical log.
+ * Therefore, we don't have to recalculate CRC value here.
+ */
+ memset(rec_write_pos, '\0',
+ precord->xl_tot_len - (SizeOfXLogRecord + precord->xl_len));
+ read_pos += MAXALIGN(SizeOfXLogRecord + precord->xl_len);
+ }
+ }
+ else
+ {
+ /* If theres not physical log in the original log, simply updates the input position. */
+ read_pos += MAXALIGN(precord->xl_tot_len);
+ }
+
+ /*
+ * Write a record image to the WAL segment image buffer. Hey, the page size
+ * is again 8kB as the original WAL.
+ */
+ if (write_record(record_buff, rec_len, isFromPrevSeg))
+ break;
+
+ }
+
+ return 0;
+ }
+
+ /*
+ * Build a record image to fit in 8kB page to WAL segment image buffer.
+ *
+ * Return value:
+ * 0: One record data build complete.
+ * 1: Hit the tail of the segment.
+ */
+ static int
+ write_record(char *record_buff, int rem_len, bool isFromPrevSeg)
+ {
+ char *phead_pos = NULL;
+ char *rec_read_pos = record_buff; /* Read position in the record buffer. */
+ char *rec_head_pos = NULL;
+ int freespace; /* Size of the free space in the page. */
+ bool hasContRecord = isFromPrevSeg;
+
+ /*
+ * Hold the position of the record header (or XLogContRecord).
+ * It is needed for an alignment.
+ */
+ rec_head_pos = write_pos; /* Set the write position of the record data. */
+ if (isFromPrevSeg)
+ rec_head_pos -= SizeOfXLogContRecord;
+
+ freespace = get_freespace();
+
+ /*
+ * If free space size is the same as the page size, it means that the last record restoration
+ * filled the last page. So we add page header here.
+ * Because the record is complete in the last page, we don't need XLogContRecord.
+ * Page header at the top of the segment must have had written at the first call of this function.
+ * So we always add short format header here.
+ */
+ if (freespace == XLOG_BLCKSZ)
+ {
+ phead_pos = write_pos;
+ insert_pageheader(write_pos, &baseheader, false);
+ write_pos += SizeOfXLogShortPHD;
+ freespace = (XLOG_BLCKSZ - SizeOfXLogShortPHD);
+ rec_head_pos = write_pos;
+ dump_page_header(((write_pos - xlog_buff) / XLOG_BLCKSZ),
+ (XLogPageHeader)phead_pos);
+ }
+
+ /*
+ * Loop page by page.
+ */
+ while(1)
+ {
+ /*
+ * If the record header does not fit the page, then insert a page header to the next
+ * page and copy the record data.
+ */
+ if (!hasContRecord && freespace < SizeOfXLogRecord)
+ {
+ write_pos += freespace;
+ }
+ else if (freespace < rem_len)
+ {
+ /*
+ * If the record data does not fit the page, fill this page with the former
+ * part of the record, copy the rest to the next page, insert a page header
+ * and XLogContRecord.
+ * the next page.
+ */
+ memcpy(write_pos, rec_read_pos, freespace);
+ if (!hasContRecord)
+ dumpXLogRecord(&baseheader.xlp_pageaddr,
+ (size_t)(rec_head_pos - xlog_buff),
+ (XLogRecord *)rec_head_pos);
+ write_pos += freespace;
+ rec_read_pos += freespace;
+ rem_len -= freespace;
+ hasContRecord = true;
+ }
+ else
+ {
+ /*
+ * If th recor data fits to the page, copy the whole record data to the
+ * buffer and switch to the next record.
+ */
+ int len;
+ memcpy(write_pos, rec_read_pos, rem_len);
+ if (!hasContRecord)
+ dumpXLogRecord(&baseheader.xlp_pageaddr,
+ (size_t)(rec_head_pos - xlog_buff),
+ (XLogRecord *)rec_head_pos);
+
+ /*
+ * Alignment handling.
+ * Alignment has to be adjusted for each record.
+ */
+ if (hasContRecord)
+ len = MAXALIGN(SizeOfXLogContRecord + rem_len);
+ else
+ len = MAXALIGN(rem_len);
+ write_pos = rec_head_pos + len;
+ hasContRecord = false;
+
+ break;
+ }
+
+ /*
+ * Insert a page header.
+ * If the start of the page is a continuous data from the last page,
+ * insert XLogContRecor too.
+ */
+ if ((write_pos - xlog_buff) >= XLogSegSize)
+ return 1;
+ phead_pos = write_pos;
+ insert_pageheader(write_pos, &baseheader, hasContRecord);
+ write_pos += SizeOfXLogShortPHD;
+ freespace = (XLOG_BLCKSZ - SizeOfXLogShortPHD);
+ rec_head_pos = write_pos;
+ if (hasContRecord)
+ {
+ insert_XLogContRecord(write_pos, rem_len);
+ write_pos += SizeOfXLogContRecord;
+ freespace -= SizeOfXLogContRecord;
+ }
+ dump_page_header(((write_pos - xlog_buff) / XLOG_BLCKSZ),
+ (XLogPageHeader)phead_pos);
+ }
+ return 0;
+ }
+
+ /*
+ * Calculate free space size of the page.
+ */
+ static int
+ get_freespace(void)
+ {
+ return XLOG_BLCKSZ - (write_pos - xlog_buff) % XLOG_BLCKSZ;
+ }
+
+ /*
+ * Insert a XLogContRecord to the buffer.
+ *
+ * Parameters:
+ * write_pos: Write position in the buffer.
+ * rem_len: Length of the remaining record which continues from the last
+ * page.
+ */
+ static void
+ insert_XLogContRecord(char *write_pos, int rem_len)
+ {
+ XLogContRecord contrec;
+
+ contrec.xl_rem_len = rem_len;
+ memcpy(write_pos, (char *)&contrec, SizeOfXLogContRecord);
+ }
+
+ /*
+ * Insert a page header to the buffer.
+ *
+ * Parameters:
+ * write_pos: Write position in the buffer.
+ * pheader: Pointer to the structure holding header info at the firt page of
+ * the segment.
+ * hasContRecord: Flag to indicate a continuous record from the last page.
+ */
+ static void
+ insert_pageheader(char *write_pos, XLogPageHeader pheader, bool hasContRecord)
+ {
+ /*
+ * Each page header is restored using the page header at the first page of
+ * the WAL segment. Magic number (xlp_magic), timeline id (xlp_tli) and
+ * XLOGID (xlogid) should no change within a segment and they are copied
+ * from the first page header. Continuous data (xlp_info) depends on the
+ * record of a given page.
+ * xrecoff is calculated by adding XLOG_BLKSZ to xrecoff value in the first
+ * page header.
+ */
+ pheader->xlp_info &= ~XLP_ALL_FLAGS;
+ if (hasContRecord)
+ pheader->xlp_info |= XLP_FIRST_IS_CONTRECORD;
+ pheader->xlp_pageaddr.xrecoff += XLOG_BLCKSZ;
+ memcpy(write_pos, (char *)pheader, SizeOfXLogShortPHD);
+ }
+
+ /*
+ * Open thie archive log file to be restored and return file descriptor.
+ *
+ * The first argument of the command is an input file name.
+ * If omitted or specified as "-", stdin will be used as an input file.
+ *
+ * Parameters:
+ * argc: This is one of the argument to the command, number of the arguments.
+ * argv: This is one of the argument to the command, a pointer arry to the
+ * argument list.
+ *
+ * Note: If error occurs within this function, whole command will exit here
+ * using exit() and the caller will not have any chance to take care of errors.
+ */
+ static int
+ open_arch_file(int argc, char *argv[])
+ {
+ int from_fd = -1;
+
+ if (argc > 1 && strcmp(argv[1], "-") != 0)
+ {
+ /* Open archive log file to restore. */
+ from_fd = open(argv[1], O_RDONLY, 0);
+ if (from_fd < 0)
+ {
+ fprintf(stderr, "failed to open `%s': %s\n", argv[1],
+ strerror(errno));
+ exit(1);
+ }
+
+ /* Obtain the segment ID from the file name (for record dump). */
+ get_segment_id(argv[1]);
+ }
+ else
+ from_fd = fileno(stdin);
+
+ return from_fd;
+ }
+
+ /*
+ * Open the WAL segment file to write the restored data and return file
+ * descriptor.
+ *
+ * The secomd argument to the command will be regarded as an output file.
+ * If omitted or specified as "-", stdout will be used as the output file.
+ *
+ * Parameters:
+ * argc: This is one of the argument to the command, number of the arguments.
+ * argv: This is one of the argument to the command, a pointer arry to the
+ * argument list.
+ *
+ * Note: If error occurs within this function, whole command will exit here
+ * using exit() and the caller will not have any chance to take care of errors.
+ */
+ static int
+ open_xlog_file(int argc, char *argv[])
+ {
+ int to_fd = -1;
+
+ if (argc > 2 && strcmp(argv[2], "-") != 0)
+ {
+ /* Open the WAL segment file */
+ to_fd = open(argv[2], O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
+ S_IRUSR | S_IWUSR);
+ if (to_fd < 0)
+ {
+ fprintf(stderr, "failed to open `%s': %s\n", argv[2],
+ strerror(errno));
+ exit(1);
+ }
+ }
+ else
+ to_fd = fileno(stdout);
+
+ return to_fd;
+ }