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"
21 /* an extensible array to keep track of elements to show */
29 /* these are the opts structures for command line params */
50 /* function prototypes */
51 static void help(const char *progname);
52 void get_opts(int, char **, struct options *);
53 void add_one_elt(char *eltname, eary *eary);
54 char *get_comma_elts(eary *eary);
55 PGconn *sql_conn(struct options *);
56 int sql_exec(PGconn *, const char *sql, bool quiet);
57 void sql_exec_dumpalldbs(PGconn *, struct options *);
58 void sql_exec_dumpalltables(PGconn *, struct options *);
59 void sql_exec_searchtables(PGconn *, struct options *);
60 void sql_exec_dumpalltbspc(PGconn *, struct options *);
62 /* function to parse command line options and check for some usage errors. */
64 get_opts(int argc, char **argv, struct options * my_opts)
69 progname = get_progname(argv[0]);
71 /* set the defaults */
72 my_opts->quiet = false;
73 my_opts->systables = false;
74 my_opts->indexes = false;
75 my_opts->nodb = false;
76 my_opts->extended = false;
77 my_opts->tablespaces = false;
78 my_opts->dbname = NULL;
79 my_opts->hostname = NULL;
81 my_opts->username = NULL;
82 my_opts->progname = progname;
86 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
91 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
93 puts("oid2name (PostgreSQL) " PG_VERSION);
99 while ((c = getopt(argc, argv, "H:p:U:d:t:o:f:qSxish")) != -1)
103 /* specify the database */
105 my_opts->dbname = pg_strdup(optarg);
108 /* specify one tablename to show */
110 add_one_elt(optarg, my_opts->tables);
113 /* specify one Oid to show */
115 add_one_elt(optarg, my_opts->oids);
118 /* specify one filenode to show */
120 add_one_elt(optarg, my_opts->filenodes);
123 /* don't show headers */
125 my_opts->quiet = true;
128 /* host to connect to */
130 my_opts->hostname = pg_strdup(optarg);
133 /* port to connect to on remote host */
135 my_opts->port = pg_strdup(optarg);
140 my_opts->username = pg_strdup(optarg);
143 /* display system tables */
145 my_opts->systables = true;
148 /* also display indexes */
150 my_opts->indexes = true;
153 /* display extra columns */
155 my_opts->extended = true;
158 /* dump tablespaces only */
160 my_opts->tablespaces = true;
169 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
176 help(const char *progname)
178 printf("%s helps examining the file structure used by PostgreSQL.\n\n"
182 " -d DBNAME database to connect to\n"
183 " -f FILENODE show info for table with given file node\n"
184 " -H HOSTNAME database server host or socket directory\n"
185 " -i show indexes and sequences too\n"
186 " -o OID show info for table with given OID\n"
187 " -p PORT database server port number\n"
188 " -q quiet (don't show headers)\n"
189 " -s show all tablespaces\n"
190 " -S show system objects too\n"
191 " -t TABLE show info for named table\n"
192 " -U NAME connect as specified database user\n"
193 " -V, --version output version information, then exit\n"
194 " -x extended (show additional columns)\n"
195 " -?, --help show this help, then exit\n"
196 "\nThe default action is to show all database OIDs.\n\n"
197 "Report bugs to <pgsql-bugs@postgresql.org>.\n",
204 * Add one element to a (possibly empty) eary struct.
207 add_one_elt(char *eltname, eary *eary)
209 if (eary->alloc == 0)
212 eary ->array = (char **) pg_malloc(8 * sizeof(char *));
214 else if (eary->num >= eary->alloc)
217 eary ->array = (char **) pg_realloc(eary->array,
218 eary->alloc * sizeof(char *));
221 eary ->array[eary->num] = pg_strdup(eltname);
228 * Return the elements of an eary as a (freshly allocated) single string, in
229 * single quotes, separated by commas and properly escaped for insertion in an
233 get_comma_elts(eary *eary)
241 return pg_strdup("");
244 * PQescapeString wants 2 * length + 1 bytes of breath space. Add two
245 * chars per element for the single quotes and one for the comma.
247 for (i = 0; i < eary->num; i++)
248 length += strlen(eary->array[i]);
250 ret = (char *) pg_malloc(length * 2 + 4 * eary->num);
253 for (i = 0; i < eary->num; i++)
258 ptr += PQescapeString(ptr, eary->array[i], strlen(eary->array[i]));
265 /* establish connection with database. */
267 sql_conn(struct options * my_opts)
270 char *password = NULL;
274 * Start the connection. Loop until we have a password if requested by
279 #define PARAMS_ARRAY_SIZE 7
281 const char *keywords[PARAMS_ARRAY_SIZE];
282 const char *values[PARAMS_ARRAY_SIZE];
284 keywords[0] = "host";
285 values[0] = my_opts->hostname;
286 keywords[1] = "port";
287 values[1] = my_opts->port;
288 keywords[2] = "user";
289 values[2] = my_opts->username;
290 keywords[3] = "password";
291 values[3] = password;
292 keywords[4] = "dbname";
293 values[4] = my_opts->dbname;
294 keywords[5] = "fallback_application_name";
295 values[5] = my_opts->progname;
300 conn = PQconnectdbParams(keywords, values, true);
304 fprintf(stderr, "%s: could not connect to database %s\n",
305 "oid2name", my_opts->dbname);
309 if (PQstatus(conn) == CONNECTION_BAD &&
310 PQconnectionNeedsPassword(conn) &&
314 password = simple_prompt("Password: ", 100, false);
322 /* check to see that the backend connection was successfully made */
323 if (PQstatus(conn) == CONNECTION_BAD)
325 fprintf(stderr, "%s: could not connect to database %s: %s",
326 "oid2name", my_opts->dbname, PQerrorMessage(conn));
331 /* return the conn if good */
336 * Actual code to make call to the database and print the output data.
339 sql_exec(PGconn *conn, const char *todo, bool quiet)
352 res = PQexec(conn, todo);
354 /* check and deal with errors */
355 if (!res || PQresultStatus(res) > 2)
357 fprintf(stderr, "oid2name: query failed: %s\n", PQerrorMessage(conn));
358 fprintf(stderr, "oid2name: query was: %s\n", todo);
365 /* get the number of fields */
366 nrows = PQntuples(res);
367 nfields = PQnfields(res);
369 /* for each field, get the needed width */
370 length = (int *) pg_malloc(sizeof(int) * nfields);
371 for (j = 0; j < nfields; j++)
372 length[j] = strlen(PQfname(res, j));
374 for (i = 0; i < nrows; i++)
376 for (j = 0; j < nfields; j++)
378 l = strlen(PQgetvalue(res, i, j));
380 length[j] = strlen(PQgetvalue(res, i, j));
387 for (j = 0, l = 0; j < nfields; j++)
389 fprintf(stdout, "%*s", length[j] + 2, PQfname(res, j));
392 fprintf(stdout, "\n");
393 pad = (char *) pg_malloc(l + 1);
396 fprintf(stdout, "%s\n", pad);
400 /* for each row, dump the information */
401 for (i = 0; i < nrows; i++)
403 for (j = 0; j < nfields; j++)
404 fprintf(stdout, "%*s", length[j] + 2, PQgetvalue(res, i, j));
405 fprintf(stdout, "\n");
416 * Dump all databases. There are no system objects to worry about.
419 sql_exec_dumpalldbs(PGconn *conn, struct options * opts)
423 /* get the oid and database name from the system pg_database table */
424 snprintf(todo, sizeof(todo),
425 "SELECT d.oid AS \"Oid\", datname AS \"Database Name\", "
426 "spcname AS \"Tablespace\" FROM pg_catalog.pg_database d JOIN pg_catalog.pg_tablespace t ON "
427 "(dattablespace = t.oid) ORDER BY 2");
429 sql_exec(conn, todo, opts->quiet);
433 * Dump all tables, indexes and sequences in the current database.
436 sql_exec_dumpalltables(PGconn *conn, struct options * opts)
439 char *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
441 snprintf(todo, sizeof(todo),
442 "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s "
444 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
445 " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),"
446 " pg_catalog.pg_tablespace t "
447 "WHERE relkind IN ('r'%s%s) AND "
450 " WHEN reltablespace <> 0 THEN reltablespace"
451 " ELSE dattablespace"
454 opts->extended ? addfields : "",
455 opts->indexes ? ", 'i', 'S'" : "",
456 opts->systables ? ", 't'" : "",
457 opts->systables ? "" : "n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname !~ '^pg_toast' AND");
459 sql_exec(conn, todo, opts->quiet);
463 * Show oid, filenode, name, schema and tablespace for each of the
464 * given objects in the current database.
467 sql_exec_searchtables(PGconn *conn, struct options * opts)
475 bool written = false;
476 char *addfields = ",c.oid AS \"Oid\", nspname AS \"Schema\", spcname as \"Tablespace\" ";
478 /* get tables qualifiers, whether names, filenodes, or OIDs */
479 comma_oids = get_comma_elts(opts->oids);
480 comma_tables = get_comma_elts(opts->tables);
481 comma_filenodes = get_comma_elts(opts->filenodes);
483 /* 80 extra chars for SQL expression */
484 qualifiers = (char *) pg_malloc(strlen(comma_oids) + strlen(comma_tables) +
485 strlen(comma_filenodes) + 80);
488 if (opts->oids->num > 0)
490 ptr += sprintf(ptr, "c.oid IN (%s)", comma_oids);
493 if (opts->filenodes->num > 0)
496 ptr += sprintf(ptr, " OR ");
497 ptr += sprintf(ptr, "pg_catalog.pg_relation_filenode(c.oid) IN (%s)", comma_filenodes);
500 if (opts->tables->num > 0)
503 ptr += sprintf(ptr, " OR ");
504 sprintf(ptr, "c.relname ~~ ANY (ARRAY[%s])", comma_tables);
508 free(comma_filenodes);
510 /* now build the query */
511 todo = (char *) pg_malloc(650 + strlen(qualifiers));
512 snprintf(todo, 650 + strlen(qualifiers),
513 "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s\n"
514 "FROM pg_catalog.pg_class c \n"
515 " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \n"
516 " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),\n"
517 " pg_catalog.pg_tablespace t \n"
518 "WHERE relkind IN ('r', 'i', 'S', 't') AND \n"
520 " WHEN reltablespace <> 0 THEN reltablespace\n"
521 " ELSE dattablespace\n"
524 "ORDER BY relname\n",
525 opts->extended ? addfields : "",
530 sql_exec(conn, todo, opts->quiet);
534 sql_exec_dumpalltbspc(PGconn *conn, struct options * opts)
538 snprintf(todo, sizeof(todo),
539 "SELECT oid AS \"Oid\", spcname as \"Tablespace Name\"\n"
540 "FROM pg_catalog.pg_tablespace");
542 sql_exec(conn, todo, opts->quiet);
546 main(int argc, char **argv)
548 struct options *my_opts;
551 my_opts = (struct options *) pg_malloc(sizeof(struct options));
553 my_opts->oids = (eary *) pg_malloc(sizeof(eary));
554 my_opts->tables = (eary *) pg_malloc(sizeof(eary));
555 my_opts->filenodes = (eary *) pg_malloc(sizeof(eary));
557 my_opts->oids->num = my_opts->oids->alloc = 0;
558 my_opts->tables->num = my_opts->tables->alloc = 0;
559 my_opts->filenodes->num = my_opts->filenodes->alloc = 0;
562 get_opts(argc, argv, my_opts);
564 if (my_opts->dbname == NULL)
566 my_opts->dbname = "postgres";
567 my_opts->nodb = true;
569 pgconn = sql_conn(my_opts);
571 /* display only tablespaces */
572 if (my_opts->tablespaces)
575 printf("All tablespaces:\n");
576 sql_exec_dumpalltbspc(pgconn, my_opts);
582 /* display the given elements in the database */
583 if (my_opts->oids->num > 0 ||
584 my_opts->tables->num > 0 ||
585 my_opts->filenodes->num > 0)
588 printf("From database \"%s\":\n", my_opts->dbname);
589 sql_exec_searchtables(pgconn, my_opts);
595 /* no elements given; dump the given database */
596 if (my_opts->dbname && !my_opts->nodb)
599 printf("From database \"%s\":\n", my_opts->dbname);
600 sql_exec_dumpalltables(pgconn, my_opts);
606 /* no database either; dump all databases */
608 printf("All databases:\n");
609 sql_exec_dumpalldbs(pgconn, my_opts);