2 * oid2name, a PostgreSQL app to map OIDs on the filesystem
3 * to table and database names.
6 * B. Palmer, bpalmer@crimelabs.net 1-17-2001
8 * contrib/oid2name/oid2name.c
10 #include "postgres_fe.h"
12 #include "catalog/pg_class_d.h"
13 #include "common/connect.h"
14 #include "common/logging.h"
15 #include "common/string.h"
16 #include "getopt_long.h"
18 #include "pg_getopt.h"
20 /* an extensible array to keep track of elements to show */
28 /* these are the opts structures for command line params */
49 /* function prototypes */
50 static void help(const char *progname
);
51 void get_opts(int argc
, char **argv
, struct options
*my_opts
);
52 void add_one_elt(char *eltname
, eary
*eary
);
53 char *get_comma_elts(eary
*eary
);
54 PGconn
*sql_conn(struct options
*my_opts
);
55 int sql_exec(PGconn
*conn
, const char *todo
, bool quiet
);
56 void sql_exec_dumpalldbs(PGconn
*conn
, struct options
*opts
);
57 void sql_exec_dumpalltables(PGconn
*conn
, struct options
*opts
);
58 void sql_exec_searchtables(PGconn
*conn
, struct options
*opts
);
59 void sql_exec_dumpalltbspc(PGconn
*conn
, struct options
*opts
);
61 /* function to parse command line options and check for some usage errors. */
63 get_opts(int argc
, char **argv
, struct options
*my_opts
)
65 static struct option long_options
[] = {
66 {"dbname", required_argument
, NULL
, 'd'},
67 {"host", required_argument
, NULL
, 'h'},
68 {"host", required_argument
, NULL
, 'H'}, /* deprecated */
69 {"filenode", required_argument
, NULL
, 'f'},
70 {"indexes", no_argument
, NULL
, 'i'},
71 {"oid", required_argument
, NULL
, 'o'},
72 {"port", required_argument
, NULL
, 'p'},
73 {"quiet", no_argument
, NULL
, 'q'},
74 {"tablespaces", no_argument
, NULL
, 's'},
75 {"system-objects", no_argument
, NULL
, 'S'},
76 {"table", required_argument
, NULL
, 't'},
77 {"username", required_argument
, NULL
, 'U'},
78 {"version", no_argument
, NULL
, 'V'},
79 {"extended", no_argument
, NULL
, 'x'},
80 {"help", no_argument
, NULL
, '?'},
88 pg_logging_init(argv
[0]);
89 progname
= get_progname(argv
[0]);
91 /* set the defaults */
92 my_opts
->quiet
= false;
93 my_opts
->systables
= false;
94 my_opts
->indexes
= false;
95 my_opts
->nodb
= false;
96 my_opts
->extended
= false;
97 my_opts
->tablespaces
= false;
98 my_opts
->dbname
= NULL
;
99 my_opts
->hostname
= NULL
;
100 my_opts
->port
= NULL
;
101 my_opts
->username
= NULL
;
102 my_opts
->progname
= progname
;
106 if (strcmp(argv
[1], "--help") == 0 || strcmp(argv
[1], "-?") == 0)
111 if (strcmp(argv
[1], "--version") == 0 || strcmp(argv
[1], "-V") == 0)
113 puts("oid2name (PostgreSQL) " PG_VERSION
);
119 while ((c
= getopt_long(argc
, argv
, "d:f:h:H:io:p:qsSt:U:x", long_options
, &optindex
)) != -1)
123 /* specify the database */
125 my_opts
->dbname
= pg_strdup(optarg
);
128 /* specify one filenumber to show */
130 add_one_elt(optarg
, my_opts
->filenumbers
);
133 /* host to connect to */
134 case 'H': /* deprecated */
136 my_opts
->hostname
= pg_strdup(optarg
);
139 /* also display indexes */
141 my_opts
->indexes
= true;
144 /* specify one Oid to show */
146 add_one_elt(optarg
, my_opts
->oids
);
149 /* port to connect to on remote host */
151 my_opts
->port
= pg_strdup(optarg
);
154 /* don't show headers */
156 my_opts
->quiet
= true;
159 /* dump tablespaces only */
161 my_opts
->tablespaces
= true;
164 /* display system tables */
166 my_opts
->systables
= true;
169 /* specify one tablename to show */
171 add_one_elt(optarg
, my_opts
->tables
);
176 my_opts
->username
= pg_strdup(optarg
);
179 /* display extra columns */
181 my_opts
->extended
= true;
185 /* getopt_long already emitted a complaint */
186 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
193 pg_log_error("too many command-line arguments (first is \"%s\")",
195 pg_log_error_hint("Try \"%s --help\" for more information.", progname
);
201 help(const char *progname
)
203 printf("%s helps examining the file structure used by PostgreSQL.\n\n"
207 " -f, --filenode=FILENODE show info for table with given file node\n"
208 " -i, --indexes show indexes and sequences too\n"
209 " -o, --oid=OID show info for table with given OID\n"
210 " -q, --quiet quiet (don't show headers)\n"
211 " -s, --tablespaces show all tablespaces\n"
212 " -S, --system-objects show system objects too\n"
213 " -t, --table=TABLE show info for named table\n"
214 " -V, --version output version information, then exit\n"
215 " -x, --extended extended (show additional columns)\n"
216 " -?, --help show this help, then exit\n"
217 "\nConnection options:\n"
218 " -d, --dbname=DBNAME database to connect to\n"
219 " -h, --host=HOSTNAME database server host or socket directory\n"
220 " -H same as -h, deprecated option\n"
221 " -p, --port=PORT database server port number\n"
222 " -U, --username=USERNAME connect as specified database user\n"
223 "\nThe default action is to show all database OIDs.\n\n"
224 "Report bugs to <%s>.\n"
225 "%s home page: <%s>\n",
226 progname
, progname
, PACKAGE_BUGREPORT
, PACKAGE_NAME
, PACKAGE_URL
);
232 * Add one element to a (possibly empty) eary struct.
235 add_one_elt(char *eltname
, eary
*eary
)
237 if (eary
->alloc
== 0)
240 eary
->array
= (char **) pg_malloc(8 * sizeof(char *));
242 else if (eary
->num
>= eary
->alloc
)
245 eary
->array
= (char **) pg_realloc(eary
->array
,
246 eary
->alloc
* sizeof(char *));
249 eary
->array
[eary
->num
] = pg_strdup(eltname
);
256 * Return the elements of an eary as a (freshly allocated) single string, in
257 * single quotes, separated by commas and properly escaped for insertion in an
261 get_comma_elts(eary
*eary
)
269 return pg_strdup("");
272 * PQescapeString wants 2 * length + 1 bytes of breath space. Add two
273 * chars per element for the single quotes and one for the comma.
275 for (i
= 0; i
< eary
->num
; i
++)
276 length
+= strlen(eary
->array
[i
]);
278 ret
= (char *) pg_malloc(length
* 2 + 4 * eary
->num
);
281 for (i
= 0; i
< eary
->num
; i
++)
286 ptr
+= PQescapeString(ptr
, eary
->array
[i
], strlen(eary
->array
[i
]));
293 /* establish connection with database. */
295 sql_conn(struct options
*my_opts
)
298 char *password
= NULL
;
303 * Start the connection. Loop until we have a password if requested by
308 #define PARAMS_ARRAY_SIZE 7
310 const char *keywords
[PARAMS_ARRAY_SIZE
];
311 const char *values
[PARAMS_ARRAY_SIZE
];
313 keywords
[0] = "host";
314 values
[0] = my_opts
->hostname
;
315 keywords
[1] = "port";
316 values
[1] = my_opts
->port
;
317 keywords
[2] = "user";
318 values
[2] = my_opts
->username
;
319 keywords
[3] = "password";
320 values
[3] = password
;
321 keywords
[4] = "dbname";
322 values
[4] = my_opts
->dbname
;
323 keywords
[5] = "fallback_application_name";
324 values
[5] = my_opts
->progname
;
329 conn
= PQconnectdbParams(keywords
, values
, true);
332 pg_fatal("could not connect to database %s",
335 if (PQstatus(conn
) == CONNECTION_BAD
&&
336 PQconnectionNeedsPassword(conn
) &&
340 password
= simple_prompt("Password: ", false);
345 /* check to see that the backend connection was successfully made */
346 if (PQstatus(conn
) == CONNECTION_BAD
)
348 pg_log_error("%s", PQerrorMessage(conn
));
353 res
= PQexec(conn
, ALWAYS_SECURE_SEARCH_PATH_SQL
);
354 if (PQresultStatus(res
) != PGRES_TUPLES_OK
)
356 pg_log_error("could not clear search_path: %s",
357 PQerrorMessage(conn
));
364 /* return the conn if good */
369 * Actual code to make call to the database and print the output data.
372 sql_exec(PGconn
*conn
, const char *todo
, bool quiet
)
385 res
= PQexec(conn
, todo
);
387 /* check and deal with errors */
388 if (!res
|| PQresultStatus(res
) > 2)
390 pg_log_error("query failed: %s", PQerrorMessage(conn
));
391 pg_log_error_detail("Query was: %s", todo
);
398 /* get the number of fields */
399 nrows
= PQntuples(res
);
400 nfields
= PQnfields(res
);
402 /* for each field, get the needed width */
403 length
= (int *) pg_malloc(sizeof(int) * nfields
);
404 for (j
= 0; j
< nfields
; j
++)
405 length
[j
] = strlen(PQfname(res
, j
));
407 for (i
= 0; i
< nrows
; i
++)
409 for (j
= 0; j
< nfields
; j
++)
411 l
= strlen(PQgetvalue(res
, i
, j
));
413 length
[j
] = strlen(PQgetvalue(res
, i
, j
));
420 for (j
= 0, l
= 0; j
< nfields
; j
++)
422 fprintf(stdout
, "%*s", length
[j
] + 2, PQfname(res
, j
));
425 fprintf(stdout
, "\n");
426 pad
= (char *) pg_malloc(l
+ 1);
429 fprintf(stdout
, "%s\n", pad
);
433 /* for each row, dump the information */
434 for (i
= 0; i
< nrows
; i
++)
436 for (j
= 0; j
< nfields
; j
++)
437 fprintf(stdout
, "%*s", length
[j
] + 2, PQgetvalue(res
, i
, j
));
438 fprintf(stdout
, "\n");
449 * Dump all databases. There are no system objects to worry about.
452 sql_exec_dumpalldbs(PGconn
*conn
, struct options
*opts
)
456 /* get the oid and database name from the system pg_database table */
457 snprintf(todo
, sizeof(todo
),
458 "SELECT d.oid AS \"Oid\", datname AS \"Database Name\", "
459 "spcname AS \"Tablespace\" FROM pg_catalog.pg_database d JOIN pg_catalog.pg_tablespace t ON "
460 "(dattablespace = t.oid) ORDER BY 2");
462 sql_exec(conn
, todo
, opts
->quiet
);
466 * Dump all tables, indexes and sequences in the current database.
469 sql_exec_dumpalltables(PGconn
*conn
, struct options
*opts
)
472 char *addfields
= ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
474 snprintf(todo
, sizeof(todo
),
475 "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s "
476 "FROM pg_catalog.pg_class c "
477 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
478 " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),"
479 " pg_catalog.pg_tablespace t "
480 "WHERE relkind IN (" CppAsString2(RELKIND_RELATION
) ","
481 CppAsString2(RELKIND_MATVIEW
) "%s%s) AND "
484 " WHEN reltablespace <> 0 THEN reltablespace"
485 " ELSE dattablespace"
488 opts
->extended
? addfields
: "",
489 opts
->indexes
? "," CppAsString2(RELKIND_INDEX
) "," CppAsString2(RELKIND_SEQUENCE
) : "",
490 opts
->systables
? "," CppAsString2(RELKIND_TOASTVALUE
) : "",
491 opts
->systables
? "" : "n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname !~ '^pg_toast' AND");
493 sql_exec(conn
, todo
, opts
->quiet
);
497 * Show oid, filenumber, name, schema and tablespace for each of the
498 * given objects in the current database.
501 sql_exec_searchtables(PGconn
*conn
, struct options
*opts
)
509 bool written
= false;
510 char *addfields
= ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
512 /* get tables qualifiers, whether names, filenumbers, or OIDs */
513 comma_oids
= get_comma_elts(opts
->oids
);
514 comma_tables
= get_comma_elts(opts
->tables
);
515 comma_filenumbers
= get_comma_elts(opts
->filenumbers
);
517 /* 80 extra chars for SQL expression */
518 qualifiers
= (char *) pg_malloc(strlen(comma_oids
) + strlen(comma_tables
) +
519 strlen(comma_filenumbers
) + 80);
522 if (opts
->oids
->num
> 0)
524 ptr
+= sprintf(ptr
, "c.oid IN (%s)", comma_oids
);
527 if (opts
->filenumbers
->num
> 0)
530 ptr
+= sprintf(ptr
, " OR ");
531 ptr
+= sprintf(ptr
, "pg_catalog.pg_relation_filenode(c.oid) IN (%s)",
535 if (opts
->tables
->num
> 0)
538 ptr
+= sprintf(ptr
, " OR ");
539 sprintf(ptr
, "c.relname ~~ ANY (ARRAY[%s])", comma_tables
);
543 free(comma_filenumbers
);
545 /* now build the query */
546 todo
= psprintf("SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s\n"
547 "FROM pg_catalog.pg_class c\n"
548 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n"
549 " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),\n"
550 " pg_catalog.pg_tablespace t\n"
551 "WHERE relkind IN (" CppAsString2(RELKIND_RELATION
) ","
552 CppAsString2(RELKIND_MATVIEW
) ","
553 CppAsString2(RELKIND_INDEX
) ","
554 CppAsString2(RELKIND_SEQUENCE
) ","
555 CppAsString2(RELKIND_TOASTVALUE
) ") AND\n"
557 " WHEN reltablespace <> 0 THEN reltablespace\n"
558 " ELSE dattablespace\n"
561 "ORDER BY relname\n",
562 opts
->extended
? addfields
: "",
567 sql_exec(conn
, todo
, opts
->quiet
);
571 sql_exec_dumpalltbspc(PGconn
*conn
, struct options
*opts
)
575 snprintf(todo
, sizeof(todo
),
576 "SELECT oid AS \"Oid\", spcname as \"Tablespace Name\"\n"
577 "FROM pg_catalog.pg_tablespace");
579 sql_exec(conn
, todo
, opts
->quiet
);
583 main(int argc
, char **argv
)
585 struct options
*my_opts
;
588 my_opts
= (struct options
*) pg_malloc(sizeof(struct options
));
590 my_opts
->oids
= (eary
*) pg_malloc(sizeof(eary
));
591 my_opts
->tables
= (eary
*) pg_malloc(sizeof(eary
));
592 my_opts
->filenumbers
= (eary
*) pg_malloc(sizeof(eary
));
594 my_opts
->oids
->num
= my_opts
->oids
->alloc
= 0;
595 my_opts
->tables
->num
= my_opts
->tables
->alloc
= 0;
596 my_opts
->filenumbers
->num
= my_opts
->filenumbers
->alloc
= 0;
599 get_opts(argc
, argv
, my_opts
);
601 if (my_opts
->dbname
== NULL
)
603 my_opts
->dbname
= "postgres";
604 my_opts
->nodb
= true;
606 pgconn
= sql_conn(my_opts
);
608 /* display only tablespaces */
609 if (my_opts
->tablespaces
)
612 printf("All tablespaces:\n");
613 sql_exec_dumpalltbspc(pgconn
, my_opts
);
619 /* display the given elements in the database */
620 if (my_opts
->oids
->num
> 0 ||
621 my_opts
->tables
->num
> 0 ||
622 my_opts
->filenumbers
->num
> 0)
625 printf("From database \"%s\":\n", my_opts
->dbname
);
626 sql_exec_searchtables(pgconn
, my_opts
);
632 /* no elements given; dump the given database */
633 if (my_opts
->dbname
&& !my_opts
->nodb
)
636 printf("From database \"%s\":\n", my_opts
->dbname
);
637 sql_exec_dumpalltables(pgconn
, my_opts
);
643 /* no database either; dump all databases */
645 printf("All databases:\n");
646 sql_exec_dumpalldbs(pgconn
, my_opts
);