pax_global_header00006660000000000000000000000064150002267140014507gustar00rootroot0000000000000052 comment=68d9c084876d37b8045b8cd8ae8d24416d700930 pg_filedump-REL_17_4/000077500000000000000000000000001500022671400145205ustar00rootroot00000000000000pg_filedump-REL_17_4/.editorconfig000066400000000000000000000002671500022671400172020ustar00rootroot00000000000000root = true [*.{c,h,l,y,pl,pm}] indent_style = tab indent_size = tab tab_width = 4 [*.{sgml,xml}] indent_style = space indent_size = 1 [*.xsl] indent_style = space indent_size = 2 pg_filedump-REL_17_4/.github/000077500000000000000000000000001500022671400160605ustar00rootroot00000000000000pg_filedump-REL_17_4/.github/workflows/000077500000000000000000000000001500022671400201155ustar00rootroot00000000000000pg_filedump-REL_17_4/.github/workflows/ci.yml000066400000000000000000000020211500022671400212260ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: test: strategy: fail-fast: false matrix: pg: - 18 - 17 - 16 - 15 - 14 # versions before 14 no not support Infinity in numeric and lack lz4 support - 13 - 12 # versions before 12 have a different output format for floats (inf/Infinity, nan/NaN) - 11 - 10 name: 🐘 PostgreSQL ${{ matrix.pg }} runs-on: ubuntu-latest container: image: pgxn/pgxn-tools options: -e AS_USER=postgres steps: - name: Start PostgreSQL ${{ matrix.pg }} run: pg-start ${{ matrix.pg }} - name: Check out the repo uses: actions/checkout@v4 - name: Give build directory to postgres run: 'chown -R postgres: .' - name: Install extra build dependencies run: apt-get install -y liblz4-dev libipc-run-perl - name: Build and test on PostgreSQL ${{ matrix.pg }} run: sudo -u postgres pg-build-test pg_filedump-REL_17_4/.gitignore000066400000000000000000000001331500022671400165050ustar00rootroot00000000000000/*.bc /*.heap /[1-9]*[0-9] /*.o /pg_filedump /regression.* /results/ /tmp_check/ testfile* pg_filedump-REL_17_4/Makefile000066400000000000000000000012241500022671400161570ustar00rootroot00000000000000PROGRAM = pg_filedump OBJS = pg_filedump.o decode.o stringinfo.o REGRESS = datatypes float numeric xml toast TAP_TESTS = 1 EXTRA_CLEAN = *.heap $(wildcard [1-9]???[0-9]) # testsuite leftovers PG_CONFIG ?= pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) # make regression tests find pg_filedump (srcdir for build-time testing, bindir for later installcheck) PATH += :$(srcdir):$(shell $(PG_CONFIG) --bindir) # avoid linking against all libs that the server links against (xml, selinux, ...) ifneq ($(findstring -llz4,$(LIBS)),) LIBS = -L$(pkglibdir) -lpgcommon -lpgport -llz4 else LIBS = -L$(pkglibdir) -lpgcommon -lpgport endif pg_filedump-REL_17_4/README.md000077700000000000000000000000001500022671400215432README.pg_filedump.mdustar00rootroot00000000000000pg_filedump-REL_17_4/README.pg_filedump.md000066400000000000000000000074371500022671400203040ustar00rootroot00000000000000# pg_filedump - Display formatted contents of a PostgreSQL heap, index, or control file Copyright (c) 2002-2010 Red Hat, Inc. Copyright (c) 2011-2025, PostgreSQL Global Development Group This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Original Author: Patrick Macdonald ## Overview: pg_filedump is a utility to format PostgreSQL heap/index/control files into a human-readable form. You can format/dump the files several ways, as listed in the Invocation section, as well as dumping straight binary. The type of file (heap/index) can usually be determined automatically by the content of the blocks within the file. However, to format a pg_control file you must use the -c option. The default is to format the entire file using the block size listed in block 0 and display block relative addresses. These defaults can be modified using run-time options. Some options may seem strange but they're there for a reason. For example, block size. It's there because if the header of block 0 is corrupt, you need a method of forcing a block size. ## Compile/Installation: To compile pg_filedump, you will need to have a properly configured PostgreSQL source tree or the devel packages (with include files) of the appropriate PostgreSQL major version. ``` make PG_CONFIG=/path/to/postgresql/bin/pg_config make install PG_CONFIG=/path/to/postgresql/bin/pg_config ``` ## Invocation: ``` Usage: pg_filedump [-abcdfhikxy] [-R startblock [endblock]] [-D attrlist] [-S blocksize] [-s segsize] [-n segnumber] file Display formatted contents of a PostgreSQL heap/index/control file Defaults are: relative addressing, range of the entire file, block size as listed on block 0 in the file The following options are valid for heap and index files: -a Display absolute addresses when formatting (Block header information is always block relative) -b Display binary block images within a range (Option will turn off all formatting options) -d Display formatted block content dump (Option will turn off all other formatting options) -D Decode tuples using given comma separated list of types Supported types: bigint bigserial bool char charN date float float4 float8 int json macaddr name numeric oid real serial smallint smallserial text time timestamp timestamptz timetz uuid varchar varcharN xid xml ~ ignores all attributes left in a tuple -f Display formatted block content dump along with interpretation -h Display this information -i Display interpreted item details -k Verify block checksums -o Do not dump old values. -R Display specific block ranges within the file (Blocks are indexed from 0) [startblock]: block to start at [endblock]: block to end at A startblock without an endblock will format the single block -s Force segment size to [segsize] -t Dump TOAST files -v Ouput additional information about TOAST relations -n Force segment number to [segnumber] -S Force block size to [blocksize] -x Force interpreted formatting of block items as index items -y Force interpreted formatting of block items as heap items The following options are valid for control files: -c Interpret the file listed as a control file -f Display formatted content dump along with interpretation -S Force block size to [blocksize] Additional functions: -m Interpret file as pg_filenode.map file and print contents (all other options will be ignored) Report bugs to ``` In most cases it's recommended to use the -i and -f options to get the most useful dump output. pg_filedump-REL_17_4/decode.c000066400000000000000000001076501500022671400161200ustar00rootroot00000000000000#include "postgres.h" #include "decode.h" #include "pg_filedump.h" #include #include #include #if PG_VERSION_NUM >= 140000 #ifdef USE_LZ4 #include #endif #include #include #endif #if PG_VERSION_NUM >= 130000 #include #include #else #include #endif #include #include #include #include #include #include #include #define ATTRTYPES_STR_MAX_LEN (1024-1) static int ReadStringFromToast(const char *buffer, unsigned int buff_size, unsigned int* out_size, int (*parse_value)(const char *, int)); /* * Utilities for manipulation of header information for compressed * toast entries. */ #if PG_VERSION_NUM < 140000 /* * These macros define the "saved size" portion of va_extinfo. Its remaining * two high-order bits identify the compression method. * Before std14 only pglz compression method existed (with 00 bits). */ #define VARLENA_EXTSIZE_BITS 30 #define VARLENA_EXTSIZE_MASK ((1U << VARLENA_EXTSIZE_BITS) - 1) #define VARDATA_COMPRESSED_GET_COMPRESS_METHOD(ptr) ((*((uint32 *)ptr + 1)) >> VARLENA_EXTSIZE_BITS) typedef enum ToastCompressionId { TOAST_PGLZ_COMPRESSION_ID = 0, TOAST_LZ4_COMPRESSION_ID = 1, TOAST_INVALID_COMPRESSION_ID = 2 } ToastCompressionId; #endif #define TOAST_COMPRESS_RAWSIZE(ptr) ((*(uint32 *) ptr) & VARLENA_EXTSIZE_MASK) #define TOAST_COMPRESS_RAWMETHOD(ptr) ((*(uint32 *) ptr) >> VARLENA_EXTSIZE_BITS) #define TOAST_COMPRESS_RAWDATA(ptr) (ptr + sizeof(uint32)) #define TOAST_COMPRESS_HEADER_SIZE (sizeof(uint32)) typedef int (*decode_callback_t) (const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_smallint(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_int(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_uint(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_bigint(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_time(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_timetz(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_date(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_timestamp_internal(const char *buffer, unsigned int buff_size, unsigned int *out_size, bool with_timezone); static int decode_timestamp(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_timestamptz(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_float4(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_float8(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_bool(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_uuid(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_macaddr(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_string(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_char(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_name(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int decode_numeric(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int extract_data(const char *buffer, unsigned int buff_size, unsigned int *out_size, int (*parse_value)(const char *, int)); static int decode_ignore(const char *buffer, unsigned int buff_size, unsigned int *out_size); static int ncallbacks = 0; static decode_callback_t callbacks[ATTRTYPES_STR_MAX_LEN / 2] = { NULL }; typedef struct { char *name; decode_callback_t callback; } ParseCallbackTableItem; static ParseCallbackTableItem callback_table[] = { { "smallserial", &decode_smallint }, { "smallint", &decode_smallint }, { "int", &decode_int }, { "oid", &decode_uint }, { "xid", &decode_uint }, { "serial", &decode_int }, { "bigint", &decode_bigint }, { "bigserial", &decode_bigint }, { "time", &decode_time }, { "timetz", &decode_timetz }, { "date", &decode_date }, { "timestamp", &decode_timestamp }, { "timestamptz", &decode_timestamptz }, { "real", &decode_float4 }, { "float4", &decode_float4 }, { "float8", &decode_float8 }, { "float", &decode_float8 }, { "bool", &decode_bool }, { "uuid", &decode_uuid }, { "macaddr", &decode_macaddr }, { "name", &decode_name }, { "numeric", &decode_numeric }, { "char", &decode_char }, { "~", &decode_ignore }, /* internally all string types are stored the same way */ { "charn", &decode_string }, { "varchar", &decode_string }, { "varcharn", &decode_string }, { "text", &decode_string }, { "json", &decode_string }, { "xml", &decode_string }, { NULL, NULL }, }; static StringInfoData copyString; static bool copyStringInitDone = false; /* Used by some PostgreSQL macro definitions */ #if PG_VERSION_NUM < 160000 void ExceptionalCondition(const char *conditionName, const char *errorType, const char *fileName, int lineNumber) { printf("Exceptional condition: name = %s, type = %s, fname = %s, line = %d\n", conditionName ? conditionName : "(NULL)", errorType ? errorType : "(NULL)", fileName ? fileName : "(NULL)", lineNumber); exit(1); } #else void ExceptionalCondition(const char *conditionName, const char *fileName, int lineNumber) { printf("Exceptional condition: name = %s, type = FailedAssertion, fname = %s, line = %d\n", conditionName ? conditionName : "(NULL)", fileName ? fileName : "(NULL)", lineNumber); exit(1); } #endif /* Append given string to current COPY line */ static void CopyAppend(const char *str) { if (!copyStringInitDone) { initStringInfo(©String); copyStringInitDone = true; } /* Caller probably wanted just to init copyString */ if (str == NULL) return; if (copyString.data[0] != '\0') appendStringInfoString(©String, "\t"); appendStringInfoString(©String, str); } /* * Append given string to current COPY line and encode special symbols * like \r, \n, \t and \\. */ static int CopyAppendEncode(const char *str, int orig_len) { int curr_offset = 0; int len = orig_len; char *tmp_buff = malloc(2 * orig_len + 1); if (tmp_buff == NULL) { perror("malloc"); exit(1); } while (len > 0) { /* * Since we are working with potentially corrupted data we can * encounter \0 as well. */ if (*str == '\0') { tmp_buff[curr_offset] = '\\'; tmp_buff[curr_offset + 1] = '0'; curr_offset += 2; } else if (*str == '\r') { tmp_buff[curr_offset] = '\\'; tmp_buff[curr_offset + 1] = 'r'; curr_offset += 2; } else if (*str == '\n') { tmp_buff[curr_offset] = '\\'; tmp_buff[curr_offset + 1] = 'n'; curr_offset += 2; } else if (*str == '\t') { tmp_buff[curr_offset] = '\\'; tmp_buff[curr_offset + 1] = 'r'; curr_offset += 2; } else if (*str == '\\') { tmp_buff[curr_offset] = '\\'; tmp_buff[curr_offset + 1] = '\\'; curr_offset += 2; } else { /* It's a regular symbol. */ tmp_buff[curr_offset] = *str; curr_offset++; } str++; len--; } tmp_buff[curr_offset] = '\0'; CopyAppend(tmp_buff); free(tmp_buff); return 0; } /* CopyAppend version with format string support */ #define CopyAppendFmt(fmt, ...) do { \ char __copy_format_buff[512]; \ snprintf(__copy_format_buff, sizeof(__copy_format_buff), fmt, ##__VA_ARGS__); \ CopyAppend(__copy_format_buff); \ } while(0) /* * Decode a numeric type and append the result to current COPY line */ static int CopyAppendNumeric(const char *buffer, int num_size) { struct NumericData *num = (struct NumericData *) malloc(num_size); if (num == NULL) return -2; memcpy((char *) num, buffer, num_size); if (NUMERIC_IS_SPECIAL(num)) { int result = -2; if (NUMERIC_IS_NINF(num)) { CopyAppend("-Infinity"); result = 0; } if (NUMERIC_IS_PINF(num)) { CopyAppend("Infinity"); result = 0; } if (NUMERIC_IS_NAN(num)) { CopyAppend("NaN"); result = 0; } free(num); return result; } else { int sign; int weight; int dscale; int ndigits; int i; char *str; char *cp; char *endcp; int d; bool putit; NumericDigit d1; NumericDigit dig; NumericDigit *digits; sign = NUMERIC_SIGN(num); weight = NUMERIC_WEIGHT(num); dscale = NUMERIC_DSCALE(num); if (num_size == NUMERIC_HEADER_SIZE(num)) { /* No digits - compressed zero. */ CopyAppendFmt("%d", 0); free(num); return 0; } else { ndigits = (num_size - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit); digits = (NumericDigit *) ((char *) num + NUMERIC_HEADER_SIZE(num)); i = (weight + 1) * DEC_DIGITS; if (i <= 0) i = 1; str = palloc(i + dscale + DEC_DIGITS + 2); cp = str; /* * Output a dash for negative values */ if (sign == NUMERIC_NEG) *cp++ = '-'; /* * Output all digits before the decimal point */ if (weight < 0) { d = weight + 1; *cp++ = '0'; } else { for (d = 0; d <= weight; d++) { dig = (d < ndigits) ? digits[d] : 0; /* * In the first digit, suppress extra leading decimal * zeroes */ putit = (d > 0); d1 = dig / 1000; dig -= d1 * 1000; putit |= (d1 > 0); if (putit) *cp++ = d1 + '0'; d1 = dig / 100; dig -= d1 * 100; putit |= (d1 > 0); if (putit) *cp++ = d1 + '0'; d1 = dig / 10; dig -= d1 * 10; putit |= (d1 > 0); if (putit) *cp++ = d1 + '0'; *cp++ = dig + '0'; } } /* * If requested, output a decimal point and all the digits that * follow it. We initially put out a multiple of DEC_DIGITS * digits, then truncate if needed. */ if (dscale > 0) { *cp++ = '.'; endcp = cp + dscale; for (i = 0; i < dscale; d++, i += DEC_DIGITS) { dig = (d >= 0 && d < ndigits) ? digits[d] : 0; d1 = dig / 1000; dig -= d1 * 1000; *cp++ = d1 + '0'; d1 = dig / 100; dig -= d1 * 100; *cp++ = d1 + '0'; d1 = dig / 10; dig -= d1 * 10; *cp++ = d1 + '0'; *cp++ = dig + '0'; } cp = endcp; } *cp = '\0'; CopyAppend(str); pfree(str); free(num); return 0; } } } /* Discard accumulated COPY line */ static void CopyClear(void) { /* Make sure init is done */ CopyAppend(NULL); resetStringInfo(©String); } /* Output and then clear accumulated COPY line */ static void CopyFlush(void) { /* Make sure init is done */ CopyAppend(NULL); printf("COPY: %s\n", copyString.data); CopyClear(); } /* * Add a callback to `callbacks` table for given type name * * Arguments: * type - name of a single type, always lowercase * * Return value is: * == 0 - no error * < 0 - invalid type name */ static int AddTypeCallback(const char *type) { int idx = 0; if (*type == '\0') /* ignore empty strings */ return 0; while (callback_table[idx].name != NULL) { if (strcmp(callback_table[idx].name, type) == 0) { callbacks[ncallbacks] = callback_table[idx].callback; ncallbacks++; return 0; } idx++; } printf("Error: type <%s> doesn't exist or is not currently supported\n", type); printf("Full list of known types: "); idx = 0; while (callback_table[idx].name != NULL) { printf("%s ", callback_table[idx].name); idx++; } printf("\n"); return -1; } /* * Decode attribute types string like "int,timestamp,bool,uuid" * * Arguments: * str - types string * Return value is: * == 0 - if string is valid * < 0 - if string is invalid */ int ParseAttributeTypesString(const char *str) { char *curr_type, *next_type; char attrtypes[ATTRTYPES_STR_MAX_LEN + 1]; int i, len = strlen(str); if (len > ATTRTYPES_STR_MAX_LEN) { printf("Error: attribute types string is longer then %u characters!\n", ATTRTYPES_STR_MAX_LEN); return -1; } strcpy(attrtypes, str); for (i = 0; i < len; i++) attrtypes[i] = tolower(attrtypes[i]); curr_type = attrtypes; while (curr_type) { next_type = strstr(curr_type, ","); if (next_type) { *next_type = '\0'; next_type++; } if (AddTypeCallback(curr_type) < 0) return -1; curr_type = next_type; } return 0; } /* * Convert Julian day number (JDN) to a date. * Copy-pasted from src/backend/utils/adt/datetime.c */ static void j2date(int jd, int *year, int *month, int *day) { unsigned int julian; unsigned int quad; unsigned int extra; int y; julian = jd; julian += 32044; quad = julian / 146097; extra = (julian - quad * 146097) * 4 + 3; julian += 60 + quad * 3 + extra / 146097; quad = julian / 1461; julian -= quad * 1461; y = julian * 4 / 1461; julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366)) + 123; y += quad * 4; *year = y - 4800; quad = julian * 2141 / 65536; *day = julian - 7834 * quad / 256; *month = (quad + 10) % MONTHS_PER_YEAR + 1; } /* Decode a smallint type */ static int decode_smallint(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) SHORTALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(int16)) return -2; CopyAppendFmt("%d", (int) (*(int16 *) buffer)); *out_size = sizeof(int16) + delta; return 0; } /* Decode an int type */ static int decode_int(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) INTALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(int32)) return -2; CopyAppendFmt("%d", *(int32 *) buffer); *out_size = sizeof(int32) + delta; return 0; } /* Decode an unsigned int type */ static int decode_uint(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) INTALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(uint32)) return -2; CopyAppendFmt("%u", *(uint32 *) buffer); *out_size = sizeof(uint32) + delta; return 0; } /* Decode a bigint type */ static int decode_bigint(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) DOUBLEALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(int64)) return -2; CopyAppendFmt(INT64_FORMAT, *(int64 *) buffer); *out_size = sizeof(int64) + delta; return 0; } /* Decode a time type */ static int decode_time(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) DOUBLEALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); int64 timestamp, timestamp_sec; if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(int64)) return -2; timestamp = *(int64 *) buffer; timestamp_sec = timestamp / 1000000; *out_size = sizeof(int64) + delta; CopyAppendFmt("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%06" PRIu64, timestamp_sec / 60 / 60, (timestamp_sec / 60) % 60, timestamp_sec % 60, timestamp % 1000000); return 0; } /* Decode a timetz type */ static int decode_timetz(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) DOUBLEALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); int64 timestamp, timestamp_sec; int32 tz_sec, tz_min; if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < (sizeof(int64) + sizeof(int32))) return -2; timestamp = *(int64 *) buffer; tz_sec = *(int32 *) (buffer + sizeof(int64)); timestamp_sec = timestamp / 1000000; tz_min = -(tz_sec / 60); *out_size = sizeof(int64) + sizeof(int32) + delta; CopyAppendFmt("%02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%06" PRIu64 "%c%02d:%02d", timestamp_sec / 60 / 60, (timestamp_sec / 60) % 60, timestamp_sec % 60, timestamp % 1000000, (tz_min > 0 ? '+' : '-'), abs(tz_min / 60), abs(tz_min % 60)); return 0; } /* Decode a date type */ static int decode_date(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) INTALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); int32 d, jd, year, month, day; if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(int32)) return -2; *out_size = sizeof(int32) + delta; d = *(int32 *) buffer; if (d == PG_INT32_MIN) { CopyAppend("-infinity"); return 0; } if (d == PG_INT32_MAX) { CopyAppend("infinity"); return 0; } jd = d + POSTGRES_EPOCH_JDATE; j2date(jd, &year, &month, &day); CopyAppendFmt("%04d-%02d-%02d%s", (year <= 0) ? -year + 1 : year, month, day, (year <= 0) ? " BC" : ""); return 0; } /* Decode a timestamp type */ static int decode_timestamp_internal(const char *buffer, unsigned int buff_size, unsigned int *out_size, bool with_timezone) { const char *new_buffer = (const char *) DOUBLEALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); int64 timestamp, timestamp_sec; int32 jd, year, month, day; if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(int64)) return -2; *out_size = sizeof(int64) + delta; timestamp = *(int64 *) buffer; if (timestamp == DT_NOBEGIN) { CopyAppend("-infinity"); return 0; } if (timestamp == DT_NOEND) { CopyAppend("infinity"); return 0; } jd = timestamp / USECS_PER_DAY; if (jd != 0) timestamp -= jd * USECS_PER_DAY; if (timestamp < INT64CONST(0)) { timestamp += USECS_PER_DAY; jd -= 1; } /* add offset to go from J2000 back to standard Julian date */ jd += POSTGRES_EPOCH_JDATE; j2date(jd, &year, &month, &day); timestamp_sec = timestamp / 1000000; CopyAppendFmt("%04d-%02d-%02d %02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%06" PRIu64 "%s%s", (year <= 0) ? -year + 1 : year, month, day, timestamp_sec / 60 / 60, (timestamp_sec / 60) % 60, timestamp_sec % 60, timestamp % 1000000, with_timezone ? "+00" : "", (year <= 0) ? " BC" : ""); return 0; } static int decode_timestamp(const char *buffer, unsigned int buff_size, unsigned int *out_size) { return decode_timestamp_internal(buffer, buff_size, out_size, false); } static int decode_timestamptz(const char *buffer, unsigned int buff_size, unsigned int *out_size) { return decode_timestamp_internal(buffer, buff_size, out_size, true); } /* Decode a float4 type */ static int decode_float4(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) INTALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(float)) return -2; CopyAppendFmt("%.12f", *(float *) buffer); *out_size = sizeof(float) + delta; return 0; } /* Decode a float8 type */ static int decode_float8(const char *buffer, unsigned int buff_size, unsigned int *out_size) { const char *new_buffer = (const char *) DOUBLEALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(double)) return -2; CopyAppendFmt("%.12lf", *(double *) buffer); *out_size = sizeof(double) + delta; return 0; } /* Decode an uuid type */ static int decode_uuid(const char *buffer, unsigned int buff_size, unsigned int *out_size) { unsigned char uuid[16]; if (buff_size < sizeof(uuid)) return -1; memcpy(uuid, buffer, sizeof(uuid)); CopyAppendFmt("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15] ); *out_size = sizeof(uuid); return 0; } /* Decode a macaddr type */ static int decode_macaddr(const char *buffer, unsigned int buff_size, unsigned int *out_size) { unsigned char macaddr[6]; const char *new_buffer = (const char *) INTALIGN(buffer); unsigned int delta = (unsigned int) ((uintptr_t) new_buffer - (uintptr_t) buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(macaddr)) return -2; memcpy(macaddr, buffer, sizeof(macaddr)); CopyAppendFmt("%02x:%02x:%02x:%02x:%02x:%02x", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5] ); *out_size = sizeof(macaddr) + delta; return 0; } /* Decode a bool type */ static int decode_bool(const char *buffer, unsigned int buff_size, unsigned int *out_size) { if (buff_size < sizeof(bool)) return -1; CopyAppend(*(bool *) buffer ? "t" : "f"); *out_size = sizeof(bool); return 0; } /* Decode a name type (used mostly in catalog tables) */ static int decode_name(const char *buffer, unsigned int buff_size, unsigned int *out_size) { if (buff_size < NAMEDATALEN) return -1; CopyAppendEncode(buffer, strnlen(buffer, NAMEDATALEN)); *out_size = NAMEDATALEN; return 0; } /* * Decode numeric type. */ static int decode_numeric(const char *buffer, unsigned int buff_size, unsigned int *out_size) { int result = extract_data(buffer, buff_size, out_size, &CopyAppendNumeric); return result; } /* Decode a char type */ static int decode_char(const char *buffer, unsigned int buff_size, unsigned int *out_size) { if (buff_size < sizeof(char)) return -2; CopyAppendEncode(buffer, 1); *out_size = 1; return 0; } /* Ignore all data left */ static int decode_ignore(const char *buffer, unsigned int buff_size, unsigned int *out_size) { *out_size = buff_size; return 0; } /* Decode char(N), varchar(N), text, json or xml types */ static int decode_string(const char *buffer, unsigned int buff_size, unsigned int *out_size) { int result = extract_data(buffer, buff_size, out_size, &CopyAppendEncode); return result; } /* * Align data, parse varlena header, detoast and decompress. * Last parameters responds for actual parsing according to type. */ static int extract_data(const char *buffer, unsigned int buff_size, unsigned int *out_size, int (*parse_value)(const char *, int)) { int padding = 0; int result = 0; if (VARATT_IS_1B_E(buffer)) { /* * 00000001 1-byte length word, unaligned, TOAST pointer */ uint32 len = VARSIZE_EXTERNAL(buffer); if (len > buff_size) return -1; if (blockOptions & BLOCK_DECODE_TOAST) { result = ReadStringFromToast(buffer, buff_size, out_size, parse_value); } else if (VARATT_IS_EXTERNAL_ONDISK(buffer)) { varatt_external toast_ptr; VARATT_EXTERNAL_GET_POINTER(toast_ptr, buffer); if (VARATT_EXTERNAL_IS_COMPRESSED(toast_ptr)) { #if PG_VERSION_NUM >= 140000 switch (VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_ptr)) { case TOAST_PGLZ_COMPRESSION_ID: #endif CopyAppend("(TOASTED,pglz)"); #if PG_VERSION_NUM >= 140000 break; case TOAST_LZ4_COMPRESSION_ID: CopyAppend("(TOASTED,lz4)"); break; default: CopyAppend("(TOASTED,unknown)"); break; } #endif } else CopyAppend("(TOASTED,uncompressed)"); } /* If tag is indirect or expanded, it was stored in memory. */ else CopyAppend("(TOASTED IN MEMORY)"); *out_size = padding + len; return result; } if (VARATT_IS_1B(buffer)) { /* * xxxxxxx1 1-byte length word, unaligned, uncompressed data (up to * 126b) xxxxxxx is 1 + string length */ uint8 len = VARSIZE_1B(buffer); if (len > buff_size) return -1; result = parse_value(buffer + 1, len - 1); *out_size = padding + len; return result; } /* Skip padding bytes. */ padding = (char *)INTALIGN(buffer) - buffer; buffer += padding; buff_size -= padding; if (VARATT_IS_4B_U(buffer) && buff_size >= 4) { /* * xxxxxx00 4-byte length word, aligned, uncompressed data (up to 1G) */ uint32 len = VARSIZE_4B(buffer); if (len > buff_size) return -1; result = parse_value(buffer + 4, len - 4); *out_size = padding + len; return result; } if (VARATT_IS_4B_C(buffer) && buff_size >= 8) { /* * xxxxxx10 4-byte length word, aligned, *compressed* data (up to 1G) */ int decompress_ret; uint32 len = VARSIZE_4B(buffer); uint32 decompressed_len = 0; char *decompress_tmp_buff; #if PG_VERSION_NUM >= 140000 ToastCompressionId cmid; #endif #if PG_VERSION_NUM >= 140000 decompressed_len = VARDATA_COMPRESSED_GET_EXTSIZE(buffer); #else decompressed_len = VARRAWSIZE_4B_C(buffer); #endif if (len > buff_size) return -1; if ((decompress_tmp_buff = malloc(decompressed_len)) == NULL) { perror("malloc"); exit(1); } #if PG_VERSION_NUM >= 140000 cmid = VARDATA_COMPRESSED_GET_COMPRESS_METHOD(buffer); switch(cmid) { case TOAST_PGLZ_COMPRESSION_ID: decompress_ret = pglz_decompress(VARDATA_4B_C(buffer), len - 2 * sizeof(uint32), decompress_tmp_buff, decompressed_len, true); break; #ifdef USE_LZ4 case TOAST_LZ4_COMPRESSION_ID: decompress_ret = LZ4_decompress_safe(VARDATA_4B_C(buffer), decompress_tmp_buff, len - 2 * sizeof(uint32), decompressed_len); break; #endif default: decompress_ret = -1; break; } #else /* PG_VERSION_NUM < 140000 */ decompress_ret = pglz_decompress(VARDATA_4B_C(buffer), len - 2 * sizeof(uint32), decompress_tmp_buff, decompressed_len #if PG_VERSION_NUM >= 120000 , true #endif ); #endif /* PG_VERSION_NUM >= 140000 */ if ((decompress_ret != decompressed_len) || (decompress_ret < 0)) { printf("WARNING: Corrupted toast data, unable to decompress.\n"); CopyAppend("(inline compressed, corrupted)"); *out_size = padding + len; free(decompress_tmp_buff); return 0; } result = parse_value(decompress_tmp_buff, decompressed_len); *out_size = padding + len; free(decompress_tmp_buff); return result; } return -9; } /* * Try to decode a tuple using a types string provided previously. * * Arguments: * tupleData - pointer to the tuple data * tupleSize - tuple size in bytes */ void FormatDecode(const char *tupleData, unsigned int tupleSize) { HeapTupleHeader header = (HeapTupleHeader) tupleData; const char *data = tupleData + header->t_hoff; unsigned int size = tupleSize - header->t_hoff; int curr_attr; CopyClear(); for (curr_attr = 0; curr_attr < ncallbacks; curr_attr++) { int ret; unsigned int processed_size = 0; if ((header->t_infomask & HEAP_HASNULL) && att_isnull(curr_attr, header->t_bits)) { CopyAppend("\\N"); continue; } if (size <= 0) { printf("Error: unable to decode a tuple, no more bytes left. Partial data: %s\n", copyString.data); return; } ret = callbacks[curr_attr] (data, size, &processed_size); if (ret < 0) { printf("Error: unable to decode a tuple, callback #%d returned %d. Partial data: %s\n", curr_attr + 1, ret, copyString.data); return; } size -= processed_size; data += processed_size; } if (size != 0) { printf("Error: unable to decode a tuple, %d bytes left, 0 expected. Partial data: %s\n", size, copyString.data); return; } CopyFlush(); } static int DumpCompressedString(const char *data, int32 compressed_size, int (*parse_value)(const char *, int)) { int decompress_ret; char *decompress_tmp_buff = malloc(TOAST_COMPRESS_RAWSIZE(data)); ToastCompressionId cmid; cmid = TOAST_COMPRESS_RAWMETHOD(data); switch(cmid) { case TOAST_PGLZ_COMPRESSION_ID: decompress_ret = pglz_decompress(TOAST_COMPRESS_RAWDATA(data), compressed_size - TOAST_COMPRESS_HEADER_SIZE, decompress_tmp_buff, TOAST_COMPRESS_RAWSIZE(data) #if PG_VERSION_NUM >= 120000 , true #endif ); break; case TOAST_LZ4_COMPRESSION_ID: #ifdef USE_LZ4 decompress_ret = LZ4_decompress_safe(TOAST_COMPRESS_RAWDATA(data), decompress_tmp_buff, compressed_size - TOAST_COMPRESS_HEADER_SIZE, TOAST_COMPRESS_RAWSIZE(data)); break; #else printf("Error: compression method lz4 not supported.\n"); printf("Try to rebuild pg_filedump for PostgreSQL server of version 14+ with --with-lz4 option.\n"); free(decompress_tmp_buff); return -2; #endif default: decompress_ret = -1; break; } if ((decompress_ret != TOAST_COMPRESS_RAWSIZE(data)) || (decompress_ret < 0)) { printf("WARNING: Unable to decompress a string. Data is corrupted.\n"); printf("Returned %d while expected %d.\n", decompress_ret, TOAST_COMPRESS_RAWSIZE(data)); } else { CopyAppendEncode(decompress_tmp_buff, decompress_ret); } free(decompress_tmp_buff); return decompress_ret; } static int ReadStringFromToast(const char *buffer, unsigned int buff_size, unsigned int* out_size, int (*parse_value)(const char *, int)) { int result = 0; /* If toasted value is on disk, we'll try to restore it. */ if (VARATT_IS_EXTERNAL_ONDISK(buffer)) { varatt_external toast_ptr; char *toast_data = NULL; /* Number of chunks the TOAST data is divided into */ uint32 num_chunks; /* Next chunk to read */ uint32 want_chunk_id = 0; /* Actual size of external TOASTed value */ int32 toast_ext_size; /* Path to directory with TOAST relation file */ char *toast_relation_path; /* Filename of TOAST relation file */ char toast_relation_filename[MAXPGPATH]; FILE *toast_rel_fp; unsigned int toast_relation_block_size; unsigned int toastDataRead = 0; unsigned int block_options = 0; unsigned int control_options = 0; int loops = 0; VARATT_EXTERNAL_GET_POINTER(toast_ptr, buffer); /* Extract TOASTed value */ #if PG_VERSION_NUM >= 140000 toast_ext_size = VARATT_EXTERNAL_GET_EXTSIZE(toast_ptr); #else toast_ext_size = toast_ptr.va_extsize; #endif num_chunks = ((toast_ext_size - 1) / TOAST_MAX_CHUNK_SIZE) + 1; printf(" TOAST value. Raw size: %8d, external size: %8d, " "value id: %6d, toast relation id: %6d, chunks: %6d\n", toast_ptr.va_rawsize, toast_ext_size, toast_ptr.va_valueid, toast_ptr.va_toastrelid, num_chunks); /* Open TOAST relation file */ toast_relation_path = strdup(fileName); get_parent_directory(toast_relation_path); sprintf(toast_relation_filename, "%s/%d", *toast_relation_path ? toast_relation_path : ".", toast_ptr.va_toastrelid); free(toast_relation_path); toast_rel_fp = fopen(toast_relation_filename, "rb"); if (!toast_rel_fp) { printf("Cannot open TOAST relation %s\n", toast_relation_filename); return -1; } toast_relation_block_size = GetBlockSize(toast_rel_fp); toast_data = malloc(toast_ptr.va_rawsize); /* Loop until all chunks have been read */ while (want_chunk_id < num_chunks) { /* Restart at beginning of file */ fseek(toast_rel_fp, 0, SEEK_SET); result = DumpFileContents(block_options, control_options, toast_rel_fp, toast_relation_block_size, -1, /* no start block */ -1, /* no end block */ true, /* is toast relation */ toast_ptr.va_valueid, &want_chunk_id, toast_ext_size, toast_data, &toastDataRead); if (loops++ > num_chunks) { printf("Not all TOAST chunks found after scanning TOAST table %" PRIu32 " times, giving up\n", num_chunks); result = -1; } if (result != 0) break; } fclose(toast_rel_fp); if (result == 0) { if (VARATT_EXTERNAL_IS_COMPRESSED(toast_ptr)) result = DumpCompressedString(toast_data, toast_ext_size, parse_value); else result = parse_value(toast_data, toast_ext_size); } else { printf("Error in TOAST file.\n"); } free(toast_data); } /* If tag is indirect or expanded, it was stored in memory. */ else { CopyAppend("(TOASTED IN MEMORY)"); } return result; } /* Decode an Oid as int type and pass value out. */ static int DecodeOidBinary(const char *buffer, unsigned int buff_size, unsigned int *processed_size, Oid *result) { const char *new_buffer = (const char *) INTALIGN(buffer); unsigned int delta = (unsigned int)((uintptr_t)new_buffer - (uintptr_t)buffer); if (buff_size < delta) return -1; buff_size -= delta; buffer = new_buffer; if (buff_size < sizeof(int32)) return -2; *result = *(Oid *)buffer; *processed_size = sizeof(Oid) + delta; return 0; } /* Decode char(N), varchar(N), text, json or xml types and pass data out. */ static int DecodeBytesBinary(const char *buffer, unsigned int buff_size, unsigned int *processed_size, char *out_data, unsigned int *out_length) { if (!VARATT_IS_EXTENDED(buffer)) { *out_length = VARSIZE(buffer) - VARHDRSZ; *processed_size = VARSIZE(buffer); memcpy(out_data, VARDATA(buffer), *out_length); } else { printf("Error: unable read TOAST value.\n"); } return 0; } /* * Decode a TOAST chunk as a tuple (Oid toast_id, Oid chunk_id, text data). * If decoded OID is equal toast_oid, copy data into chunk_data. * * Parameters: * tuple_data - data of the tuple * tuple_size - length of the tuple * toast_oid - [out] oid of the TOAST value * chunk_id - [out] number of the TOAST chunk stored in the tuple * chunk - [out] extracted chunk data * chunk_size - [out] number of bytes extracted from the chunk */ void ToastChunkDecode(const char *tuple_data, unsigned int tuple_size, Oid toast_oid, Oid *read_toast_oid, uint32 *chunk_id, uint32 *want_chunk_id, char *chunk_data, unsigned int *chunk_data_size) { HeapTupleHeader header = (HeapTupleHeader)tuple_data; const char *data = tuple_data + header->t_hoff; unsigned int size = tuple_size - header->t_hoff; unsigned int processed_size = 0; int ret; *chunk_data_size = 0; *chunk_id = 0; /* decode toast_id */ ret = DecodeOidBinary(data, size, &processed_size, read_toast_oid); if (ret < 0) { printf("Error: unable to decode a TOAST tuple toast_id, " "decode function returned %d. Partial data: %s\n", ret, copyString.data); return; } size -= processed_size; data += processed_size; if (size <= 0) { printf("Error: unable to decode a TOAST chunk tuple, no more bytes " "left. Partial data: %s\n", copyString.data); return; } /* It is not what we are looking for */ if (toast_oid != *read_toast_oid) return; /* decode chunk_id */ ret = DecodeOidBinary(data, size, &processed_size, chunk_id); if (ret < 0) { printf("Error: unable to decode a TOAST tuple chunk_id, decode " "function returned %d. Partial data: %s\n", ret, copyString.data); return; } /* Not the chunk we want to read next */ if (*chunk_id != *want_chunk_id) return; size -= processed_size; data += processed_size; if (size <= 0) { printf("Error: unable to decode a TOAST chunk tuple, no more bytes " "left. Partial data: %s\n", copyString.data); return; } /* decode data */ ret = DecodeBytesBinary(data, size, &processed_size, chunk_data, chunk_data_size); if (ret < 0) { printf("Error: unable to decode a TOAST chunk data, decode function " "returned %d. Partial data: %s\n", ret, copyString.data); return; } size -= processed_size; if (size != 0) { printf("Error: unable to decode a TOAST chunk tuple, %d bytes left. " "Partial data: %s\n", size, copyString.data); return; } /* advance to next chunk */ *want_chunk_id += 1; } pg_filedump-REL_17_4/decode.h000066400000000000000000000127061500022671400161220ustar00rootroot00000000000000#ifndef _PG_FILEDUMP_DECODE_H_ #define _PG_FILEDUMP_DECODE_H_ #define NBASE 10000 #define HALF_NBASE 5000 #define DEC_DIGITS 4 /* decimal digits per NBASE digit */ #define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */ #define DIV_GUARD_DIGITS 4 typedef int16 NumericDigit; int ParseAttributeTypesString(const char *str); void FormatDecode(const char *tupleData, unsigned int tupleSize); void ToastChunkDecode(const char* tuple_data, unsigned int tuple_size, Oid toast_oid, Oid *read_toast_oid, uint32 *chunk_id, uint32 *want_chunk_id, char *chunk_data, unsigned int *chunk_data_size); struct NumericShort { uint16 n_header; /* Sign + display scale + weight */ NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ }; struct NumericLong { uint16 n_sign_dscale; /* Sign + display scale */ int16 n_weight; /* Weight of 1st digit */ NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */ }; union NumericChoice { uint16 n_header; /* Header word */ struct NumericLong n_long; /* Long form (4-byte header) */ struct NumericShort n_short; /* Short form (2-byte header) */ }; struct NumericData { union NumericChoice choice; /* choice of format */ }; /* * Interpretation of high bits. */ #define NUMERIC_SIGN_MASK 0xC000 #define NUMERIC_POS 0x0000 #define NUMERIC_NEG 0x4000 #define NUMERIC_SHORT 0x8000 #define NUMERIC_SPECIAL 0xC000 #define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK) #define NUMERIC_IS_SHORT(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT) #define NUMERIC_IS_SPECIAL(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SPECIAL) #define NUMERIC_HDRSZ (VARHDRSZ + sizeof(uint16) + sizeof(int16)) #define NUMERIC_HDRSZ_SHORT (VARHDRSZ + sizeof(uint16)) /* * If the flag bits are NUMERIC_SHORT or NUMERIC_SPECIAL, we want the short * header; otherwise, we want the long one. Instead of testing against each * value, we can just look at the high bit, for a slight efficiency gain. */ #define NUMERIC_HEADER_IS_SHORT(n) (((n)->choice.n_header & 0x8000) != 0) #define NUMERIC_HEADER_SIZE(n) \ (sizeof(uint16) + \ (NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16))) /* * Definitions for special values (NaN, positive infinity, negative infinity). * * The two bits after the NUMERIC_SPECIAL bits are 00 for NaN, 01 for positive * infinity, 11 for negative infinity. (This makes the sign bit match where * it is in a short-format value, though we make no use of that at present.) * We could mask off the remaining bits before testing the active bits, but * currently those bits must be zeroes, so masking would just add cycles. */ #define NUMERIC_EXT_SIGN_MASK 0xF000 /* high bits plus NaN/Inf flag bits */ #define NUMERIC_NAN 0xC000 #define NUMERIC_PINF 0xD000 #define NUMERIC_NINF 0xF000 #define NUMERIC_INF_SIGN_MASK 0x2000 #define NUMERIC_EXT_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_EXT_SIGN_MASK) #define NUMERIC_IS_NAN(n) ((n)->choice.n_header == NUMERIC_NAN) #define NUMERIC_IS_PINF(n) ((n)->choice.n_header == NUMERIC_PINF) #define NUMERIC_IS_NINF(n) ((n)->choice.n_header == NUMERIC_NINF) #define NUMERIC_IS_INF(n) \ (((n)->choice.n_header & ~NUMERIC_INF_SIGN_MASK) == NUMERIC_PINF) /* * Short format definitions. */ #define NUMERIC_SHORT_SIGN_MASK 0x2000 #define NUMERIC_SHORT_DSCALE_MASK 0x1F80 #define NUMERIC_SHORT_DSCALE_SHIFT 7 #define NUMERIC_SHORT_DSCALE_MAX \ (NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT) #define NUMERIC_SHORT_WEIGHT_SIGN_MASK 0x0040 #define NUMERIC_SHORT_WEIGHT_MASK 0x003F #define NUMERIC_SHORT_WEIGHT_MAX NUMERIC_SHORT_WEIGHT_MASK #define NUMERIC_SHORT_WEIGHT_MIN (-(NUMERIC_SHORT_WEIGHT_MASK+1)) /* * Extract sign, display scale, weight. These macros extract field values * suitable for the NumericVar format from the Numeric (on-disk) format. * * Note that we don't trouble to ensure that dscale and weight read as zero * for an infinity; however, that doesn't matter since we never convert * "special" numerics to NumericVar form. Only the constants defined below * (const_nan, etc) ever represent a non-finite value as a NumericVar. */ #define NUMERIC_DSCALE_MASK 0x3FFF #define NUMERIC_DSCALE_MAX NUMERIC_DSCALE_MASK #define NUMERIC_SIGN(n) \ (NUMERIC_IS_SHORT(n) ? \ (((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \ NUMERIC_NEG : NUMERIC_POS) : \ (NUMERIC_IS_SPECIAL(n) ? \ NUMERIC_EXT_FLAGBITS(n) : NUMERIC_FLAGBITS(n))) #define NUMERIC_DSCALE(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ ((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \ >> NUMERIC_SHORT_DSCALE_SHIFT \ : ((n)->choice.n_long.n_sign_dscale & NUMERIC_DSCALE_MASK)) #define NUMERIC_WEIGHT(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \ (((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \ ~NUMERIC_SHORT_WEIGHT_MASK : 0) \ | ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \ : ((n)->choice.n_long.n_weight)) #endif pg_filedump-REL_17_4/expected/000077500000000000000000000000001500022671400163215ustar00rootroot00000000000000pg_filedump-REL_17_4/expected/datatypes.out000066400000000000000000001052071500022671400210550ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table "int,text" (i int, t text); insert into "int,text" values (1, 'one'), (null, 'two'), (3, null), (4, 'four'); \set relname int,text \ir run_test.sql \echo Testing :relname Testing int,text vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: int,text.heap * Options used: -D int,text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8056 (0x1f78) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8016 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 1 one Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: \N two Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 3 \N Item 4 -- Length: 33 Offset: 8056 (0x1f78) Flags: NORMAL COPY: 4 four *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- -- do one test without options \! pg_filedump int,text.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: int,text.heap * Options used: None ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8056 (0x1f78) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8016 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL Item 4 -- Length: 33 Offset: 8056 (0x1f78) Flags: NORMAL *** End of File Encountered. Last Block Read: 0 *** ---------------------------------------------------------------------------------------------- create table bigint (x bigint); insert into bigint values (-1), (0), (1), (null); \set relname bigint \ir run_test.sql \echo Testing :relname Testing bigint vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: bigint.heap * Options used: -D bigint ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: -1 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table bool (x bool); insert into bool values (true), (false), (null); \set relname bool \ir run_test.sql \echo Testing :relname Testing bool vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: bool.heap * Options used: -D bool ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8104 (0x1fa8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8068 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 25 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: t Item 2 -- Length: 25 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: f Item 3 -- Length: 24 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table char (x "char"); insert into char values ('x'), (null); \set relname char \ir run_test.sql \echo Testing :relname Testing char vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: char.heap * Options used: -D char ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8136 (0x1fc8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8104 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 25 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: x Item 2 -- Length: 24 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table "charN" (x char(5)); insert into "charN" values ('x'), ('xxxxx'), (null); \set relname charN \ir run_test.sql \echo Testing :relname Testing charN vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: charN.heap * Options used: -D charN ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8104 (0x1fa8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8068 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 30 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: x Item 2 -- Length: 30 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: xxxxx Item 3 -- Length: 24 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table date (x date); insert into date values ('2000-01-01'), ('1900-02-02'), ('2100-12-31'), ('100-01-01 BC'), ('-infinity'), ('infinity'), (null); \set relname date \ir run_test.sql \echo Testing :relname Testing date vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: date.heap * Options used: -D date ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7976 (0x1f28) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7924 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 28 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 2000-01-01 Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 1900-02-02 Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 2100-12-31 Item 4 -- Length: 28 Offset: 8064 (0x1f80) Flags: NORMAL COPY: 0100-01-01 BC Item 5 -- Length: 28 Offset: 8032 (0x1f60) Flags: NORMAL COPY: -infinity Item 6 -- Length: 28 Offset: 8000 (0x1f40) Flags: NORMAL COPY: infinity Item 7 -- Length: 24 Offset: 7976 (0x1f28) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table int (x int); insert into int values (-1), (0), (1), (null); \set relname int \ir run_test.sql \echo Testing :relname Testing int vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: int.heap * Options used: -D int ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 28 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: -1 Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0 Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table json (x json); insert into json values ('1'), ('"one"'), ('{"a":"b"}'), ('null'), (null); \set relname json \ir run_test.sql \echo Testing :relname Testing json vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: json.heap * Options used: -D json ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8032 (0x1f60) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7988 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 26 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 1 Item 2 -- Length: 30 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: "one" Item 3 -- Length: 34 Offset: 8088 (0x1f98) Flags: NORMAL COPY: {"a":"b"} Item 4 -- Length: 29 Offset: 8056 (0x1f78) Flags: NORMAL COPY: null Item 5 -- Length: 24 Offset: 8032 (0x1f60) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table macaddr (x macaddr); insert into macaddr values ('00:10:20:30:40:50'), (null); \set relname macaddr \ir run_test.sql \echo Testing :relname Testing macaddr vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: macaddr.heap * Options used: -D macaddr ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8136 (0x1fc8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8104 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 30 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 00:10:20:30:40:50 Item 2 -- Length: 24 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table name (x name); insert into name values ('name'), ('1234567890123456789012345678901234567890123456789012345678901234567890'), (null); \set relname name \ir run_test.sql \echo Testing :relname Testing name vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: name.heap * Options used: -D name ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 7992 (0x1f38) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 7956 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 88 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: name Item 2 -- Length: 88 Offset: 8016 (0x1f50) Flags: NORMAL COPY: 123456789012345678901234567890123456789012345678901234567890123 Item 3 -- Length: 24 Offset: 7992 (0x1f38) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table oid (x oid); insert into oid values (-1), (0), (1), (null); \set relname oid \ir run_test.sql \echo Testing :relname Testing oid vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: oid.heap * Options used: -D oid ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 28 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 4294967295 Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0 Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table smallint (x smallint); insert into smallint values (-1), (0), (1), (null); \set relname smallint \ir run_test.sql \echo Testing :relname Testing smallint vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: smallint.heap * Options used: -D smallint ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 26 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: -1 Item 2 -- Length: 26 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0 Item 3 -- Length: 26 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table text (x text); insert into text values ('hello world'), (null); \set relname text \ir run_test.sql \echo Testing :relname Testing text vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: text.heap * Options used: -D text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8128 (0x1fc0) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8096 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 36 Offset: 8152 (0x1fd8) Flags: NORMAL COPY: hello world Item 2 -- Length: 24 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table time (x time); insert into time values ('00:00'), ('23:59:59'), ('23:59:60'), (null); \set relname time \ir run_test.sql \echo Testing :relname Testing time vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: time.heap * Options used: -D time ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 00:00:00.000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 23:59:59.000000 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 24:00:00.000000 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table timestamp (x timestamp); insert into timestamp values ('2000-01-01 00:00'), ('100-01-01 BC 2:22'), ('infinity'), ('-infinity'), (null); \set relname timestamp \ir run_test.sql \echo Testing :relname Testing timestamp vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: timestamp.heap * Options used: -D timestamp ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8040 (0x1f68) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7996 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 2000-01-01 00:00:00.000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0100-01-01 02:22:00.000000 BC Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: infinity Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: -infinity Item 5 -- Length: 24 Offset: 8040 (0x1f68) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- set timezone = 'Etc/UTC'; create table timestamptz (x timestamptz); insert into timestamptz values ('2000-01-01 00:00'), ('100-01-01 BC 2:22'), ('infinity'), ('-infinity'), (null); \set relname timestamptz \ir run_test.sql \echo Testing :relname Testing timestamptz vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: timestamptz.heap * Options used: -D timestamptz ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8040 (0x1f68) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7996 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 2000-01-01 00:00:00.000000+00 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0100-01-01 02:22:00.000000+00 BC Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: infinity Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: -infinity Item 5 -- Length: 24 Offset: 8040 (0x1f68) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table timetz (x timetz); insert into timetz values ('00:00 Etc/UTC'), ('23:59:59 Etc/UTC'), ('23:59:60 Etc/UTC'), ('1:23+4:56'), (null); \set relname timetz \ir run_test.sql \echo Testing :relname Testing timetz vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: timetz.heap * Options used: -D timetz ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8008 (0x1f48) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7964 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 36 Offset: 8152 (0x1fd8) Flags: NORMAL COPY: 00:00:00.000000-00:00 Item 2 -- Length: 36 Offset: 8112 (0x1fb0) Flags: NORMAL COPY: 23:59:59.000000-00:00 Item 3 -- Length: 36 Offset: 8072 (0x1f88) Flags: NORMAL COPY: 24:00:00.000000-00:00 Item 4 -- Length: 36 Offset: 8032 (0x1f60) Flags: NORMAL COPY: 01:23:00.000000+04:56 Item 5 -- Length: 24 Offset: 8008 (0x1f48) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table uuid (x uuid); insert into uuid values ('b4f0e2d6-429b-48bd-af06-6578829dd980'), ('00000000-0000-0000-0000-000000000000'), (null); \set relname uuid \ir run_test.sql \echo Testing :relname Testing uuid vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: uuid.heap * Options used: -D uuid ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8088 (0x1f98) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8052 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 40 Offset: 8152 (0x1fd8) Flags: NORMAL COPY: b4f0e2d6-429b-48bd-af06-6578829dd980 Item 2 -- Length: 40 Offset: 8112 (0x1fb0) Flags: NORMAL COPY: 00000000-0000-0000-0000-000000000000 Item 3 -- Length: 24 Offset: 8088 (0x1f98) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table varchar (x varchar); insert into varchar values ('Hello World'), (''), (null); \set relname varchar \ir run_test.sql \echo Testing :relname Testing varchar vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: varchar.heap * Options used: -D varchar ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8096 (0x1fa0) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8060 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 36 Offset: 8152 (0x1fd8) Flags: NORMAL COPY: Hello World Item 2 -- Length: 25 Offset: 8120 (0x1fb8) Flags: NORMAL COPY: Item 3 -- Length: 24 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table "varcharN" (x varchar(11)); insert into "varcharN" values ('Hello World'), (''), (null); \set relname varcharN \ir run_test.sql \echo Testing :relname Testing varcharN vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: varcharN.heap * Options used: -D varcharN ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8096 (0x1fa0) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8060 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 36 Offset: 8152 (0x1fd8) Flags: NORMAL COPY: Hello World Item 2 -- Length: 25 Offset: 8120 (0x1fb8) Flags: NORMAL COPY: Item 3 -- Length: 24 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table xid (x xid); insert into xid values ('-1'), ('0'), ('1'), (null); \set relname xid \ir run_test.sql \echo Testing :relname Testing xid vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: xid.heap * Options used: -D xid ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 28 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 4294967295 Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0 Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/datatypes_3.out000066400000000000000000001052071500022671400212770ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table "int,text" (i int, t text); insert into "int,text" values (1, 'one'), (null, 'two'), (3, null), (4, 'four'); \set relname int,text \ir run_test.sql \echo Testing :relname Testing int,text vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: int,text.heap * Options used: -D int,text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8068 (0x1f84) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8028 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 1 one Item 2 -- Length: 28 Offset: 8132 (0x1fc4) Flags: NORMAL COPY: \N two Item 3 -- Length: 28 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: 3 \N Item 4 -- Length: 33 Offset: 8068 (0x1f84) Flags: NORMAL COPY: 4 four *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- -- do one test without options \! pg_filedump int,text.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: int,text.heap * Options used: None ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8068 (0x1f84) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8028 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL Item 2 -- Length: 28 Offset: 8132 (0x1fc4) Flags: NORMAL Item 3 -- Length: 28 Offset: 8104 (0x1fa8) Flags: NORMAL Item 4 -- Length: 33 Offset: 8068 (0x1f84) Flags: NORMAL *** End of File Encountered. Last Block Read: 0 *** ---------------------------------------------------------------------------------------------- create table bigint (x bigint); insert into bigint values (-1), (0), (1), (null); \set relname bigint \ir run_test.sql \echo Testing :relname Testing bigint vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: bigint.heap * Options used: -D bigint ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: -1 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table bool (x bool); insert into bool values (true), (false), (null); \set relname bool \ir run_test.sql \echo Testing :relname Testing bool vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: bool.heap * Options used: -D bool ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8112 (0x1fb0) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8076 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 25 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: t Item 2 -- Length: 25 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: f Item 3 -- Length: 24 Offset: 8112 (0x1fb0) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table char (x "char"); insert into char values ('x'), (null); \set relname char \ir run_test.sql \echo Testing :relname Testing char vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: char.heap * Options used: -D char ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8140 (0x1fcc) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8108 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 25 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: x Item 2 -- Length: 24 Offset: 8140 (0x1fcc) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table "charN" (x char(5)); insert into "charN" values ('x'), ('xxxxx'), (null); \set relname charN \ir run_test.sql \echo Testing :relname Testing charN vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: charN.heap * Options used: -D charN ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8104 (0x1fa8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8068 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 30 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: x Item 2 -- Length: 30 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: xxxxx Item 3 -- Length: 24 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table date (x date); insert into date values ('2000-01-01'), ('1900-02-02'), ('2100-12-31'), ('100-01-01 BC'), ('-infinity'), ('infinity'), (null); \set relname date \ir run_test.sql \echo Testing :relname Testing date vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: date.heap * Options used: -D date ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 8000 (0x1f40) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7948 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 28 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 2000-01-01 Item 2 -- Length: 28 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: 1900-02-02 Item 3 -- Length: 28 Offset: 8108 (0x1fac) Flags: NORMAL COPY: 2100-12-31 Item 4 -- Length: 28 Offset: 8080 (0x1f90) Flags: NORMAL COPY: 0100-01-01 BC Item 5 -- Length: 28 Offset: 8052 (0x1f74) Flags: NORMAL COPY: -infinity Item 6 -- Length: 28 Offset: 8024 (0x1f58) Flags: NORMAL COPY: infinity Item 7 -- Length: 24 Offset: 8000 (0x1f40) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table int (x int); insert into int values (-1), (0), (1), (null); \set relname int \ir run_test.sql \echo Testing :relname Testing int vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: int.heap * Options used: -D int ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8084 (0x1f94) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8044 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 28 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: -1 Item 2 -- Length: 28 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: 0 Item 3 -- Length: 28 Offset: 8108 (0x1fac) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8084 (0x1f94) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table json (x json); insert into json values ('1'), ('"one"'), ('{"a":"b"}'), ('null'), (null); \set relname json \ir run_test.sql \echo Testing :relname Testing json vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: json.heap * Options used: -D json ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8040 (0x1f68) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7996 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 26 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 1 Item 2 -- Length: 30 Offset: 8132 (0x1fc4) Flags: NORMAL COPY: "one" Item 3 -- Length: 34 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: {"a":"b"} Item 4 -- Length: 29 Offset: 8064 (0x1f80) Flags: NORMAL COPY: null Item 5 -- Length: 24 Offset: 8040 (0x1f68) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table macaddr (x macaddr); insert into macaddr values ('00:10:20:30:40:50'), (null); \set relname macaddr \ir run_test.sql \echo Testing :relname Testing macaddr vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: macaddr.heap * Options used: -D macaddr ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8136 (0x1fc8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8104 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 30 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 00:10:20:30:40:50 Item 2 -- Length: 24 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table name (x name); insert into name values ('name'), ('1234567890123456789012345678901234567890123456789012345678901234567890'), (null); \set relname name \ir run_test.sql \echo Testing :relname Testing name vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: name.heap * Options used: -D name ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 7992 (0x1f38) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 7956 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 88 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: name Item 2 -- Length: 88 Offset: 8016 (0x1f50) Flags: NORMAL COPY: 123456789012345678901234567890123456789012345678901234567890123 Item 3 -- Length: 24 Offset: 7992 (0x1f38) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table oid (x oid); insert into oid values (-1), (0), (1), (null); \set relname oid \ir run_test.sql \echo Testing :relname Testing oid vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: oid.heap * Options used: -D oid ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8084 (0x1f94) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8044 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 28 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 4294967295 Item 2 -- Length: 28 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: 0 Item 3 -- Length: 28 Offset: 8108 (0x1fac) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8084 (0x1f94) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table smallint (x smallint); insert into smallint values (-1), (0), (1), (null); \set relname smallint \ir run_test.sql \echo Testing :relname Testing smallint vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: smallint.heap * Options used: -D smallint ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8084 (0x1f94) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8044 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 26 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: -1 Item 2 -- Length: 26 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: 0 Item 3 -- Length: 26 Offset: 8108 (0x1fac) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8084 (0x1f94) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table text (x text); insert into text values ('hello world'), (null); \set relname text \ir run_test.sql \echo Testing :relname Testing text vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: text.heap * Options used: -D text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8132 (0x1fc4) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8100 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 36 Offset: 8156 (0x1fdc) Flags: NORMAL COPY: hello world Item 2 -- Length: 24 Offset: 8132 (0x1fc4) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table time (x time); insert into time values ('00:00'), ('23:59:59'), ('23:59:60'), (null); \set relname time \ir run_test.sql \echo Testing :relname Testing time vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: time.heap * Options used: -D time ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8072 (0x1f88) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8032 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 00:00:00.000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 23:59:59.000000 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: 24:00:00.000000 Item 4 -- Length: 24 Offset: 8072 (0x1f88) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table timestamp (x timestamp); insert into timestamp values ('2000-01-01 00:00'), ('100-01-01 BC 2:22'), ('infinity'), ('-infinity'), (null); \set relname timestamp \ir run_test.sql \echo Testing :relname Testing timestamp vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: timestamp.heap * Options used: -D timestamp ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8040 (0x1f68) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7996 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 2000-01-01 00:00:00.000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0100-01-01 02:22:00.000000 BC Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: infinity Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: -infinity Item 5 -- Length: 24 Offset: 8040 (0x1f68) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- set timezone = 'Etc/UTC'; create table timestamptz (x timestamptz); insert into timestamptz values ('2000-01-01 00:00'), ('100-01-01 BC 2:22'), ('infinity'), ('-infinity'), (null); \set relname timestamptz \ir run_test.sql \echo Testing :relname Testing timestamptz vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: timestamptz.heap * Options used: -D timestamptz ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8040 (0x1f68) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7996 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 2000-01-01 00:00:00.000000+00 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 0100-01-01 02:22:00.000000+00 BC Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: infinity Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: -infinity Item 5 -- Length: 24 Offset: 8040 (0x1f68) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table timetz (x timetz); insert into timetz values ('00:00 Etc/UTC'), ('23:59:59 Etc/UTC'), ('23:59:60 Etc/UTC'), ('1:23+4:56'), (null); \set relname timetz \ir run_test.sql \echo Testing :relname Testing timetz vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: timetz.heap * Options used: -D timetz ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8024 (0x1f58) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7980 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 36 Offset: 8156 (0x1fdc) Flags: NORMAL COPY: 00:00:00.000000-00:00 Item 2 -- Length: 36 Offset: 8120 (0x1fb8) Flags: NORMAL COPY: 23:59:59.000000-00:00 Item 3 -- Length: 36 Offset: 8084 (0x1f94) Flags: NORMAL COPY: 24:00:00.000000-00:00 Item 4 -- Length: 36 Offset: 8048 (0x1f70) Flags: NORMAL COPY: 01:23:00.000000+04:56 Item 5 -- Length: 24 Offset: 8024 (0x1f58) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table uuid (x uuid); insert into uuid values ('b4f0e2d6-429b-48bd-af06-6578829dd980'), ('00000000-0000-0000-0000-000000000000'), (null); \set relname uuid \ir run_test.sql \echo Testing :relname Testing uuid vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: uuid.heap * Options used: -D uuid ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8088 (0x1f98) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8052 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 40 Offset: 8152 (0x1fd8) Flags: NORMAL COPY: b4f0e2d6-429b-48bd-af06-6578829dd980 Item 2 -- Length: 40 Offset: 8112 (0x1fb0) Flags: NORMAL COPY: 00000000-0000-0000-0000-000000000000 Item 3 -- Length: 24 Offset: 8088 (0x1f98) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table varchar (x varchar); insert into varchar values ('Hello World'), (''), (null); \set relname varchar \ir run_test.sql \echo Testing :relname Testing varchar vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: varchar.heap * Options used: -D varchar ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8104 (0x1fa8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8068 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 36 Offset: 8156 (0x1fdc) Flags: NORMAL COPY: Hello World Item 2 -- Length: 25 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: Item 3 -- Length: 24 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table "varcharN" (x varchar(11)); insert into "varcharN" values ('Hello World'), (''), (null); \set relname varcharN \ir run_test.sql \echo Testing :relname Testing varcharN vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: varcharN.heap * Options used: -D varcharN ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 36 (0x0024) Block: Size 8192 Version 4 Upper 8104 (0x1fa8) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 3 Free Space: 8068 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 36 ----- Item 1 -- Length: 36 Offset: 8156 (0x1fdc) Flags: NORMAL COPY: Hello World Item 2 -- Length: 25 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: Item 3 -- Length: 24 Offset: 8104 (0x1fa8) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table xid (x xid); insert into xid values ('-1'), ('0'), ('1'), (null); \set relname xid \ir run_test.sql \echo Testing :relname Testing xid vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: xid.heap * Options used: -D xid ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 40 (0x0028) Block: Size 8192 Version 4 Upper 8084 (0x1f94) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 4 Free Space: 8044 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 40 ----- Item 1 -- Length: 28 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 4294967295 Item 2 -- Length: 28 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: 0 Item 3 -- Length: 28 Offset: 8108 (0x1fac) Flags: NORMAL COPY: 1 Item 4 -- Length: 24 Offset: 8084 (0x1f94) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/float.out000066400000000000000000000075131500022671400201650ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG12+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table float4 (x float4); insert into float4 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float4 \ir run_test.sql \echo Testing :relname Testing float4 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float4.heap * Options used: -D float4 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8008 (0x1f48) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7960 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 28 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -Infinity Item 4 -- Length: 28 Offset: 8064 (0x1f80) Flags: NORMAL COPY: Infinity Item 5 -- Length: 28 Offset: 8032 (0x1f60) Flags: NORMAL COPY: NaN Item 6 -- Length: 24 Offset: 8008 (0x1f48) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table float8 (x float8); insert into float8 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float8 \ir run_test.sql \echo Testing :relname Testing float8 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float8.heap * Options used: -D float8 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8008 (0x1f48) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7960 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -Infinity Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: Infinity Item 5 -- Length: 32 Offset: 8032 (0x1f60) Flags: NORMAL COPY: NaN Item 6 -- Length: 24 Offset: 8008 (0x1f48) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/float_1.out000066400000000000000000000074671500022671400204150ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG12+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table float4 (x float4); insert into float4 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float4 \ir run_test.sql \echo Testing :relname Testing float4 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float4.heap * Options used: -D float4 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8008 (0x1f48) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7960 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 28 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 28 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 28 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -inf Item 4 -- Length: 28 Offset: 8064 (0x1f80) Flags: NORMAL COPY: inf Item 5 -- Length: 28 Offset: 8032 (0x1f60) Flags: NORMAL COPY: nan Item 6 -- Length: 24 Offset: 8008 (0x1f48) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table float8 (x float8); insert into float8 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float8 \ir run_test.sql \echo Testing :relname Testing float8 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float8.heap * Options used: -D float8 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8008 (0x1f48) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7960 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -inf Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: inf Item 5 -- Length: 32 Offset: 8032 (0x1f60) Flags: NORMAL COPY: nan Item 6 -- Length: 24 Offset: 8008 (0x1f48) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/float_3.out000066400000000000000000000075131500022671400204070ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG12+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table float4 (x float4); insert into float4 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float4 \ir run_test.sql \echo Testing :relname Testing float4 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float4.heap * Options used: -D float4 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8028 (0x1f5c) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7980 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 28 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 28 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 28 Offset: 8108 (0x1fac) Flags: NORMAL COPY: -Infinity Item 4 -- Length: 28 Offset: 8080 (0x1f90) Flags: NORMAL COPY: Infinity Item 5 -- Length: 28 Offset: 8052 (0x1f74) Flags: NORMAL COPY: NaN Item 6 -- Length: 24 Offset: 8028 (0x1f5c) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table float8 (x float8); insert into float8 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float8 \ir run_test.sql \echo Testing :relname Testing float8 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float8.heap * Options used: -D float8 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8008 (0x1f48) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7960 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -Infinity Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: Infinity Item 5 -- Length: 32 Offset: 8032 (0x1f60) Flags: NORMAL COPY: NaN Item 6 -- Length: 24 Offset: 8008 (0x1f48) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/float_4.out000066400000000000000000000074671500022671400204200ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG12+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table float4 (x float4); insert into float4 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float4 \ir run_test.sql \echo Testing :relname Testing float4 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float4.heap * Options used: -D float4 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8028 (0x1f5c) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7980 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 28 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 28 Offset: 8136 (0x1fc8) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 28 Offset: 8108 (0x1fac) Flags: NORMAL COPY: -inf Item 4 -- Length: 28 Offset: 8080 (0x1f90) Flags: NORMAL COPY: inf Item 5 -- Length: 28 Offset: 8052 (0x1f74) Flags: NORMAL COPY: nan Item 6 -- Length: 24 Offset: 8028 (0x1f5c) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- create table float8 (x float8); insert into float8 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float8 \ir run_test.sql \echo Testing :relname Testing float8 vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: float8.heap * Options used: -D float8 ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8008 (0x1f48) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 6 Free Space: 7960 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 48 ----- Item 1 -- Length: 32 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0.000000000000 Item 2 -- Length: 32 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: -0.000000000000 Item 3 -- Length: 32 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -inf Item 4 -- Length: 32 Offset: 8064 (0x1f80) Flags: NORMAL COPY: inf Item 5 -- Length: 32 Offset: 8032 (0x1f60) Flags: NORMAL COPY: nan Item 6 -- Length: 24 Offset: 8008 (0x1f48) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/numeric.out000066400000000000000000000042631500022671400205210ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG14+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table numeric (x numeric); insert into numeric values (0), ('12341234'), ('-567890'), ('NaN'), (null); insert into numeric values ('-Infinity'), ('Infinity'); -- needs PG 14 \set relname numeric \ir run_test.sql \echo Testing :relname Testing numeric vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: numeric.heap * Options used: -D numeric ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7976 (0x1f28) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7924 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 27 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0 Item 2 -- Length: 31 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 12341234 Item 3 -- Length: 31 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -567890 Item 4 -- Length: 27 Offset: 8064 (0x1f80) Flags: NORMAL COPY: NaN Item 5 -- Length: 24 Offset: 8040 (0x1f68) Flags: NORMAL COPY: \N Item 6 -- Length: 27 Offset: 8008 (0x1f48) Flags: NORMAL COPY: -Infinity Item 7 -- Length: 27 Offset: 7976 (0x1f28) Flags: NORMAL COPY: Infinity *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/numeric_1.out000066400000000000000000000042651500022671400207430ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG14+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table numeric (x numeric); insert into numeric values (0), ('12341234'), ('-567890'), ('NaN'), (null); insert into numeric values ('-Infinity'), ('Infinity'); -- needs PG 14 ERROR: invalid input syntax for type numeric: "-Infinity" LINE 1: insert into numeric values ('-Infinity'), ('Infinity'); ^ \set relname numeric \ir run_test.sql \echo Testing :relname Testing numeric vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: numeric.heap * Options used: -D numeric ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8040 (0x1f68) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 7996 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 27 Offset: 8160 (0x1fe0) Flags: NORMAL COPY: 0 Item 2 -- Length: 31 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: 12341234 Item 3 -- Length: 31 Offset: 8096 (0x1fa0) Flags: NORMAL COPY: -567890 Item 4 -- Length: 27 Offset: 8064 (0x1f80) Flags: NORMAL COPY: NaN Item 5 -- Length: 24 Offset: 8040 (0x1f68) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/numeric_3.out000066400000000000000000000042631500022671400207430ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG14+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table numeric (x numeric); insert into numeric values (0), ('12341234'), ('-567890'), ('NaN'), (null); insert into numeric values ('-Infinity'), ('Infinity'); -- needs PG 14 \set relname numeric \ir run_test.sql \echo Testing :relname Testing numeric vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: numeric.heap * Options used: -D numeric ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7992 (0x1f38) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7940 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 27 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 0 Item 2 -- Length: 31 Offset: 8132 (0x1fc4) Flags: NORMAL COPY: 12341234 Item 3 -- Length: 31 Offset: 8100 (0x1fa4) Flags: NORMAL COPY: -567890 Item 4 -- Length: 27 Offset: 8072 (0x1f88) Flags: NORMAL COPY: NaN Item 5 -- Length: 24 Offset: 8048 (0x1f70) Flags: NORMAL COPY: \N Item 6 -- Length: 27 Offset: 8020 (0x1f54) Flags: NORMAL COPY: -Infinity Item 7 -- Length: 27 Offset: 7992 (0x1f38) Flags: NORMAL COPY: Infinity *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/numeric_4.out000066400000000000000000000042651500022671400207460ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG14+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table numeric (x numeric); insert into numeric values (0), ('12341234'), ('-567890'), ('NaN'), (null); insert into numeric values ('-Infinity'), ('Infinity'); -- needs PG 14 ERROR: invalid input syntax for type numeric: "-Infinity" LINE 1: insert into numeric values ('-Infinity'), ('Infinity'); ^ \set relname numeric \ir run_test.sql \echo Testing :relname Testing numeric vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: numeric.heap * Options used: -D numeric ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 44 (0x002c) Block: Size 8192 Version 4 Upper 8048 (0x1f70) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 5 Free Space: 8004 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 44 ----- Item 1 -- Length: 27 Offset: 8164 (0x1fe4) Flags: NORMAL COPY: 0 Item 2 -- Length: 31 Offset: 8132 (0x1fc4) Flags: NORMAL COPY: 12341234 Item 3 -- Length: 31 Offset: 8100 (0x1fa4) Flags: NORMAL COPY: -567890 Item 4 -- Length: 27 Offset: 8072 (0x1f88) Flags: NORMAL COPY: NaN Item 5 -- Length: 24 Offset: 8048 (0x1f70) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/toast.out000066400000000000000000000122441500022671400202070ustar00rootroot00000000000000-- PG14+ output in toast.out/_3.out (32-bit); PG13- output in toast_1.out/_4.out create table toast ( description text, data text ); insert into toast values ('short inline', 'xxx'); insert into toast values ('long inline uncompressed', repeat('x', 200)); alter table toast alter column data set storage external; insert into toast values ('external uncompressed', repeat('0123456789 8< ', 200)); alter table toast alter column data set storage extended; insert into toast values ('inline compressed pglz', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed pglz', repeat('0123456789 8< ', 20000)); alter table toast alter column data set compression lz4; insert into toast values ('inline compressed lz4', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed lz4', repeat('0123456789 8< ', 50000)); vacuum toast; checkpoint; -- copy tables where client can read it \set relname 'toast' select oid as datoid from pg_database where datname = current_database() \gset select relfilenode, reltoastrelid from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as loid \gset \set output :relname '.heap' \lo_export :loid :output select lo_import(format('base/%s/%s', :'datoid', :'reltoastrelid')) as toast_loid \gset \set output :reltoastrelid \lo_export :toast_loid :output \setenv relname :relname \! pg_filedump -D text,text $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7472 (0x1d30) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7420 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8144 (0x1fd0) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7888 (0x1ed0) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7824 (0x1e90) Flags: NORMAL COPY: external uncompressed (TOASTED,uncompressed) Item 4 -- Length: 107 Offset: 7712 (0x1e20) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7640 (0x1dd8) Flags: NORMAL COPY: extended compressed pglz (TOASTED,pglz) Item 6 -- Length: 90 Offset: 7544 (0x1d78) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7472 (0x1d30) Flags: NORMAL COPY: extended compressed lz4 (TOASTED,lz4) *** End of File Encountered. Last Block Read: 0 *** \! pg_filedump -D text,text -t $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text -t ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7472 (0x1d30) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7420 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8144 (0x1fd0) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7888 (0x1ed0) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7824 (0x1e90) Flags: NORMAL TOAST value. Raw size: 2804, external size: 2800, value id: ......, toast relation id: ......, chunks: 2 COPY: external uncompressed 0123456789 8< [snipped] Item 4 -- Length: 107 Offset: 7712 (0x1e20) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7640 (0x1dd8) Flags: NORMAL TOAST value. Raw size: 280004, external size: 3226, value id: ......, toast relation id: ......, chunks: 2 COPY: extended compressed pglz 0123456789 8< [snipped] Item 6 -- Length: 90 Offset: 7544 (0x1d78) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7472 (0x1d30) Flags: NORMAL TOAST value. Raw size: 700004, external size: 2772, value id: ......, toast relation id: ......, chunks: 2 COPY: extended compressed lz4 0123456789 8< [snipped] *** End of File Encountered. Last Block Read: 0 *** pg_filedump-REL_17_4/expected/toast_1.out000066400000000000000000000125061500022671400204300ustar00rootroot00000000000000-- PG14+ output in toast.out/_3.out (32-bit); PG13- output in toast_1.out/_4.out create table toast ( description text, data text ); insert into toast values ('short inline', 'xxx'); insert into toast values ('long inline uncompressed', repeat('x', 200)); alter table toast alter column data set storage external; insert into toast values ('external uncompressed', repeat('0123456789 8< ', 200)); alter table toast alter column data set storage extended; insert into toast values ('inline compressed pglz', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed pglz', repeat('0123456789 8< ', 20000)); alter table toast alter column data set compression lz4; ERROR: syntax error at or near "compression" LINE 1: alter table toast alter column data set compression lz4; ^ insert into toast values ('inline compressed lz4', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed lz4', repeat('0123456789 8< ', 50000)); vacuum toast; checkpoint; -- copy tables where client can read it \set relname 'toast' select oid as datoid from pg_database where datname = current_database() \gset select relfilenode, reltoastrelid from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as loid \gset \set output :relname '.heap' \lo_export :loid :output select lo_import(format('base/%s/%s', :'datoid', :'reltoastrelid')) as toast_loid \gset \set output :reltoastrelid \lo_export :toast_loid :output \setenv relname :relname \! pg_filedump -D text,text $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7456 (0x1d20) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7404 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8144 (0x1fd0) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7888 (0x1ed0) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7824 (0x1e90) Flags: NORMAL COPY: external uncompressed (TOASTED,uncompressed) Item 4 -- Length: 107 Offset: 7712 (0x1e20) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7640 (0x1dd8) Flags: NORMAL COPY: extended compressed pglz (TOASTED,pglz) Item 6 -- Length: 107 Offset: 7528 (0x1d68) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7456 (0x1d20) Flags: NORMAL COPY: extended compressed lz4 (TOASTED,pglz) *** End of File Encountered. Last Block Read: 0 *** \! pg_filedump -D text,text -t $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text -t ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7456 (0x1d20) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7404 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8144 (0x1fd0) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7888 (0x1ed0) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7824 (0x1e90) Flags: NORMAL TOAST value. Raw size: 2804, external size: 2800, value id: ......, toast relation id: ......, chunks: 2 COPY: external uncompressed 0123456789 8< [snipped] Item 4 -- Length: 107 Offset: 7712 (0x1e20) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7640 (0x1dd8) Flags: NORMAL TOAST value. Raw size: 280004, external size: 3226, value id: ......, toast relation id: ......, chunks: 2 COPY: extended compressed pglz 0123456789 8< [snipped] Item 6 -- Length: 107 Offset: 7528 (0x1d68) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7456 (0x1d20) Flags: NORMAL TOAST value. Raw size: 700004, external size: 8035, value id: ......, toast relation id: ......, chunks: 5 COPY: extended compressed lz4 0123456789 8< [snipped] *** End of File Encountered. Last Block Read: 0 *** pg_filedump-REL_17_4/expected/toast_3.out000066400000000000000000000122441500022671400204310ustar00rootroot00000000000000-- PG14+ output in toast.out/_3.out (32-bit); PG13- output in toast_1.out/_4.out create table toast ( description text, data text ); insert into toast values ('short inline', 'xxx'); insert into toast values ('long inline uncompressed', repeat('x', 200)); alter table toast alter column data set storage external; insert into toast values ('external uncompressed', repeat('0123456789 8< ', 200)); alter table toast alter column data set storage extended; insert into toast values ('inline compressed pglz', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed pglz', repeat('0123456789 8< ', 20000)); alter table toast alter column data set compression lz4; insert into toast values ('inline compressed lz4', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed lz4', repeat('0123456789 8< ', 50000)); vacuum toast; checkpoint; -- copy tables where client can read it \set relname 'toast' select oid as datoid from pg_database where datname = current_database() \gset select relfilenode, reltoastrelid from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as loid \gset \set output :relname '.heap' \lo_export :loid :output select lo_import(format('base/%s/%s', :'datoid', :'reltoastrelid')) as toast_loid \gset \set output :reltoastrelid \lo_export :toast_loid :output \setenv relname :relname \! pg_filedump -D text,text $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7492 (0x1d44) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7440 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8148 (0x1fd4) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7892 (0x1ed4) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7828 (0x1e94) Flags: NORMAL COPY: external uncompressed (TOASTED,uncompressed) Item 4 -- Length: 107 Offset: 7720 (0x1e28) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7652 (0x1de4) Flags: NORMAL COPY: extended compressed pglz (TOASTED,pglz) Item 6 -- Length: 90 Offset: 7560 (0x1d88) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7492 (0x1d44) Flags: NORMAL COPY: extended compressed lz4 (TOASTED,lz4) *** End of File Encountered. Last Block Read: 0 *** \! pg_filedump -D text,text -t $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text -t ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7492 (0x1d44) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7440 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8148 (0x1fd4) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7892 (0x1ed4) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7828 (0x1e94) Flags: NORMAL TOAST value. Raw size: 2804, external size: 2800, value id: ......, toast relation id: ......, chunks: 2 COPY: external uncompressed 0123456789 8< [snipped] Item 4 -- Length: 107 Offset: 7720 (0x1e28) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7652 (0x1de4) Flags: NORMAL TOAST value. Raw size: 280004, external size: 3226, value id: ......, toast relation id: ......, chunks: 2 COPY: extended compressed pglz 0123456789 8< [snipped] Item 6 -- Length: 90 Offset: 7560 (0x1d88) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7492 (0x1d44) Flags: NORMAL TOAST value. Raw size: 700004, external size: 2772, value id: ......, toast relation id: ......, chunks: 2 COPY: extended compressed lz4 0123456789 8< [snipped] *** End of File Encountered. Last Block Read: 0 *** pg_filedump-REL_17_4/expected/toast_4.out000066400000000000000000000125061500022671400204330ustar00rootroot00000000000000-- PG14+ output in toast.out/_3.out (32-bit); PG13- output in toast_1.out/_4.out create table toast ( description text, data text ); insert into toast values ('short inline', 'xxx'); insert into toast values ('long inline uncompressed', repeat('x', 200)); alter table toast alter column data set storage external; insert into toast values ('external uncompressed', repeat('0123456789 8< ', 200)); alter table toast alter column data set storage extended; insert into toast values ('inline compressed pglz', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed pglz', repeat('0123456789 8< ', 20000)); alter table toast alter column data set compression lz4; ERROR: syntax error at or near "compression" LINE 1: alter table toast alter column data set compression lz4; ^ insert into toast values ('inline compressed lz4', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed lz4', repeat('0123456789 8< ', 50000)); vacuum toast; checkpoint; -- copy tables where client can read it \set relname 'toast' select oid as datoid from pg_database where datname = current_database() \gset select relfilenode, reltoastrelid from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as loid \gset \set output :relname '.heap' \lo_export :loid :output select lo_import(format('base/%s/%s', :'datoid', :'reltoastrelid')) as toast_loid \gset \set output :reltoastrelid \lo_export :toast_loid :output \setenv relname :relname \! pg_filedump -D text,text $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7476 (0x1d34) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7424 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8148 (0x1fd4) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7892 (0x1ed4) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7828 (0x1e94) Flags: NORMAL COPY: external uncompressed (TOASTED,uncompressed) Item 4 -- Length: 107 Offset: 7720 (0x1e28) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7652 (0x1de4) Flags: NORMAL COPY: extended compressed pglz (TOASTED,pglz) Item 6 -- Length: 107 Offset: 7544 (0x1d78) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7476 (0x1d34) Flags: NORMAL COPY: extended compressed lz4 (TOASTED,pglz) *** End of File Encountered. Last Block Read: 0 *** \! pg_filedump -D text,text -t $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: toast.heap * Options used: -D text,text -t ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 52 (0x0034) Block: Size 8192 Version 4 Upper 7476 (0x1d34) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 7 Free Space: 7424 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 52 ----- Item 1 -- Length: 41 Offset: 8148 (0x1fd4) Flags: NORMAL COPY: short inline xxx Item 2 -- Length: 256 Offset: 7892 (0x1ed4) Flags: NORMAL COPY: long inline uncompressed xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Item 3 -- Length: 64 Offset: 7828 (0x1e94) Flags: NORMAL TOAST value. Raw size: 2804, external size: 2800, value id: ......, toast relation id: ......, chunks: 2 COPY: external uncompressed 0123456789 8< [snipped] Item 4 -- Length: 107 Offset: 7720 (0x1e28) Flags: NORMAL COPY: inline compressed pglz 0123456789 8< [snipped] Item 5 -- Length: 67 Offset: 7652 (0x1de4) Flags: NORMAL TOAST value. Raw size: 280004, external size: 3226, value id: ......, toast relation id: ......, chunks: 2 COPY: extended compressed pglz 0123456789 8< [snipped] Item 6 -- Length: 107 Offset: 7544 (0x1d78) Flags: NORMAL COPY: inline compressed lz4 0123456789 8< [snipped] Item 7 -- Length: 66 Offset: 7476 (0x1d34) Flags: NORMAL TOAST value. Raw size: 700004, external size: 8035, value id: ......, toast relation id: ......, chunks: 5 COPY: extended compressed lz4 0123456789 8< [snipped] *** End of File Encountered. Last Block Read: 0 *** pg_filedump-REL_17_4/expected/xml.out000066400000000000000000000032711500022671400176550ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- server without --with-libxml support output in *_1.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table xml (x xml); insert into xml values (''), (null); \set relname xml \ir run_test.sql \echo Testing :relname Testing xml vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: xml.heap * Options used: -D xml ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8128 (0x1fc0) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8096 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 36 Offset: 8152 (0x1fd8) Flags: NORMAL COPY: Item 2 -- Length: 24 Offset: 8128 (0x1fc0) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/xml_1.out000066400000000000000000000030471500022671400200760ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- server without --with-libxml support output in *_1.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table xml (x xml); insert into xml values (''), (null); ERROR: unsupported XML feature LINE 1: insert into xml values (''), (null); ^ DETAIL: This functionality requires the server to be built with libxml support. HINT: You need to rebuild PostgreSQL using --with-libxml. \set relname xml \ir run_test.sql \echo Testing :relname Testing xml vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: xml.heap * Options used: -D xml ******************************************************************* Error: Unable to read full page header from block 0. ===> Read 0 bytes Notice: Block size determined from reading block 0 is zero, using default 8192 instead. Hint: Use -S to specify the size manually. Error: Premature end of file encountered. -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/expected/xml_3.out000066400000000000000000000032711500022671400200770ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- server without --with-libxml support output in *_1.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table xml (x xml); insert into xml values (''), (null); \set relname xml \ir run_test.sql \echo Testing :relname Testing xml vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh ******************************************************************* * PostgreSQL File/Block Formatted Dump Utility * * File: xml.heap * Options used: -D xml ******************************************************************* Block 0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 32 (0x0020) Block: Size 8192 Version 4 Upper 8132 (0x1fc4) LSN: logid ...... recoff 0x........ Special 8192 (0x2000) Items: 2 Free Space: 8100 Checksum: 0x.... Prune XID: 0x00000000 Flags: 0x0004 (ALL_VISIBLE) Length (including item array): 32 ----- Item 1 -- Length: 36 Offset: 8156 (0x1fdc) Flags: NORMAL COPY: Item 2 -- Length: 24 Offset: 8132 (0x1fc4) Flags: NORMAL COPY: \N *** End of File Encountered. Last Block Read: 0 *** -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/pg_filedump.c000066400000000000000000002211151500022671400171610ustar00rootroot00000000000000/* * pg_filedump.c - PostgreSQL file dump utility for dumping and * formatting heap (data), index and control files. * * Copyright (c) 2002-2010 Red Hat, Inc. * Copyright (c) 2011-2025, PostgreSQL Global Development Group * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Original Author: Patrick Macdonald */ #include "pg_filedump.h" #include /* checksum_impl.h uses Assert, which doesn't work outside the server */ #undef Assert #define Assert(X) #include "storage/checksum.h" #include "storage/checksum_impl.h" #include "decode.h" #include /* * Global variables for ease of use mostly */ /* Options for Block formatting operations */ unsigned int blockOptions = 0; /* Segment-related options */ unsigned int segmentOptions = 0; /* -R[start]:Block range start */ int blockStart = -1; /* -R[end]:Block range end */ int blockEnd = -1; /* Options for Item formatting operations */ unsigned int itemOptions = 0; /* Options for Control File formatting operations */ unsigned int controlOptions = 0; unsigned int specialType = SPEC_SECT_NONE; static bool verbose = false; /* File to dump or format */ static FILE *fp = NULL; /* File name for display */ char *fileName = NULL; /* Current block size */ static unsigned int blockSize = 0; /* Segment size in bytes */ static unsigned int segmentSize = RELSEG_SIZE * BLCKSZ; /* Number of current segment */ static unsigned int segmentNumber = 0; /* Offset of current block */ static unsigned int pageOffset = 0; /* Number of bytes to format */ static unsigned int bytesToFormat = 0; /* Block version number */ static unsigned int blockVersion = 0; /* Flag to indicate pg_filenode.map file */ static bool isRelMapFile = false; /* Program exit code */ static int exitCode = 0; /* Relmapper structs */ typedef struct RelMapping { Oid mapoid; /* OID of a catalog */ Oid mapfilenode; /* its filenode number */ } RelMapping; /* crc and pad are ignored here, even though they are * present in the backend code. We assume that anyone * seeking to inspect the contents of pg_filenode.map * probably have a corrupted or non-functional cluster */ typedef struct RelMapFile { int32 magic; /* always RELMAPPER_FILEMAGIC */ int32 num_mappings; /* number of valid RelMapping entries */ RelMapping mappings[FLEXIBLE_ARRAY_MEMBER]; } RelMapFile; /* * Function Prototypes */ unsigned int GetBlockSize(FILE *fp); static void DisplayOptions(unsigned int validOptions); static unsigned int ConsumeOptions(int numOptions, char **options); static int GetOptionValue(char *optionString); static void FormatBlock(unsigned int blockOptions, unsigned int controlOptions, char *buffer, BlockNumber currentBlock, unsigned int blockSize, bool isToast, Oid toastOid, uint32 *want_chunk_id, unsigned int toastExternalSize, char *toastValue, unsigned int *toastRead); static unsigned int GetSpecialSectionType(char *buffer, Page page); static bool IsBtreeMetaPage(Page page); static void CreateDumpFileHeader(int numOptions, char **options); static int FormatHeader(char *buffer, Page page, BlockNumber blkno, bool isToast); static void FormatItemBlock(char *buffer, Page page, bool isToast, Oid toastOid, uint32 *want_chunk_id, unsigned int toastExternalSize, char *toastValue, unsigned int *toastRead); static void FormatItem(char *buffer, unsigned int numBytes, unsigned int startIndex, unsigned int formatAs); static void FormatSpecial(char *buffer); static void FormatControl(char *buffer); static void FormatBinary(char *buffer, unsigned int numBytes, unsigned int startIndex); static void DumpBinaryBlock(char *buffer); static int PrintRelMappings(void); static ItemPointer ginPostingListDecodeAllSegmentsItems( GinPostingList *segment, int len, int *ndecoded_out); static ItemPointer ginReadTupleItems( IndexTuple itup, int *nitems); /* Send properly formed usage information to the user. */ static void DisplayOptions(unsigned int validOptions) { if (validOptions == OPT_RC_COPYRIGHT) printf ("\nVersion %s (for %s)" "\nCopyright (c) 2002-2010 Red Hat, Inc." "\nCopyright (c) 2011-2025, PostgreSQL Global Development Group\n", FD_VERSION, FD_PG_VERSION); printf ("\nUsage: pg_filedump [-abcdfhikxy] [-R startblock [endblock]] [-D attrlist] [-S blocksize] [-s segsize] [-n segnumber] file\n\n" "Display formatted contents of a PostgreSQL heap/index/control file\n" "Defaults are: relative addressing, range of the entire file, block\n" " size as listed on block 0 in the file\n\n" "The following options are valid for heap and index files:\n" " -a Display absolute addresses when formatting (Block header\n" " information is always block relative)\n" " -b Display binary block images within a range (Option will turn\n" " off all formatting options)\n" " -d Display formatted block content dump (Option will turn off\n" " all other formatting options)\n" " -D Decode tuples using given comma separated list of types\n" " Supported types:\n" " bigint bigserial bool char charN date float float4 float8 int\n" " json macaddr name numeric oid real serial smallint smallserial text\n" " time timestamp timestamptz timetz uuid varchar varcharN xid xml\n" " ~ ignores all attributes left in a tuple\n" " -f Display formatted block content dump along with interpretation\n" " -h Display this information\n" " -i Display interpreted item details\n" " -k Verify block checksums\n" " -o Do not dump old values.\n" " -R Display specific block ranges within the file (Blocks are\n" " indexed from 0)\n" " [startblock]: block to start at\n" " [endblock]: block to end at\n" " A startblock without an endblock will format the single block\n" " -s Force segment size to [segsize]\n" " -t Dump TOAST files\n" " -v Ouput additional information about TOAST relations\n" " -n Force segment number to [segnumber]\n" " -S Force block size to [blocksize]\n" " -x Force interpreted formatting of block items as index items\n" " -y Force interpreted formatting of block items as heap items\n\n" "The following options are valid for control files:\n" " -c Interpret the file listed as a control file\n" " -f Display formatted content dump along with interpretation\n" " -S Force block size to [blocksize]\n" "Additional functions:\n" " -m Interpret file as pg_filenode.map file and print contents (all\n" " other options will be ignored)\n" "\nReport bugs to \n"); } /* * Determine segment number by segment file name. For instance, if file * name is /path/to/xxxx.7 procedure returns 7. Default return value is 0. */ static unsigned int GetSegmentNumberFromFileName(const char *fileName) { int segnumOffset = strlen(fileName) - 1; if (segnumOffset < 0) return 0; while (isdigit(fileName[segnumOffset])) { segnumOffset--; if (segnumOffset < 0) return 0; } if (fileName[segnumOffset] != '.') return 0; return atoi(&fileName[segnumOffset + 1]); } /* Iterate through the provided options and set the option flags. * An error will result in a positive rc and will force a display * of the usage information. This routine returns enum * optionReturnCode values. */ static unsigned int ConsumeOptions(int numOptions, char **options) { unsigned int rc = OPT_RC_VALID; unsigned int x; unsigned int optionStringLength; char *optionString; char duplicateSwitch = 0x00; for (x = 1; x < numOptions; x++) { optionString = options[x]; optionStringLength = strlen(optionString); /* Range is a special case where we have to consume the next 1 or 2 * parameters to mark the range start and end */ if ((optionStringLength == 2) && (strcmp(optionString, "-R") == 0)) { int range = 0; SET_OPTION(blockOptions, BLOCK_RANGE, 'R'); /* Only accept the range option once */ if (rc == OPT_RC_DUPLICATE) break; /* Make sure there are options after the range identifier */ if (x >= (numOptions - 2)) { rc = OPT_RC_INVALID; printf("Error: Missing range start identifier.\n"); exitCode = 1; break; } /* * Mark that we have the range and advance the option to what * should be the range start. Check the value of the next * parameter */ optionString = options[++x]; if ((range = GetOptionValue(optionString)) < 0) { rc = OPT_RC_INVALID; printf("Error: Invalid range start identifier <%s>.\n", optionString); exitCode = 1; break; } /* The default is to dump only one block */ blockStart = blockEnd = (unsigned int) range; /* We have our range start marker, check if there is an end * marker on the option line. Assume that the last option * is the file we are dumping, so check if there are options * range start marker and the file */ if (x <= (numOptions - 3)) { if ((range = GetOptionValue(options[x + 1])) >= 0) { /* End range must be => start range */ if (blockStart <= range) { blockEnd = (unsigned int) range; x++; } else { rc = OPT_RC_INVALID; printf("Error: Requested block range start <%d> is " "greater than end <%d>.\n", blockStart, range); exitCode = 1; break; } } } } /* Check for the special case where the user forces a block size * instead of having the tool determine it. This is useful if * the header of block 0 is corrupt and gives a garbage block size */ else if ((optionStringLength == 2) && (strcmp(optionString, "-S") == 0)) { int localBlockSize; SET_OPTION(blockOptions, BLOCK_FORCED, 'S'); /* Only accept the forced size option once */ if (rc == OPT_RC_DUPLICATE) break; /* The token immediately following -S is the block size */ if (x >= (numOptions - 2)) { rc = OPT_RC_INVALID; printf("Error: Missing block size identifier.\n"); break; } /* Next option encountered must be forced block size */ optionString = options[++x]; if ((localBlockSize = GetOptionValue(optionString)) > 0) blockSize = (unsigned int) localBlockSize; else { rc = OPT_RC_INVALID; printf("Error: Invalid block size requested <%s>.\n", optionString); exitCode = 1; break; } } /* Check for the special case where the user forces a segment size. */ else if ((optionStringLength == 2) && (strcmp(optionString, "-s") == 0)) { int localSegmentSize; SET_OPTION(segmentOptions, SEGMENT_SIZE_FORCED, 's'); /* Only accept the forced size option once */ if (rc == OPT_RC_DUPLICATE) break; /* The token immediately following -s is the segment size */ if (x >= (numOptions - 2)) { rc = OPT_RC_INVALID; printf("Error: Missing segment size identifier.\n"); exitCode = 1; break; } /* Next option encountered must be forced segment size */ optionString = options[++x]; if ((localSegmentSize = GetOptionValue(optionString)) > 0) segmentSize = (unsigned int) localSegmentSize; else { rc = OPT_RC_INVALID; printf("Error: Invalid segment size requested <%s>.\n", optionString); exitCode = 1; break; } } /* Check for the special case where the user forces tuples decoding. */ else if ((optionStringLength == 2) && (strcmp(optionString, "-D") == 0)) { SET_OPTION(blockOptions, BLOCK_DECODE, 'D'); /* Only accept the decode option once */ if (rc == OPT_RC_DUPLICATE) break; /* The token immediately following -D is attrubute types string */ if (x >= (numOptions - 2)) { rc = OPT_RC_INVALID; printf("Error: Missing attribute types string.\n"); exitCode = 1; break; } /* Next option encountered must be attribute types string */ optionString = options[++x]; if (ParseAttributeTypesString(optionString) < 0) { rc = OPT_RC_INVALID; printf("Error: Invalid attribute types string <%s>.\n", optionString); exitCode = 1; break; } } /* Check for the special case where the user forces a segment number * instead of having the tool determine it by file name. */ else if ((optionStringLength == 2) && (strcmp(optionString, "-n") == 0)) { int localSegmentNumber; SET_OPTION(segmentOptions, SEGMENT_NUMBER_FORCED, 'n'); /* Only accept the forced segment number option once */ if (rc == OPT_RC_DUPLICATE) break; /* The token immediately following -n is the segment number */ if (x >= (numOptions - 2)) { rc = OPT_RC_INVALID; printf("Error: Missing segment number identifier.\n"); exitCode = 1; break; } /* Next option encountered must be forced segment number */ optionString = options[++x]; if ((localSegmentNumber = GetOptionValue(optionString)) > 0) segmentNumber = (unsigned int) localSegmentNumber; else { rc = OPT_RC_INVALID; printf("Error: Invalid segment number requested <%s>.\n", optionString); exitCode = 1; break; } } /* The last option MUST be the file name */ else if (x == (numOptions - 1)) { /* Check to see if this looks like an option string before opening */ if (optionString[0] != '-') { fp = fopen(optionString, "rb"); if (fp) { fileName = options[x]; if (!(segmentOptions & SEGMENT_NUMBER_FORCED)) segmentNumber = GetSegmentNumberFromFileName(fileName); } else { rc = OPT_RC_FILE; printf("Error: Could not open file <%s>.\n", optionString); exitCode = 1; break; } } else { /* Could be the case where the help flag is used without a * filename. Otherwise, the last option isn't a file */ if (strcmp(optionString, "-h") == 0) rc = OPT_RC_COPYRIGHT; else { rc = OPT_RC_FILE; printf("Error: Missing file name to dump.\n"); exitCode = 1; } break; } } else { unsigned int y; /* Option strings must start with '-' and contain switches */ if (optionString[0] != '-') { rc = OPT_RC_INVALID; printf("Error: Invalid option string <%s>.\n", optionString); exitCode = 1; break; } /* Iterate through the singular option string, throw out * garbage, duplicates and set flags to be used in formatting */ for (y = 1; y < optionStringLength; y++) { switch (optionString[y]) { /* Use absolute addressing */ case 'a': SET_OPTION(blockOptions, BLOCK_ABSOLUTE, 'a'); break; /* Dump the binary contents of the page */ case 'b': SET_OPTION(blockOptions, BLOCK_BINARY, 'b'); break; /* Dump the listed file as a control file */ case 'c': SET_OPTION(controlOptions, CONTROL_DUMP, 'c'); break; /* Do not interpret the data. Format to hex and ascii. */ case 'd': SET_OPTION(blockOptions, BLOCK_NO_INTR, 'd'); break; /* * Format the contents of the block with * interpretation of the headers */ case 'f': SET_OPTION(blockOptions, BLOCK_FORMAT, 'f'); break; /* Display the usage screen */ case 'h': rc = OPT_RC_COPYRIGHT; break; /* Format the items in detail */ case 'i': SET_OPTION(itemOptions, ITEM_DETAIL, 'i'); break; /* Verify block checksums */ case 'k': SET_OPTION(blockOptions, BLOCK_CHECKSUMS, 'k'); break; /* Treat file as pg_filenode.map file */ case 'm': isRelMapFile = true; break; /* Display old values. Ignore Xmax */ case 'o': SET_OPTION(blockOptions, BLOCK_IGNORE_OLD, 'o'); break; case 't': SET_OPTION(blockOptions, BLOCK_DECODE_TOAST, 't'); break; case 'v': verbose = true; break; /* Interpret items as standard index values */ case 'x': SET_OPTION(itemOptions, ITEM_INDEX, 'x'); if (itemOptions & ITEM_HEAP) { rc = OPT_RC_INVALID; printf("Error: Options and are " "mutually exclusive.\n"); exitCode = 1; } break; /* Interpret items as heap values */ case 'y': SET_OPTION(itemOptions, ITEM_HEAP, 'y'); if (itemOptions & ITEM_INDEX) { rc = OPT_RC_INVALID; printf("Error: Options and are " "mutually exclusive.\n"); exitCode = 1; } break; default: rc = OPT_RC_INVALID; printf("Error: Unknown option <%c>.\n", optionString[y]); exitCode = 1; break; } if (rc) break; } } } if (rc == OPT_RC_DUPLICATE) { printf("Error: Duplicate option listed <%c>.\n", duplicateSwitch); exitCode = 1; } /* If the user requested a control file dump, a pure binary * block dump or a non-interpreted formatted dump, mask off * all other block level options (with a few exceptions) */ if (rc == OPT_RC_VALID) { /* The user has requested a control file dump, only -f and */ /* -S are valid... turn off all other formatting */ if (controlOptions & CONTROL_DUMP) { if ((blockOptions & ~(BLOCK_FORMAT | BLOCK_FORCED)) || (itemOptions)) { rc = OPT_RC_INVALID; printf("Error: Invalid options used for Control File dump.\n" " Only options may be used with .\n"); exitCode = 1; } else { controlOptions |= (blockOptions & (BLOCK_FORMAT | BLOCK_FORCED)); blockOptions = itemOptions = 0; } } /* The user has requested a binary block dump... only -R and -f * are honoured */ else if (blockOptions & BLOCK_BINARY) { blockOptions &= (BLOCK_BINARY | BLOCK_RANGE | BLOCK_FORCED); itemOptions = 0; } /* The user has requested a non-interpreted dump... only -a, -R * and -f are honoured */ else if (blockOptions & BLOCK_NO_INTR) { blockOptions &= (BLOCK_NO_INTR | BLOCK_ABSOLUTE | BLOCK_RANGE | BLOCK_FORCED); itemOptions = 0; } } return (rc); } /* Given the index into the parameter list, convert and return the * current string to a number if possible */ static int GetOptionValue(char *optionString) { unsigned int x; int value = -1; int optionStringLength = strlen(optionString); /* Verify the next option looks like a number */ for (x = 0; x < optionStringLength; x++) if (!isdigit((int) optionString[x])) break; /* Convert the string to a number if it looks good */ if (x == optionStringLength) value = atoi(optionString); return (value); } /* Read the page header off of block 0 to determine the block size * used in this file. Can be overridden using the -S option. The * returned value is the block size of block 0 on disk */ unsigned int GetBlockSize(FILE *fp) { unsigned int localSize = 0; int bytesRead = 0; char localCache[sizeof(PageHeaderData)]; /* Read the first header off of block 0 to determine the block size */ bytesRead = fread(&localCache, 1, sizeof(PageHeaderData), fp); rewind(fp); if (bytesRead == sizeof(PageHeaderData)) localSize = (unsigned int) PageGetPageSize(localCache); else { printf("Error: Unable to read full page header from block 0.\n" " ===> Read %u bytes\n", bytesRead); exitCode = 1; } if (localSize == 0) { printf("Notice: Block size determined from reading block 0 is zero, using default %d instead.\n", BLCKSZ); printf("Hint: Use -S to specify the size manually.\n"); localSize = BLCKSZ; } return (localSize); } /* Determine the contents of the special section on the block and * return this enum value */ static unsigned int GetSpecialSectionType(char *buffer, Page page) { unsigned int rc; unsigned int specialOffset; unsigned int specialSize; unsigned int specialValue; PageHeader pageHeader = (PageHeader) page; /* If this is not a partial header, check the validity of the * special section offset and contents */ if (bytesToFormat > sizeof(PageHeaderData)) { specialOffset = (unsigned int) pageHeader->pd_special; /* Check that the special offset can remain on the block or * the partial block */ if ((specialOffset == 0) || (specialOffset > blockSize) || (specialOffset > bytesToFormat)) rc = SPEC_SECT_ERROR_BOUNDARY; else { /* we may need to examine last 2 bytes of page to identify index */ uint16 *ptype = (uint16 *) (buffer + blockSize - sizeof(uint16)); specialSize = blockSize - specialOffset; /* If there is a special section, use its size to guess its * contents, checking the last 2 bytes of the page in cases * that are ambiguous. Note we don't attempt to dereference * the pointers without checking bytesToFormat == blockSize. */ if (specialSize == 0) rc = SPEC_SECT_NONE; else if (specialSize == MAXALIGN(sizeof(uint32))) { /* If MAXALIGN is 8, this could be either a sequence or * SP-GiST or GIN. */ if (bytesToFormat == blockSize) { specialValue = *((int *) (buffer + specialOffset)); if (specialValue == SEQUENCE_MAGIC) rc = SPEC_SECT_SEQUENCE; else if (specialSize == MAXALIGN(sizeof(SpGistPageOpaqueData)) && *ptype == SPGIST_PAGE_ID) rc = SPEC_SECT_INDEX_SPGIST; else if (specialSize == MAXALIGN(sizeof(GinPageOpaqueData))) rc = SPEC_SECT_INDEX_GIN; else rc = SPEC_SECT_ERROR_UNKNOWN; } else rc = SPEC_SECT_ERROR_UNKNOWN; } /* SP-GiST and GIN have same size special section, so check * the page ID bytes first. */ else if (specialSize == MAXALIGN(sizeof(SpGistPageOpaqueData)) && bytesToFormat == blockSize && *ptype == SPGIST_PAGE_ID) rc = SPEC_SECT_INDEX_SPGIST; else if (specialSize == MAXALIGN(sizeof(GinPageOpaqueData))) rc = SPEC_SECT_INDEX_GIN; else if (specialSize > 2 && bytesToFormat == blockSize) { /* As of 8.3, BTree, Hash, and GIST all have the same size * special section, but the last two bytes of the section * can be checked to determine what's what. */ if (*ptype <= MAX_BT_CYCLE_ID && specialSize == MAXALIGN(sizeof(BTPageOpaqueData))) rc = SPEC_SECT_INDEX_BTREE; else if (*ptype == HASHO_PAGE_ID && specialSize == MAXALIGN(sizeof(HashPageOpaqueData))) rc = SPEC_SECT_INDEX_HASH; else if (*ptype == GIST_PAGE_ID && specialSize == MAXALIGN(sizeof(GISTPageOpaqueData))) rc = SPEC_SECT_INDEX_GIST; else rc = SPEC_SECT_ERROR_UNKNOWN; } else rc = SPEC_SECT_ERROR_UNKNOWN; } } else rc = SPEC_SECT_ERROR_UNKNOWN; return (rc); } /* Check whether page is a btree meta page */ static bool IsBtreeMetaPage(Page page) { PageHeader pageHeader = (PageHeader) page; if ((PageGetSpecialSize(page) == (MAXALIGN(sizeof(BTPageOpaqueData)))) && (bytesToFormat == blockSize)) { BTPageOpaque btpo = (BTPageOpaque) ((char *) page + pageHeader->pd_special); /* Must check the cycleid to be sure it's really btree. */ if ((btpo->btpo_cycleid <= MAX_BT_CYCLE_ID) && (btpo->btpo_flags & BTP_META)) return true; } return false; } /* Check whether page is a gin meta page */ static bool IsGinMetaPage(Page page) { if ((PageGetSpecialSize(page) == (MAXALIGN(sizeof(GinPageOpaqueData)))) && (bytesToFormat == blockSize)) { GinPageOpaque gpo = GinPageGetOpaque(page); if (gpo->flags & GIN_META) return true; } return false; } /* Check whether page is a SpGist meta page */ static bool IsSpGistMetaPage(Page page) { if ((PageGetSpecialSize(page) == (MAXALIGN(sizeof(SpGistPageOpaqueData)))) && (bytesToFormat == blockSize)) { SpGistPageOpaque spgpo = SpGistPageGetOpaque(page); if ((spgpo->spgist_page_id == SPGIST_PAGE_ID) && (spgpo->flags & SPGIST_META)) return true; } return false; } /* Display a header for the dump so we know the file name, the options * used and the time the dump was taken */ static void CreateDumpFileHeader(int numOptions, char **options) { unsigned int x; char optionBuffer[52] = "\0"; /* Iterate through the options and cache them. * The maximum we can display is 50 option characters + spaces. */ for (x = 1; x < (numOptions - 1); x++) { if ((strlen(optionBuffer) + strlen(options[x])) > 50) break; strcat(optionBuffer, options[x]); if (x < numOptions - 2) strcat(optionBuffer, " "); } printf ("\n*******************************************************************\n" "* PostgreSQL File/Block Formatted Dump Utility\n" "*\n" "* File: %s\n" "* Options used: %s\n" "*******************************************************************\n", fileName, (strlen(optionBuffer)) ? optionBuffer : "None"); } /* Dump out a formatted block header for the requested block */ static int FormatHeader(char *buffer, Page page, BlockNumber blkno, bool isToast) { int rc = 0; unsigned int headerBytes; PageHeader pageHeader = (PageHeader) page; char *indent = isToast ? "\t" : ""; if (!isToast || verbose) printf("%s
-----\n", indent); /* Only attempt to format the header if the entire header (minus the item * array) is available */ if (bytesToFormat < offsetof(PageHeaderData, pd_linp[0])) { headerBytes = bytesToFormat; rc = EOF_ENCOUNTERED; } else { XLogRecPtr pageLSN = PageGetLSN(page); int maxOffset = PageGetMaxOffsetNumber(page); char flagString[100]; headerBytes = offsetof(PageHeaderData, pd_linp[0]); blockVersion = (unsigned int) PageGetPageLayoutVersion(page); /* The full header exists but we have to check that the item array * is available or how far we can index into it */ if (maxOffset > 0) { unsigned int itemsLength = maxOffset * sizeof(ItemIdData); if (bytesToFormat < (headerBytes + itemsLength)) { headerBytes = bytesToFormat; rc = EOF_ENCOUNTERED; } else headerBytes += itemsLength; } flagString[0] = '\0'; if (pageHeader->pd_flags & PD_HAS_FREE_LINES) strcat(flagString, "HAS_FREE_LINES|"); if (pageHeader->pd_flags & PD_PAGE_FULL) strcat(flagString, "PAGE_FULL|"); if (pageHeader->pd_flags & PD_ALL_VISIBLE) strcat(flagString, "ALL_VISIBLE|"); if (strlen(flagString)) flagString[strlen(flagString) - 1] = '\0'; /* Interpret the content of the header */ if (!isToast || verbose) { printf("%s Block Offset: 0x%08x Offsets: Lower %4u (0x%04hx)\n", indent, pageOffset, pageHeader->pd_lower, pageHeader->pd_lower); printf("%s Block: Size %4d Version %4u Upper %4u (0x%04hx)\n", indent, (int) PageGetPageSize(page), blockVersion, pageHeader->pd_upper, pageHeader->pd_upper); printf("%s LSN: logid %6d recoff 0x%08x Special %4u (0x%04hx)\n", indent, (uint32) (pageLSN >> 32), (uint32) pageLSN, pageHeader->pd_special, pageHeader->pd_special); printf("%s Items: %4d Free Space: %4u\n", indent, maxOffset, pageHeader->pd_upper - pageHeader->pd_lower); printf("%s Checksum: 0x%04x Prune XID: 0x%08x Flags: 0x%04x (%s)\n", indent, pageHeader->pd_checksum, pageHeader->pd_prune_xid, pageHeader->pd_flags, flagString); printf("%s Length (including item array): %u\n\n", indent, headerBytes); } /* If it's a btree meta page, print the contents of the meta block. */ if (IsBtreeMetaPage(page)) { BTMetaPageData *btpMeta = BTPageGetMeta(buffer); if (!isToast || verbose) { printf("%s BTree Meta Data: Magic (0x%08x) Version (%u)\n", indent, btpMeta->btm_magic, btpMeta->btm_version); printf("%s Root: Block (%u) Level (%u)\n", indent, btpMeta->btm_root, btpMeta->btm_level); printf("%s FastRoot: Block (%u) Level (%u)\n\n", indent, btpMeta->btm_fastroot, btpMeta->btm_fastlevel); } headerBytes += sizeof(BTMetaPageData); } else if (IsGinMetaPage(page)) { GinMetaPageData *gpMeta = GinPageGetMeta(buffer); if (!isToast || verbose) { printf("%s GIN Meta Data: Version (%u)\n", indent, gpMeta->ginVersion); printf("%s Pending list: Head: (%u) Tail: (%u) Tail Free Size: (%u)\n", indent, gpMeta->head, gpMeta->tail, gpMeta->tailFreeSize); printf("%s Num of Pending Pages: (%u) Num of Pending Heap Tuples: (%" PRIu64 ")\n", indent, gpMeta->nPendingPages, gpMeta->nPendingHeapTuples); printf("%s Statistic for planner: Num of Total Pages: (%u) Num of Entry Pages: (%u)\n", indent, gpMeta->nTotalPages, gpMeta->nEntryPages); printf("%s Num of Data Pages: (%u) Num of Entries: (%" PRIu64 ")\n\n", indent, gpMeta->nDataPages, gpMeta->nEntries); } headerBytes += sizeof(GinMetaPageData); } /* Eye the contents of the header and alert the user to possible * problems. */ if ((maxOffset < 0) || (maxOffset > blockSize) || (blockVersion != PG_PAGE_LAYOUT_VERSION) || /* only one we support */ (pageHeader->pd_upper > blockSize) || (pageHeader->pd_upper > pageHeader->pd_special) || (pageHeader->pd_lower < (sizeof(PageHeaderData) - sizeof(ItemIdData))) || (pageHeader->pd_lower > blockSize) || (pageHeader->pd_upper < pageHeader->pd_lower) || (pageHeader->pd_special > blockSize)) { printf(" Error: Invalid header information.\n\n"); exitCode = 1; } if (blockOptions & BLOCK_CHECKSUMS) { uint32 delta = (segmentSize / blockSize) * segmentNumber; uint16 calc_checksum = pg_checksum_page(page, delta + blkno); if (calc_checksum != pageHeader->pd_checksum) { printf(" Error: checksum failure: calculated 0x%04x.\n\n", calc_checksum); exitCode = 1; } } } /* If we have reached the end of file while interpreting the header, let * the user know about it */ if (rc == EOF_ENCOUNTERED) { if (!isToast || verbose) { printf("%s Error: End of block encountered within the header." " Bytes read: %4u.\n\n", indent, bytesToFormat); } exitCode = 1; } /* A request to dump the formatted binary of the block (header, * items and special section). It's best to dump even on an error * so the user can see the raw image. */ if (blockOptions & BLOCK_FORMAT) FormatBinary(buffer, headerBytes, 0); return (rc); } /* Copied from ginpostinglist.c */ #define MaxHeapTuplesPerPageBits 11 static uint64 itemptr_to_uint64(const ItemPointer iptr) { uint64 val; val = GinItemPointerGetBlockNumber(iptr); val <<= MaxHeapTuplesPerPageBits; val |= GinItemPointerGetOffsetNumber(iptr); return val; } static void uint64_to_itemptr(uint64 val, ItemPointer iptr) { GinItemPointerSetOffsetNumber(iptr, val & ((1 << MaxHeapTuplesPerPageBits) - 1)); val = val >> MaxHeapTuplesPerPageBits; GinItemPointerSetBlockNumber(iptr, val); } /* * Decode varbyte-encoded integer at *ptr. *ptr is incremented to next integer. */ static uint64 decode_varbyte(unsigned char **ptr) { uint64 val; unsigned char *p = *ptr; uint64 c; /* 1st byte */ c = *(p++); val = c & 0x7F; if (c & 0x80) { /* 2nd byte */ c = *(p++); val |= (c & 0x7F) << 7; if (c & 0x80) { /* 3rd byte */ c = *(p++); val |= (c & 0x7F) << 14; if (c & 0x80) { /* 4th byte */ c = *(p++); val |= (c & 0x7F) << 21; if (c & 0x80) { /* 5th byte */ c = *(p++); val |= (c & 0x7F) << 28; if (c & 0x80) { /* 6th byte */ c = *(p++); val |= (c & 0x7F) << 35; if (c & 0x80) { /* 7th byte, should not have continuation bit */ c = *(p++); val |= c << 42; Assert((c & 0x80) == 0); } } } } } } *ptr = p; return val; } static ItemPointer ginPostingListDecodeAllSegmentsItems(GinPostingList *segment, int len, int *ndecoded_out) { ItemPointer result; int nallocated; uint64 val; char *endseg = ((char *) segment) + len; int ndecoded; unsigned char *ptr; unsigned char *endptr; /* * Guess an initial size of the array. */ nallocated = segment->nbytes * 2 + 1; result = palloc(nallocated * sizeof(ItemPointerData)); ndecoded = 0; while ((char *) segment < endseg) { /* enlarge output array if needed */ if (ndecoded >= nallocated) { nallocated *= 2; result = repalloc(result, nallocated * sizeof(ItemPointerData)); } /* copy the first item */ result[ndecoded] = segment->first; ndecoded++; val = itemptr_to_uint64(&segment->first); ptr = segment->bytes; endptr = segment->bytes + segment->nbytes; while (ptr < endptr) { /* enlarge output array if needed */ if (ndecoded >= nallocated) { nallocated *= 2; result = repalloc(result, nallocated * sizeof(ItemPointerData)); } val += decode_varbyte(&ptr); uint64_to_itemptr(val, &result[ndecoded]); ndecoded++; } segment = GinNextPostingListSegment(segment); } if (ndecoded_out) *ndecoded_out = ndecoded; return result; } static ItemPointer ginReadTupleItems(IndexTuple itup, int *nitems) { Pointer ptr = GinGetPosting(itup); int nipd = GinGetNPosting(itup); ItemPointer ipd; if (GinItupIsCompressed(itup)) { if (nipd > 0) { ipd = ginPostingListDecodeAllSegmentsItems((GinPostingList *)ptr, SizeOfGinPostingList((GinPostingList *)ptr), nitems); } else { ipd = palloc(0); } } else { ipd = (ItemPointer) palloc(sizeof(ItemPointerData) * nipd); memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd); } *nitems = nipd; return ipd; } /* Dump out gin-specific content of block */ static void FormatGinBlock(char *buffer, bool isToast, Oid toastOid, unsigned int toastExternalSize, char *toastValue, unsigned int *toastRead) { Page page = (Page) buffer; char *indent = isToast ? "\t" : ""; if (isToast && !verbose) return; if (GinPageIsDeleted(page)) { printf("%s Deleted page.\n\n", indent); return; } printf("%s -----\n", indent); if (GinPageIsData(page) && GinPageIsLeaf(page)) { printf("\n%s Leaf Page of TID B-tree\n",indent); if (GinPageIsCompressed(page)) { GinPostingList *seg = GinDataLeafPageGetPostingList(page); int plist_idx = 1; Size len = GinDataLeafPageGetPostingListSize(page); Pointer endptr = ((Pointer) seg) + len; ItemPointer cur; while ((Pointer) seg < endptr) { int item_idx = 1; uint64 val; unsigned char *endseg = seg->bytes + seg->nbytes; unsigned char *ptr = seg->bytes; cur = &seg->first; printf("\n%s Posting List %3d -- Length: %4u\n", indent, plist_idx, seg->nbytes); printf("%s ItemPointer %3d -- Block Id: %4u linp Index: %4u\n", indent, item_idx, ((uint32) ((cur->ip_blkid.bi_hi << 16) | (uint16) cur->ip_blkid.bi_lo)), cur->ip_posid); val = itemptr_to_uint64(&seg->first); while (ptr < endseg) { val += decode_varbyte(&ptr); item_idx++; uint64_to_itemptr(val, cur); printf("%s ItemPointer %3d -- Block Id: %4u linp Index: %4u\n", indent, item_idx, ((uint32) ((cur->ip_blkid.bi_hi << 16) | (uint16) cur->ip_blkid.bi_lo)), cur->ip_posid); } plist_idx++; seg = GinNextPostingListSegment(seg); } } else { int i, nitems = GinPageGetOpaque(page)->maxoff; ItemPointer items = (ItemPointer) GinDataPageGetData(page); for (i = 0; i < nitems; i++) { printf("%s ItemPointer %d -- Block Id: %u linp Index: %u\n", indent, i + 1, ((uint32) ((items[i].ip_blkid.bi_hi << 16) | (uint16) items[i].ip_blkid.bi_lo)), items[i].ip_posid); } } } else if (!GinPageIsData(page) && GinPageIsLeaf(page)) { printf("\n%s Leaf Page of Element B-tree", indent); for (int offset = FirstOffsetNumber; offset <= PageGetMaxOffsetNumber(page); offset++) { IndexTuple itup; itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offset)); if (!GinIsPostingTree(itup)) { int nitems; ItemPointer items = ginReadTupleItems(itup, &nitems); printf("\n%s Posting List %3d -- Length: %4u -- isCompressed: %d\n", indent, offset, nitems, GinItupIsCompressed(itup)); for (int i = 0; i < nitems; i++) { printf("%s ItemPointer %d -- Block Id: %u linp Index: %u\n", indent, i + 1, ((uint32) ((items[i].ip_blkid.bi_hi << 16) | (uint16) items[i].ip_blkid.bi_lo)), items[i].ip_posid); } pfree(items); } else { BlockNumber rootPostingTree = GinGetPostingTree(itup); printf("\n%s Root posting tree -- Block Id: %u\n", indent, rootPostingTree); } } } else if (!GinPageIsLeaf(page) && GinPageIsData(page)) { OffsetNumber cur, high = GinPageGetOpaque(page)->maxoff; /* number of PostingItems on GIN_DATA & ~GIN_LEAF page.*/ PostingItem *pitem = NULL; ItemPointer rightBound = GinDataPageGetRightBound(page); printf("%s Internal Page of TID B-tree -- Block Id: %u linp Index: %u\n", indent, ((uint32) ((rightBound->ip_blkid.bi_hi << 16) | (uint16) rightBound->ip_blkid.bi_lo)), rightBound->ip_posid); for (cur = FirstOffsetNumber; cur <= high; cur = OffsetNumberNext(cur)) { pitem = GinDataPageGetPostingItem(page, cur); printf("%s PostingItem %d -- child Block Id: (%u) Key Block Id: %u Key linp Index: %u\n", indent, cur, ((uint32) ((pitem->child_blkno.bi_hi << 16) | (uint16) pitem->child_blkno.bi_lo)), ((uint32) ((pitem->key.ip_blkid.bi_hi << 16) | (uint16) pitem->key.ip_blkid.bi_lo)), pitem->key.ip_posid); } } else if (GinPageIsList(page)) { printf("\n%s Pending List Page -- Length: %4u\n", indent, GinPageGetOpaque(page)->maxoff); //Fast list index page, not compressed for (OffsetNumber offset = FirstOffsetNumber; offset <= GinPageGetOpaque(page)->maxoff; offset = OffsetNumberNext(offset)) { IndexTuple itup; itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offset)); printf("%s ItemPointer -- Block Id: %u linp Index: %u\n", indent, ((uint32) ((itup->t_tid.ip_blkid.bi_hi << 16) | (uint16) itup->t_tid.ip_blkid.bi_lo)), itup->t_tid.ip_posid); } } else if (GinPageGetOpaque(page)->flags == 0) { /*details postgrespro/src/backend/access/gin/ginentrypage::GinFormInteriorTuple, *the specified child block number is inserted into t_tid. */ printf("\n%s Internal Page of Element B-tree \n", indent); for (OffsetNumber offset = FirstOffsetNumber; offset <= PageGetMaxOffsetNumber(page); offset = OffsetNumberNext(offset)) { IndexTuple itup; itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offset)); printf("%s ItemPointer -- Block Id: %u linp Index: %u\n", indent, ((uint32) ((itup->t_tid.ip_blkid.bi_hi << 16) | (uint16) itup->t_tid.ip_blkid.bi_lo)), itup->t_tid.ip_posid); } } else { printf("%s Error: Unknown page type.\n", indent); exitCode = 1; } printf("\n"); } /* Dump out formatted items that reside on this block */ static void FormatItemBlock(char *buffer, Page page, bool isToast, Oid toastOid, uint32 *want_chunk_id, unsigned int toastExternalSize, char *toastValue, unsigned int *toastRead) { unsigned int x; unsigned int itemSize; unsigned int itemOffset; unsigned int itemFlags; ItemId itemId; int maxOffset = PageGetMaxOffsetNumber(page); char *indent = isToast ? "\t" : ""; /* If it's a btree meta page, the meta block is where items would normally * be; don't print garbage. */ if (IsBtreeMetaPage(page)) return; /* Same as above */ if (IsSpGistMetaPage(page)) return; /* Same as above */ if (IsGinMetaPage(page)) return; /* Leaf pages of GIN index contain posting lists * instead of item array. */ if (specialType == SPEC_SECT_INDEX_GIN) { FormatGinBlock(buffer, isToast, toastOid, toastExternalSize, toastValue, toastRead); return; } if (!isToast || verbose) printf("%s -----\n", indent); /* Loop through the items on the block. Check if the block is * empty and has a sensible item array listed before running * through each item */ if (maxOffset == 0) { if (!isToast || verbose) printf("%s Empty block - no items listed \n\n", indent); } else if ((maxOffset < 0) || (maxOffset > blockSize)) { if (!isToast || verbose) printf("%s Error: Item index corrupt on block. Offset: <%d>.\n\n", indent, maxOffset); exitCode = 1; } else { int formatAs; char textFlags[16]; uint32 chunkId; unsigned int chunkSize = 0; /* First, honour requests to format items a special way, then * use the special section to determine the format style */ if (itemOptions & ITEM_INDEX) formatAs = ITEM_INDEX; else if (itemOptions & ITEM_HEAP) formatAs = ITEM_HEAP; else switch (specialType) { case SPEC_SECT_INDEX_BTREE: case SPEC_SECT_INDEX_HASH: case SPEC_SECT_INDEX_GIST: case SPEC_SECT_INDEX_GIN: formatAs = ITEM_INDEX; break; case SPEC_SECT_INDEX_SPGIST: { SpGistPageOpaque spgpo = (SpGistPageOpaque) ((char *) page + ((PageHeader) page)->pd_special); if (spgpo->flags & SPGIST_LEAF) formatAs = ITEM_SPG_LEAF; else formatAs = ITEM_SPG_INNER; } break; default: formatAs = ITEM_HEAP; break; } for (x = 1; x < (maxOffset + 1); x++) { itemId = PageGetItemId(page, x); itemFlags = (unsigned int) ItemIdGetFlags(itemId); itemSize = (unsigned int) ItemIdGetLength(itemId); itemOffset = (unsigned int) ItemIdGetOffset(itemId); switch (itemFlags) { case LP_UNUSED: strcpy(textFlags, "UNUSED"); break; case LP_NORMAL: strcpy(textFlags, "NORMAL"); break; case LP_REDIRECT: strcpy(textFlags, "REDIRECT"); break; case LP_DEAD: strcpy(textFlags, "DEAD"); break; default: /* shouldn't be possible */ sprintf(textFlags, "0x%02x", itemFlags); break; } if (!isToast || verbose) printf("%s Item %3u -- Length: %4u Offset: %4u (0x%04x)" " Flags: %s\n", indent, x, itemSize, itemOffset, itemOffset, textFlags); /* Make sure the item can physically fit on this block before * formatting */ if ((itemOffset + itemSize > blockSize) || (itemOffset + itemSize > bytesToFormat)) { if (!isToast || verbose) printf("%s Error: Item contents extend beyond block.\n" "%s BlockSize<%d> Bytes Read<%d> Item Start<%d>.\n", indent, indent, blockSize, bytesToFormat, itemOffset + itemSize); exitCode = 1; } else if (itemFlags != LP_UNUSED) { HeapTupleHeader tuple_header; TransactionId xmax; /* If the user requests that the items be interpreted as * heap or index items... */ if (itemOptions & ITEM_DETAIL) FormatItem(buffer, itemSize, itemOffset, formatAs); /* Dump the items contents in hex and ascii */ if (blockOptions & BLOCK_FORMAT) FormatBinary(buffer, itemSize, itemOffset); /* Check if tuple was deleted */ tuple_header = (HeapTupleHeader) (&buffer[itemOffset]); xmax = HeapTupleHeaderGetRawXmax(tuple_header); if ((blockOptions & BLOCK_IGNORE_OLD) && (xmax != 0)) { if (!isToast || verbose) printf("%stuple was removed by transaction #%d\n", indent, xmax); } else if (isToast) { Oid read_toast_oid; ToastChunkDecode(&buffer[itemOffset], itemSize, toastOid, &read_toast_oid, &chunkId, want_chunk_id, toastValue + *toastRead, &chunkSize); if (verbose && read_toast_oid == toastOid) { if (chunkId == *want_chunk_id - 1) /* value already incremented */ printf("%s Read TOAST chunk. TOAST Oid: %d, chunk id: %d, " "chunk data size: %d\n", indent, toastOid, chunkId, chunkSize); else printf("%s Skipped out-of-order TOAST chunk. TOAST Oid: %d, chunk id: %d\n", indent, toastOid, chunkId); } *toastRead += chunkSize; if (*toastRead >= toastExternalSize) break; } else if ((blockOptions & BLOCK_DECODE) && (itemFlags == LP_NORMAL)) { /* Decode tuple data */ FormatDecode(&buffer[itemOffset], itemSize); } } if (!isToast && x == maxOffset) printf("\n"); } } } /* Interpret the contents of the item based on whether it has a special * section and/or the user has hinted */ static void FormatItem(char *buffer, unsigned int numBytes, unsigned int startIndex, unsigned int formatAs) { static const char *const spgist_tupstates[4] = { "LIVE", "REDIRECT", "DEAD", "PLACEHOLDER" }; if (formatAs == ITEM_INDEX) { /* It is an IndexTuple item, so dump the index header */ if (numBytes < sizeof(ItemPointerData)) { if (numBytes) { printf(" Error: This item does not look like an index item.\n"); exitCode = 1; } } else { IndexTuple itup = (IndexTuple) (&(buffer[startIndex])); printf(" Block Id: %u linp Index: %u Size: %d\n" " Has Nulls: %u Has Varwidths: %u\n\n", ((uint32) ((itup->t_tid.ip_blkid.bi_hi << 16) | (uint16) itup->t_tid.ip_blkid.bi_lo)), itup->t_tid.ip_posid, (int) IndexTupleSize(itup), IndexTupleHasNulls(itup) ? 1 : 0, IndexTupleHasVarwidths(itup) ? 1 : 0); if (numBytes != IndexTupleSize(itup)) { printf(" Error: Index item size difference. Given <%u>, " "Internal <%d>.\n", numBytes, (int) IndexTupleSize(itup)); exitCode = 1; } } } else if (formatAs == ITEM_SPG_INNER) { /* It is an SpGistInnerTuple item, so dump the index header */ if (numBytes < SGITHDRSZ) { if (numBytes) { printf(" Error: This item does not look like an SPGiST item.\n"); exitCode = 1; } } else { SpGistInnerTuple itup = (SpGistInnerTuple) (&(buffer[startIndex])); printf(" State: %s allTheSame: %d nNodes: %u prefixSize: %u\n\n", spgist_tupstates[itup->tupstate], itup->allTheSame, itup->nNodes, itup->prefixSize); if (numBytes != itup->size) { printf(" Error: SPGiST inner item size difference. Given <%u>, " "Internal <%d>.\n", numBytes, (int) itup->size); exitCode = 1; } else if (itup->prefixSize == MAXALIGN(itup->prefixSize)) { int i; SpGistNodeTuple node; /* Dump the prefix contents in hex and ascii */ if ((blockOptions & BLOCK_FORMAT) && SGITHDRSZ + itup->prefixSize <= numBytes) FormatBinary(buffer, SGITHDRSZ + itup->prefixSize, startIndex); /* Try to print the nodes, but only while pointer is sane */ SGITITERATE(itup, i, node) { int off = (char *) node - (char *) itup; if (off + SGNTHDRSZ > numBytes) break; printf(" Node %2u: Downlink: %u/%u Size: %d Null: %u\n", i, ((uint32) ((node->t_tid.ip_blkid.bi_hi << 16) | (uint16) node->t_tid.ip_blkid.bi_lo)), node->t_tid.ip_posid, (int) IndexTupleSize(node), IndexTupleHasNulls(node) ? 1 : 0); /* Dump the node's contents in hex and ascii */ if ((blockOptions & BLOCK_FORMAT) && off + IndexTupleSize(node) <= numBytes) FormatBinary(buffer, IndexTupleSize(node), startIndex + off); if (IndexTupleSize(node) != MAXALIGN(IndexTupleSize(node))) break; } } printf("\n"); } } else if (formatAs == ITEM_SPG_LEAF) { /* It is an SpGistLeafTuple item, so dump the index header */ #if PG_VERSION_NUM >= 140000 if (numBytes < SGLTHDRSZ(SGLT_GET_HASNULLMASK((SpGistLeafTuple) &(buffer[startIndex])))) #else if (numBytes < SGLTHDRSZ) #endif { if (numBytes) { printf(" Error: This item does not look like an SPGiST item.\n"); exitCode = 1; } } else { SpGistLeafTuple itup = (SpGistLeafTuple) (&(buffer[startIndex])); printf(" State: %s nextOffset: %u Block Id: %u linp Index: %u\n\n", spgist_tupstates[itup->tupstate], #if PG_VERSION_NUM >= 140000 SGLT_GET_NEXTOFFSET(itup), #else itup->nextOffset, #endif ((uint32) ((itup->heapPtr.ip_blkid.bi_hi << 16) | (uint16) itup->heapPtr.ip_blkid.bi_lo)), itup->heapPtr.ip_posid); if (numBytes != itup->size) { printf(" Error: SPGiST leaf item size difference. Given <%u>, " "Internal <%d>.\n", numBytes, (int) itup->size); exitCode = 1; } } } else { /* It is a HeapTuple item, so dump the heap header */ int alignedSize = MAXALIGN(sizeof(HeapTupleHeaderData)); if (numBytes < alignedSize) { if (numBytes) { printf(" Error: This item does not look like a heap item.\n"); exitCode = 1; } } else { char flagString[256]; unsigned int x; unsigned int bitmapLength = 0; unsigned int oidLength = 0; unsigned int computedLength; unsigned int infoMask; unsigned int infoMask2; int localNatts; unsigned int localHoff; bits8 *localBits; unsigned int localBitOffset; HeapTupleHeader htup = (HeapTupleHeader) (&buffer[startIndex]); infoMask = htup->t_infomask; infoMask2 = htup->t_infomask2; localBits = &(htup->t_bits[0]); localNatts = HeapTupleHeaderGetNatts(htup); localHoff = htup->t_hoff; localBitOffset = offsetof(HeapTupleHeaderData, t_bits); printf(" XMIN: %u XMAX: %u CID|XVAC: %u", HeapTupleHeaderGetXmin(htup), HeapTupleHeaderGetRawXmax(htup), HeapTupleHeaderGetRawCommandId(htup)); #if PG_VERSION_NUM < 120000 if (infoMask & HEAP_HASOID) printf(" OID: %u", HeapTupleHeaderGetOid(htup)); #endif printf("\n" " Block Id: %u linp Index: %u Attributes: %d Size: %d\n", ((uint32) ((htup->t_ctid.ip_blkid.bi_hi << 16) | (uint16) htup-> t_ctid.ip_blkid.bi_lo)), htup->t_ctid.ip_posid, localNatts, htup->t_hoff); /* Place readable versions of the tuple info mask into a buffer. * Assume that the string can not expand beyond 256. */ flagString[0] = '\0'; if (infoMask & HEAP_HASNULL) strcat(flagString, "HASNULL|"); if (infoMask & HEAP_HASVARWIDTH) strcat(flagString, "HASVARWIDTH|"); if (infoMask & HEAP_HASEXTERNAL) strcat(flagString, "HASEXTERNAL|"); #if PG_VERSION_NUM < 120000 if (infoMask & HEAP_HASOID) strcat(flagString, "HASOID|"); #endif if (infoMask & HEAP_XMAX_KEYSHR_LOCK) strcat(flagString, "XMAX_KEYSHR_LOCK|"); if (infoMask & HEAP_COMBOCID) strcat(flagString, "COMBOCID|"); if (infoMask & HEAP_XMAX_EXCL_LOCK) strcat(flagString, "XMAX_EXCL_LOCK|"); if (infoMask & HEAP_XMAX_LOCK_ONLY) strcat(flagString, "XMAX_LOCK_ONLY|"); if (infoMask & HEAP_XMIN_COMMITTED) strcat(flagString, "XMIN_COMMITTED|"); if (infoMask & HEAP_XMIN_INVALID) strcat(flagString, "XMIN_INVALID|"); if (infoMask & HEAP_XMAX_COMMITTED) strcat(flagString, "XMAX_COMMITTED|"); if (infoMask & HEAP_XMAX_INVALID) strcat(flagString, "XMAX_INVALID|"); if (infoMask & HEAP_XMAX_IS_MULTI) strcat(flagString, "XMAX_IS_MULTI|"); if (infoMask & HEAP_UPDATED) strcat(flagString, "UPDATED|"); if (infoMask & HEAP_MOVED_OFF) strcat(flagString, "MOVED_OFF|"); if (infoMask & HEAP_MOVED_IN) strcat(flagString, "MOVED_IN|"); if (infoMask2 & HEAP_KEYS_UPDATED) strcat(flagString, "KEYS_UPDATED|"); if (infoMask2 & HEAP_HOT_UPDATED) strcat(flagString, "HOT_UPDATED|"); if (infoMask2 & HEAP_ONLY_TUPLE) strcat(flagString, "HEAP_ONLY|"); if (strlen(flagString)) flagString[strlen(flagString) - 1] = '\0'; printf(" infomask: 0x%04x (%s) \n", infoMask, flagString); /* As t_bits is a variable length array, determine the length of * the header proper */ if (infoMask & HEAP_HASNULL) bitmapLength = BITMAPLEN(localNatts); else bitmapLength = 0; #if PG_VERSION_NUM < 120000 if (infoMask & HEAP_HASOID) oidLength += sizeof(Oid); #endif computedLength = MAXALIGN(localBitOffset + bitmapLength + oidLength); /* Inform the user of a header size mismatch or dump the t_bits * array */ if (computedLength != localHoff) { printf (" Error: Computed header length not equal to header size.\n" " Computed <%u> Header: <%d>\n", computedLength, localHoff); exitCode = 1; } else if ((infoMask & HEAP_HASNULL) && bitmapLength) { printf(" t_bits: "); for (x = 0; x < bitmapLength; x++) { printf("[%u]: 0x%02x ", x, localBits[x]); if (((x & 0x03) == 0x03) && (x < bitmapLength - 1)) printf("\n "); } printf("\n"); } printf("\n"); } } } /* On blocks that have special sections, print the contents * according to previously determined special section type */ static void FormatSpecial(char *buffer) { PageHeader pageHeader = (PageHeader) buffer; char flagString[100] = "\0"; unsigned int specialOffset = pageHeader->pd_special; unsigned int specialSize = (blockSize >= specialOffset) ? (blockSize - specialOffset) : 0; printf(" -----\n"); switch (specialType) { case SPEC_SECT_ERROR_UNKNOWN: case SPEC_SECT_ERROR_BOUNDARY: printf(" Error: Invalid special section encountered.\n"); exitCode = 1; break; case SPEC_SECT_SEQUENCE: printf(" Sequence: 0x%08x\n", SEQUENCE_MAGIC); break; /* Btree index section */ case SPEC_SECT_INDEX_BTREE: { BTPageOpaque btreeSection = (BTPageOpaque) (buffer + specialOffset); if (btreeSection->btpo_flags & BTP_LEAF) strcat(flagString, "LEAF|"); if (btreeSection->btpo_flags & BTP_ROOT) strcat(flagString, "ROOT|"); if (btreeSection->btpo_flags & BTP_DELETED) strcat(flagString, "DELETED|"); if (btreeSection->btpo_flags & BTP_META) strcat(flagString, "META|"); if (btreeSection->btpo_flags & BTP_HALF_DEAD) strcat(flagString, "HALFDEAD|"); if (btreeSection->btpo_flags & BTP_SPLIT_END) strcat(flagString, "SPLITEND|"); if (btreeSection->btpo_flags & BTP_HAS_GARBAGE) strcat(flagString, "HASGARBAGE|"); if (btreeSection->btpo_flags & BTP_INCOMPLETE_SPLIT) strcat(flagString, "INCOMPLETESPLIT|"); #if PG_VERSION_NUM >= 140000 if (btreeSection->btpo_flags & BTP_HAS_FULLXID) strcat(flagString, "HASFULLXID|"); #endif if (strlen(flagString)) flagString[strlen(flagString) - 1] = '\0'; printf(" BTree Index Section:\n" " Flags: 0x%04x (%s)\n" " Blocks: Previous (%d) Next (%d) %s (%d) CycleId (%d)\n\n", btreeSection->btpo_flags, flagString, btreeSection->btpo_prev, btreeSection->btpo_next, (btreeSection-> btpo_flags & BTP_DELETED) ? "Next XID" : "Level", #if PG_VERSION_NUM >= 140000 btreeSection->btpo_level, #else btreeSection->btpo.level, #endif btreeSection->btpo_cycleid); } break; /* Hash index section */ case SPEC_SECT_INDEX_HASH: { HashPageOpaque hashSection = (HashPageOpaque) (buffer + specialOffset); if ((hashSection->hasho_flag & LH_PAGE_TYPE) == LH_UNUSED_PAGE) strcat(flagString, "UNUSED|"); if (hashSection->hasho_flag & LH_OVERFLOW_PAGE) strcat(flagString, "OVERFLOW|"); if (hashSection->hasho_flag & LH_BUCKET_PAGE) strcat(flagString, "BUCKET|"); if (hashSection->hasho_flag & LH_BITMAP_PAGE) strcat(flagString, "BITMAP|"); if (hashSection->hasho_flag & LH_META_PAGE) strcat(flagString, "META|"); if (hashSection->hasho_flag & LH_BUCKET_BEING_POPULATED) strcat(flagString, "BUCKET_BEING_POPULATED|"); if (hashSection->hasho_flag & LH_BUCKET_BEING_SPLIT) strcat(flagString, "BUCKET_BEING_SPLIT|"); if (hashSection->hasho_flag & LH_BUCKET_NEEDS_SPLIT_CLEANUP) strcat(flagString, "BUCKET_NEEDS_SPLIT_CLEANUP|"); if (hashSection->hasho_flag & LH_PAGE_HAS_DEAD_TUPLES) strcat(flagString, "PAGE_HAS_DEAD_TUPLES|"); if (strlen(flagString)) flagString[strlen(flagString) - 1] = '\0'; printf(" Hash Index Section:\n" " Flags: 0x%04x (%s)\n" " Bucket Number: 0x%04x\n" " Blocks: Previous (%d) Next (%d)\n\n", hashSection->hasho_flag, flagString, hashSection->hasho_bucket, hashSection->hasho_prevblkno, hashSection->hasho_nextblkno); } break; /* GIST index section */ case SPEC_SECT_INDEX_GIST: { GISTPageOpaque gistSection = (GISTPageOpaque) (buffer + specialOffset); if (gistSection->flags & F_LEAF) strcat(flagString, "LEAF|"); if (gistSection->flags & F_DELETED) strcat(flagString, "DELETED|"); if (gistSection->flags & F_TUPLES_DELETED) strcat(flagString, "TUPLES_DELETED|"); if (gistSection->flags & F_FOLLOW_RIGHT) strcat(flagString, "FOLLOW_RIGHT|"); if (gistSection->flags & F_HAS_GARBAGE) strcat(flagString, "HAS_GARBAGE|"); if (strlen(flagString)) flagString[strlen(flagString) - 1] = '\0'; printf(" GIST Index Section:\n" " NSN: 0x%08x/0x%08x\n" " RightLink: %d\n" " Flags: 0x%08x (%s)\n\n", gistSection->nsn.xlogid, gistSection->nsn.xrecoff, gistSection->rightlink, gistSection->flags, flagString); } break; /* GIN index section */ case SPEC_SECT_INDEX_GIN: { GinPageOpaque ginSection = (GinPageOpaque) (buffer + specialOffset); if (ginSection->flags & GIN_DATA) strcat(flagString, "DATA|"); if (ginSection->flags & GIN_LEAF) strcat(flagString, "LEAF|"); if (ginSection->flags & GIN_DELETED) strcat(flagString, "DELETED|"); if (ginSection->flags & GIN_META) strcat(flagString, "META|"); if (ginSection->flags & GIN_LIST) strcat(flagString, "LIST|"); if (ginSection->flags & GIN_LIST_FULLROW) strcat(flagString, "FULLROW|"); if (ginSection->flags & GIN_INCOMPLETE_SPLIT) strcat(flagString, "INCOMPLETESPLIT|"); if (ginSection->flags & GIN_COMPRESSED) strcat(flagString, "COMPRESSED|"); if (strlen(flagString)) flagString[strlen(flagString) - 1] = '\0'; printf(" GIN Index Section:\n" " Flags: 0x%08x (%s) Maxoff: %d\n" " Blocks: RightLink (%d)\n\n", ginSection->flags, flagString, ginSection->maxoff, ginSection->rightlink); } break; /* SP-GIST index section */ case SPEC_SECT_INDEX_SPGIST: { SpGistPageOpaque spgistSection = (SpGistPageOpaque) (buffer + specialOffset); if (spgistSection->flags & SPGIST_META) strcat(flagString, "META|"); if (spgistSection->flags & SPGIST_DELETED) strcat(flagString, "DELETED|"); if (spgistSection->flags & SPGIST_LEAF) strcat(flagString, "LEAF|"); if (spgistSection->flags & SPGIST_NULLS) strcat(flagString, "NULLS|"); if (strlen(flagString)) flagString[strlen(flagString) - 1] = '\0'; printf(" SPGIST Index Section:\n" " Flags: 0x%08x (%s)\n" " nRedirection: %d\n" " nPlaceholder: %d\n\n", spgistSection->flags, flagString, spgistSection->nRedirection, spgistSection->nPlaceholder); } break; /* No idea what type of special section this is */ default: printf(" Unknown special section type. Type: <%u>.\n", specialType); exitCode = 1; break; } /* Dump the formatted contents of the special section */ if (blockOptions & BLOCK_FORMAT) { if (specialType == SPEC_SECT_ERROR_BOUNDARY) { printf(" Error: Special section points off page." " Unable to dump contents.\n"); exitCode = 1; } else FormatBinary(buffer, specialSize, specialOffset); } } /* For each block, dump out formatted header and content information */ static void FormatBlock(unsigned int blockOptions, unsigned int controlOptions, char *buffer, BlockNumber currentBlock, unsigned int blockSize, bool isToast, Oid toastOid, uint32 *want_chunk_id, unsigned int toastExternalSize, char *toastValue, unsigned int *toastRead) { Page page = (Page) buffer; char *indent = isToast ? "\t" : ""; pageOffset = blockSize * currentBlock; specialType = GetSpecialSectionType(buffer, page); if (!isToast || verbose) printf("\n%sBlock %4u **%s***************************************\n", indent, currentBlock, (bytesToFormat == blockSize) ? "***************" : " PARTIAL BLOCK "); /* Either dump out the entire block in hex+acsii fashion or * interpret the data based on block structure */ if (blockOptions & BLOCK_NO_INTR) FormatBinary(buffer, bytesToFormat, 0); else { int rc; /* Every block contains a header, items and possibly a special * section. Beware of partial block reads though */ rc = FormatHeader(buffer, page, currentBlock, isToast); /* If we didn't encounter a partial read in the header, carry on... */ if (rc != EOF_ENCOUNTERED) { FormatItemBlock(buffer, page, isToast, toastOid, want_chunk_id, toastExternalSize, toastValue, toastRead); if (specialType != SPEC_SECT_NONE) FormatSpecial(buffer); } } } /* Dump out the content of the PG control file */ static void FormatControl(char *buffer) { unsigned int localPgVersion = 0; unsigned int controlFileSize = 0; time_t cd_time; time_t cp_time; printf ("\n *********************************************\n\n"); /* Check the version */ if (bytesToFormat >= offsetof(ControlFileData, catalog_version_no)) localPgVersion = ((ControlFileData *) buffer)->pg_control_version; if (localPgVersion >= 72) controlFileSize = sizeof(ControlFileData); else { printf("pg_filedump: pg_control version %u not supported.\n", localPgVersion); return; } /* Interpret the control file if it's all there */ if (bytesToFormat >= controlFileSize) { ControlFileData *controlData = (ControlFileData *) buffer; CheckPoint *checkPoint = &(controlData->checkPointCopy); pg_crc32 crcLocal; char *dbState; /* Compute a local copy of the CRC to verify the one on disk */ INIT_CRC32C(crcLocal); COMP_CRC32C(crcLocal, buffer, offsetof(ControlFileData, crc)); FIN_CRC32C(crcLocal); /* Grab a readable version of the database state */ switch (controlData->state) { case DB_STARTUP: dbState = "STARTUP"; break; case DB_SHUTDOWNED: dbState = "SHUTDOWNED"; break; case DB_SHUTDOWNED_IN_RECOVERY: dbState = "SHUTDOWNED_IN_RECOVERY"; break; case DB_SHUTDOWNING: dbState = "SHUTDOWNING"; break; case DB_IN_CRASH_RECOVERY: dbState = "IN CRASH RECOVERY"; break; case DB_IN_ARCHIVE_RECOVERY: dbState = "IN ARCHIVE RECOVERY"; break; case DB_IN_PRODUCTION: dbState = "IN PRODUCTION"; break; default: dbState = "UNKNOWN"; break; } /* convert timestamps to system's time_t width */ cd_time = controlData->time; cp_time = checkPoint->time; printf(" CRC: %s\n" " pg_control Version: %u%s\n" " Catalog Version: %u\n" " System Identifier: " UINT64_FORMAT "\n" " State: %s\n" " Last Mod Time: %s" " Last Checkpoint Record: Log File (%u) Offset (0x%08x)\n" #if PG_VERSION_NUM < 110000 " Previous Checkpoint Record: Log File (%u) Offset (0x%08x)\n" #endif " Last Checkpoint Record Redo: Log File (%u) Offset (0x%08x)\n" " |- TimeLineID: %u\n" " |- Next XID: %u/%u\n" " |- Next OID: %u\n" " |- Next Multi: %u\n" " |- Next MultiOff: %u\n" " |- Time: %s" " Minimum Recovery Point: Log File (%u) Offset (0x%08x)\n" " Maximum Data Alignment: %u\n" " Floating-Point Sample: %.7g%s\n" " Database Block Size: %u\n" " Blocks Per Segment: %u\n" " XLOG Block Size: %u\n" " XLOG Segment Size: %u\n" " Maximum Identifier Length: %u\n" " Maximum Index Keys: %u\n" " TOAST Chunk Size: %u\n\n", EQ_CRC32C(crcLocal, controlData->crc) ? "Correct" : "Not Correct", controlData->pg_control_version, (controlData->pg_control_version == PG_CONTROL_VERSION ? "" : " (Not Correct!)"), controlData->catalog_version_no, controlData->system_identifier, dbState, ctime(&(cd_time)), (uint32) (controlData->checkPoint >> 32), (uint32) controlData->checkPoint, #if PG_VERSION_NUM < 110000 (uint32) (controlData->prevCheckPoint >> 32), (uint32) controlData->prevCheckPoint, #endif (uint32) (checkPoint->redo >> 32), (uint32) checkPoint->redo, checkPoint->ThisTimeLineID, #if PG_VERSION_NUM < 120000 checkPoint->nextXidEpoch, checkPoint->nextXid, #elif PG_VERSION_NUM < 140000 EpochFromFullTransactionId(checkPoint->nextFullXid), XidFromFullTransactionId(checkPoint->nextFullXid), #else EpochFromFullTransactionId(checkPoint->nextXid), XidFromFullTransactionId(checkPoint->nextXid), #endif checkPoint->nextOid, checkPoint->nextMulti, checkPoint->nextMultiOffset, ctime(&cp_time), (uint32) (controlData->minRecoveryPoint >> 32), (uint32) controlData->minRecoveryPoint, controlData->maxAlign, controlData->floatFormat, (controlData->floatFormat == FLOATFORMAT_VALUE ? "" : " (Not Correct!)"), controlData->blcksz, controlData->relseg_size, controlData->xlog_blcksz, controlData->xlog_seg_size, controlData->nameDataLen, controlData->indexMaxKeys, controlData->toast_max_chunk_size); } else { printf(" Error: pg_control file size incorrect.\n" " Size: Correct <%u> Received <%u>.\n\n", controlFileSize, bytesToFormat); /* If we have an error, force a formatted dump so we can see * where things are going wrong */ controlOptions |= CONTROL_FORMAT; exitCode = 1; } /* Dump hex and ascii representation of data */ if (controlOptions & CONTROL_FORMAT) { printf(" *****************" "**********************\n\n"); FormatBinary(buffer, bytesToFormat, 0); } } /* Dump out the contents of the block in hex and ascii. * BYTES_PER_LINE bytes are formatted in each line. */ static void FormatBinary(char *buffer, unsigned int numBytes, unsigned int startIndex) { unsigned int index = 0; unsigned int stopIndex = 0; unsigned int x = 0; unsigned int lastByte = startIndex + numBytes; if (numBytes) { /* Iterate through a printable row detailing the current * address, the hex and ascii values */ for (index = startIndex; index < lastByte; index += BYTES_PER_LINE) { stopIndex = index + BYTES_PER_LINE; /* Print out the address */ if (blockOptions & BLOCK_ABSOLUTE) printf(" %08x: ", (unsigned int) (pageOffset + index)); else printf(" %04x: ", (unsigned int) index); /* Print out the hex version of the data */ for (x = index; x < stopIndex; x++) { if (x < lastByte) printf("%02x", 0xff & ((unsigned) buffer[x])); else printf(" "); if ((x & 0x03) == 0x03) printf(" "); } printf(" "); /* Print out the ascii version of the data */ for (x = index; x < stopIndex; x++) { if (x < lastByte) printf("%c", isprint(buffer[x]) ? buffer[x] : '.'); else printf(" "); } printf("\n"); } printf("\n"); } } /* Dump the binary image of the block */ static void DumpBinaryBlock(char *buffer) { unsigned int x; for (x = 0; x < bytesToFormat; x++) putchar(buffer[x]); } /* Control the dumping of the blocks within the file */ int DumpFileContents(unsigned int blockOptions, unsigned int controlOptions, FILE *fp, unsigned int blockSize, int blockStart, int blockEnd, bool isToast, Oid toastOid, uint32 *want_chunk_id, unsigned int toastExternalSize, char *toastValue, unsigned int *toastDataRead) { unsigned int initialRead = 1; unsigned int contentsToDump = 1; BlockNumber currentBlock = 0; int result = 0; /* On a positive block size, allocate a local buffer to store * the subsequent blocks */ char *block = (char *)malloc(blockSize); if (!block) { printf("\nError: Unable to create buffer of size <%d>.\n", blockSize); result = 1; } /* If the user requested a block range, seek to the correct position * within the file for the start block. */ if (result == 0 && blockOptions & BLOCK_RANGE) { unsigned int position = blockSize * blockStart; if (fseek(fp, position, SEEK_SET) != 0) { printf("Error: Seek error encountered before requested " "start block <%d>.\n", blockStart); contentsToDump = 0; result = 1; } else currentBlock = blockStart; } /* Iterate through the blocks in the file until you reach the end or * the requested range end */ while (contentsToDump && result == 0) { bytesToFormat = fread(block, 1, blockSize, fp); if (bytesToFormat == 0) { /* fseek() won't pop an error if you seek passed eof. The next * subsequent read gets the error. */ if (initialRead) printf("Error: Premature end of file encountered.\n"); else if (!(blockOptions & BLOCK_BINARY) && (!isToast || verbose)) printf("\n*** End of File Encountered. Last Block " "Read: %d ***\n", currentBlock - 1); contentsToDump = 0; } else { if (blockOptions & BLOCK_BINARY) DumpBinaryBlock(block); else { if (controlOptions & CONTROL_DUMP) { FormatControl(block); contentsToDump = false; } else { FormatBlock(blockOptions, controlOptions, block, currentBlock, blockSize, isToast, toastOid, want_chunk_id, toastExternalSize, toastValue, toastDataRead); } } } /* Check to see if we are at the end of the requested range. */ if ((blockOptions & BLOCK_RANGE) && (currentBlock >= blockEnd) && (contentsToDump)) { /* Don't print out message if we're doing a binary dump */ if (!(blockOptions & BLOCK_BINARY)) printf("\n*** End of Requested Range Encountered. " "Last Block Read: %d ***\n", currentBlock); contentsToDump = 0; } else currentBlock++; initialRead = 0; /* If TOAST data is read */ if (isToast && *toastDataRead >= toastExternalSize) break; } free(block); return result; } int PrintRelMappings(void) { // For storing ingested data char charbuf[RELMAPPER_FILESIZE]; RelMapFile *map; RelMapping *mappings; RelMapping m; int bytesRead; // For confirming Magic Number correctness char m1[RELMAPPER_MAGICSIZE]; char m2[RELMAPPER_MAGICSIZE]; int magic_ref = RELMAPPER_FILEMAGIC; int magic_val; int num_loops; // Read in the file rewind(fp); // Make sure to start from the beginning bytesRead = fread(charbuf,1,RELMAPPER_FILESIZE,fp); if ( bytesRead != RELMAPPER_FILESIZE ) { printf("Read %d bytes, expected %d\n", bytesRead, RELMAPPER_FILESIZE); return 0; } // Convert to RelMapFile type for usability map = (RelMapFile *) charbuf; // Check and print Magic Number correctness printf("Magic Number: 0x%x",map->magic); magic_val = map->magic; memcpy(m1,&magic_ref,RELMAPPER_MAGICSIZE); memcpy(m2,&magic_val,RELMAPPER_MAGICSIZE); if ( memcmp(m1,m2,RELMAPPER_MAGICSIZE) == 0 ) { printf(" (CORRECT)\n"); } else { printf(" (INCORRECT)\n"); } // Print Mappings printf("Num Mappings: %d\n",map->num_mappings); printf("Detailed Mappings list:\n"); mappings = map->mappings; // Limit number of mappings as per MAX_MAPPINGS num_loops = map->num_mappings; if ( map->num_mappings > MAX_MAPPINGS ) { num_loops = MAX_MAPPINGS; printf(" NOTE: listing has been limited to the first %d mappings\n", MAX_MAPPINGS); printf(" (perhaps your file is not a valid pg_filenode.map file?)\n"); } for (int i=0; i < num_loops; i++) { m = mappings[i]; printf("OID: %u\tFilenode: %u\n", m.mapoid, m.mapfilenode); } return 1; } /* Consume the options and iterate through the given file, formatting as * requested. */ int main(int argv, char **argc) { /* If there is a parameter list, validate the options */ unsigned int validOptions; validOptions = (argv < 2) ? OPT_RC_COPYRIGHT : ConsumeOptions(argv, argc); /* Display valid options if no parameters are received or invalid options * where encountered */ if (validOptions != OPT_RC_VALID) DisplayOptions(validOptions); else if (isRelMapFile) { CreateDumpFileHeader(argv, argc); exitCode = PrintRelMappings(); } else { /* Don't dump the header if we're dumping binary pages */ if (!(blockOptions & BLOCK_BINARY)) CreateDumpFileHeader(argv, argc); /* If the user has not forced a block size, use the size of the * control file data or the information from the block 0 header */ if (controlOptions) { if (!(controlOptions & CONTROL_FORCED)) blockSize = sizeof(ControlFileData); } else if (!(blockOptions & BLOCK_FORCED)) blockSize = GetBlockSize(fp); exitCode = DumpFileContents(blockOptions, controlOptions, fp, blockSize, blockStart, blockEnd, false /* is toast realtion */, 0, /* no toast Oid */ NULL, /* no current TOAST chunk */ 0, /* no toast external size */ NULL, /* no out toast value */ NULL /* no toast value length */ ); } if (fp) fclose(fp); exit(exitCode); } pg_filedump-REL_17_4/pg_filedump.h000066400000000000000000000131531500022671400171670ustar00rootroot00000000000000/* * pg_filedump.h - PostgreSQL file dump utility for dumping and * formatting heap (data), index and control files. * * Copyright (c) 2002-2010 Red Hat, Inc. * Copyright (c) 2011-2025, PostgreSQL Global Development Group * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Original Author: Patrick Macdonald */ #define FD_VERSION "17.4" /* version ID of pg_filedump */ #define FD_PG_VERSION "PostgreSQL 8.x .. 17.x" /* PG version it works with */ #include "postgres.h" #include #include #include "access/gin_private.h" #include "access/gist.h" #include "access/hash.h" #include "access/htup.h" #include "access/htup_details.h" #include "access/itup.h" #include "access/nbtree.h" #include "access/spgist_private.h" #include "catalog/pg_control.h" #include "storage/bufpage.h" /* Options for Block formatting operations */ extern unsigned int blockOptions; typedef enum blockSwitches { BLOCK_ABSOLUTE = 0x00000001, /* -a: Absolute(vs Relative) addressing */ BLOCK_BINARY = 0x00000002, /* -b: Binary dump of block */ BLOCK_FORMAT = 0x00000004, /* -f: Formatted dump of blocks / control file */ BLOCK_FORCED = 0x00000008, /* -S: Block size forced */ BLOCK_NO_INTR = 0x00000010, /* -d: Dump straight blocks */ BLOCK_RANGE = 0x00000020, /* -R: Specific block range to dump */ BLOCK_CHECKSUMS = 0x00000040, /* -k: verify block checksums */ BLOCK_DECODE = 0x00000080, /* -D: Try to decode tuples */ BLOCK_DECODE_TOAST = 0x00000100, /* -t: Try to decode TOAST values */ BLOCK_IGNORE_OLD = 0x00000200 /* -o: Decode old values */ } blockSwitches; /* Segment-related options */ extern unsigned int segmentOptions; typedef enum segmentSwitches { SEGMENT_SIZE_FORCED = 0x00000001, /* -s: Segment size forced */ SEGMENT_NUMBER_FORCED = 0x00000002, /* -n: Segment number forced */ } segmentSwitches; /* -R[start]:Block range start */ extern int blockStart; /* -R[end]:Block range end */ extern int blockEnd; /* Options for Item formatting operations */ extern unsigned int itemOptions; typedef enum itemSwitches { ITEM_DETAIL = 0x00000001, /* -i: Display interpreted items */ ITEM_HEAP = 0x00000002, /* -y: Blocks contain HeapTuple items */ ITEM_INDEX = 0x00000004, /* -x: Blocks contain IndexTuple items */ ITEM_SPG_INNER = 0x00000008, /* Blocks contain SpGistInnerTuple items */ ITEM_SPG_LEAF = 0x00000010 /* Blocks contain SpGistLeafTuple items */ } itemSwitches; /* Options for Control File formatting operations */ extern unsigned int controlOptions; typedef enum controlSwitches { CONTROL_DUMP = 0x00000001, /* -c: Dump control file */ CONTROL_FORMAT = BLOCK_FORMAT, /* -f: Formatted dump of control file */ CONTROL_FORCED = BLOCK_FORCED /* -S: Block size forced */ } controlSwitches; /* Possible value types for the Special Section */ typedef enum specialSectionTypes { SPEC_SECT_NONE, /* No special section on block */ SPEC_SECT_SEQUENCE, /* Sequence info in special section */ SPEC_SECT_INDEX_BTREE, /* BTree index info in special section */ SPEC_SECT_INDEX_HASH, /* Hash index info in special section */ SPEC_SECT_INDEX_GIST, /* GIST index info in special section */ SPEC_SECT_INDEX_GIN, /* GIN index info in special section */ SPEC_SECT_INDEX_SPGIST, /* SP - GIST index info in special section */ SPEC_SECT_ERROR_UNKNOWN, /* Unknown error */ SPEC_SECT_ERROR_BOUNDARY /* Boundary error */ } specialSectionTypes; extern unsigned int specialType; /* Possible return codes from option validation routine. * pg_filedump doesn't do much with them now but maybe in * the future... */ typedef enum optionReturnCodes { OPT_RC_VALID, /* All options are valid */ OPT_RC_INVALID, /* Improper option string */ OPT_RC_FILE, /* File problems */ OPT_RC_DUPLICATE, /* Duplicate option encountered */ OPT_RC_COPYRIGHT /* Copyright should be displayed */ } optionReturnCodes; /* Simple macro to check for duplicate options and then set * an option flag for later consumption */ #define SET_OPTION(_x,_y,_z) if (_x & _y) \ { \ rc = OPT_RC_DUPLICATE; \ duplicateSwitch = _z; \ } \ else \ _x |= _y; #define SEQUENCE_MAGIC 0x1717 /* PostgreSQL defined magic number */ #define EOF_ENCOUNTERED (-1) /* Indicator for partial read */ #define BYTES_PER_LINE 16 /* Format the binary 16 bytes per line */ /* Constants for pg_relnode.map decoding */ #define RELMAPPER_MAGICSIZE 4 #define RELMAPPER_FILESIZE 512 /* From utils/cache/relmapper.c -- Maybe ask community to put * these into utils/cache/relmapper.h? */ #define RELMAPPER_FILEMAGIC 0x592717 #define MAX_MAPPINGS 62 extern char *fileName; /* * Function Prototypes */ unsigned int GetBlockSize(FILE *fp); int DumpFileContents(unsigned int blockOptions, unsigned int controlOptions, FILE *fp, unsigned int blockSize, int blockStart, int blockEnd, bool isToast, Oid toastOid, uint32 *want_chunk_id, unsigned int toastExternalSize, char *toastValue, unsigned int *toastDataRead); pg_filedump-REL_17_4/run_test.sql000066400000000000000000000006621500022671400171100ustar00rootroot00000000000000\echo Testing :relname vacuum :"relname"; checkpoint; select relfilenode from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as oid \gset \set output :relname '.heap' \lo_export :oid :output \setenv relname :relname \! pg_filedump -D $relname $relname.heap | ./sed.sh -- ---------------------------------------------------------------------------------------------- -- pg_filedump-REL_17_4/sed.sh000077500000000000000000000003331500022671400156310ustar00rootroot00000000000000#!/bin/sh sed -e "s/logid ....../logid ....../" \ -e "s/recoff 0x......../recoff 0x......../" \ -e "s/Checksum: 0x..../Checksum: 0x..../" \ -e "s/id: ....../id: ....../g" \ -e "s/ 8< .*/ 8< [snipped]/" pg_filedump-REL_17_4/sql/000077500000000000000000000000001500022671400153175ustar00rootroot00000000000000pg_filedump-REL_17_4/sql/datatypes.sql000066400000000000000000000065321500022671400200440ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table "int,text" (i int, t text); insert into "int,text" values (1, 'one'), (null, 'two'), (3, null), (4, 'four'); \set relname int,text \ir run_test.sql -- do one test without options \! pg_filedump int,text.heap | ./sed.sh ---------------------------------------------------------------------------------------------- create table bigint (x bigint); insert into bigint values (-1), (0), (1), (null); \set relname bigint \ir run_test.sql create table bool (x bool); insert into bool values (true), (false), (null); \set relname bool \ir run_test.sql create table char (x "char"); insert into char values ('x'), (null); \set relname char \ir run_test.sql create table "charN" (x char(5)); insert into "charN" values ('x'), ('xxxxx'), (null); \set relname charN \ir run_test.sql create table date (x date); insert into date values ('2000-01-01'), ('1900-02-02'), ('2100-12-31'), ('100-01-01 BC'), ('-infinity'), ('infinity'), (null); \set relname date \ir run_test.sql create table int (x int); insert into int values (-1), (0), (1), (null); \set relname int \ir run_test.sql create table json (x json); insert into json values ('1'), ('"one"'), ('{"a":"b"}'), ('null'), (null); \set relname json \ir run_test.sql create table macaddr (x macaddr); insert into macaddr values ('00:10:20:30:40:50'), (null); \set relname macaddr \ir run_test.sql create table name (x name); insert into name values ('name'), ('1234567890123456789012345678901234567890123456789012345678901234567890'), (null); \set relname name \ir run_test.sql create table oid (x oid); insert into oid values (-1), (0), (1), (null); \set relname oid \ir run_test.sql create table smallint (x smallint); insert into smallint values (-1), (0), (1), (null); \set relname smallint \ir run_test.sql create table text (x text); insert into text values ('hello world'), (null); \set relname text \ir run_test.sql create table time (x time); insert into time values ('00:00'), ('23:59:59'), ('23:59:60'), (null); \set relname time \ir run_test.sql create table timestamp (x timestamp); insert into timestamp values ('2000-01-01 00:00'), ('100-01-01 BC 2:22'), ('infinity'), ('-infinity'), (null); \set relname timestamp \ir run_test.sql set timezone = 'Etc/UTC'; create table timestamptz (x timestamptz); insert into timestamptz values ('2000-01-01 00:00'), ('100-01-01 BC 2:22'), ('infinity'), ('-infinity'), (null); \set relname timestamptz \ir run_test.sql create table timetz (x timetz); insert into timetz values ('00:00 Etc/UTC'), ('23:59:59 Etc/UTC'), ('23:59:60 Etc/UTC'), ('1:23+4:56'), (null); \set relname timetz \ir run_test.sql create table uuid (x uuid); insert into uuid values ('b4f0e2d6-429b-48bd-af06-6578829dd980'), ('00000000-0000-0000-0000-000000000000'), (null); \set relname uuid \ir run_test.sql create table varchar (x varchar); insert into varchar values ('Hello World'), (''), (null); \set relname varchar \ir run_test.sql create table "varcharN" (x varchar(11)); insert into "varcharN" values ('Hello World'), (''), (null); \set relname varcharN \ir run_test.sql create table xid (x xid); insert into xid values ('-1'), ('0'), ('1'), (null); \set relname xid \ir run_test.sql pg_filedump-REL_17_4/sql/float.sql000066400000000000000000000011271500022671400171460ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG12+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table float4 (x float4); insert into float4 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float4 \ir run_test.sql create table float8 (x float8); insert into float8 values (0), ('-0'), ('-infinity'), ('infinity'), ('NaN'), (null); \set relname float8 \ir run_test.sql pg_filedump-REL_17_4/sql/numeric.sql000066400000000000000000000007751500022671400175130ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- PG14+ output in *.out/*_3.out, earlier in *_1.out/*_4.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table numeric (x numeric); insert into numeric values (0), ('12341234'), ('-567890'), ('NaN'), (null); insert into numeric values ('-Infinity'), ('Infinity'); -- needs PG 14 \set relname numeric \ir run_test.sql pg_filedump-REL_17_4/sql/toast.sql000066400000000000000000000027731500022671400172030ustar00rootroot00000000000000-- PG14+ output in toast.out/_3.out (32-bit); PG13- output in toast_1.out/_4.out create table toast ( description text, data text ); insert into toast values ('short inline', 'xxx'); insert into toast values ('long inline uncompressed', repeat('x', 200)); alter table toast alter column data set storage external; insert into toast values ('external uncompressed', repeat('0123456789 8< ', 200)); alter table toast alter column data set storage extended; insert into toast values ('inline compressed pglz', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed pglz', repeat('0123456789 8< ', 20000)); alter table toast alter column data set compression lz4; insert into toast values ('inline compressed lz4', repeat('0123456789 8< ', 200)); insert into toast values ('extended compressed lz4', repeat('0123456789 8< ', 50000)); vacuum toast; checkpoint; -- copy tables where client can read it \set relname 'toast' select oid as datoid from pg_database where datname = current_database() \gset select relfilenode, reltoastrelid from pg_class where relname = :'relname' \gset select lo_import(format('base/%s/%s', :'datoid', :'relfilenode')) as loid \gset \set output :relname '.heap' \lo_export :loid :output select lo_import(format('base/%s/%s', :'datoid', :'reltoastrelid')) as toast_loid \gset \set output :reltoastrelid \lo_export :toast_loid :output \setenv relname :relname \! pg_filedump -D text,text $relname.heap | ./sed.sh \! pg_filedump -D text,text -t $relname.heap | ./sed.sh pg_filedump-REL_17_4/sql/xml.sql000066400000000000000000000006131500022671400166400ustar00rootroot00000000000000-- 64 bit output in *.out, 32 bit output in *_3.out -- server without --with-libxml support output in *_1.out select oid as datoid from pg_database where datname = current_database() \gset ---------------------------------------------------------------------------------------------- create table xml (x xml); insert into xml values (''), (null); \set relname xml \ir run_test.sql pg_filedump-REL_17_4/stringinfo.c000066400000000000000000000106221500022671400170470ustar00rootroot00000000000000/* * Code mostly borrowed from PostgreSQL's stringinfo.c * palloc replaced to malloc, etc. */ #include "postgres.h" #include #include #include #define MaxAllocSize ((Size) 0x3fffffff) /* 1 gigabyte - 1 */ /*------------------------- * StringInfoData holds information about an extensible string. * data is the current buffer for the string. * len is the current string length. There is guaranteed to be * a terminating '\0' at data[len], although this is not very * useful when the string holds binary data rather than text. * maxlen is the allocated size in bytes of 'data', i.e. the maximum * string size (including the terminating '\0' char) that we can * currently store in 'data' without having to reallocate * more space. We must always have maxlen > len. * cursor is initialized to zero by makeStringInfo or initStringInfo, * but is not otherwise touched by the stringinfo.c routines. * Some routines use it to scan through a StringInfo. *------------------------- */ /* * initStringInfo * * Initialize a StringInfoData struct (with previously undefined contents) * to describe an empty string. */ void initStringInfo(StringInfo str) { int size = 1024; /* initial default buffer size */ str->data = (char *) malloc(size); str->maxlen = size; resetStringInfo(str); } /* * resetStringInfo * * Reset the StringInfo: the data buffer remains valid, but its * previous content, if any, is cleared. */ void resetStringInfo(StringInfo str) { str->data[0] = '\0'; str->len = 0; str->cursor = 0; } /* * appendStringInfoString * * Append a null-terminated string to str. */ void appendStringInfoString(StringInfo str, const char *s) { appendBinaryStringInfo(str, s, strlen(s)); } /* * appendBinaryStringInfo * * Append arbitrary binary data to a StringInfo, allocating more space * if necessary. */ void #if PG_VERSION_NUM < 160000 appendBinaryStringInfo(StringInfo str, const char *data, int datalen) #else appendBinaryStringInfo(StringInfo str, const void *data, int datalen) #endif { assert(str != NULL); /* Make more room if needed */ enlargeStringInfo(str, datalen); /* OK, append the data */ memcpy(str->data + str->len, data, datalen); str->len += datalen; /* * Keep a trailing null in place, even though it's probably useless for * binary data. (Some callers are dealing with text but call this because * their input isn't null-terminated.) */ str->data[str->len] = '\0'; } /* * enlargeStringInfo * * Make sure there is enough space for 'needed' more bytes * ('needed' does not include the terminating null). * * External callers usually need not concern themselves with this, since * all stringinfo.c routines do it automatically. However, if a caller * knows that a StringInfo will eventually become X bytes large, it * can save some malloc overhead by enlarging the buffer before starting * to store data in it. */ void enlargeStringInfo(StringInfo str, int needed) { Size newlen; Size limit; char *old_data; limit = MaxAllocSize; /* * Guard against out-of-range "needed" values. Without this, we can get * an overflow or infinite loop in the following. */ if (needed < 0) /* should not happen */ { printf("Error: invalid string enlargement request size: %d", needed); exit(1); } if (((Size) needed) >= (limit - (Size) str->len)) { printf("Error: cannot enlarge string buffer containing %d bytes by %d more bytes.", str->len, needed); exit(1); } needed += str->len + 1; /* total space required now */ /* Because of the above test, we now have needed <= limit */ if (needed <= str->maxlen) return; /* got enough space already */ /* * We don't want to allocate just a little more space with each append; * for efficiency, double the buffer size each time it overflows. * Actually, we might need to more than double it if 'needed' is big... */ newlen = 2 * str->maxlen; while (needed > newlen) newlen = 2 * newlen; /* * Clamp to the limit in case we went past it. Note we are assuming here * that limit <= INT_MAX/2, else the above loop could overflow. We will * still have newlen >= needed. */ if (newlen > limit) newlen = limit; old_data = str->data; str->data = (char *) realloc(str->data, (Size) newlen); if (str->data == NULL) { free(old_data); printf("Error: realloc() failed!\n"); exit(1); } str->maxlen = newlen; } pg_filedump-REL_17_4/t/000077500000000000000000000000001500022671400147635ustar00rootroot00000000000000pg_filedump-REL_17_4/t/001_basic.pl000066400000000000000000000102021500022671400167540ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Config; use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; use File::Spec; use IPC::Run qw( run timeout ); note "setting up PostgreSQL instance"; my $node = PostgreSQL::Test::Cluster->new('master'); $node->init(extra => ["--data-checksums"]); $node->append_conf('postgresql.conf', 'fsync = True'); $node->start; my $query = qq( drop table if exists t1; checkpoint; create table t1(a int, b text, c bigint, filler char(400)); insert into t1 values (1, 'asdasd1', 29347293874234444); insert into t1 values (2, 'asdasd2', 29347293874234445); insert into t1 values (3, 'asdasd', 29347293874234446); insert into t1 values (4, 'asdasd', 29347293874234447); checkpoint; ); $node->safe_psql('postgres', $query); note "running tests"; sub setup_test { # for test isolation purposes $node->safe_psql('postgres', $query); } test_basic_output(); test_btree_output(); test_spgist_output(); test_gin_output(); $node->stop; done_testing(); sub get_table_location { return File::Spec->catfile( $node->data_dir, $node->safe_psql('postgres', qq(SELECT pg_relation_filepath('@_');)) ); } sub run_pg_filedump { my ($rel, @options) = @_; my ($stdout, $stderr); my $loc = get_table_location($rel); my $cmd = [ 'pg_filedump', @options, $loc ]; my $result = run $cmd, '>', \$stdout, '2>', \$stderr or die "Error: could not execute pg_filedump"; ok($stdout !~ qr/Error/, "error not found"); return $stdout; } sub test_basic_output { setup_test(); my $out_ = run_pg_filedump('t1', ("-D", "int,text,bigint")); ok($out_ =~ qr/Header/, "Header found"); ok($out_ =~ qr/COPY: 1/, "first COPY found"); ok($out_ =~ qr/COPY: 2/, "second COPY found"); ok($out_ =~ qr/COPY: 3/, "third COPY found"); ok($out_ =~ qr/COPY: 4/, "fourth COPY found"); ok($out_ =~ qr/29347293874234447/, "number found"); ok($out_ =~ qr/asdasd/, "string found"); } sub test_btree_output { setup_test(); my $query = qq( insert into t1 select * FROM generate_series(1, 10000); create index i1 on t1(b); checkpoint; ); $node->safe_psql('postgres', $query); my $out_ = run_pg_filedump('i1', ('-i')); ok($out_ =~ qr/Header/, "Header found"); ok($out_ =~ qr/BTree Index Section/, "BTree Index Section found"); ok($out_ =~ qr/BTree Meta Data/, "BTree Meta Data found"); ok($out_ =~ qr/Item 3/, "Item found"); ok($out_ =~ qr/Previous/, "Previous item found"); ok($out_ =~ qr/Next/, "Next item found"); ok($out_ =~ qr/Level/, "Level found"); ok($out_ !~ qr/Next XID/, "Next XID not found"); # make leaf with BTP_DELETED flag $node->safe_psql('postgres', "delete from t1 where a >= 2000 and a < 4000;"); $node->safe_psql('postgres', "vacuum t1; checkpoint;"); $out_ = run_pg_filedump('i1', ('-i')); ok($out_ =~ qr/Next XID/, "Next XID found"); } sub test_spgist_output { setup_test(); $node->safe_psql('postgres', "create index i2 on t1 using spgist(b); checkpoint;"); my $out_ = run_pg_filedump('i2'); ok($out_ =~ qr/Header/, "Header found"); ok($out_ =~ qr/SPGIST Index Section/, "SPGIST Index Section found"); ok($out_ =~ qr/Item 4/, "Item found"); } sub test_gin_output { setup_test(); my $query = qq( create extension btree_gin; create index i3 on t1 using gin(b); checkpoint; ); $node->safe_psql('postgres', $query); my $out_ = run_pg_filedump('i3'); ok($out_ =~ qr/GIN Meta Data/, "Metadata found"); ok($out_ =~ qr/Leaf Page of Element B-tree/, "Leaf Page of Element B-tree found"); ok($out_ =~ qr/Posting List 1/, "Posting List 1 found"); ok($out_ =~ qr/ItemPointer 1/, "ItemPointer 1 found"); ok($out_ =~ qr/ItemPointer 2/, "ItemPointer 2 found"); ok($out_ =~ qr/Posting List 2/, "Posting List 2 found"); ok($out_ =~ qr/ItemPointer 1/, "ItemPointer 3 found"); ok($out_ =~ qr/Posting List 3/, "Posting List 3 found"); ok($out_ =~ qr/ItemPointer 1/, "ItemPointer 4 found"); ok($out_ =~ qr/GIN Index Section/, "GIN Index Section found"); }