├── Makefile ├── main.h ├── dblib.c ├── main.c └── display.c /Makefile: -------------------------------------------------------------------------------- 1 | PG_CONFIG = pg_config 2 | 3 | PROGRAM = pg_internals_explorer 4 | OBJS = main.o dblib.o display.o 5 | 6 | PG_CPPFLAGS = -I$(libpq_srcdir) 7 | PG_LIBS = $(libpq_pgport) -lcurses 8 | 9 | PGXS := $(shell $(PG_CONFIG) --pgxs) 10 | include $(PGXS) 11 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include 5 | 6 | #define FRONTEND 1 7 | #include "postgres_fe.h" 8 | #include "storage/block.h" 9 | 10 | #include 11 | 12 | #define reporterror(...) \ 13 | do { \ 14 | werase(errorw); \ 15 | mvwprintw(errorw, 0,0, __VA_ARGS__); \ 16 | wrefresh(errorw); \ 17 | } while(0); 18 | 19 | typedef struct 20 | { 21 | char relname[NAMEDATALEN*2+1]; 22 | BlockNumber nblocks; 23 | char relkind; 24 | } relation_info; 25 | 26 | WINDOW *errorw; 27 | 28 | 29 | extern void db_connect(void); 30 | extern bool db_is_connected(void); 31 | extern relation_info *db_fetch_relations(int *nrels); 32 | extern char *db_fetch_block(char *relname, char *forkname, BlockNumber blkno); 33 | 34 | 35 | extern void display_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno); 36 | extern void display_relations(WINDOW *hdrw, WINDOW *w, relation_info *rels, int nrels); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /dblib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Functions for managing the connection to the backend. 3 | */ 4 | 5 | #include "main.h" 6 | 7 | #include "libpq-fe.h" 8 | 9 | static PGconn *conn = NULL; 10 | 11 | bool 12 | db_is_connected(void) 13 | { 14 | return conn != NULL; 15 | } 16 | 17 | void 18 | db_connect(void) 19 | { 20 | /* 21 | * FIXME: ATM, you have to use env variables to specify where and how to 22 | * connect. 23 | */ 24 | conn = PQconnectdb(""); 25 | 26 | /* Check to see that the backend connection was successfully made */ 27 | if (PQstatus(conn) != CONNECTION_OK) 28 | { 29 | reporterror("Connection to database failed: %s", 30 | PQerrorMessage(conn)); 31 | PQfinish(conn); 32 | conn = NULL; 33 | } 34 | } 35 | 36 | relation_info * 37 | db_fetch_relations(int *nrels) 38 | { 39 | PGresult *res; 40 | relation_info *result; 41 | int i; 42 | 43 | res = PQexec(conn, "SELECT relname, relpages, relkind FROM pg_catalog.pg_class ORDER BY relname"); 44 | if (PQresultStatus(res) != PGRES_TUPLES_OK) 45 | { 46 | reporterror("could not get relation list: %s", PQerrorMessage(conn)); 47 | PQclear(res); 48 | return NULL; 49 | } 50 | 51 | /* first, print out the attribute names */ 52 | if (PQnfields(res) != 3) 53 | { 54 | reporterror("unexpected result set"); 55 | PQclear(res); 56 | return NULL; 57 | } 58 | 59 | *nrels = PQntuples(res); 60 | result = pg_malloc(sizeof(relation_info) * (*nrels)); 61 | 62 | for (i = 0; i < *nrels; i++) 63 | { 64 | relation_info *rel = &result[i]; 65 | 66 | strncpy(rel->relname, PQgetvalue(res, i, 0), sizeof(rel->relname)); 67 | rel->nblocks = strtoul(PQgetvalue(res, i, 1), NULL, 10); 68 | rel->relkind = *PQgetvalue(res, i, 2); 69 | } 70 | PQclear(res); 71 | return result; 72 | } 73 | 74 | char * 75 | db_fetch_block(char *relname, char *forkname, BlockNumber blkno) 76 | { 77 | const char *params[3]; 78 | char blknobuf[10]; 79 | PGresult *res; 80 | char *result; 81 | 82 | snprintf(blknobuf, sizeof(blknobuf), "%d", blkno); 83 | 84 | params[0] = relname; 85 | params[1] = forkname; 86 | params[2] = blknobuf; 87 | 88 | /* FIXME: schema-qualify this? */ 89 | res = PQexecParams(conn, "SELECT get_raw_page($1, $2, $3)", 3, 90 | NULL, params, NULL, NULL, 91 | 1); /* binary result */ 92 | if (PQresultStatus(res) != PGRES_TUPLES_OK) 93 | { 94 | reporterror("get_raw_page call failed: %s", PQerrorMessage(conn)); 95 | PQclear(res); 96 | return NULL; 97 | } 98 | 99 | /* first, print out the attribute names */ 100 | if (PQnfields(res) != 1 || PQntuples(res) != 1) 101 | { 102 | reporterror("unexpected result set from get_raw_page()"); 103 | PQclear(res); 104 | return NULL; 105 | } 106 | 107 | if (PQgetlength(res, 0, 0) != BLCKSZ) 108 | { 109 | reporterror("unexpected result length from get_raw_page: %d, expected %d", 110 | PQgetlength(res, 0, 0), BLCKSZ); 111 | PQclear(res); 112 | return NULL; 113 | } 114 | 115 | result = pg_malloc(BLCKSZ); 116 | memcpy(result, PQgetvalue(res, 0, 0), BLCKSZ); 117 | 118 | PQclear(res); 119 | return result; 120 | } 121 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | 4 | #include 5 | 6 | static WINDOW *statuswin; 7 | static WINDOW *hdrwin; 8 | static WINDOW *mainwin; 9 | static WINDOW *blockpad; 10 | static int blockpad_pos = 0; 11 | 12 | static relation_info *rels; 13 | static int nrels; 14 | 15 | static int selected_rel = 0; 16 | static int blockpad_pos_save = 0; 17 | 18 | static int blockpad_lines = 20; 19 | 20 | 21 | static BlockNumber displayed_block = InvalidBlockNumber; 22 | static char *block; 23 | 24 | static void 25 | scroll_blockpad(int n) 26 | { 27 | blockpad_pos += n; 28 | if (blockpad_pos < 0) 29 | blockpad_pos = 0; 30 | if (blockpad_pos > 550) 31 | blockpad_pos = 550; 32 | } 33 | 34 | static void 35 | move_selection(int n) 36 | { 37 | mvwchgat(blockpad, selected_rel, 0, 40, A_NORMAL, 0, NULL); 38 | 39 | selected_rel += n; 40 | if (selected_rel >= nrels) 41 | selected_rel = nrels - 1; 42 | if (selected_rel < 0) 43 | selected_rel = 0; 44 | 45 | if (selected_rel < blockpad_pos) 46 | scroll_blockpad(selected_rel - blockpad_pos); 47 | else if (selected_rel - blockpad_pos > blockpad_lines) 48 | scroll_blockpad(selected_rel - blockpad_pos - blockpad_lines); 49 | 50 | mvwchgat(blockpad, selected_rel, 0, 40, A_REVERSE, 0, NULL); 51 | } 52 | 53 | static void 54 | refresh_screen(void) 55 | { 56 | wrefresh(hdrwin); 57 | prefresh(blockpad, blockpad_pos, 0, 2, 0, blockpad_lines + 2, 80); 58 | } 59 | 60 | int main() 61 | { 62 | int ch; 63 | 64 | initscr(); /* start curses */ 65 | raw(); /* disable line buffering */ 66 | keypad(stdscr, TRUE); 67 | noecho(); 68 | 69 | start_color(); 70 | use_default_colors(); 71 | init_pair(10, COLOR_WHITE, COLOR_RED); 72 | 73 | statuswin = subwin(stdscr, 1, 0, 0, 0); 74 | hdrwin = subwin(stdscr, 1, 0, 1, 0); 75 | 76 | blockpad = newpad(1000, 60); 77 | 78 | errorw = statuswin; 79 | 80 | wclear(stdscr); 81 | 82 | db_connect(); 83 | if (db_is_connected()) 84 | { 85 | reporterror("connected"); 86 | rels = db_fetch_relations(&nrels); 87 | if (rels) 88 | display_relations(hdrwin, blockpad, rels, nrels); 89 | } 90 | 91 | move_selection(0); 92 | refresh(); 93 | 94 | for (;;) 95 | { 96 | refresh_screen(); 97 | 98 | ch = getch(); 99 | 100 | werase(statuswin); 101 | wrefresh(statuswin); 102 | 103 | switch (ch) 104 | { 105 | case KEY_UP: 106 | if (displayed_block != InvalidBlockNumber) 107 | scroll_blockpad(-1); 108 | else 109 | move_selection(-1); 110 | break; 111 | case KEY_DOWN: 112 | if (displayed_block != InvalidBlockNumber) 113 | scroll_blockpad(1); 114 | else 115 | move_selection(1); 116 | break; 117 | case KEY_NPAGE: 118 | if (displayed_block != InvalidBlockNumber) 119 | scroll_blockpad(15); 120 | else 121 | move_selection(15); 122 | break; 123 | case KEY_PPAGE: 124 | if (displayed_block != InvalidBlockNumber) 125 | scroll_blockpad(-15); 126 | else 127 | move_selection(-15); 128 | break; 129 | 130 | case KEY_ENTER: 131 | case KEY_RIGHT: 132 | if (displayed_block == InvalidBlockNumber) 133 | { 134 | block = db_fetch_block(rels[selected_rel].relname, "main", 0); 135 | if (block) 136 | { 137 | blockpad_pos_save = blockpad_pos; 138 | blockpad_pos = 0; 139 | displayed_block = 0; 140 | display_block(hdrwin, blockpad, block, displayed_block); 141 | } 142 | } 143 | else 144 | { 145 | displayed_block++; 146 | block = db_fetch_block(rels[selected_rel].relname, "main", displayed_block); 147 | if (block) 148 | { 149 | blockpad_pos_save = blockpad_pos; 150 | blockpad_pos = 0; 151 | display_block(hdrwin, blockpad, block, displayed_block); 152 | } 153 | } 154 | break; 155 | 156 | case KEY_LEFT: 157 | if (displayed_block != InvalidBlockNumber) 158 | { 159 | displayed_block--; 160 | if (displayed_block == InvalidBlockNumber) 161 | { 162 | blockpad_pos = blockpad_pos_save; 163 | if (displayed_block == InvalidBlockNumber) 164 | { 165 | display_relations(hdrwin, blockpad, rels, nrels); 166 | mvwchgat(blockpad, selected_rel, 0, 40, A_REVERSE, 0, NULL); 167 | } 168 | } 169 | else 170 | { 171 | block = db_fetch_block(rels[selected_rel].relname, "main", displayed_block); 172 | if (block) 173 | { 174 | blockpad_pos_save = blockpad_pos; 175 | blockpad_pos = 0; 176 | display_block(hdrwin, blockpad, block, displayed_block); 177 | } 178 | } 179 | 180 | } 181 | break; 182 | 183 | case 'g': /* goto block */ 184 | if (displayed_block != InvalidBlockNumber) 185 | { 186 | char str[11]; 187 | BlockNumber blkno; 188 | char *endptr; 189 | 190 | werase(hdrwin); 191 | mvwprintw(hdrwin, 0, 0, "Goto block: "); 192 | refresh_screen(); 193 | echo(); 194 | getnstr(str, sizeof(str) - 1); 195 | noecho(); 196 | 197 | blkno = strtoul(str, &endptr, 10); 198 | if (*endptr != '\0') 199 | { 200 | werase(hdrwin); 201 | mvwprintw(hdrwin, 0, 0, "Invalid block number"); 202 | } 203 | else 204 | { 205 | block = db_fetch_block(rels[selected_rel].relname, "main", blkno); 206 | if (block) 207 | { 208 | displayed_block = blkno; 209 | blockpad_pos_save = blockpad_pos; 210 | blockpad_pos = 0; 211 | display_block(hdrwin, blockpad, block, displayed_block); 212 | } 213 | } 214 | } 215 | break; 216 | 217 | case 'q': 218 | endwin(); /* End curses mode */ 219 | exit(0); 220 | break; 221 | default: 222 | reporterror("unknown key: %c", ch); 223 | } 224 | } 225 | endwin(); /* End curses mode */ 226 | 227 | return 0; 228 | } 229 | -------------------------------------------------------------------------------- /display.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | #include "storage/bufpage.h" 4 | #include "storage/itemptr.h" 5 | 6 | #include 7 | 8 | /***** stuff copy-pasted from btree.h *****/ 9 | 10 | /* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */ 11 | typedef uint16 BTCycleId; 12 | #define MAX_BT_CYCLE_ID 0xFF7F 13 | 14 | typedef struct BTPageOpaqueData 15 | { 16 | BlockNumber btpo_prev; /* left sibling, or P_NONE if leftmost */ 17 | BlockNumber btpo_next; /* right sibling, or P_NONE if rightmost */ 18 | union 19 | { 20 | uint32 level; /* tree level --- zero for leaf pages */ 21 | TransactionId xact; /* next transaction ID, if deleted */ 22 | } btpo; 23 | uint16 btpo_flags; /* flag bits, see below */ 24 | BTCycleId btpo_cycleid; /* vacuum cycle ID of latest split */ 25 | } BTPageOpaqueData; 26 | 27 | typedef BTPageOpaqueData *BTPageOpaque; 28 | 29 | typedef struct IndexTupleData 30 | { 31 | ItemPointerData t_tid; /* reference TID to heap tuple */ 32 | 33 | /* --------------- 34 | * t_info is laid out in the following fashion: 35 | * 36 | * 15th (high) bit: has nulls 37 | * 14th bit: has var-width attributes 38 | * 13th bit: unused 39 | * 12-0 bit: size of tuple 40 | * --------------- 41 | */ 42 | 43 | unsigned short t_info; /* various info about tuple */ 44 | 45 | } IndexTupleData; /* MORE DATA FOLLOWS AT END OF STRUCT */ 46 | 47 | typedef IndexTupleData *IndexTuple; 48 | 49 | 50 | #define IndexInfoFindDataOffset(t_info) \ 51 | ( \ 52 | (!((t_info) & INDEX_NULL_MASK)) ? \ 53 | ( \ 54 | (Size)MAXALIGN(sizeof(IndexTupleData)) \ 55 | ) \ 56 | : \ 57 | ( \ 58 | (Size)MAXALIGN(sizeof(IndexTupleData) + sizeof(IndexAttributeBitMapData)) \ 59 | ) \ 60 | ) 61 | #define INDEX_NULL_MASK 0x8000 62 | 63 | typedef struct IndexAttributeBitMapData 64 | { 65 | bits8 bits[(INDEX_MAX_KEYS + 8 - 1) / 8]; 66 | } IndexAttributeBitMapData; 67 | 68 | /***** *****/ 69 | 70 | 71 | static void display_heap_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno); 72 | static void display_btree_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno); 73 | static void display_raw_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno); 74 | 75 | void 76 | display_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno) 77 | { 78 | int special_size; 79 | 80 | special_size = BLCKSZ - ((PageHeader) (block))->pd_special; 81 | 82 | if (special_size == 0) 83 | display_heap_block(hdrw, w, block, blkno); 84 | else if (special_size == sizeof(BTPageOpaqueData) && ((BTPageOpaque) PageGetSpecialPointer(block))->btpo_cycleid <= MAX_BT_CYCLE_ID) 85 | display_btree_block(hdrw, w, block, blkno); 86 | else 87 | display_raw_block(hdrw, w, block, blkno); 88 | } 89 | 90 | int itemy, itemx; 91 | int rawdatay, rawdatax; 92 | 93 | static void 94 | colorrawbytes(WINDOW *w, int start, int len, attr_t attr, short color) 95 | { 96 | int y, x; 97 | int pos; 98 | 99 | for (pos = start; pos < start + len; pos++) 100 | { 101 | y = rawdatay + pos / 16; 102 | x = 6 + (pos % 16) * 2; 103 | if (pos % 16 >= 8) 104 | x++; 105 | mvwchgat(w, y, x, 2, attr, color, NULL); 106 | } 107 | } 108 | 109 | 110 | static void 111 | display_heap_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno) 112 | { 113 | PageHeader phdr = (PageHeader) block; 114 | OffsetNumber off, 115 | maxoff; 116 | XLogRecPtr lsn; 117 | int i; 118 | 119 | werase(hdrw); 120 | werase(w); 121 | wmove(w, 0, 0); 122 | 123 | mvwprintw(hdrw, 0, 0, "Displaying heap block %u", blkno); 124 | 125 | lsn = PageGetLSN(block); 126 | wprintw(w, "pd_lsn: %X/%08X\n", (uint32) (lsn >> 32), (uint32) lsn); 127 | wprintw(w, "pd_checksum: %04X\n", phdr->pd_checksum); 128 | wprintw(w, "pd_flags: %04X\n", phdr->pd_flags); 129 | wprintw(w, "pd_lower: %d\n", phdr->pd_lower); 130 | wprintw(w, "pd_upper: %d\n", phdr->pd_upper); 131 | wprintw(w, "pd_special: %d\n", phdr->pd_special); 132 | wprintw(w, "pd_pagesize_version: %04X\n", phdr->pd_pagesize_version); 133 | wprintw(w, "pd_prune_xid: %u\n", phdr->pd_prune_xid); 134 | 135 | maxoff = PageGetMaxOffsetNumber(block); 136 | wprintw(w, "\nItems (%d):\n", maxoff); 137 | getyx(w, itemy, itemx); 138 | for (off = 1; off <= maxoff; off++) 139 | { 140 | ItemId iid = PageGetItemId(block, off); 141 | char *flags; 142 | 143 | switch (iid->lp_flags) 144 | { 145 | case LP_UNUSED: 146 | flags = "UNUSED"; 147 | break; 148 | case LP_NORMAL: 149 | flags = "NORMAL"; 150 | break; 151 | case LP_REDIRECT: 152 | flags = "REDIRECT"; 153 | break; 154 | case LP_DEAD: 155 | flags = "DEAD"; 156 | break; 157 | default: 158 | flags = "???"; 159 | break; 160 | } 161 | 162 | wprintw(w, "%u: off: %u, flags: %s, len: %u\n", 163 | off, iid->lp_off, flags, iid->lp_len); 164 | } 165 | 166 | wprintw(w, "\nRaw:\n"); 167 | getyx(w, rawdatay, rawdatax); 168 | for (i = 0; i < BLCKSZ; i+=16) 169 | { 170 | unsigned char *p = (unsigned char *) &block[i]; 171 | wprintw(w, 172 | "%04X %02X%02X%02X%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X%02X%02X%02X\n", 173 | i, 174 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 175 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 176 | } 177 | 178 | /* Color the raw data */ 179 | 180 | /* page header */ 181 | colorrawbytes(w, 0, SizeOfPageHeaderData, A_NORMAL, COLOR_PAIR(10)); 182 | } 183 | 184 | static void 185 | display_btree_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno) 186 | { 187 | PageHeader phdr = (PageHeader) block; 188 | OffsetNumber off, 189 | maxoff; 190 | XLogRecPtr lsn; 191 | int i; 192 | BTPageOpaque btpageop; 193 | 194 | werase(hdrw); 195 | werase(w); 196 | wmove(w, 0, 0); 197 | 198 | mvwprintw(hdrw, 0, 0, "Displaying B-tree block %u", blkno); 199 | 200 | lsn = PageGetLSN(block); 201 | btpageop = (BTPageOpaque) PageGetSpecialPointer(block); 202 | 203 | wprintw(w, "pd_lsn: %X/%08X\n", (uint32) (lsn >> 32), (uint32) lsn); 204 | wprintw(w, "pd_checksum: %04X\n", phdr->pd_checksum); 205 | wprintw(w, "pd_flags: %04X\n", phdr->pd_flags); 206 | wprintw(w, "pd_lower: %d\n", phdr->pd_lower); 207 | wprintw(w, "pd_upper: %d\n", phdr->pd_upper); 208 | wprintw(w, "pd_special: %d\n", phdr->pd_special); 209 | wprintw(w, "pd_pagesize_version: %04X\n", phdr->pd_pagesize_version); 210 | wprintw(w, "pd_prune_xid: %u\n", phdr->pd_prune_xid); 211 | 212 | wprintw(w, "\n"); 213 | wprintw(w, "btpo_prev: %u\n", btpageop->btpo_prev); 214 | wprintw(w, "btpo_next: %u\n", btpageop->btpo_next); 215 | wprintw(w, "btpo_level: %u\n", btpageop->btpo.level); 216 | wprintw(w, "btpo_flags: %u\n", btpageop->btpo_flags); 217 | wprintw(w, "btpo_cycleid: %u\n", btpageop->btpo_cycleid); 218 | 219 | maxoff = PageGetMaxOffsetNumber(block); 220 | wprintw(w, "\nItems (%d):\n", maxoff); 221 | getyx(w, itemy, itemx); 222 | for (off = 1; off <= maxoff; off++) 223 | { 224 | ItemId iid = PageGetItemId(block, off); 225 | char *flags; 226 | 227 | switch (iid->lp_flags) 228 | { 229 | case LP_UNUSED: 230 | flags = "UNUSED"; 231 | break; 232 | case LP_NORMAL: 233 | flags = "NORMAL"; 234 | break; 235 | case LP_REDIRECT: 236 | flags = "REDIRECT"; 237 | break; 238 | case LP_DEAD: 239 | flags = "DEAD"; 240 | break; 241 | default: 242 | flags = "???"; 243 | break; 244 | } 245 | 246 | wprintw(w, "%u: off: %u, flags: %s, len: %u", 247 | off, iid->lp_off, flags, iid->lp_len); 248 | 249 | if (ItemIdHasStorage(iid)) 250 | { 251 | IndexTuple itup = (IndexTuple) PageGetItem(block, iid); 252 | ItemPointerData t_tid = itup->t_tid; 253 | int32 key; 254 | 255 | wprintw(w, " (%u, %u)", 256 | (BlockNumber) ((t_tid.ip_blkid.bi_hi << 16) | (uint16) (t_tid.ip_blkid.bi_lo)), 257 | t_tid.ip_posid); 258 | 259 | /* key value, assuming it's an int4 */ 260 | key = *(int32 *)(((char *) itup) + IndexInfoFindDataOffset(itup->t_info)); 261 | wprintw(w, " %d", key); 262 | 263 | wprintw(w, "\n"); 264 | } 265 | } 266 | 267 | wprintw(w, "\nRaw:\n"); 268 | getyx(w, rawdatay, rawdatax); 269 | for (i = 0; i < BLCKSZ; i+=16) 270 | { 271 | unsigned char *p = (unsigned char *) &block[i]; 272 | wprintw(w, 273 | "%04X %02X%02X%02X%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X%02X%02X%02X\n", 274 | i, 275 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 276 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 277 | } 278 | 279 | /* Color the raw data */ 280 | 281 | /* page header */ 282 | colorrawbytes(w, 0, SizeOfPageHeaderData, A_NORMAL, COLOR_PAIR(10)); 283 | } 284 | 285 | 286 | static void 287 | display_raw_block(WINDOW *hdrw, WINDOW *w, char *block, BlockNumber blkno) 288 | { 289 | int i; 290 | 291 | werase(hdrw); 292 | werase(w); 293 | 294 | wmove(w, 0, 0); 295 | 296 | for (i = 0; i < BLCKSZ; i += 16) 297 | { 298 | unsigned char *p = (unsigned char *) &block[i]; 299 | wprintw(w, 300 | "%04X %02X%02X%02X%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X%02X%02X%02X\n", 301 | i, 302 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], 303 | p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); 304 | } 305 | } 306 | 307 | 308 | 309 | void 310 | display_relations(WINDOW *hdrw, WINDOW *w, relation_info *rels, int nrels) 311 | { 312 | int i; 313 | 314 | werase(hdrw); 315 | werase(w); 316 | 317 | /* print header */ 318 | mvwprintw(hdrw, 0, 0, "name"); 319 | mvwprintw(hdrw, 0, 30, "size"); 320 | mvwprintw(hdrw, 0, 40, "relkind"); 321 | 322 | for (i = 0; i < nrels; i++) 323 | { 324 | relation_info *rel = &rels[i]; 325 | mvwprintw(w, i, 0, "%.29s", rel->relname); 326 | mvwprintw(w, i, 30, "%u", rel->nblocks); 327 | mvwprintw(w, i, 40, "%c", rel->relkind); 328 | } 329 | reporterror("relations fetched"); 330 | } 331 | 332 | 333 | --------------------------------------------------------------------------------